Skip to content

Translations

Localize BDEngine plugins with registerTranslations, namespaces, language dictionaries, gT, fallback text, and language switching.

A plugin registers its strings with editorAPI.registerTranslations() and reads them through the global gT() function. Translations extend the editor’s dictionaries for the current session. They are not loaded from the catalog automatically or saved in the project.

registerTranslations

const result = editorAPI.registerTranslations(namespace, translations = {});

The method is synchronous. namespace is a nonempty string shared by your plugin’s keys. Use the same unique identifier as in the plugin’s metadata. translations is an object whose top-level keys specify languages and whose values are flat string dictionaries.

const result = editorAPI.registerTranslations('sample-panel', {
'ru-RU': {
title: 'Панель инструментов',
open: 'Открыть панель',
},
'en-US': {
title: 'Tool panel',
open: 'Open panel',
},
});

The supported editor languages are en-US, ru-RU, zh-CN, zh-TW, ko-KR, and de-DE. Codes are normalized: for example, en and ru are accepted as en-US and ru-RU. Prefer full codes in documentation and plugins. An unsupported language is skipped with a warning.

Dictionary values must be strings. Nested objects, arrays, and numbers are skipped. To group keys, use fully qualified string keys rather than nested JSON objects.

Return value:

{ ok: true, added: 4, conflicts: 0 }
Field Meaning
ok false if the namespace or translations object is invalid. If the outer structure is valid, returns true even when individual languages or entries are skipped.
added Number of accepted entries across all languages. One key in two languages counts as two entries. Rewriting your own key also counts.
conflicts Number of attempts to use a core key or a key already owned by another namespace. Those entries are not added.

Invalid languages and values do not increase conflicts. Therefore, ok: true does not mean that every supplied string was accepted: check the counters and console messages during development.

Simple and fully qualified keys

If a key contains no dot, the API prefixes it with namespace.. If it contains at least one dot, the key is used as is, without an additional prefix.

Namespace Dictionary entry Key for gT()
sample-panel title sample-panel.title
sample-panel sample-panel.window.title sample-panel.window.title
sample-panel window.title window.title

The last form is technically valid but can easily conflict with other code. For names that represent nested concepts, include the plugin’s full prefix: sample-panel.window.title.

The editor prevents overwriting occupied core keys and keys owned by another namespace. Registering a key again under the same namespace updates its value. This lets you reapply dictionaries during development, but each plugin must use a unique namespace. Registering again does not remove keys that you leave out; there is no public dictionary-removal method.

gT and fallback text

const text = gT(key, fallback = key, options = {});

gT is available on window, separately from editorAPI. gT(...) and window.gT(...) are equivalent. Use the full key, including the namespace prefix.

Lookup first uses the editor’s language, then English en-US. If no translation is found and the lookup result equals the key itself, gT returns the supplied fallback. Without fallback text, the result is the key.

const title = gT('sample-panel.title', 'Tool panel');

The third argument is passed to the localization library. It can substitute values into a registered string:

editorAPI.registerTranslations('sample-panel', {
'ru-RU': { selected: 'Выбрано объектов: {{count}}' },
'en-US': { selected: 'Selected objects: {{count}}' },
});
const count = 3;
const text = gT(
'sample-panel.selected',
`Selected objects: ${count}`,
{ count },
);

Fallback text is returned directly: its placeholders are not processed separately. That is why the example’s fallback already includes the value of count. For ordinary labels, assign the result to textContent; using a translation does not require HTML markup.

Language and interface updates

Changing the language through the editor’s settings saves the selected language and reloads the editor. An installed plugin runs again, registers its dictionaries, and builds its interface in the new language.

However, gT() returns an ordinary value at the time of the call. An existing button with previously assigned text does not update automatically when strings are registered again. If you change a dictionary during the current session, call gT() again and update the relevant DOM elements or recreate your window.

registerTranslations() does not make network requests for translations. Include dictionaries in the plugin’s JavaScript, or fetch the data yourself before registration. Register strings before creating interface labels. This mechanism has no public bde:language-changed event.

Minimal example

The example registers two locales and adds a toolbar button. Clicking it prints a localized string to the console:

(() => {
function init() {
window.editorAPI.registerTranslations('sample-i18n', {
'ru-RU': { message: 'Плагин готов к работе' },
'en-US': { message: 'The plugin is ready' },
});
window.editorAPI.addButtonTool('icon-languages', () => {
console.log(gT('sample-i18n.message', 'The plugin is ready'));
});
}
if (window.editorAPI?.headPaint) init();
else window.addEventListener('bde:started', init, { once: true });
})();

For a complete example with a localized button label and window, see Localize a plugin interface.

Put it into practice