Skip to content

feat(i18n): add English and Simplified Chinese localization - #181

Open
squarepots wants to merge 121 commits into
Lampese:mainfrom
squarepots:feat/i18n-foundation
Open

squarepots wants to merge 121 commits into
Lampese:mainfrom
squarepots:feat/i18n-foundation

Conversation

@squarepots

@squarepots squarepots commented Sep 21, 2026 •

Copy link
Copy Markdown

中文

实现状态

本 PR 完成 Codex Switcher 的双语本地化基础,并基于最终依赖栈 #180、#182、#185、#184、#186 和 #183 完成整合。

  • 桌面语言偏好由 AppSettings.ui_language_preference 作为唯一持久化 authority,支持 System default、English、简体中文;解析后的 en-US / zh-CN 同步到主窗口、托盘 webview、原生应用菜单和原生托盘菜单。
  • 浏览器语言偏好只属于当前浏览器,支持 Browser default、English、简体中文。当前 JS session 的 in-memory preference 是唯一 semantic authority;localStorage 只做尽力持久化。显式选择先更新内存,即使 storage 不可用,React、设置 picker 和 web-secret prompt 也保持一致;Browser default 才按 navigator.languages 的第一个受支持语言解析,且不会写入桌面设置。
  • Desktop 安装身份只由 initialize_app_settings() 决定:新安装持久化 System,已有 pre-localization 安装及缺少语言字段的 legacy settings 持久化 English,显式偏好和 legacy language 字段保持不变。普通 runtime read 只读取 AppSettings;缺失 settings 时固定使用 English safe fallback,绝不再依据 accounts.json 重新推断语言。设置写入继续复用 locked/atomic persistence。
  • 明确区分简体与繁体环境,覆盖 zh-CN、zh_CN、zh-Hans、zh-Hans-*、zh-SG,并让 zh-TW、zh-HK、zh-MO、zh-Hant*、裸 zh 回退到 English。
  • 使用显式 React render-time translation、双 JSON catalog、已迁移 surfaces 的语义化文案和 i18next 插值/复数;不使用 DOM observer 或 DOM 重写。
  • 原生应用菜单和托盘菜单使用稳定的语义化 native text keys,再由统一映射提供英文与简体中文,避免把英文显示文案当作状态标识。
  • 补齐设置、备份、浏览器 secret、强制关闭、reset credits、usage stats、可访问名称和 native surface 文案;保持原始账号、错误、路径、provider 值及日期/数字/时区格式不变。
  • 语言写入成功后返回 authoritative DisplaySettings,先尽力投影 native menu/tray,再通过现有 app-settings-changed 机制通知 webview;投影失败不会伪报持久化失败。
  • 初始化与读取失败只记录日志,不阻塞 Desktop 启动;原生应用菜单、托盘和 React 首屏使用 English safe fallback,并可在 storage 恢复后通过既有 settings 流程 reconciliation。document.documentElement.lang 仅是已解析语言的 projection,不是 browser authority。
  • 分支按最终依赖栈 fix(web): bound request concurrency #180(597b970)、fix(storage): make state writes atomic and single-writer #182(2fadfc7)、fix(auth): align OAuth refresh policy with Codex #185(7aa083b)、fix(auth): make activation a single transaction #184(5a606f6)、feat(warmup): persist host-owned scheduling policy #186(c66cc5e)和 feat(backup): add user-passphrase encryption #183(d52e172)验证,保留 web boundary、passphrase backup、auth transaction、OAuth refresh 和 Rust-owned warm-up scheduler 的既有行为。

验证

  • pnpm test:i18n:8 项通过
  • pnpm test:desktop-reopen:7 项通过
  • pnpm test:browser-runtime:2 项通过
  • pnpm test:close-preference:3 项通过
  • pnpm test:reset-credits:3 项通过
  • pnpm test:warmup-policy:1 项通过
  • pnpm test:web-boundary:1 项通过
  • pnpm test:backup-passphrase:3 项通过
  • pnpm build:通过
  • cargo fmt --manifest-path src-tauri/Cargo.toml --all -- --check:通过
  • cargo check --locked --manifest-path src-tauri/Cargo.toml:通过
  • cargo test --locked --manifest-path src-tauri/Cargo.toml:101 项通过
  • git diff --check:通过

Closes #157


English

Implementation status

This PR completes the bilingual localization foundation for Codex Switcher on the final dependency stack: #180, #182, #185, #184, #186, and #183.

  • Desktop language preference has one persisted authority, AppSettings.ui_language_preference, with System default, English, and 简体中文. The resolved en-US / zh-CN language is projected to the main window, tray webview, native application menu, and native tray menu.
  • Browser language preference belongs only to the current browser, with Browser default, English, and 简体中文. The current JS session in-memory preference is the single semantic authority; localStorage is best-effort persistence only. Explicit choices update memory first, so React, the settings picker, and the web-secret prompt stay aligned even when storage is unavailable. Only Browser default resolves the first supported entry from navigator.languages, and it never writes desktop settings.
  • Desktop installation identity is decided only by initialize_app_settings(): new installs persist System; existing pre-localization installs and legacy settings without language persist English; explicit preferences and the legacy language field survive. Ordinary runtime reads only read AppSettings; a missing settings file always uses the English safe fallback and never re-infers language from accounts.json. Settings writes reuse locked/atomic persistence.
  • Simplified and Traditional Chinese are separated. The resolver covers zh-CN, zh_CN, zh-Hans, zh-Hans-*, and zh-SG; zh-TW, zh-HK, zh-MO, zh-Hant*, and bare zh fall back to English.
  • Translation uses explicit React render-time rendering, two JSON catalogs, semantic copy for the migrated surfaces, and i18next interpolation/pluralization. No DOM observer or DOM rewrite is used.
  • Native application and tray menus use stable semantic text keys mapped to English and Simplified Chinese, so translated display strings never become program state.
  • Settings, backup, browser-secret, force-close, reset-credit, usage-statistics, accessibility, and native-surface copy is localized. Raw account, error, path, provider, date, number, and timezone semantics remain unchanged.
  • Successful language writes return authoritative DisplaySettings, attempt native menu/tray projection before notifying webviews through the existing app-settings-changed mechanism, and do not report persistence failure when a projection fails.
  • Initialization and read failures are logged without blocking Desktop startup; native menus, the tray, and the React first screen use the English safe fallback and can reconcile through the existing settings flow when storage becomes usable. document.documentElement.lang is only a projection of the resolved language, never browser authority.
  • The branch was verified against the final dependency stack: fix(web): bound request concurrency #180 (597b970), fix(storage): make state writes atomic and single-writer #182 (2fadfc7), fix(auth): align OAuth refresh policy with Codex #185 (7aa083b), fix(auth): make activation a single transaction #184 (5a606f6), feat(warmup): persist host-owned scheduling policy #186 (c66cc5e), and feat(backup): add user-passphrase encryption #183 (d52e172), preserving the existing web boundary, passphrase backup, auth transaction, OAuth refresh, and Rust-owned warm-up scheduler behavior.

Validation

  • pnpm test:i18n: 8 passed
  • pnpm test:desktop-reopen: 7 passed
  • pnpm test:browser-runtime: 2 passed
  • pnpm test:close-preference: 3 passed
  • pnpm test:reset-credits: 3 passed
  • pnpm test:warmup-policy: 1 passed
  • pnpm test:web-boundary: 1 passed
  • pnpm test:backup-passphrase: 3 passed
  • pnpm build: passed
  • cargo fmt --manifest-path src-tauri/Cargo.toml --all -- --check: passed
  • cargo check --locked --manifest-path src-tauri/Cargo.toml: passed
  • cargo test --locked --manifest-path src-tauri/Cargo.toml: 101 passed
  • git diff --check: passed

Closes #157

Restores #168 after the source fork was accidentally deleted.


Maintenance triage — 2026-09-30

Prerequisite PRs: #178, #180, #182, #185, #184, #186, #183.

@Lampese Lampese added this to the Backlog: product improvements milestone Sep 30, 2026
@Lampese Lampese added enhancement New feature or request priority:P2 Normal maintenance or useful improvement after the P1 queue. area:desktop Desktop lifecycle, tray, UI, process handling and platform support. status:needs-review Maintainer review required; this label is not merge approval. status:needs-ci No check rollup is visible for the current PR head; validate before landing. status:blocked Depends on another open PR; prerequisite numbers are in the maintenance note. labels Sep 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:desktop Desktop lifecycle, tray, UI, process handling and platform support. enhancement New feature or request priority:P2 Normal maintenance or useful improvement after the P1 queue. status:blocked Depends on another open PR; prerequisite numbers are in the maintenance note. status:needs-ci No check rollup is visible for the current PR head; validate before landing. status:needs-review Maintainer review required; this label is not merge approval.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Simplified Chinese and UI language preferences

2 participants