贡献 / #1836

#1836 serve: a Chinese / English language switch for the web app (adapted to v0.1.43)

open · @hogtinbao · 0 评论 · 去 GitHub 看

BenchmarksServer & APIAMD / HIPModels & quantsSecurityWindows

说明

Adapted to **Strata v0.1.43**: the dictionary and the rules are current for the web interface of 0.1.43 (re-checked against every `app.js` change since this branch's base — see Coverage), and the branch is rebased onto today's `main` (`7695859a`, the v0.1.43 release). The branch is a single commit.

*(History: this supersedes #682, which GitHub closed automatically when the history was rewritten, and #1204, which was closed when its fork was deleted and re-created — same commit, same content, nothing changed but the PR number.)*

## What this adds

An optional **Chinese / English language switch** for the web app, with a `Language` button at the left of the header: **English / Simplified Chinese / Chinese + English**.

It is opt-in and reversible: the stock interface is what you get until someone picks another language.

## Coverage: the whole 0.1.43 web interface is translated

Not a partial pass — every user-facing string in the two pages of this release, including the views added in 0.1.40, the two strings 0.1.40.2 added and the request-table columns 0.1.42 split out (0.1.43 added nothing):

- the header, the three tabs and the status pill;
- **Chat**: the empty state, the composer and its buttons, the toasts, the attachments, the tool-call blocks, the per-message meta line, the export and copy labels;
- **Monitor**: the state badges, the eight metric cards, the context gauge, the request table (headers — including `Prefill t/s` and `Decode t/s`, the columns 0.1.42 split out of the old `Tok/s` — status badges, `+N% PCIe`), the totals line, and the new **Conversation cache** panel — its bars, its four facts rows and both of its notes;
- **About**: the model/engine facts, the hardware facts, the API addresses, the settings — and the sentence the page composes from the engine's `cvec` value, the experimental speed projection's line (`Projection control vector on layers 4–44. Per chat in Sampling. …` → 在第 4~44 层上投影控制向量。…), which a rule matches because the mode (project / additive) and the layer numbers come from the engine at run time;
- the **sampling drawer**: thinking levels, temperature, Top-p, Top-k, Max tokens, Seed, the three switches;
- the new **Model settings** view: the help sentence of all 15 editable keys (those sentences come from `runconfig.py` and are matched as data), *and* the two pieces of the form that are not sentences — the `<option>` text of its dropdowns (`default` / `on` / `off` render as 默认 / 开 / 关; an enum choice keeps its config word with the meaning beside it: `model(按模型习惯)`, `start(开头)` …) and the monospace config key beside each field, which keeps the key untouched and gets a short gloss after it (`sampling.top_p(核采样范围)`, `reasoning_budget_tokens(思考长度上限)` …). The key names and the `value` attributes are never rewritten, so what a save writes to the run config stays exactly the English keys the server expects;
- and the strings only a screen reader or a hover would show: every `title`, `placeholder` and `aria-label`.

How that was verified: every string literal and text node was extracted from `index.html`, `monitor.html`, `app.js`, `monitor.js` and the `EDITABLE` list in `runconfig.py` (~264 candidates), then compared against the dictionary and the rules, with HTML line-wrapping normalised; every `<option>` label the settings form can render is checked against the option and general tables, and the short enum words are asserted *absent* from the general dictionary. For every later release the diff of `serve/web/` was re-checked the same way: 0.1.40.2 added exactly two strings (`not available`, `not available on Windows AMD yet` — the GPU-fact placeholders shown on Windows AMD), both now in the dictionary, and **0.1.40.3 and 0.1.41 changed nothing in `serve/web/`**. **0.1.42 did:** it added a favicon (no text), split the request table's `Tok/s` column into `Prefill t/s` and `Decode t/s`, and added the tooltip of the first (`New prompt tokens per second, excluding reused cache tokens (engine prompt time)`) — all of those are in the dictionary now. **0.1.43 changed nothing in `serve/web/` either**, so the coverage above carries over. The projection sentence the About page composes from the engine's `cvec` (`Projection control vector on layers 4–44. …`) is covered by a rule. The remaining unlisted items are CSS class names, code fragments, Python docstrings and proper nouns (e.g. `Top-p`, `Strata`, `Tok/s`) — nothing a user reads.

A new string added upstream later shows up untranslated rather than breaking anything, so this list only ever grows.

## Files

| File | Change |
| --- | --- |
| `serve/web/i18n-zh.js` | new, self-contained |
| `serve/web/index.html` | one `<script>` tag |
| `serve/web/monitor.html` | one `<script>` tag |

No existing function is modified, there is no new dependency, no server-side change and no API change (`/health`, `/v1/...` are untouched — only the two HTML pages load the new file). Removing the feature is deleting one line per page.

## How it works

- A dictionary covers the fixed labels; a list of regex rules covers the strings the app builds at run time (token counts, `tok/s`, the request table, the `Since …` totals line, the conversation-cache statistics, the composed per-message meta line, the projection sentence built from the engine's `cvec`, and so on).
- A `MutationObserver` re-translates after the app re-renders, so the live Monitor — the status pill included — keeps updating in the chosen language, and it does so before the next paint, so nothing flickers back to English. Every write is guarded (`if (el.textContent !== next)`), which makes the observer converge: a second `translateAll()` over the same page performs zero DOM writes (asserted in a test), so it can never feed itself into a loop.
- The Chinese and the English text both live in the DOM; a `data-lang` attribute on `<html>` plus CSS decides which one is shown. Switching is instant and does not re-run the translation.
- The button's own label and menu follow the chosen language (`Language` / `语言` / `语言 Language`), and the choice is kept in `localStorage` (`strata.lang`).

## Guarantees

- **The model's output is never rewritten.** Chat text, reasoning, code blocks and MCP tool arguments and results are explicitly excluded. Checked by asking the model for an English answer and confirming the bubble, the reasoning and the code read exactly as the model produced them in every language mode.
- **`English` restores the stock interface**, attributes included: every translated `title`, `placeholder` and `aria-label` keeps its original value in a `data-i18n-orig-*` attribute, and every `<option>` and `<code>` key its original text in `data-i18n-orig-text`.
- Text with no dictionary entry or rule is left alone, so a partial translation never breaks the page.

## Tested

On Windows 11, engine 0.1.40.3, 0.1.41, 0.1.42 and 0.1.43, in a real browser against a running server (a headless Edge dump of the live DOM — not a mock — plus the interactive page), on Qwen3.8-Flash-Next IQ2_XS and IQ3_XXS: all three modes on the Chat, Monitor and About tabs, in the sampling drawer and in the Model settings view; switching back and forth without a reload; reloading to confirm the stored choice comes back; the composed projection sentence on the About page; and the model-output check above.

## Notes

- The dictionary is deliberately data rather than code: another language is another table, and the rules are shared.
- Happy to rework any of it — a different button position, the dictionary split into its own file, a different default — if you would rather have it another way.




本站相关内容

相关页面的快捷入口。