docs: correct the web localization status for the finished i18n work (#2400)
Co-authored-by: Kandev Agent <agent@kandev.dev>
This commit is contained in:
+7
-3
@@ -6,9 +6,11 @@ European Portuguese (`pt-pt`) and Simplified Chinese (`zh-cn`) are the shipped
|
||||
human locales, and a generated **pseudo-locale** is used for QA. Requirements live in
|
||||
[`docs/specs/platform/i18n.md`](specs/platform/i18n.md).
|
||||
|
||||
The migration is **in progress, one directory per PR**. The runtime, the gates,
|
||||
and Settings → General → Appearance are done; everything else still holds
|
||||
English literals.
|
||||
The migration is **complete**. Every routed web UI surface renders through `t()`
|
||||
/ `<Trans>`, and `i18nGuardFiles` now enumerates the directories that hold them.
|
||||
Running the guard across `components app hooks lib` reports literals in only the
|
||||
unrouted QA pages under `app/demo/`. New copy is still guarded everywhere, so a
|
||||
hardcoded literal is now a regression rather than unfinished migration.
|
||||
|
||||
Two enforcement layers, and it matters which is which:
|
||||
|
||||
@@ -176,6 +178,8 @@ pnpm run i18n:pseudo # regenerate the pseudo catalog from en
|
||||
pnpm run lint:i18n <path> # preview the guard on a path that is NOT yet migrated
|
||||
pnpm run i18n:ratchet # new-code guard + allowlist-only-grows (pre-commit + CI)
|
||||
pnpm run i18n:removed # did this change rewrite any English? (diff-based, advisory)
|
||||
pnpm run i18n:parity # per-locale missing/extra keys (report, never a gate)
|
||||
pnpm run i18n:sweep <path> # copy the jsx-only rule cannot see (report, never a gate)
|
||||
|
||||
# Codemods — always inspect the diff; each declines what it cannot prove safe.
|
||||
node scripts/externalize-strings.mjs --write components app hooks lib
|
||||
|
||||
@@ -75,7 +75,7 @@ Released builds can lag this page. Check **Settings > System > About** or `kande
|
||||
| Language servers | Limited | Desktop Monaco maps TypeScript/JavaScript, Go, Rust, Python, and experimental Kotlin to task-host servers with diagnostics, completion, hover, navigation, signature help, and semantic tokens. Status distinguishes a launched process awaiting `initialize` from server-reported project work; slow Kotlin Gradle imports can take minutes, with no automatic timeout or universal percentage/ETA. Fine-pointer desktop can place active-file status in the editor toolbar or Application status bar; touch tablet falls back to the toolbar, and phone has no LSP control. Auto-start/installation default off; Kotlin is manual-only. Local PC and Local Docker are supported, while SSH, Sprites, remote Docker, and mobile LSP are not. |
|
||||
| Integrated shell terminals | Dependency-bound | Create, reopen, rename, and terminate are shipped. Shell settings affect new terminals, and executor/task-environment availability controls where the PTY runs. |
|
||||
| Prompts, keyboard shortcuts, notifications, appearance, and task actions | Supported | These are normal settings surfaces. Browser notification permission, custom command validity, and local shell/editor availability still apply to their effects. |
|
||||
| Web UI localization | Limited | **Settings > General > Appearance** offers English and 简体中文. The selection persists across reloads, but localization migration is incremental: currently externalized surfaces use the selected language while unmigrated surfaces remain in English. |
|
||||
| Web UI localization | Limited | **Settings > General > Appearance** offers English, Português (Portugal), and 简体中文. The choice applies immediately, persists per browser through a `kandev_locale` cookie, and is negotiated from `Accept-Language` before defaulting to English when no cookie is set. Externalization is complete: every reachable web UI surface renders through the translation layer, and dates, numbers, and relative timestamps follow the selected locale. Per-language completeness still differs. Portuguese tracks English closely; Simplified Chinese is maintained out of band and lags, most visibly in the task workbench. An untranslated key falls back to English rather than failing. About fifteen shared `@kandev/ui` accessibility labels — mostly screen-reader-only text such as a dialog's **Close** — stay English even where the visible UI is translated. Desktop-shell and CLI surfaces, API/log diagnostics, and agent output were never in scope; user content such as task titles, chat, and diffs is never translated. Development and E2E builds list an extra **Pseudo (QA)** locale that production builds do not ship. |
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
@@ -81,6 +81,23 @@ Chat, plan, changes, files, terminal, editor/diff, preview, commit, and pull-req
|
||||
- a restored dock layout and a narrow mobile route;
|
||||
- absent optional providers or executor capabilities.
|
||||
|
||||
## Localize user-facing copy
|
||||
|
||||
Every new user-facing string must go through i18next — `t("namespace:key")` or `<Trans>` — never an inline literal. That covers visible copy, `placeholder`, `aria-label`, `title`, `alt`, toasts, validation and empty-state text, and dialog copy. Externalization of the existing UI is complete, so a hardcoded literal is now a regression rather than unfinished migration.
|
||||
|
||||
Catalogs live in `apps/web/src/locales/<locale>/<namespace>.json`. English is the source locale and the only hand-authored catalog; it is bundled into the entry chunk, while each other locale is a lazily fetched chunk loaded before that locale activates. Shipped locales are English, European Portuguese, and Simplified Chinese, plus a generated `pseudo` catalog that only development and E2E builds contain. A missing key falls back to English at runtime.
|
||||
|
||||
Three mistakes cause silent bugs: never translate a string compared with `===`, never call `t()` at module scope, and never pass an English plural ending as a value — use `count` with `_one`/`_other` keys.
|
||||
|
||||
```bash
|
||||
pnpm run i18n:check # gate: plurals, module-scope t(), Trans indices
|
||||
pnpm run i18n:ratchet # gate: no new hardcoded literals in changed lines
|
||||
pnpm run i18n:parity # advisory report: per-locale missing and extra keys
|
||||
pnpm run i18n:sweep <path> # advisory report: copy the jsx-only lint rule cannot see
|
||||
```
|
||||
|
||||
The first two run in pre-commit and CI. The last two are reports that always exit zero and are wired into neither, because real-locale translation is maintained out of band and must not fail an English-only change. A clean lint does not prove a file is done: the rule only inspects JSX literals and skips anything assigned to a SCREAMING_CASE identifier, so review config tables by eye and use the pseudo-locale (**Settings > General > Appearance** in development builds) as the completeness check. Shared `@kandev/ui` primitives cannot import the app's i18n runtime, so their accessibility labels are localized at the call site. Full guide: [`docs/i18n.md`](../i18n.md).
|
||||
|
||||
## Security, accessibility, and responsiveness
|
||||
|
||||
Frontend checks improve the experience; they are not authorization. The backend must enforce identity, workspace/task scope, and destructive operations. Treat provider text, repository content, Markdown/HTML, URLs, and agent output as untrusted. Keep raw HTML rendering paired with the existing sanitizer and never interpolate shell commands or credentials in the browser.
|
||||
|
||||
+17
-15
@@ -21,13 +21,13 @@ language and the pseudo-locale as the migration QA oracle.
|
||||
`alt`, toast/`sonner` messages, form-validation messages, empty-state and
|
||||
error copy, and confirm/dialog text.
|
||||
- English SHALL remain the source locale. The shipped human locales SHALL be
|
||||
`en` and `zh-cn`; copy lives in per-namespace JSON catalogs keyed by stable
|
||||
ids. The `pseudo` catalog is generated from `en`, never hand-edited.
|
||||
- The app SHALL expose a **language switcher** in Settings. It lists English and
|
||||
Simplified Chinese by their fixed endonyms. Selecting a locale re-renders the
|
||||
UI without a full reload and persists the
|
||||
choice across restarts. Development and E2E builds additionally list the
|
||||
**pseudo-locale** (see below).
|
||||
`en`, `pt-pt`, and `zh-cn`; copy lives in per-namespace JSON catalogs keyed by
|
||||
stable ids. The `pseudo` catalog is generated from `en`, never hand-edited.
|
||||
- The app SHALL expose a **language switcher** in Settings. It lists English,
|
||||
European Portuguese, and Simplified Chinese by their fixed endonyms. Selecting
|
||||
a locale re-renders the UI without a full reload and persists the choice across
|
||||
restarts. Development and E2E builds additionally list the **pseudo-locale**
|
||||
(see below).
|
||||
- The app SHALL ship a **pseudo-locale** (`pseudo`) that transforms every
|
||||
extracted message (accents + padding, e.g. `Ĉřéàţé ţàśķ`) so QA can visually
|
||||
confirm a string was externalized and detect truncation. The pseudo-locale is
|
||||
@@ -95,12 +95,12 @@ merge. Sequence:
|
||||
i18n adds no server-side persistent entities. It introduces:
|
||||
|
||||
- **Message catalogs** — checked-in JSON under
|
||||
`apps/web/src/locales/<locale>/<namespace>.json` for `en`, `zh-cn`, and
|
||||
`pseudo`. `en` is hand-authored (keys are stable ids; the English copy lives
|
||||
only here); `zh-cn` is a human-maintained translation, maintained out of band,
|
||||
whose namespace and key sets aim to mirror `en` but may lag it — a gap warns
|
||||
rather than failing, and i18next falls back to `en` at runtime; `pseudo` is
|
||||
generated from `en` by
|
||||
`apps/web/src/locales/<locale>/<namespace>.json` for `en`, `pt-pt`, `zh-cn`,
|
||||
and `pseudo`. `en` is hand-authored (keys are stable ids; the English copy
|
||||
lives only here); `pt-pt` and `zh-cn` are human-maintained translations,
|
||||
maintained out of band, whose namespace and key sets aim to mirror `en` but may
|
||||
lag it — a gap warns rather than failing, and i18next falls back to `en` at
|
||||
runtime; `pseudo` is generated from `en` by
|
||||
`scripts/generate-pseudo-locale.mjs` and is never hand-edited. Catalogs load at
|
||||
runtime via Vite glob import — no build plugin. Only `en` is bundled into the
|
||||
initial download: it is the fallback locale, so it has to be in memory the
|
||||
@@ -177,7 +177,8 @@ Frontend module contract (new):
|
||||
- **GIVEN** a plural key (`x_one` / `x_other`) rendered with a `count`, **WHEN**
|
||||
count is 1 vs many, **THEN** i18next selects the matching form.
|
||||
- **GIVEN** a production build, **WHEN** the language switcher renders, **THEN**
|
||||
English and 简体中文 are offered and the pseudo-locale is not.
|
||||
English, Português (Portugal), and 简体中文 are offered and the pseudo-locale
|
||||
is not.
|
||||
- **GIVEN** an English namespace or key changes, **WHEN** `i18n:check` runs,
|
||||
**THEN** every real locale missing or adding a namespace or key is reported as
|
||||
a warning naming the locale, namespace, and missing or extra entries, **AND**
|
||||
@@ -210,7 +211,8 @@ Frontend module contract (new):
|
||||
switching locale is a pure client re-render.
|
||||
|
||||
## Out of scope
|
||||
- Shipping human languages other than English and Simplified Chinese.
|
||||
- Shipping human languages other than English, European Portuguese, and
|
||||
Simplified Chinese.
|
||||
- Translating the CLI launcher, logs, or ACP/agent output, and the ~1,100
|
||||
diagnostic API error strings (`http.Error` / `gin.H{"error": …}`) — the SPA maps
|
||||
API failures onto its own translated copy, so those stay English by design.
|
||||
|
||||
Reference in New Issue
Block a user