l10n.mjs · locales.json · Components
Small library for reading translated strings and subscribing
components to language changes. On module load it fetches
/assets/locales.json and negotiates the initial locale:
the persisted choice (localStorage['lang']) → the first
supported navigator.languages entry → 'en'.
If the catalog can't be loaded it falls back to an empty one and
t() returns raw keys.
The catalog fetch is not awaited at module scope, because a top-level await would delay every importing module and its customElements.define() call. Import t normally; it returns the key until the catalog is available. Components use l10n_register_listener to render again when the catalog loads or the locale changes. A page that needs translations before showing its content can await L10N.readyP.
| Export | Signature | Purpose |
|---|---|---|
L10N |
{ready, readyP, t, l10n_plural, …} |
Access the module through one object: await L10N.readyP reads like the ready / readyP pair every component exposes. ready is the boolean, readyP the promise — never await the boolean, because await false does not wait for readiness. The utility functions below are on it as well as exported flat: rendering code can use import {t}, startup code can use import {L10N}. |
set_locale |
(tag, {persist?}) => void |
Validates via Intl.Locale, falls back to 'en', sets <html lang>, notifies all listeners. Pass {persist: true} (a user choice) to also write localStorage['lang']. |
t |
(key, params?) => string |
Looks up the current language, falls back to English, then to the key itself. {name} placeholders are replaced from params. |
l10n_plural |
(key, count, params?) => string |
Count-aware lookup. The catalog value is a {one, other} object; the form is chosen with Intl.PluralRules and {count} is available for interpolation alongside params. |
l10n_ready |
Promise<void> |
The older flat spelling of L10N.readyP, and the same promise object. Settles when the catalog has been installed, or has failed and left the empty one in place. Never rejects. Await it from a page's boot script; never at the top level of a module others import — that reintroduces the suspension the note above describes. Every awaiting site still reads this name; write new code against L10N.readyP. |
apply_static_i18n |
(root?) => Promise<void> |
Translates declarative static markup: data-i18n="key" sets textContent; data-i18n-attr="attr:key;…" sets attributes. Idempotent — safe to re-run on locale change. Awaits L10N.readyP first, so tagged markup keeps its authored text until there is a translation to replace it with. |
get_locale |
() => Intl.Locale |
The currently selected locale object. |
supported_locales |
() => string[] |
Base language tags the catalog carries (e.g. ['en', 'sv']). |
l10n_register_listener |
(el) => void |
Calls el._on_lang?.() once now, then on every locale change and when the catalog arrives — so a component connected before catalog readiness replaces raw keys with translations. Use in connectedCallback. |
l10n_unregister_listener |
(el) => void |
Stops reactivity. Pair with the above in disconnectedCallback. |
A <select> populated from the top-level keys of
/assets/locales.json. Changing it calls
set_locale(value) — every registered listener on the
page fires synchronously, so the demos below react immediately.
_on_lang()
A component registers itself with
l10n_register_listener in
connectedCallback, implements _on_lang(),
and unregisters in disconnectedCallback. Below are two
instances with different name attributes — switching
the picker above updates each instance independently. This also
shows {name} parameter substitution via t().
Components that call l10n_register_listener internally
need no extra wiring. Hover the copy button on the
ui-code below to see its title toggle
between Copy and Kopiera. Open the dialog to see
the Cancel / OK footer labels follow the picker — the dialog is
constructed via response_dialog, which attaches its
own _on_lang hook for the default buttons.