Repository navigation
feat(i18n): add interface localization with 10 languages - #105
Conversation
Introduces a localization layer for the application UI and ships an English and a Simplified Chinese catalog. Previously every user-facing string was a hardcoded English literal spread across view, type, and error modules, so the interface could not be translated at all. Translations live in JSON files under `otty/src/i18n/locales/` and are embedded with `include_str!`, keeping the binary self-contained. `Key` stays a Rust enum so call sites remain type-checked, and serde deserializes each catalog into `HashMap<Key, String>`; an unknown key in a JSON file fails deserialization, and tests assert that both catalogs cover every `Key` and contain no extras. No new dependency is required — `serde` and `serde_json` were already used by the crate. The active locale is process-global rather than threaded through every widget signature, which would have touched every view function for no benefit. `Settings > General > Language` offers System / English / 简体中文, persisted as `general.language`; `System` resolves through the POSIX variables `LC_ALL`, `LC_MESSAGES`, `LANG`. Settings written by older builds simply lack the field and fall back to `System`. Section titles double as `TreePath` segments in the settings navigation tree, so a language change invalidates every stored path. `SettingsState::replace_with_settings` now rebuilds the tree and the selected path, and the locale is applied before that happens — both at startup in `load_initial_settings_state` and on save in the reducer. The settings tests read their paths from the tree instead of hardcoding English titles, so they no longer depend on which locale is active. Theme preset names (One Dark, Solarized Dark, Dracula) are left untranslated as proper nouns, as are sample placeholder values such as `example.com` and `~/.ssh/id_ed25519`. Log messages and infrastructure error types stay in English; only strings that reach the UI are localized.
Adds Traditional Chinese, Japanese, Korean, French, German, Spanish, Brazilian Portuguese, and Russian, bringing the shipped set to ten languages.
Adding a language previously meant touching three places: a `Locale` variant, a hand-written `OnceLock` in `catalog`, and a matching `LanguageSetting` variant. That does not scale past a couple of languages, so both types are now data-driven. `Locale::ALL` is the single list, `catalog` caches into an array indexed by `Locale::index`, and `LanguageSetting` collapsed from one variant per language into `System | Fixed(Locale)` with its selector derived from `Locale::ALL`. Adding a language is now a JSON file plus one enum variant, with no view or settings changes.
Locale detection handles the cases that matter for these languages. Chinese resolves by script rather than by primary subtag, so `zh-TW`, `zh-HK`, `zh-MO`, and an explicit `Hant` select Traditional while everything else Chinese selects Simplified. POSIX strings are normalized before matching, so `de_AT@euro` and `ja_JP.UTF-8` resolve correctly. A stored `general.language` value, in contrast, must match a shipped tag exactly, so an unrecognized or partial tag keeps following the system instead of silently picking a language.
Three tests were added that matter specifically for hand-written translations: every locale keeps the `{title}`, `{command}`, `{error}`, and `{index}` placeholders in its templates; no catalog entry or palette label is blank; and every `Locale::index` matches its position in `ALL`, which is the invariant the catalog array indexing relies on. The existing completeness tests now iterate `Locale::ALL` rather than a hardcoded pair, so a new language is covered the moment it is listed.
Language names in the selector are endonyms (日本語, Русский, Português (Brasil)) so a user can find their language without already being able to read the current interface language.
Use the native system locale provider, honor explicit Chinese scripts, preserve literal template arguments, refresh the settings tab title, and localize visible quick-launch diagnostics.
|
need rebase |
Bring in otty-shell#96 (block settings edits while a save is in flight). Conflict in app/src/widgets/settings/view/settings_form.rs: keep the localized labels from this branch and main's save lock (Save/Reset disabled and inputs read-only while saving). Also add LanguageChanged to the reducer's in-flight edit guard, so a language change made during a save is ignored like other draft edits, with a regression test.
|
Merged current Conflict resolved in One follow-up from the merge: Verification on 0438519:
|
The settings tree is rendered through TreeView, which sorts sibling rows by title. That put Appearance before General in English and made the order depend on the active language. Add TreeView::keep_source_order() so callers with a fixed page list can render rows in the order they provide, use it for the settings navigation, and order the sections as General, Appearance, Terminal.
Summary
Settings > General > LanguageselectorSystemas the default and preserve compatibility with settings files that do not containgeneral.languageReplacement for #88
This is a conflict-resolved replacement for #88, rebased onto the current
main. The two original feature commits retain Evan (houko) as their author.The replacement resolves the repository reorganization and later settings changes by:
otty/srcpath toapp/src/i18nVerification
cargo +nightly fmt -- --checkcargo clippy --workspace --all-targets --all-features -- -D warningscargo deny check— passed; reports the existing yanked transitive versionscore2 0.4.0andspin 0.9.8cargo test --workspace --all-features— 561 passed, 2 ignoredcargo llvm-cov --workspace --all-features --fail-under-lines 80— tests passed, but the repository-wide line coverage gate remains below its configured threshold: 68.17% on this branch versus 67.97% on currentmainLC_ALL=zh_CN.UTF-8— Chinese Settings UI rendered, including the language control and pane-layout textLC_ALL=ja_JP.UTF-8— Japanese Quick Launch UI renderedNot verified