DataMind Oda resolves all of its interface text through one language registry. The interface runs in English today and there is no Language row in Settings; a language pack adds a language to the registry and registers the text the interface shows in that language. This page covers the calls a pack makes, the rules they must pass and the messages they return. Language in Oda covers what a user can change today.
en, the label is English, and it is the fallback language: every fallback chain ends at en.addLanguage adds the definition, made of an id, a label and a fallback. register adds dictionary text for one namespace and one language at a time.lang attribute), updating the mark only when the language changes.common namespace carries interface vocabulary a pack can translate: OK, Cancel, Close, Copy, Copied, Retry, Loading…, Save, Search, Expand, Back, Unknown, None, Truncated, JSON, plus the compact number formats {value}K and {value}M.en. The call returns a disposer that removes the definition.The two calls may run in either order: definitions and dictionaries are independent contributions, and a saved choice for a language that is not registered yet waits until its definition arrives.
export const inject = ['locale']
export function apply(ctx) {
ctx.effect(
() => ctx.locale.addLanguage({ id: 'ja', label: '日本語', fallback: 'en' }),
'language pack: language',
)
ctx.effect(
() => ctx.locale.register('common', 'ja', { cancel: 'キャンセル', close: '閉じる' }),
'language pack: dictionary',
)
}en or pt-BR. Otherwise: locale id "<id>" is not a BCP 47-style tag.locale "<id>" is not offered.locale label must not be empty.locale fallback "<x>" is not a BCP 47-style tag.locale fallback "<x>" is not registered.locale "<id>" fallback chain does not reach "en"; a loop with locale fallback cycle includes "<id>".locale "<id>" is already registered.common, and finally the key text itself is shown.{name} placeholder is replaced by the parameter you pass under that name; a placeholder you leave unfilled stays in the text as written.bind returns the same translate function for a namespace on every call, so it is safe to keep and reuse; it reads the running language each time it is called.setLocale is the only write entry. It takes a registered language id; an unknown one is refused: locale "<id>" is not registered.en and pt-BR are accepted, bad locale and 123 are refused.locale subscriber crashed:) and the others still receive the change.locale: invalid native initialization data.A definition and text. The definition carries an id, a label and a fallback; the text arrives as dictionaries, registered one namespace and one language at a time. Both calls return disposers, so the pack unloads cleanly.
The definition must pass every rule above: a tag-shaped id, a tag Oda offers, a non-blank label, a registered fallback whose chain reaches en without a loop, and an id that is not already registered. The refusal message names the rule, and a refused definition leaves the registry unchanged.
Either order works: definitions and dictionaries are independent contributions. A saved choice for a language that is not registered yet waits unchanged and is adopted as soon as its definition registers.
Yes. Register the common namespace for your language, as the example above does, and the keys you supply resolve to your text; keys you leave out fall through to English.
The lookup walks the running language's fallback chain in the namespace you registered, then repeats that walk in common, and, when nothing else matches, shows the key text itself. Nothing breaks when a translation is missing; the fallback answers.
Dictionary lookups read the running language every time, but copy captured while registering, such as a command's description, keeps the language it was registered under until that part registers it again. Slot-rendered copy updates without a reload.
A template can carry {name} placeholders; each one is replaced by the parameter you pass under that name, and a placeholder you leave unfilled stays in the text as written.
Yes. bind returns the same function for a namespace on every call, so it is safe to keep and reuse, and it reads the running language when it is called, so it follows switches.
The second call is refused with locale namespace "<ns>" already has locale "<locale>": a namespace keeps one owner per language. To replace it, unload the first registration with its disposer and register again.
Yes. Every call returns a disposer and disposing is idempotent. Unloading a language the interface is running returns it to English; a saved choice for that language is kept, so registering the definition again brings it back.
On the machine that runs Oda, the choice is kept in Oda's settings; on an interface reached from another machine it lasts only for that page's session. setLocale is the only call that writes: it saves even when it names the language already running, and a saved value must look like a language tag (en and pt-BR pass; bad locale and 123 are refused).