Skip to content

Complete client UI i18n migration#1282

Open
chyendongnhanh338 wants to merge 84 commits into
getpaseo:mainfrom
chyendongnhanh338:feat/client-i18n
Open

Complete client UI i18n migration#1282
chyendongnhanh338 wants to merge 84 commits into
getpaseo:mainfrom
chyendongnhanh338:feat/client-i18n

Conversation

@chyendongnhanh338
Copy link
Copy Markdown

@chyendongnhanh338 chyendongnhanh338 commented Jun 2, 2026

Linked issue

Fixes #273.
Fixes #1069.
Fixes #1162.

This PR covers the staged client i18n framework and the current client-owned UI migration through Batch 4Z. The remaining untranslated text is intentionally outside client-owned UI copy: agent/daemon/protocol output, terminal/log/code/user content, provider/catalog metadata, shortcut key names, runtime enum values, file paths, branch/PR names, URLs, and raw server/runtime errors.

Design and implementation plan are included in:

  • docs/superpowers/specs/2026-06-01-client-i18n-design.md
  • docs/superpowers/plans/2026-06-01-client-i18n.md
  • docs/superpowers/specs/2026-06-02-full-client-ui-i18n-design.md
  • docs/superpowers/plans/2026-06-02-full-client-ui-i18n-batch-1.md
  • docs/superpowers/plans/2026-06-02-full-client-ui-i18n-batch-2.md
  • docs/superpowers/plans/2026-06-02-full-client-ui-i18n-batch-3a.md
  • docs/superpowers/plans/2026-06-02-full-client-ui-i18n-batch-3b.md
  • docs/superpowers/plans/2026-06-02-full-client-ui-i18n-batch-4a.md
  • docs/superpowers/plans/2026-06-02-full-client-ui-i18n-batch-4b.md

Type of change

  • Bug fix
  • New feature (with prior issue + design alignment)
  • Docs
  • Refactor / code improvement

What does this PR do

Adds client i18n infrastructure and migrates the current client-owned UI copy:

  • Adds translation resources for the six UN official languages with parity coverage and scanner coverage for recurring local fallback errors. Arabic, French, Russian, and Spanish now have full client-owned UI resource coverage, with generated translations guarded by key parity, fallback-ratio, and interpolation-placeholder tests.
  • Wires the app-level i18n provider, persists the Settings -> General language preference, and resolves system locales to supported locales: Arabic, Chinese, English, French, Russian, and Spanish.
  • Migrates app shell, Settings, Composer, agent workflow chrome, workspace panels, workspace tabs, Git/PR chrome, sidebar/project/workspace menus, onboarding/pairing, provider/model selection chrome, keyboard shortcuts help, sessions, message utilities, desktop settings/permissions/update/daemon chrome, autocomplete/attachments, utility defaults, and remaining local wrapper/accessibility fallback text.
  • Tightens the translation boundary: React components and custom hooks use useTranslation() / t(...); pure helpers, module-level utilities, stores, policies, and non-React fallbacks use direct i18n.t(...) where a hook cannot be used.
  • Documents staged migration progress through Batch 4Z in docs/i18n.md.

Provider/model names, provider catalog metadata, provider-defined labels/descriptions, project/host/workspace/user labels, script names and commands, diagnostic output, pairing URLs, endpoint values, protocol parser errors, daemon connection details, file paths, URLs, branch names, PR titles/bodies, check names, workflow names, diff contents, commands, logs, terminal output, shortcut key names, shortcut combos, binding IDs, action IDs, agent output, raw runtime/server error details, caller-provided override labels, and protocol/server diagnostics remain untranslated by design.

How did you verify it

Local verification run on feat/client-i18n:

  • Review-fix RED checks first failed as expected for Traditional Chinese locale mapping and missing init/language-sync helpers.
  • UN-language RED checks first failed as expected for unsupported ar/es/fr/ru locales, missing resource files, and fallback-heavy generated resources.
  • node node_modules/vitest/vitest.mjs run packages/app/src/i18n/locales.test.ts packages/app/src/i18n/sync-language.test.ts packages/app/src/i18n/init.test.ts packages/app/src/i18n/resources.test.ts packages/app/src/composer/actions.test.ts packages/app/src/composer/submit.test.ts packages/app/src/client-slash-commands/index.test.ts packages/app/src/git/policy.test.ts packages/app/src/panels/agent-panel-descriptor.test.tsx packages/app/src/composer/agent-controls/utils.test.ts --bail=1 passed: 10 test files, 133 tests.
  • node node_modules/@typescript/native-preview/bin/tsgo.js --noEmit -p packages/app/tsconfig.json --pretty false passed.
  • node_modules/oxlint/bin/oxlint passed with 0 warnings and 0 errors.
  • node_modules/oxfmt/bin/oxfmt --check docs/i18n.md packages/app/src/i18n passed.
  • git diff --check passed.
  • Web/Desktop Chrome screenshots captured with a targeted Playwright run and saved locally under /tmp/paseo-i18n-screenshots/:
    • settings-general-english.png
    • settings-language-menu-un-official.png
    • settings-general-spanish.png
    • settings-general-french.png
    • settings-general-russian.png
    • settings-general-arabic.png
  • Native iOS/Android screenshots were not captured in this environment because no simulator/device tooling is available (adb, emulator, and xcrun are absent).

Current local npm wrapper scripts could not resolve their binaries in this install:

  • npm run test --workspace=@getpaseo/app -- <targeted files> failed before running tests with sh: 1: vitest: not found.
  • npm run typecheck --workspace=@getpaseo/app failed before typecheck with sh: 1: tsgo: not found.
  • npm run lint -- packages/app/src/i18n/resources/en.ts failed before lint with sh: 1: oxlint: not found.
  • npm run format:check:files -- docs/i18n.md packages/app/src/i18n/resources/en.ts failed before format check with sh: 1: oxfmt: not found.

Checklist

  • One focused change. Unrelated cleanups split out.
  • npm run typecheck passes or an equivalent direct typecheck command passed
  • npm run lint passes or an equivalent direct lint command passed
  • npm run format ran (or equivalent direct formatter check passed)
  • UI changes include screenshots or video for every affected platform
  • Tests added or updated where it made sense

@chyendongnhanh338 chyendongnhanh338 marked this pull request as ready for review June 2, 2026 01:09
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Jun 2, 2026

Greptile Summary

This PR adds a complete client-side i18n framework with English and Simplified Chinese translations, wires an I18nProvider into the app's provider tree, persists language preference in settings, and migrates a large portion of the client UI string literals to translation keys across ~90 files.

  • Adds packages/app/src/i18n/ with i18next singleton, locale resolution logic, React provider, and EN/ZH-CN resource files; backs language preference with the existing AppSettings store including validation and round-trip tests.
  • Migrates UI strings across agent controls, composer, sidebar, settings screens, workspace panels, git pane, onboarding, and keyboard-shortcuts dialog to useTranslation() / t() calls; adjusts pure helper functions to accept labels as parameters for testability.
  • Adds a key-parity test enforcing EN/ZH-CN sync, unit tests for locale resolution and label helpers, and an E2E smoke test verifying the language selector switches labels at runtime.

Confidence Score: 5/5

Safe to merge; the i18n layer is purely additive, all existing behaviour is preserved, and the key-parity test guards translation completeness.

The change is well-structured: a clear packages/app/src/i18n/ module owns the feature core, integration points are few and named (provider wrapper, settings field, useTranslation call sites), and pure helper functions accept labels as parameters rather than reaching into the i18n runtime. Tests cover locale resolution, key parity, label helpers, and an E2E round-trip. No existing runtime paths are broken.

The two previously-flagged issues in packages/app/src/i18n/i18next.ts (silenced init errors) and packages/app/src/i18n/provider.tsx (first-render flash) remain open from earlier review rounds and are worth a follow-up.

Important Files Changed

Filename Overview
packages/app/src/i18n/i18next.ts New i18n singleton — initializes with hardcoded lng: "en" and drops the init promise without a .catch(), both of which were flagged in previous threads.
packages/app/src/i18n/locales.ts Locale types and resolution logic; startsWith("zh-") mapping to zh-CN was flagged in a previous thread but otherwise clean.
packages/app/src/i18n/provider.tsx React I18n provider; first-render flash issue was flagged in a previous thread; otherwise minimal and correct.
packages/app/src/i18n/resources/en.ts 1338-line English resource file; contains two dead duplicate keys (common.back and common.states.loading) that are never referenced in code.
packages/app/src/i18n/resources/zh-CN.ts Simplified Chinese translations with full parity to en.ts; includes the same dead keys mirroring en.ts.
packages/app/src/hooks/use-settings/storage.ts Adds language: AppLanguage field to AppSettings with correct validation via parseAppLanguage; unknown values fall back to "system".
packages/app/e2e/settings-i18n.spec.ts E2E smoke test for language switching; language-selection interactions are raw Playwright calls inline in the test body rather than helper-wrapped domain actions.
packages/app/src/git/worktree-archive-warning.ts Correctly converts hardcoded strings to a WorktreeArchiveWarningLabels parameter with an English-default constant; both callers pass translated labels.
packages/app/src/components/agent-list.tsx Refactors date-section bucketing to use stable DateSectionKey enum values; translates status labels, badges, and archive sheet via useTranslation().
packages/app/src/composer/agent-controls/utils.ts Converts getAgentControlHint to getAgentControlHintKey (returns translation key strings); adds optional fallbackLabels to resolveAgentModelSelection with English defaults.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[App boot] --> B[RootProviders]
    B --> C[QueryProvider]
    C --> D[I18nProvider]
    D --> E{settings.language}
    E -->|system| F[getSystemLocales]
    F --> G[resolveSupportedLocale]
    G -->|zh-* match| H[zh-CN]
    G -->|no match| I[en fallback]
    E -->|en| I
    E -->|zh-CN| H
    H --> J[i18n.changeLanguage]
    I --> J
    J --> K[I18nextProvider]
    K --> L[App children - useTranslation hooks]
    M[AppSettings storage] -->|language field| E
    N[AsyncStorage] <-->|persist / load| M
Loading

Reviews (13): Last reviewed commit: "feat: translate sessions and agent list ..." | Re-trigger Greptile

Comment thread packages/app/src/i18n/provider.tsx Outdated
Comment on lines +33 to +38
for (const locale of systemLocales) {
const normalized = locale.toLowerCase();
if (normalized === "zh" || normalized.startsWith("zh-")) {
return "zh-CN";
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Traditional Chinese locales mapped to Simplified Chinese

startsWith("zh-") matches zh-TW, zh-Hant, zh-HK, and other Traditional Chinese BCP-47 tags, silently promoting them all to zh-CN (Simplified Chinese). Traditional Chinese speakers whose system locale is zh-TW would receive Simplified Chinese UI instead of falling back to English — which they may actually prefer. If this mapping is intentional, a comment explaining the decision would help; otherwise, restricting the match to Simplified-Chinese tags (zh-CN, zh-Hans) avoids surprising Traditional Chinese users.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in abc61a4. resolveSupportedLocale now maps only generic zh, zh-CN, and zh-Hans* tags to zh-CN; Traditional Chinese tags such as zh-TW, zh-Hant, and zh-HK now fall back to English. Added locale resolver test coverage for those tags.

Comment thread packages/app/src/i18n/i18next.ts Outdated

const i18n = createInstance();

void i18n.use(initReactI18next).init({
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Init errors silently discarded

void i18n.use(initReactI18next).init(...) drops the returned promise without .catch(). If initialization fails for any reason (e.g., a plugin conflict or a misconfigured option), the i18n instance will be left in an uninitialized state and all t() calls will silently return the raw translation keys. Attaching an error handler (even a console.error) would make failures detectable in development.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in abc61a4. i18next initialization is now wrapped with observeI18nInit(...), which attaches a catch handler and reports initialization failures. Added init.test.ts coverage for reporting rejected init promises.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Jun 2, 2026

Want your agent to iterate on Greptile's feedback? Try greploops.

@chyendongnhanh338 chyendongnhanh338 changed the title Add client i18n settings slice Complete client UI i18n migration Jun 2, 2026
@chyendongnhanh338
Copy link
Copy Markdown
Author

Web screenshots captured with Playwright for the client i18n UI changes:

View Screenshot
Settings general - English Settings general - English
Language menu - UN official languages Language menu - UN official languages
Settings general - Spanish Settings general - Spanish
Settings general - French Settings general - French
Settings general - Russian Settings general - Russian
Settings general - Arabic Settings general - Arabic

Notes:

  • Web screenshots were generated locally via Playwright.
  • iOS/Android screenshots were not captured in this environment because the mobile simulator tooling is unavailable.
  • The public image-hosting links above are hosted on litterbox.catbox.moe and expire after 72 hours.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant