Skip to content

Localization

Fighter90 edited this page Jul 9, 2026 · 6 revisions

Localization

The UI ships in 16 locales: en, es, pt-BR, ko, ja, ru, zh-CN, zh-TW, fr, pl, uk, da, ar, de, it, tr. No build step, no fetch — everything loads via <script src>.

Architecture (per-locale split, I18N-SPLIT v1.60.0)

  • One dict file per locale: public/js/lib/locales/i18n-dict.<lang>.js, each defining window.__I18N_DICT_<LANG> = { key: string }, plus i18n-dict.aliases.js.
  • Assembler: public/js/lib/i18n-dict.js merges them into window.__I18N_DICT (its LANGS/TABLES lists enumerate the locales).
  • Runtime: public/js/lib/i18n.js — locale detection (LANGS + detect()), the t() lookup, and the data-i18n DOM walker. The language picker is a flag <select> rendered by renderLangSwitcher in app.js.
  • Node tests and tooling load the dictionaries through tests/helpers/i18n-vm.mjs (I18N_LANGS).

RTL (Arabic)

i18n.js sets <html dir="rtl"> for locales in RTL_LANGS; the [dir="rtl"] block in public/css/app.css mirrors the chrome (sidebar, drawers, the docs FAB moves bottom-left, the usage HUD bottom-right). LTR locales are byte-for-byte unchanged by RTL work.

Adding a translation key

  1. Add the key to all 16 locale files. Parity is gated by tests/i18n-locale-files.test.mjs + tests/i18n-coverage.test.mjs.
  2. Regenerate the snapshot tests/fixtures/i18n-dict.snapshot.json.
  3. Run node tools/i18n-audit.mjs (also part of npm run test:ci).

Fan-out hazard: parallel translation fan-outs have silently produced duplicated key-blocks and English glosses that the parity gate is blind to — grep each dict for dup keys and stray English after every fan-out. Note that grep may treat emoji-heavy dict files as binary (use grep -a or rg).

Adding a locale

Touches, in one PR: the new locale file · the assembler's LANGS/TABLES · i18n.js LANGS + detect() · the <script> order in public/index.html · tests/helpers/i18n-vm.mjs I18N_LANGS · tools/i18n-audit.mjs · scripts/check-changelog-parity.mjs · then the snapshot. If the locale is RTL, also RTL_LANGS + CSS mirroring.

Help bundles (28 H2 / 103 H3)

The in-app help guide lives at docs/help/<lang>.md, fully translated in all 16 locales and served by GET /api/help/:lang (fallback to en). Structure parity is test-gated:

  • 28 ## sections / 103 ### subsections as of v1.118.0 (§26 "Lifetime & compensation" added the 103rd H3).
  • Gates: tests/canonical-docs-coverage.test.mjs, tests/help-ru-config-section.test.mjs, tests/help-ui.test.mjs. Adding an H2 means bumping the count in canonical-docs-coverage + help-ui; adding an H3 means bumping help-ru-config-section.
  • The grounded docs assistant (POST /api/docs-assistant/ask) answers from these bundles only — help quality is product quality.

×16 documentation parity

Every release fans out user-facing docs to all locales:

  • CHANGELOG ×16CHANGELOG.md + 15 translated clones, gated by scripts/check-changelog-parity.mjs ("all 15 locales at v").
  • README ×16README.md + 15 clones (each localized README must point at its own ./images/dashboard-<locale>.png).
  • Help ×16 — every touched help section updated in all 16 bundles.

Language policy

The server speaks English (logs, API error strings); all user-facing translation happens in the SPA layer. The SPA injects lang on <html>.

Clone this wiki locally