# Supporting Multiple Languages in the Web UI The firmware uses a client-side i18n approach: all translations are stored in one JavaScript dictionary embedded in the firmware. No server-side changes are needed. ## Architecture All translation logic lives at the top of `html/app.js`: - A `LANG` object with one sub-object per language (`en`, `ja`, `zh`) - Language auto-detection (browser language, overridden by the `rtl_lang` key in `localStorage`, which the language selector in the System section writes) - `t(key, vars)` looks up a translated string; `{name}` placeholders in the string are replaced from `vars`, e.g. `t("v_del_q", {n: vid})` - `i18nApply()` applies the dictionary to the static markup once at load Translation keys are flat strings. The English entry is the fallback when a key is missing in another language, and the key itself is the fallback when it is missing everywhere. Changing the language stores the choice and reloads the page, so every dynamically built table is rebuilt in the new language. `login.html` is served before authentication and does not load `app.js`; it carries its own five strings per language in an inline table. ## How to Add a New Language ### 1. Add a dictionary entry in `html/app.js` Append a sub-object to `LANG`. Every key from `LANG.en` should be present: ```js var LANG={ en:{ nav_dash:"Dashboard", ... }, LANGCODE:{ // add your language here nav_dash:"...", ... } }; ``` ### 2. Add the language to the selector in `html/index.html` ```html ``` ### 3. Add the login strings in `html/login.html` The inline table there holds, per language: page title, subtitle, password label, button label, wrong-password message. ### 4. Verify auto-detection The detection normalises `navigator.language` to its first two characters and selects that language if `LANG` has it, otherwise English. ## Two Translation Mechanisms ### (A) `data-i18n` attributes (declarative, for HTML) ```html