Skip to content

Internationalization

Supported locales

The main frontend has exactly eight locale files under static/locales/:

CodeFile
zh-CNzh-CN.json
zh-TWzh-TW.json
enen.json
jaja.json
koko.json
ruru.json
eses.json
ptpt.json

All eight files must be updated in the same change. They are nested JSON objects addressed through i18next dot paths, not flat key-value files.

Runtime and language selection

static/i18n-i18next.js loads the local i18next libraries and then fetches /static/locales/.json with an asset version. The fallback locale is zh-CN.

The initial language is selected in this order:

  1. the backend uiLanguage override returned by GET /api/config/steam_language;
  2. ?ui_lang= or ?lang= in the URL;
  3. the normalized Steam language returned by the same endpoint;
  4. localStorage.i18nextLng;
  5. navigator.language;
  6. zh-CN.

Changing language stores i18nextLng, updates the document language, refreshes translated DOM nodes, and dispatches localechange.

Markup and script APIs

Use the attribute that matches the destination:

html
<span data-i18n="settings.title"></span>
<input data-i18n-placeholder="chat.textInputPlaceholder">
<button data-i18n-title="common.close" data-i18n-aria="common.close"></button>
<img data-i18n-alt="avatar.previewAlt" alt="">

data-i18n-params contains JSON interpolation parameters; data-i18n-options remains supported for older markup. Put translated text in a child span when a button also contains icons or other structured content.

Classic scripts can call window.t(key, params). The loader also exposes window.i18n, window.changeLanguage, window.updatePageTexts, window.updateLive2DDynamicTexts, and window.translateStatusMessage.

Ordinary translations are assigned with textContent. Only translations containing supported <br> or <img> markup take the sanitized HTML path; arbitrary translation HTML is not allowed.

Frontend boundaries

  • The React chat source uses frontend/react-neko-chat/src/i18n.ts and the host's window.t, with English copy as a last-resort fallback. Add the main locale keys whenever the UI exposes new text.
  • The Vue plugin manager owns a separate TypeScript locale bundle under frontend/plugin-manager/src/i18n/locales/. Keep its eight language variants aligned, but do not expect main-page JSON keys to appear automatically there.
  • Backend prompts and runtime messages use their own translation structures. A main frontend locale change does not translate server text by itself.

Review checklist

When changing user-visible text:

  1. add or update the same nested key in all eight static/locales/*.json files;
  2. use the appropriate data-i18n-* attribute or window.t() call;
  3. preserve interpolation names across every locale;
  4. check the page after a runtime language switch, not only after reload;
  5. rebuild React or Vue artifacts when their source locale files changed;
  6. validate that all locale files parse and have the same leaf-key set.