Skip to content

feat(i18n): localize backend (main-process) strings, default Chinese#256

Open
craigamcw wants to merge 1 commit into
OpenCoworkAI:mainfrom
craigamcw:feat/backend-i18n-pr
Open

feat(i18n): localize backend (main-process) strings, default Chinese#256
craigamcw wants to merge 1 commit into
OpenCoworkAI:mainfrom
craigamcw:feat/backend-i18n-pr

Conversation

@craigamcw

Copy link
Copy Markdown

What

The renderer is fully localized, but a handful of strings are produced by the main process and reached the UI hardcoded in Chinese, untranslated:

  • the model/network error messages (agent-runner-message-end.ts — timeout, empty result, 400/401/429/5xx, network interrupted, and the retry/​check-config hints),
  • the "no usable credentials" prompt shown when starting a session on an unconfigured set (index.ts),
  • the startup-failure dialog (index.ts),
  • the default config-set name (默认方案) and the numbered fallback name (config-store.ts).

This adds a small main-process i18n layer so those strings follow the user's chosen language, while keeping Chinese as the default and ultimate fallback (the product is Chinese-first).

How

  • src/main/i18n/catalog.ts — backend message catalog for all 12 shipped languages. zh is the source; the other 11 (en, es, fr, de, it, uk, pl, sv, no, nl, ro) are translations. Keys cover only the backend strings above.
  • src/main/i18n/index.tsmt(key, params) translator: {{placeholder}} interpolation, locale normalization (es-ES → es, zh-CN → zh, nb/nn → no), and zh → key fallback. setBackendLanguage() switches the active language.
  • Language plumbing — the renderer mirrors its active react-i18next language into a new config.uiLanguage; the main process seeds setBackendLanguage() from the persisted value on launch and updates it whenever the config is saved. uiLanguage is threaded through normalizeConfig/update so it round-trips.
  • The hardcoded strings are routed through mt().

Behavior / compatibility

  • Default is unchanged: with no language synced, mt() returns the original Chinese text — existing Chinese users see exactly what they see today.
  • A language switch does not reload the agent runner or any session (uiLanguage is not part of the agent runtime signature), so switching is side-effect-free for in-flight work.
  • Persisted config-set names are data, so an existing set named 默认方案 keeps its stored name; the localization applies to the name chosen at creation time. (Live display-translation of system set names could be a small renderer follow-up if desired.)

Tests

  • src/tests/i18n/backend-i18n.test.ts — asserts catalog key-parity across all 12 languages, placeholder/underscore preservation, mt() interpolation, locale normalization, and the zh/key fallbacks. tsc --noEmit is clean.

Notes

This completes the localization story started in #253 (renderer European locales) by covering the main-process strings, but it is self-contained — it ships its own catalog and does not depend on #253.

🤖 Generated with Claude Code

The renderer is fully localized, but strings produced by the main process —
model/network error messages, the "no usable credentials" prompt, the
startup-failure dialog, and the default config-set name — were hardcoded in
Chinese and reached the UI untranslated.

This adds a small main-process i18n layer so those strings follow the user's
language while keeping Chinese as the default and ultimate fallback (the
product is Chinese-first):

- src/main/i18n/catalog.ts — backend message catalog for all 12 shipped
  languages (zh is the source; the other 11 are translations).
- src/main/i18n/index.ts — mt(key, params) translator with {{placeholder}}
  interpolation, locale normalization (es-ES -> es, nb/nn -> no), and
  zh -> key fallback. setBackendLanguage() switches the active language.
- The renderer mirrors its react-i18next language into config.uiLanguage; the
  main process seeds setBackendLanguage() from the persisted value on launch
  and updates it whenever the config is saved.
- Routed the hardcoded strings in agent-runner-message-end.ts, index.ts and
  config-store.ts through mt().

Behavior is unchanged by default: with no language synced, mt() returns the
original Chinese text. A key-parity + behavior unit test is included.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Review mode: initial

Findings

  • [Minor] Module-level mt() call in config-store.ts produces a static string at import time, preventing live language changes from retroactively affecting the default config set name. This is intentional per the PR description ("Persisted config-set names are data"), but consider documenting it more explicitly near the defaultConfigSet definition to avoid future confusion that the name is frozen at module load time. (src/main/config/config-store.ts:223)

  • [Minor] The normalizeBackendLanguage function does not trim whitespace from the input. If a locale tag with surrounding whitespace is passed (e.g., " en "), it gets split-corrected, but leading whitespace would cause the first split element to be " en" which would not match any catalog key, falling through to the default. This is defensible since input comes from react-i18next (which trims), but adding a lang = lang.trim() before the fallback check would harden the function against unexpected input. (src/main/i18n/index.ts:22)
    Suggested fix:

    +  if (!lang) return DEFAULT_BACKEND_LANGUAGE;
    +  lang = lang.trim();

Questions

  • None.

Summary

Review mode: initial

Well-structured PR with proper test coverage, no regressions, no new security concerns. The i18n layer is cleanly separated, the language sync path from renderer through config to backend translator is correct, and the fallback behavior (Chinese default) preserves existing behavior. The static nature of defaultConfigSet.name is intentional and documented. No blockers or major issues found.

Testing

src/tests/i18n/backend-i18n.test.ts covers catalog key parity, placeholder presence, mt() interpolation, locale normalization, and fallback logic. Additional tests for the renderer-to-main language sync path (e.g., simulated renderer language change IPC) and for the error message interpolation with edge-case error texts (e.g., error containing {{error}} literal) would improve confidence, but the current coverage is adequate for an initial implementation.

Open Cowork Bot

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant