Resource Files¶
In a nutshell
Skills keep their words in separate text files rather than buried in the program code. These "resource files" hold things like the phrases the assistant can say, the example sentences it listens for, and the keywords it recognizes. Each language gets its own folder. This separation makes a skill easy to translate and tweak without touching the code. This page explains the folder layout and the kinds of files. See also Statements for spoken replies and the Glossary.
Formal specification
The locale folder layout and the plain-text resource formats are specified by OVOS-INTENT-2 — Locale Resource Formats (a formal architecture spec). The template grammar inside them is OVOS-INTENT-1. OVOS-INTENT-2 defines six canonical roles by extension: .intent and .dialog (slot-bearing, they may use {name} slots), .entity, .voc, and .blacklist (slot-free, expansion only), and .prompt (a whole-file language-model prompt with {{name}} substitution, not a template). Resources live under locale/<lang>/ (BCP-47 tags, compared case-insensitively, searched recursively), resolved user, then skill, then core (§2.1). The .rx, .list, and .word files below are framework extensions, not OVOS-INTENT-2 roles. Prefer .entity/.voc/.blacklist for portability.
Skills load localized resources from a structured directory layout. Resources are loaded automatically at startup for every language in native_langs (core_lang + secondary_langs).
.blacklist is a spec-defined role under OVOS-INTENT-2, and ovos-workshop loads it
automatically since 9.1.0a1: a sibling <name>.blacklist file (matching an intent,
slot, or entity base name) is merged into the registration blacklist with no skill code
needed (SkillResources.load_blacklist_file). The voc_blacklist= argument on
@intent_handler is a different, unrelated mechanism. It takes .voc filenames, not
.blacklist filenames.
A blacklist phrase matches as a contiguous sequence of whole words, not a raw substring
(OVOS-INTENT-2 §4.3): a .blacklist entry itinerary blocks "spell itinerary" but not
"spell it". A .blacklist paired with an .intent base name suppresses that intent
outright, a hard rejection independent of match confidence. Paired with an .entity or
{slot} base name instead, it excludes values from that slot: any component that writes a
value into the slot, including an intent transformer that re-extracts it from the raw
utterance after the match, must honor the exclusion. The common use is keeping an anaphoric
pronoun out of a referential slot, for example a person.blacklist of "he", "she", "they"
so {person} stays unresolved for "how tall is he" and a later stage can fill it from
context.
Directory Layout¶
The recommended layout uses a single locale/ directory:
my-skill/
├── locale/
│ ├── en-US/
│ │ ├── my.dialog # spoken responses
│ │ ├── my.intent # padatious intent examples
│ │ ├── my.voc # adapt vocabulary keywords
│ │ ├── my.entity # adapt entity examples
│ │ ├── my.rx # regex patterns for adapt
│ │ └── skill.json # skill metadata (examples for homescreen)
│ └── es-ES/
│ ├── my.dialog
│ └── my.intent
└── gui/
└── my_page.qml
Legacy skills may use separate dialog/, vocab/, regex/ subdirectories. These are still supported.
Resource Types¶
| Extension | Type | Description |
|---|---|---|
.dialog |
Dialog | Spoken responses, one template per line, random selection (OVOS-INTENT-2 role, slot-bearing) |
.intent |
Intent | Padatious training examples (OVOS-INTENT-2 role, slot-bearing) |
.voc |
Vocabulary | Adapt keyword definitions, one per line, first is canonical (OVOS-INTENT-2 role, slot-free) |
.entity |
Entity | Example values for a {slot} (OVOS-INTENT-2 role, slot-free) |
.blacklist |
Blacklist | Phrases that suppress a paired .intent (OVOS-INTENT-2 role, slot-free). Auto-loaded by ovos-workshop since 9.1.0a1 (load_blacklist_file). Unrelated to voc_blacklist= on @intent_handler, which takes .voc filenames. |
.prompt |
Prompt | A whole-file language-model prompt with {{name}} substitution (OVOS-INTENT-2 role) |
.rx |
Regex | Adapt regex patterns (framework extension, not an OVOS-INTENT-2 role) |
.list |
List | A flat list resource (framework extension) |
.word |
Word | A single word (framework extension) |
skill.json |
Metadata | {"examples": ["...", "..."]} for homescreen example utterances (framework extension) |
Dialog Files¶
Each line in a .dialog file is a possible response. One line is chosen randomly when speak_dialog is called:
Mustache template variables are filled from the data dict:
Vocab Files (Adapt)¶
Each line is registered as its own keyword, and every line matches the same entity:
Parenthesized alternation expands within a single line: (hello|hi|hey) becomes several forms
that share one canonical value, the first, with the rest registered as its aliases. That
canonical/alias relationship exists only inside the line that produced it. Separate plain lines
are independent keywords with no aliasing between them, so write alternatives on one line if you
want them collapsed into a single canonical form plus aliases:
A bare a|b is not alternation
The parentheses are required. A | outside a (...) group is not split at all. It is
kept as a literal character in a single keyword line, almost never what you want. Either
wrap it, (a|b), or drop the | and put each alternative on its own line.
The Adapt entity name is the file name without its extension (hello for hello.voc). Reference it in an IntentBuilder with .require("hello") / .optionally("hello"). Internally the keyword is namespaced as alphanumeric_skill_id + "hello" so skills never collide.
Intent Files (Padatious)¶
One example utterance per line. Supports entity slots {entity} and alternation (a | b):
Language Fallback¶
When a resource is not found for the exact lang, the skill falls back to dialects of the same language. For example, if en-AU is requested but only en-US resources exist, en-US is used.
Loading Resources Manually¶
# Get SkillResources for current lang
resources = self.resources # current self.lang
resources = self.load_lang(self.res_dir, "es-ES") # specific lang
# Find a specific file
path = self.find_resource("my.dialog", "dialog")
path = self.find_resource("hello.mp3", "snd")
SkillResources API¶
SkillResources is returned by self.resources and self.load_lang():
# Render a dialog (returns a string, does not speak)
text = self.resources.render_dialog("my.dialog", data={"key": "value"})
# Check if a vocab word matches
matches = self.voc_match("hello there", "hello") # True
# Load a vocab file into a list
words = self.resources.load_vocabulary_file("my.voc")
# Load a dialog renderer
renderer = self.dialog_renderer
skill.json Metadata¶
Optional file for homescreen integration. Placed at locale/<lang>/skill.json:
{
"name": "My Skill",
"description": "Does something useful",
"examples": [
"what is the weather",
"tell me the weather in Paris"
]
}
These examples are emitted to the homescreen as homescreen.register.examples on skill startup.
Source code: OpenVoiceOS/ovos-workshop.
Read next: GUI Skills (GUIInterface) Related: Filesystem Access · SSMLBuilder · Customization · Customizing Language Resources