Pull requests / #1427

#1427 web: Russian UI localization (dictionary + header language picker)

open · @Astemiir · 0 comentarios · En GitHub

Server & API

Descripción

## Title

web: Russian UI localization (dictionary + header language picker)

## Summary

Adds a Russian localization of the web UI. It is deliberately **non-invasive**: the interface
code keeps its English strings, and a self-contained script translates the visible chrome from a
flat JSON dictionary, so the patch does not touch app logic, needs no build step and adds no
dependencies. English stays the default — the language only changes when the user picks it.

## What changed

* **new `serve/web/i18n.js`** — the translator. Walks the DOM, replaces exact dictionary matches and
  keeps a `MutationObserver` on the body, so text the app renders later (metrics, status labels,
  toasts) is translated in the observer's microtask — before the frame is painted, otherwise the
  English word flashes first. Strings with numbers (1 token / 2 tokens / 5 tokens) go through a
  small rule table, since a flat dictionary cannot express Russian plurals. Model answers, code
  blocks and attachments are never touched: inside `#chat` only the chrome listed in `CHROME_SEL`
  is translated.
* **new `serve/web/i18n/ru.json`** — 317 flat `"english": "russian"` pairs. The reverse direction is
  built by inversion, so one file works both ways; adding a language is another `<lang>.json` plus
  one entry in `LANGS`.
* **new `serve/web/i18n.css`** — the picker button and a fixed-width slot for the status pill. The
  header is right-aligned and the pill changes width (`Idle` → `Reading prompt · 63 %`), so a button
  placed next to it kept sliding around; the invisible slot keeps the button in place while the pill
  still hugs its own text.
* **`serve/web/index.html`** — the stylesheet link, the `En`/`Ru` button left of the status pill,
  the pill slot, and `i18n.js` before `app.js` (so the static header is translated before the app
  renders and before the first paint).
* **`serve/web/monitor.html`** — the same script and a picker in the monitor window's own header,
  so the standalone monitor is translated as well.

Dictionary directory is resolved from the script's own URL, so the same file works from `/` and
from `web/monitor.html`. The choice is remembered in `localStorage` under `strata.lang` (same
convention as `strata.theme`), and `document.documentElement.lang` follows it.

**Coverage.** Checked by extracting every string the pages and their scripts can show: `index.html`
67/81, `monitor.html` 40/53, `app.js` 126/126, `monitor.js` 18/18. What is intentionally left in
English: the brand name, the sample URLs, the dash placeholders and the sampler labels `Top-p`,
`Top-k`, `Seed` (parameter names).

## Extra Notes

* Unknown text simply stays English, so a missing pair never breaks a page. A dictionary that fails
  to load falls back to English and re-labels the button.
* Known limitation: a few long sentences in the About card are split across inline tags (`<b>`,
  `<code>`), so their fragments would need separate pairs. If you prefer, I can switch those few to
  `data-i18n` keys instead of translating text nodes — your call.
* Verified locally: the patch applies to a fresh copy of `main` (git apply, LF, byte-identical to the
  tree it was built from), and `i18n.js` was run against a small DOM harness (19 assertions:
  dictionary loading, translation, chat-chrome rule, observer path, language switch and back).
* Happy to rename files, move the dictionary, or fold the CSS into `app.css` — whatever fits your
  layout. A screenshot of the Russian UI can be attached if useful.

En el sitio

Enlaces a install, modelos, releases.