Internationalization
Supported locales
The main frontend has exactly eight locale files under static/locales/:
| Code | File |
|---|---|
zh-CN | zh-CN.json |
zh-TW | zh-TW.json |
en | en.json |
ja | ja.json |
ko | ko.json |
ru | ru.json |
es | es.json |
pt | pt.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:
- the backend
uiLanguageoverride returned byGET /api/config/steam_language; ?ui_lang=or?lang=in the URL;- the normalized Steam language returned by the same endpoint;
localStorage.i18nextLng;navigator.language;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:
<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.tsand the host'swindow.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:
- add or update the same nested key in all eight
static/locales/*.jsonfiles; - use the appropriate
data-i18n-*attribute orwindow.t()call; - preserve interpolation names across every locale;
- check the page after a runtime language switch, not only after reload;
- rebuild React or Vue artifacts when their source locale files changed;
- validate that all locale files parse and have the same leaf-key set.
