Repository navigation
feat(i18n): port the Simplified Chinese translation from #89 - #91
Conversation
Adds `rgitui_i18n` and wires it into settings, so a language can be chosen and applied without a restart. This is the infrastructure only — the Appearance settings panel is converted as the working example; the other ~1500 user-facing strings are still English literals. English is the source of truth in `src/en.rs` rather than a JSON asset, for the same reason the built-in themes are Rust: it is always present and cannot drift, so any missing translation has something to fall back to. Translations are JSON under `assets/locales/`, embedded at build time, with user overrides from the config directory so a translator can iterate against a release build. The catalogue is a process-wide RwLock rather than a GPUI global, because error strings are built on background threads and need translating too. The tradeoff is that GPUI cannot know a language change dirties the frame, so `set_language` must be followed by `cx.refresh_windows()`. `t!` and `tp!` return `SharedString`, which every `rgitui_ui` builder already accepts — converting a call site is a one-line change. Locale files are validated by tests: unknown keys fail (they would silently never render), placeholders must survive translation, and plural forms must be declared in pairs. Missing keys are deliberately allowed so partial translations are worth shipping. Simplified Chinese ships as the first translation and needs native review. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… the loader
Keys: t!, tp! and the new key! take literal keys and evaluate const
assertions against the English table, so a key missing from en.rs, or named
arguments that do not match the English message's placeholders, fail the
build at the call site instead of rendering the raw key. tp! fills {count}
itself. en.rs is one `key => message` line per entry, and an unsorted or
duplicated table fails the build naming the first key out of place, which is
what lets the lookup be a binary search. A test reports English keys that no
code uses.
Language changes: set_language takes the App, refreshes every window and
replaces an ActiveLanguage global, so callers no longer have to remember
refresh_windows, and views that keep translated text outside render can
observe it. The settings view reads the selection from rgitui_i18n instead of
keeping its own copy.
Matching: Traditional Chinese systems (zh-TW, zh-HK, zh-MO, zh-Hant) matched
the shipped zh-CN on the primary subtag alone and got Simplified characters;
matching now requires the same script, stated or implied. POSIX tags such as
zh_CN.UTF-8 are accepted. The system language and per-locale missing counts
are read once at init rather than on every render, and the count no longer
includes plural forms a locale's rule never selects, so a complete zh-CN
stops reporting one missing string.
Loader: user locale files must be regular files under 1 MiB, are read in path
order, and are layered over a shipped locale with the same id instead of
replacing it; keys English does not define are logged. An unknown plural_rule
is an error rather than silently becoming one_other. Shipped locales must use
exactly the English placeholders and only the plural forms their rule selects.
The UI font gains Simplified Chinese fallback families when the UI is
Simplified Chinese, and only then, so Japanese and Traditional Chinese text in
an English UI keeps the platform's glyph choice. The cached font is keyed on
the locale as well as the family.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ports @VedaChen's translation of the interface from #89 onto rgitui_i18n. Their call-site work is kept file by file; the TrKey table, the settings language enum and the git-layer changes are not. - 469 strings carried over as written, under keys named by the panel that shows them (toolbar.push, confirm.force_push, branch_health.merged). - Every `tr(TrKey::X)` call is now a compile-checked t!("key"); keys held as data (graph menu rows, toolbar tooltips, the settings quick reference) use key!. Positional `{}` templates filled by chained replacen are named placeholders filled in one pass, so user text containing "{}" can no longer swallow the next value. - Sentence fragments joined with format! are whole sentences: "Pending changes on {branch}", "HEAD is detached at {commit}", "Return to {branch}", "Inspecting worktree: {name} ({branch})", "Failed to load comments: {error}". Counts are plurals ("1 file staged", "1 result", "1 commit"), and the four DescCount variants are one message built from two plural pieces. - English words no longer land in Chinese sentences: the PR and issue empty states have a message per filter (which also fixes "No all issues found"), and the AI section translates its credential store names, catalogue ages, "verified" suffix and model-list footer. - Relative dates in the graph, blame, history and detail panels go through shared time.* messages. - Pill groups select by value instead of comparing the clicked label against both languages. The confirm dialog returns its translated label directly. Dropped from #89: - The 27 observe_global::<SettingsState> redraw observers. A language change refreshes every window already, and those observers redrew the main window on every settings write, including each move of the settings window. - The placeholder resets in render. TextInput::set_placeholder_message takes a key and translates it when drawn, so inputs follow the language with no observer and no per-frame work. - The General-page language dropdown, which duplicated the Appearance picker. - The four #[allow(dead_code)] English label functions; the label functions that render are the ones tested now. - description_tr's hard-coded Chinese in the keymap registry; the settings quick reference has message keys instead, with a test that keeps their English equal to each command's doc comment. Terminology, from review of #89: rebase "pick" is 使用 (拣选 is cherry-pick), bisect good/bad is 正常/有问题 everywhere with the menu saying 二分查找, the "Open" filter is 打开, a stash message is 储藏说明, and the interface uses 你. The working-tree row's labels and badges and the diff header were English on the main screen in #89 too and are translated here. Co-authored-by: chenjunjie <zmiss2024@foxmail.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b50f114b5e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| footer.push(t!("settings.ai.hidden_for_tools", count = hidden_for_tools).to_string()); | ||
| } | ||
| let footer = (!footer.is_empty()).then(|| SharedString::from(footer.join(" · "))); |
There was a problem hiding this comment.
Rebuild the model picker when the language changes
If a user opens the AI model picker, navigates to Appearance to switch languages, and then returns to AI, this translated footer remains in the previous language because sync_model_picker stores the resulting SharedString in the picker entity and is not rerun by the ActiveLanguage observer (which only rebuilds API-key placeholders). Re-sync the picker from that observer, or store translation keys/data and translate during picker rendering. CLAUDE.mdL59-L59
Useful? React with 👍 / 👎.
|
good thanks |
|
I manually reviewed your changes. The translation is fine. Thanks for your contribution. |
The picker keeps the footer and status notes it was handed, and the `ActiveLanguage` observer only re-translated the API-key placeholders. Opening the picker, switching language on the Appearance page and coming back left "updated 3 h ago" and the hidden-for-tools note in the old language. The observer now re-syncs the picker as well; `set_rows` and `set_chips` keep the query and chip, so an open picker is not reset. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review |
Adds `rgitui_i18n` and wires it into settings, so the interface language can be chosen under Settings → Appearance → Language and applied without a restart. Simplified Chinese ships first. The Appearance panel is the only view converted here; the rest of the interface follows in #91. - English is the source of truth in `src/en.rs`, one `key => message` line per entry, always present so a missing translation has a fallback. Translations are JSON under `assets/locales/`, embedded at build time, and a file in `<config>/rgitui/locales/` is layered over the shipped one so a translator can work against a release build. - `t!`, `tp!` and `key!` take literal keys and check them, and their named arguments, against the English table at compile time. `tp!` fills `{count}`. An unsorted or duplicated table fails the build. - The catalogue is a process-wide RwLock, so messages can be built off the UI thread. `set_language` refreshes every window and replaces an `ActiveLanguage` global that views keeping translated text can observe. - Matching needs the same script, so a Traditional Chinese system stays in English instead of being given Simplified characters. POSIX tags such as `zh_CN.UTF-8` are accepted. - User locale files must be regular files under 1 MiB, are read in path order and overlay a shipped locale with the same id. Unknown keys are logged and an unknown plural rule is an error. - The UI font gains Simplified Chinese fallbacks only when the UI is Simplified Chinese. - Tests reject locale files with keys or placeholders English lacks, plural forms a rule never selects, and English keys no code uses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # CHANGELOG.md # CLAUDE.md # assets/locales/zh-CN.json # crates/rgitui_i18n/src/en.rs # crates/rgitui_i18n/src/lib.rs # crates/rgitui_workspace/src/settings_window/view.rs
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b7e78dc9cf
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| .gap_2() | ||
| .child( | ||
| Label::new("Enter to confirm | Esc to cancel") | ||
| Label::new(t!("common.confirm_hint")) |
There was a problem hiding this comment.
Translate confirmation titles and warnings
When Simplified Chinese is active, confirmations such as force push, discard-all, remote removal, and branch deletion still render their title and warning in English because ConfirmDialog displays the caller-provided strings unchanged, while call sites such as workspace/commands.rs:591-617 and workspace/events.rs:1589-1608 continue passing English literals. Translating only the hint and buttons leaves the most important destructive-operation text untranslated; add message keys with arguments for these titles and warnings, or store keys/data for translation during rendering. CLAUDE.mdL59-L59
Useful? React with 👍 / 👎.
The confirm dialog drew whatever title and warning its caller passed,
and every caller passed English, so a force push or a branch deletion
asked in English under translated buttons. The dialog now builds both
from the `ConfirmAction` in render: the actions already carry what the
text names (the branch, tag, remote, path, stash index or commit), so
`show_visible` takes only the action, and the text follows a language
change while the dialog is open.
- `AbortMerge` was used for every operation in progress and is now
`AbortOperation(RepoState)`, so the title says which one is being
aborted ("Abort Rebase") instead of "Abort Rebasing (interactive)".
- The two copies of the hard-reset warning, and the "Reset to Commit"
and "Reset (hard)" titles for the same action, are one message each.
- A worktree removal names the path; the worktree name was only in the
caller's text.
- `ConfirmDialog::show` had no callers and is gone, as is the commit id
`GraphViewEvent::ResetToCommit` carried beside the `Oid` it was
formatted from.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review |
|
Codex Review: Didn't find any major issues. What shall we delve into next? Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review |
|
Codex Review: Didn't find any major issues. You're on a roll. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Ports @VedaChen's Simplified Chinese translation from #89 onto the
rgitui_i18nfoundation in #57. Stacked on #57; merge that first. Git operation messages follow in a separate PR stacked on this one.@VedaChen, your call-site work is kept file by file and you're credited as co-author. About 50 strings were reworded on the way: fragments became whole sentences, counts became plurals, and English words no longer get pasted into Chinese. Those, and the terminology changes below, are where your review would help most.
What carried over
toolbar.push,confirm.force_push,branch_health.merged).tr(TrKey::X)is nowt!("key"). Keys held as data (graph menu rows, toolbar tooltips, the settings quick reference) usekey!.What changed in the move
{}filled by chainedreplacenbecame named placeholders filled in one pass. User text containing{}, like the AI base-URL host, can no longer swallow the next value.DescCountvariants are now one message built from two plural pieces.time.*messages.Left out from #89
observe_global::<SettingsState>redraw observers. A language change already refreshes every window, and these redrew the main window on every settings write, including each move of the settings window.render(about 15 views).TextInput::set_placeholder_message(key!(…))translates when drawn, so inputs follow the language with no observers and no per-frame work. Only the AI key placeholders, which name their provider, still use anActiveLanguageobserver.#[allow(dead_code)]English label functions. The label functions that render are the ones tested now.description_tr's hard-coded Chinese in the keymap registry. The quick reference has message keys instead, plus a test that keeps their English equal to each command's doc comment.op_i18n.rs). They're redone in the follow-up PR so that progress and success messages are translated too, not only failures.Terminology
Open for discussion.
pick <提交> = 使用提交Coverage
.oneplural forms Chinese doesn't use, so the picker reports zh-CN as fully translated.Verified
cargo clippy --workspace --all-targets -D warnings: clean."language": "zh-CN"against a scratch repo and checked a screenshot. CJK text renders through the fallback fonts, and the toolbar, sidebar, graph columns, relative dates and the pending-changes row all show in Chinese.The co-author trailer currently uses the identity on #89's commits (
chenjunjie <zmiss2024@foxmail.com>). It can be changed at squash time if a different identity is preferred.🤖 Generated with Claude Code