i18n API: translate Blok's interface
Internationalization support for translating UI strings. The runtime i18n.update() mutator switches language in place.
The locale catalogue itself ships as a separate published entry point, @bloklabs/core/locales. Only English is bundled, and the other 68 locales load on demand.
Use normalizeLocale() as the pre-flight check for a locale you did not hard-code. i18n.update({ locale }) with an unsupported tag keeps the current locale and warns on the console instead of throwing.
Reaching the editor instance
The methods below run on the editor you created with new Blok(). They are available once editor.isReady resolves.
// You already hold the instance returned by the constructor.
const editor = new Blok({ holder: 'editor' });
await editor.isReady;
// Call any API method on it.
editor.caret.setToLastBlock('end');Methods
i18n.t(dictKey, vars?)
stringTranslate a key from the global dictionary, optionally interpolating string or number values.
When to use
Look up a UI string from the active dictionary inside a tool, so your tool localises with the editor.
const text = editor.i18n.t('toolNames.text');
console.log(text); // 'Text' (or translated string)
const limit = editor.i18n.t('tools.image.emptyMaxSize', { size: '10 MB' });
console.log(limit); // 'max 10 MB' (or translated string)i18n.has(dictKey)
booleanCheck if a translation exists for the given key.
When to use
Check a key exists before translating. That lets you fall back gracefully when a tool ships partial dictionaries.
if (editor.i18n.has('toolNames.text')) {
const translation = editor.i18n.t('toolNames.text');
}i18n.getEnglishTranslation(key)
stringGet the English translation for a key (used for multilingual search).
When to use
Returns the English value regardless of locale. Use it to index content for multilingual search.
const english = editor.i18n.getEnglishTranslation('toolNames.heading');
console.log(english); // 'Heading'i18n.getLocale()
stringGet the active locale code (e.g. 'en').
When to use
Returns the active locale code (e.g. 'en') so a tool can branch on the editor's current language.
const locale = editor.i18n.getLocale();
console.log(locale); // 'en'i18n.getDirection()
'ltr' | 'rtl'Get the text direction currently in effect. It is derived from the active locale, unless an explicit direction override was set. This lives on the editor instance only.
The api.i18n handed to tools carries just t, has, getEnglishTranslation and getLocale. So a tool must take the direction from the host, either its own config or the i18n:changed event, rather than calling this.
When to use
Returns 'ltr' or 'rtl' for the active locale, so the host UI around the editor can mirror itself alongside it.
if (editor.i18n.getDirection() === 'rtl') {
// mirror your own chrome next to the editor
}i18n.update({ locale?, messages?, direction? })
Promise<void>Switch language at runtime. config.i18n is otherwise read once during boot, so a host with a language switcher had to recreate the editor to relabel it. That threw away caret, focus, selection and undo history. update() relabels in place instead: no recreation, nothing lost.
localeaccepts any supported code, or'auto'to re-run browser detection.messagesmerges host overrides over the locale dictionary. It is re-applied automatically after every later locale change, so a bare locale flip never silently drops your custom strings.directionoverrides the direction implied by the locale, which you normally do not need.
Calls are serialized internally, so lazily-loaded locale chunks cannot land out of order. The last call wins.
The scope is everything.
- UI built on demand (block settings, the convert menu, notifications, screen-reader announcements) picks up the new locale the next time it opens.
- UI stamped up front (toolbar and plus-button labels, tooltips, the toolbox list) is relabelled immediately.
- Block content is repainted from your data: placeholders, media-toolbar labels, cell controls, anything a tool resolved while rendering. That includes tools that know nothing about locale changes.
The repaint is invisible to you. onChange does not fire, scroll is kept, and the caret returns to the block that had it.
Fires the i18n:changed event with { locale, direction }. It is available synchronously after construction, and a call made before isReady is applied once the editor has booted.
update() and getDirection() are exposed on the editor instance only, not on the api.i18n handed to tools, which carries just t, has, getEnglishTranslation and getLocale. So a third-party tool cannot flip the host's locale.
The React, Vue and Angular adapters drive it reactively: change the i18n prop or input and the editor follows in place.
Note that defaultLocale is not accepted. It only decides the fallback while resolving the initial locale.
When to use
Switches locale and host message overrides at runtime, relabelling the editor in place. A language switcher no longer has to recreate the editor and throw away caret, focus and undo history.
// Host language switcher — no remount, caret and undo survive.
await editor.i18n.update({ locale: 'ru' });
// Locale plus your own overrides on top of it.
await editor.i18n.update({
locale: 'fr',
messages: { 'toolNames.text': 'Paragraphe' },
});
// Follow the browser again.
await editor.i18n.update({ locale: 'auto' });
editor.events.on('i18n:changed', ({ locale, direction }) => {
document.documentElement.dir = direction;
});normalizeLocale(tag)
SupportedLocale | nullFrom @bloklabs/core/locales. Normalizes an arbitrary BCP-47 language tag to a supported Blok locale, and returns null when the tag is unsupported. The tag can be region-tagged ('en-US'), script-tagged ('zh-Hant') or aliased ('nb' to 'no', 'ckb' to 'ku').
The same normalizer runs on browser detection and on explicit config.i18n.locale or i18n.update({ locale }). So a null here is exactly the tag that update() would refuse: it keeps the current locale and warns on the console rather than throwing.
import { normalizeLocale } from '@bloklabs/core/locales';
// 'en-US' -> 'en'; null when the tag is not supported
const code = normalizeLocale(navigator.language);
if (code !== null) {
await editor.i18n.update({ locale: code });
}loadLocale(code)
Promise<LocaleConfig>From @bloklabs/core/locales. Loads one locale on demand. Only English is bundled. The other 68 are fetched when asked for.
import { loadLocale } from '@bloklabs/core/locales';
const fr = await loadLocale('fr');preloadLocales(codes)
Promise<void>From @bloklabs/core/locales. Loads several locales up front, for example the ones your language switcher offers. A later switch then does not wait on a fetch.
import { preloadLocales } from '@bloklabs/core/locales';
await preloadLocales(['fr', 'de', 'ru']);buildRegistry(codes)
Promise<LocaleRegistry>From @bloklabs/core/locales. Loads the given codes and returns them together as a LocaleRegistry.
import { buildRegistry } from '@bloklabs/core/locales';
const registry = await buildRegistry(['en', 'ru']);getLocaleSync(code)
LocaleConfig | undefinedFrom @bloklabs/core/locales. Returns an already-loaded locale synchronously, or undefined when it has not been loaded yet.
import { getLocaleSync, loadLocale } from '@bloklabs/core/locales';
const ru = getLocaleSync('ru') ?? await loadLocale('ru');getDirection(code)
'ltr' | 'rtl'From @bloklabs/core/locales. Text direction for a locale CODE. This is not the same function as editor.i18n.getDirection(), which takes no argument and reports the direction the mounted editor is currently using.
import { getDirection } from '@bloklabs/core/locales';
document.documentElement.dir = getDirection('ar'); // 'rtl'Properties
| Property | Type | Description |
|---|---|---|
DEFAULT_LOCALE | SupportedLocale | From |
ALL_LOCALE_CODES | readonly SupportedLocale[] | From |
enLocale | LocaleConfig | From |