Skip to content

feat(desktop): add 15-language i18n — hybrid JSON+TypeScript system - #38846

Closed
iaendi wants to merge 188 commits into
NousResearch:mainfrom
iaendi:feat/i18n-desktop-multilingual
Closed

feat(desktop): add 15-language i18n — hybrid JSON+TypeScript system#38846
iaendi wants to merge 188 commits into
NousResearch:mainfrom
iaendi:feat/i18n-desktop-multilingual

Conversation

@iaendi

@iaendi iaendi commented Jun 4, 2026

Copy link
Copy Markdown

Hybrid JSON + TypeScript i18n — 15 Desktop locales

Adds 11 new Desktop UI locales to the framework that landed in #38241, expanding from 4 to 15 languages. Uses a hybrid workflow that combines the translator-friendliness of JSON with the type safety of upstream's TypeScript framework.

New locales (11):
ko (한국어), de (Deutsch), es (Español), fr (Français), pt-BR (Português Brasil), ar (العربية), hi (हिन्दी), th (ภาษาไทย), vi (Tiếng Việt), it (Italiano), ru (Русский)

Architecture

locales/*.json              <- Canonical source (flat keys, translators edit these)
       |
       v
scripts/sync-locales.py     <- Validates keys -> generates defineLocale() .ts files
       |
       v
i18n/{code}.ts              <- Upstream TS framework (runtime, context, types)

How it works

  • JSON files (apps/desktop/src/locales/): flat key-value, ~850 Desktop UI strings each. Translators only edit JSON.
  • TypeScript framework (apps/desktop/src/i18n/): uses upstream's defineLocale() pattern. Missing keys fall back to English. TypeScript validates against the Translations interface.
  • Bridge script (apps/desktop/scripts/sync-locales.py): validates JSON keys against en.json, generates .ts files, updates catalog.ts/types.ts/languages.ts.
  • To add a new language: drop {code}.json into locales/ → add to KNOWN_LOCALES → run sync-locales.py.

Why hybrid

Concern Old JSON-only Upstream TS-only Hybrid (this PR)
Translator DX Drop JSON Must write TS JSON still works
Type safety None Full tsc check Full tsc check
Uses merged infra Custom Yes Yes
Add lang Drop JSON 4 files to edit JSON + config line
Automation Manual Manual TS sync-locales.py

Files

  • 15 JSON locale files + locales.test.ts + TRANSLATING.md
  • 11 new .ts locale catalogs (en/ja/zh/zh-hant preserved from upstream)
  • scripts/sync-locales.py
  • i18n/catalog.ts, types.ts, languages.ts updated

Closes the Russian Desktop locale gap in #40347.

Marvin added 14 commits June 4, 2026 11:52
- Add React Context-based i18n system (store/i18n.tsx, hooks/use-translation.ts)
- Support 7 languages: en, zh-CN, ja, ko, de, es, fr
- Auto-detect system language, manual override with persistence
- Complete Chinese (zh-CN) translations — 786/799 keys (98.4%)
- Japanese and Korean partial translations (21 keys each)
- German, Spanish, French as extensible English-stub locale files
- Language selector in Settings → Appearance
- Module-level _currentLocale for non-React code compatibility
- localeLoaders map for easy new language registration
- ~50 components updated: settings, shell, chat, overlays, sidebars, command center

Closes NousResearch#37897, NousResearch#38064, NousResearch#37793, NousResearch#37562, NousResearch#37543, NousResearch#37503, NousResearch#37295, NousResearch#33749
…iptions

- Translate color modes: Light→浅色, Dark→深色, System→跟随系统
- Translate theme labels: Midnight→午夜, Ember→余烬, Mono→单色, etc.
- Add translateProviderDesc() for backend API key descriptions
- 27 provider description mappings (DeepSeek, OpenAI, Anthropic, etc.)
- Provider names preserved as-is (user request)
- Add 45 new i18n keys to en.json and zh-CN.json
- Fix 53 provider description mappings to match exact backend config.py strings
- Add missing providers: NVIDIA, LM Studio, StepFun, Arcee, GMI Cloud,
  Ollama Cloud, AWS Bedrock, Azure Foundry, Exa, Parallel, and more
- Add base URL override translations
- All descriptions now correctly matched and translated to zh-CN
- Previous approach used exported function in i18n.tsx — was tree-shaken by Vite
- New approach: module-level PROVIDER_DESC_MAP + providerDesc() in keys-settings.tsx
- Import t as providerT from @/store/i18n for standalone lookup
- 38 provider descriptions mapped to i18n keys
- No changes to i18n.tsx — keeps it clean
- Build verified: tsc + vite pass, function present in bundle
To add a new language, just drop a {code}.json file into src/locales/.
No code changes needed — i18n.tsx auto-discovers all *.json files at build time.

- Replace hardcoded localeLoaders with import.meta.glob
- SUPPORTED_LOCALES now computed from available files
- LANGUAGE_LABELS centralized in i18n store
- Locale type simplified from union to string
- appearance-settings uses availableLocales from context
- Keys revert to raw description (API key translation deferred)
…anish, French

All 7 languages now have full 857-key coverage:
- en: 857/857 (source)
- zh-CN: 843/857 (98%)
- ja: 825/857 (96%)
- ko: 832/857 (97%)
- de: 804/857 (94%)
- es: 822/857 (96%)
- fr: 811/857 (95%)

Remaining untranslated keys are intentional (language names, technical terms)
…globe icon

- Add zh-TW locale with 857 keys (Simplified→Traditional conversion)
- Add globe icon LanguageMenuButton to titlebar (right cluster)
- Language switcher shows all 8 languages with native names
- Active language highlighted with check mark
- Default follows system language
- Accidental  escape fixes in titlebar-controls.tsx
…age icon

- Fill remaining untranslated keys in ja, ko, de, es, fr (reaching 98-100% coverage)
- Rename zh-TW.json to zh-Hant.json (neutral Traditional Chinese)
- Update all 8 locale files with language.zh-Hant key
- Replace Globe icon with Language icon (Tabler IconLanguage)
- Add Language to icons.ts exports
- Delete legacy zh-TW.json
…le sync

- LanguageMenuButton: Globe 🌐 + Language 文A side-by-side, w-auto width
- Titlebar gap: gap-x-1 → gap-x-1.5 for better spacing
- useLocaleSync() hook: forces re-render of standalone t() consumers
- I18nProvider: onLocaleChange listener system for non-Context components
- TitlebarControls + useStatusbarItems now use useLocaleSync()
- Eliminates the 30s+ delay for Gateway/Agents/Cron statusbar labels
- IconWorld has clear grid lines, doesn't look like a microphone
- Added WorldGlobe to @/lib/icons.ts
- Replaced tabler IconWorld (tree-shaken in bundle) with inline SVG
- SVG: circle + equator line + two meridian arcs
- Same size and styling as other titlebar icons
…ale delay

- Language button: focus-visible:outline-0 to remove orange ring
- useLocaleSync() now returns version number for useMemo deps
- useStatusbarItems: add localeVersion to all useMemo dep arrays
- This ensures Gateway/Agents/Cron labels update instantly on locale change
@iaendi iaendi changed the title feat(desktop): add multilingual i18n support with 8 languages feat(desktop): add multilingual i18n support with 9 languages (incl. pt-BR) Jun 4, 2026
@alt-glitch alt-glitch added the sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state label Jul 18, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Re-triage: the live PR scope is 522 files, including unrelated core agent, gateway, CLI, platform, tool, CI, and packaging deltas beyond the Desktop i18n feature. It also removes background-review cache-parity regression coverage while merged #64379 retains the production repair. Please rebase and split before review; related work is #38083, #38183, and #64379.

@iaendi

iaendi commented Jul 18, 2026

Copy link
Copy Markdown
Author

Thanks for the re-triage, @alt-glitch. A few notes:

  1. 522 files concern: Understood. The branch has accumulated cruft from repeated upstream syncs. We will rebase and split into a clean foundation PR with only Desktop i18n files (locales/, i18n/, scripts/sync-locales.py, store/i18n.tsx, plus the test files). The non-i18n deltas (core agent, gateway, CLI, platform, packaging) will be stripped.

  2. Background-review cache regression: Ack. We will ensure the cache-parity regression coverage from fix(background_review): inherit parent's reasoning_config for Anthropic cache parity (salvage #30532) #64379 is preserved in the rebased branch rather than removed.

  3. Rebase-and-split approach: We will open a new clean PR against current main with the narrowed scope and reference it here. Target: pure i18n foundation (JSON locale catalogs + TS bridge + language switcher), no unrelated changes.

  4. Related PRs: TUI desktop app ignores display.language setting (no i18n support) #38083 (TUI display.language), feat(desktop): add i18n infrastructure with French (fr) support #38183, fix(background_review): inherit parent's reasoning_config for Anthropic cache parity (salvage #30532) #64379 -- noted for coordination.

Will update once the clean PR is ready. Appreciate the detailed triage.

@teknium1 teknium1 added the area/i18n Localization, locales, translations label Jul 19, 2026
@alt-glitch alt-glitch added tool/file File tools (read, write, patch, search) provider/bedrock AWS Bedrock (boto3, IAM) provider/copilot GitHub Copilot (ACP + Chat) provider/gemini Google Gemini (AI Studio, Cloud Code) provider/nous Nous Research API (OAuth) provider/openai OpenAI / Codex Responses API provider/xai xAI (Grok) area/auth Authentication, OAuth, credential pools area/sessions Session lifecycle, resume, persistence, history area/streaming Streaming responses: gateway delivery, provider wire sweeper:risk-caching Sweeper risk: may break/degrade prompt caching or cache-key stability (invariant) and removed platform/windows Native Windows-specific behavior or breakage backend/modal Modal.com cloud execution comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard comp/gateway Gateway runner, session dispatch, delivery labels Jul 23, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Re-triage on current head 1d343bb: the i18n implementation is sound, but this branch is DIRTY and contains 522 files with only 38 i18n-scoped. Please rebase and split to a Desktop-i18n-only PR, and restore the unrelated background-review reasoning_config cache-parity coverage relied on by #64379. Earlier TSX and sync-script findings were corrected on the current head.

Marvin added 2 commits July 23, 2026 10:44
…ey alignment, [skip ci]

- sync-locales.py: add TS type annotations (count:number, label:string etc)
- sync-locales.py: fix single-param arrow fn parentheses in object literal
- JSON locales: remove keys not in upstream Translations type
- JSON locales: add {provider}/{count} placeholders for function-typed keys
- JSON locales: rename mismatched keys (noProfilesYet→noProfiles etc)
- submit.ts: fix 6 merge artifact syntax errors
- upstream sync: resolve merge conflicts, push behind_by→0
@iaendi

iaendi commented Jul 23, 2026

Copy link
Copy Markdown
Author

Thanks for the re-triage @alt-glitch. Done:

-> Split to i18n-only PR: #69819 — 35 files, Desktop-i18n-only scope (down from 522)
-> Branch rebased on current upstream main (ad8c060)
-> 0 i18n type errors
-> No core agent/gateway/CLI/platform/CI deltas
-> All 15 languages at key parity with English fallback

The old branch (feat/i18n-desktop-multilingual) accumulated cruft from repeated upstream merges. The new clean branch contains only:

  • JSON locale catalogs + sync-locales.py bridge
  • Generated TS locale files with full type annotations
  • i18n store (i18n.tsx, use-locale-sync.ts)
  • Locale key parity test

@iaendi

iaendi commented Jul 23, 2026

Copy link
Copy Markdown
Author

Superseded by #69819 — clean i18n-only split as requested. All 15 languages ported, 0 type errors, 35 files (down from 522).

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

Labels

area/i18n Localization, locales, translations comp/desktop Electron desktop app (apps/desktop/*) needs-decision Awaiting maintainer decision before any implementation P3 Low — cosmetic, nice to have sweeper:blast-broad Sweeper blast radius: broad — a core path most sessions hit type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants