feat(tui): TUI i18n — config, footer, /settings selector, reverse-RPC & command/help translation#8
Draft
meymchen wants to merge 10 commits into
Draft
feat(tui): TUI i18n — config, footer, /settings selector, reverse-RPC & command/help translation#8meymchen wants to merge 10 commits into
meymchen wants to merge 10 commits into
Conversation
Introduce a zero-dependency I18n singleton (lookup, en fallback, param interpolation, runtime setLocale) modeled on the theme singleton, with per-module language packs under locales/en and locales/zh-CN. Add a language setting to tui.toml (auto | en | zh-CN), resolve auto from the system locale at startup, and migrate the footer status labels to translated strings.
Build the interactive language-switching path on top of the i18n skeleton (#1). A LanguageSelectorComponent (ChoicePicker subclass) offers Auto / English / 简体中文; /settings routes a new "Language" entry to it. Selecting a language persists to tui.toml, updates app state, flips the live i18n locale, and repaints so the UI switches language immediately without a restart. /reload re-reads the language and re-applies it via setAppState + i18n.setLocale alongside theme. Closes #2 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Migrate the user-visible English strings in the reverse-RPC approval and
question panels to the i18n layer, with en + zh-CN translations under a new
`reverseRpc` namespace.
- Approval panel headers, choice buttons, danger labels, cwd/scope/more-lines
metadata, and key hints now resolve via `i18n.t('reverseRpc.approval.*')`.
- Choice/danger labels are localized in the approval adapter at adapt time;
`selected_label` stays a stable English identifier for the upstream contract.
- Question dialog heading, review/submit prompts, Other/Not-answered labels,
submit actions, and hints resolve via `i18n.t('reverseRpc.question.*')`.
- Approval preview title and footer hints resolve via
`i18n.t('reverseRpc.preview.*')`.
- Strings stay colored via `currentTheme`; no chalk named colors, and no
coloring / printableChar() / keyboard behavior changes.
Locale-render tests assert the panels render in Simplified Chinese under
`zh-CN` and English under `en`.
Resolves #3.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Migrate builtin slash-command descriptions, the /help panel chrome (title, dismiss hint, greeting, section headers, keyboard-shortcut descriptions, scroll tail), and goal/swarm argument-completion descriptions to the i18n layer, with en + zh-CN translations under a new `commands` namespace. Command names, aliases, and subcommand values stay untranslated — only human-readable descriptions follow the active locale. Skill command descriptions remain owned by the skill; only framework builtins are localized via `localizedBuiltinSlashCommands()`. Closes #4 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…keleton # Conflicts: # apps/kimi-code/package.json
Migrate the `/usage` and `/status` panel labels to the i18n layer with
`en` + `zh-CN` translations, extending the `components` namespace with
`usage` and `status`.
- usage-panel: session/context/plan section labels, input/output/total
words, "% used", and empty-state strings now come from
`i18n.t('components.usage.*')`.
- status-panel: field labels (Model, Directory, Permissions, Plan mode,
Session, Title, Warning) and rendered values (on/off, not set, none,
thinking) come from `i18n.t('components.status.*')`.
- Panel chrome titles (' Usage ' / ' Status ') are localized; model names
and permission-mode identifiers stay untranslated.
- Add a `padEndToWidth` helper so the aligned label columns pad by display
width, keeping the panels aligned under double-width Chinese characters.
Locale-render assertions verify both panels render Chinese under `zh-CN`
and English under `en`, plus alignment/width under `zh-CN`.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Migrate the welcome panel to the i18n layer (title, login/help hints, unset-model notice, and the Directory/Session/Model/Version/MCP info labels), padding the label column by display width so it stays aligned under double-width Chinese. Introduce a `common` namespace for short phrases shared across components: the generic Submit/Cancel/Back controls plus the navigate/select/cancel/clear-search key hints. Migrate the list-style dialogs (model, choice, session, undo, experiments selectors) and the plugin-remove Cancel button to `common.*` so each shared phrase is translated once, with en + zh-CN translations. Resolve #6 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Render the `tui.toml` parse-failure notice (INVALID_TUI_CONFIG_MESSAGE) in the active locale. Config load runs before the language is known, so run-shell now records only that parsing failed and translates the notice after `i18n.setLocale(...)` resolves the locale — deferred init that falls back to the default `en` pack before config load. The `TuiConfigParseError.message` stays English so logs and issue triage remain English; only the user-facing startup notice is localized. Adds a `cli` i18n namespace (`cli.config.invalidTuiConfig`) for en and zh-CN. Closes #7. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…date thinking label
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related Issues
Resolve #1, Resolve #2, Resolve #3, Resolve #4, Resolve #5, Resolve #6, Resolve #7
Problem
See linked issues. kimi-code had no way to render the TUI in any language other than English: there was no language preference, no translation layer, and every UI string was a hardcoded English literal. And even once a preference existed, there was no in-app way to change it — no
/settingsentry and no runtime switch.What changed
Slice 1 — i18n walking skeleton (#1)
Stands up the whole path end-to-end and proves it on one surface, the footer/status bar.
languagefield totui.tomlasauto | en | zh-CN(defaultauto), wired symmetrically through parse/normalize/render so the value round-trips without loss.I18nclass plus global singleton, modeled on the existingcurrentThemesingleton. Providest('module.submodule.phrase', params?)with active-locale lookup, fallback toenon a missing key,{param}interpolation, andsetLocalefor runtime switching. Components calli18n.t(...)at render time, exactly as they callcurrentTheme.fg(...).autoresolves to a concrete locale fromLC_ALL/LC_MESSAGES/LANG(POSIX precedence) against a small match table;C/POSIX/ unknown values fall back toen.locales/en/andlocales/zh-CN/organized as per-module namespace files merged into a per-localeindex.ts, locking the contributor-facing key convention.run-shellresolves the locale and initializes the singleton right after the theme, before the TUI is constructed.AppStatenow carrieslanguage.context,thinking, and the background task/agent "running" badges) now render viai18n.t(...), withenandzh-CNtranslations. Strings stay colored throughcurrentTheme/chalk.hex— no chalk named colors introduced.Slice 2 — language selector + runtime switch (#2)
Builds the interactive path on top of the skeleton: a user can now choose the UI language from
/settingsand watch it apply immediately.LanguageSelectorComponent. AChoicePickerComponentsubclass (modeled onThemeSelectorComponent) titled "Language / 界面语言", offering Auto / English / 简体中文 with the current value highlighted./settingsrouting. A new "Language" entry extends theSettingsSelectionunion andisSettingsSelection, routed to the new selector.tui.tomlviasaveTuiConfig, updatesAppState, flips the live locale withi18n.setLocale(resolveLocale(language)), and repaints — so the whole UI switches language immediately, no restart./reload.applyReloadedTuiConfigre-readslanguageand re-applies it viasetAppState+i18n.setLocale, alongside the existing theme/notifications/upgrade reapplication.Slice 3 — approval & question panel translation (#3)
Extends the translation layer to the reverse-RPC interaction surfaces — the approval and question panels the agent puts up to ask for a decision.
reverseRpcnamespace. New per-localelocales/{en,zh-CN}/reverse-rpc.tspacks covering the approval panel, question dialog, and full-screen approval preview, merged into each localeindex.ts.cwd/scope/hidden-line metadata, the feedback prompt, and the↑/↓ · choose · ↵hint render viai18n.t('reverseRpc.approval.*'). Choice buttons and danger labels are localized in the approval adapter at adapt time;selected_labelstays a stable English identifier so the upstream contract never shifts with the UI language.Other/Not answeredlabels, Submit/Cancel actions, and the navigation hints render viai18n.t('reverseRpc.question.*').i18n.t('reverseRpc.preview.*').currentTheme; no chalk named colors, and noprintableChar()/ keyboard behavior changes. Locale-render tests assert the panels show Chinese underzh-CNand English underen.Slice 4 — slash-command descriptions & /help panel (#4)
Extends the translation layer to the command surface, so command discovery reads in the user's language.
commandsnamespace. New per-localelocales/{en,zh-CN}/commands.tspacks covering builtin command descriptions, the/helppanel chrome, and/goal+/swarmargument-completion descriptions, merged into each localeindex.ts.localizedBuiltinSlashCommands()resolves each builtin'sdescriptionviai18n.t('commands.descriptions.<name>')at render time; the static registry strings remain the English source of truth.kimi-tuifeeds the autocomplete provider and the/helppanel from it, so both follow the active locale. Skill-provided descriptions stay owned by the skill — only framework builtins are localized./helppanel. Title, dismiss hint, greeting, the "Keyboard shortcuts" / "Slash commands" section headers, every default keyboard-shortcut description, and the "showing X-Y of Z" scroll tail render viai18n.t('commands.help.*')./goaland/swarmsubcommand descriptions render viai18n.t('commands.args.*').Shift-Tab,Ctrl-G, …) stay untranslated — only human-readable descriptions change. Locale-render tests assert the/helppanel and command descriptions show Chinese underzh-CNand English underen.Slice 5 — usage/status panel & chrome label translation (#5)
Extends the translation layer to the
/usageand/statuspanels, finishing the stats/chrome surface the footer began.usage+statusnamespaces. Thecomponentspack is extended withcomponents.usage.*(session/context/plan section labels, theinput/output/totalwords,% used, and empty-state strings) andcomponents.status.*(field labels and rendered values), in bothenandzh-CN.buildUsageReportLines/ the shared managed-usage section render "Session usage", "Context window", "Plan usage", the per-model row words, and the empty states viai18n.t('components.usage.*').i18n.t('components.status.*').Usage/Status) are localized. Model names and permission-mode identifiers stay untranslated — only the surrounding labels change.padEndToWidthhelper pads the aligned label columns by display width instead of code-point count, so the fixed-width panels stay aligned and never overflow under double-width Chinese characters. Locale-render tests assert both panels show Chinese underzh-CNand English underen, plus column alignment and width safety underzh-CN.Slice 6 — welcome/onboarding & shared dialog labels (#6)
Translates the first-run welcome surface and introduces a shared
commonnamespace for the short phrases that recur across dialogs.i18n.t('components.welcome.*')in both render paths (wide box + narrow fallback). The info-label column is now padded by display width, so it stays aligned under double-width Chinese labels.commonnamespace. New per-localelocales/{en,zh-CN}/common.tspacks holding the generic dialog-control vocabulary (common.submit/cancel/back) plus the keyboard-hint phrases (common.hints.navigate/select/cancel/clearSearch) that recur in list-style dialog footers.common.hints.*, and the plugin-remove confirmation's Cancel button usescommon.cancel— so each shared phrase is translated once and reused, rather than duplicated per component.currentTheme. Locale-render tests assert the welcome surface and the shared dialog hints show Chinese underzh-CNand English underen.Slice 7 — CLI config-parse notice translation (#7)
Optional phase-1 tail: routes the highest-value CLI-facing message — the
tui.tomlconfig-parse failure notice — through the i18n layer, so the CLI startup notice matches the language the TUI renders in.clinamespace. New per-localelocales/{en,zh-CN}/cli.tspacks holdingcli.config.invalidTuiConfig, merged into each localeindex.ts. Theenentry stays in sync withINVALID_TUI_CONFIG_MESSAGE.run-shellnow records only that parsing failed and translates the notice afteri18n.setLocale(...)resolves the locale — falling back to the defaultenpack before config load.TuiConfigParseError.messagestays English so logs and issue triage remain English; only the user-facing startup notice is localized.zh-CNand English underen, and a guard test keeps theenpack in sync withINVALID_TUI_CONFIG_MESSAGE. Full translation of every CLI subcommand's help stays out of scope for phase 1.All slices built test-first, one behavior at a time.
Checklist
gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update.