Skip to content

feat(web-gui): full i18n support (react-i18next, EN/ZH-CN)#2115

Merged
jolestar merged 9 commits into
mainfrom
feat/web-gui-i18n-phase0
Jul 5, 2026
Merged

feat(web-gui): full i18n support (react-i18next, EN/ZH-CN)#2115
jolestar merged 9 commits into
mainfrom
feat/web-gui-i18n-phase0

Conversation

@jolestar

@jolestar jolestar commented Jul 5, 2026

Copy link
Copy Markdown
Collaborator

Summary

Add maintainable multi-language support to web-gui, enabling UI text to switch between English and Chinese (with clear extension points for additional languages).

Implementation

  • Library: react-i18next + i18next (Provider + hook pattern, namespace support, language detection, fallback)
  • Architecture:
    • src/i18n/ — config, I18nProvider, resource loading, type-safe key resolution
    • src/i18n/resources/en.ts & zh-CN.ts — centralized translation resources (740+ keys each)
    • Language preference persisted in local storage via existing runtime-store pattern
  • Coverage — all UI pages and components translated:
    • App shell & navigation (App.tsx)
    • Agent session page (turn labels, composer, model picker, message actions, activity trail)
    • Settings page (General/Models/Vision/Search/Advanced tabs, all form labels and descriptions)
    • Right panel (Agent overview, work items, tasks, skills, file browser)
    • Search, Skills, Agent Templates pages
    • Activity Inspector (detail labels, timeline, raw event)
    • Dashboard (metric labels, status badges)
    • StatusBadge/StatusChip component (state/scope values)
  • Not translated (by design): runtime events, API fields, provider/model IDs, log output, agent messages, tool output, markdown content

Verification

  • tsc --noEmit — zero errors
  • vitest run — 118/118 passing
  • Full scan: zero remaining hardcoded UI strings
  • Browser-verified: EN ↔ ZH-CN language switching works in real time across all pages

Changed Files

29 files changed, +2639 / -708 lines across 9 commits (Phase 0–5 + incremental fixes).

jolestar added 9 commits July 5, 2026 10:46
- Add i18next + react-i18next dependencies
- Create src/i18n/ module: config, types, resolve, I18nProvider
- Add en/zh-CN translation resource skeletons
- Add language selector in Settings > General tab
- Add key completeness and resolveLanguage tests
- Wrap App with I18nProvider in main.tsx

All 118 tests pass, typecheck clean, build succeeds.
The lock file was slightly out of sync with package.json after adding
i18next/react-i18next dependencies. Running npm install with a clean
node_modules reconciled the lock file (removed stale peer flag on
use-sync-external-store, re-resolved transitive deps).
…tibility

The previous npm install (npm 11.6.2/node v25) stripped @emnapi/core and
@emnapi/runtime peer dependency entries from the lock file, causing npm ci
to fail on npm 11.12.0/node v23 with 'Missing: @emnapi/core from lock file'.
Regenerated with matching npm version to preserve peer entries.
Phase 0 only set up the i18n framework and language selector without
connecting actual UI strings. This commit converts all page UI text
to use t() translation calls:

- App.tsx: navigation labels, dashboard, connection switcher,
  bootstrapping/missing-agent pages, create-agent modal
- DashboardPage, SearchPage, SkillsPage, TemplatesPage,
  AgentPage, SettingsPage, ActivityInspectorPanel
- Right panel components: AgentOverviewPanel, RightSidePanel,
  FileBrowserPanel
- Resource files: expanded en.ts/zh-CN.ts from ~18 to ~300+ keys
  covering all page namespaces

Verified: tsc passes, 118/118 tests pass, vite build succeeds,
browser test confirms language switch from English to Chinese
changes all converted UI text in real-time.
…RightPanel strings

Replace all remaining hardcoded English UI strings with t() calls:
- SettingsPage: header, runtime overview, model defaults, search section,
  provider status, OAuth section, advanced/raw config, diagnostics
- AgentOverviewPanel: empty states, worktree label, task detail Kind
- Add ~35 new translation keys to en.ts and zh-CN.ts

Verified: tsc --noEmit zero errors, vitest 118/118 passed
Phase 2: Agent conversation page (AgentPage.tsx)
- Timeline turn labels, composer aria/placeholder, thinking controls
- Model picker strings, message actions, activity trail, working indicator

Phase 3: Right panel deep coverage (AgentOverviewPanel, RightSidePanel)
- Agent overview: lifecycle, worktree/path, skills/tasks/work-items cards
- Skill manager panel: search, enable/disable, catalog refresh
- Work item detail, task detail, file browser panels

Phase 4: Search, Skills, Inspector
- SearchPage: preview labels via i18next for module-level functions
- SkillsPage: scope labels (Global/Workspace/Agent)
- ActivityInspectorPanel: detail labels, timeline, raw event sections

Phase 5: Dashboard + common components
- DashboardPage: aria-labels
- All sub-components now have useTranslation() or i18next.t() for module-level functions

Added ~80 new i18n keys across en/zh-CN (agent, inspector, dashboard,
searchPage, rightPanel namespaces).
Updated SearchPage.test.ts to initialize i18next for module-level t().
tsc --noEmit: zero errors. vitest: 118/118 passing.
- zh-CN.ts: 所有 "Agent" → "智能体"(AgentTemplate 类型名保留)
- AgentOverviewPanel.tsx: 修复 workspaceCount/effectiveCount i18n、
  manageSkillsDesc JSX 缺失花括号、"Refreshing…" 硬编码、
  WorkItemCard 缺少 useTranslation、compactMeta "current" 未翻译
- ActivityInspectorPanel.tsx: 52 处 labelledText 标签全部接入 i18next.t()
- SettingsPage.tsx: "API Key for" 硬编码 → t() 插值
- en.ts/zh-CN.ts: 新增 ~55 个 inspector.* keys + apiKeyFor key
- ActivityInspectorPanel.test.ts: 添加 i18next 初始化
- zh-CN: all standalone 'Agent' values → '智能体' (keep 'AgentTemplate' type name)
- Add agent.model/currentWork/scheduling keys (were missing, showed raw 'AGENT.*')
- Dashboard metric labels: use i18n keys instead of hardcoded English
  (Agents/Needs attention/Active tasks/Current work)
- Update runtime-store.ts, client.ts, fixtures.ts to store key paths
- DashboardPage.tsx: render metric.label via t()

Verified: tsc zero errors, vitest 118/118, browser ZH-CN switch confirmed
all dashboard + right panel labels translate correctly.
StatusBadge/AgentStateBadge had hardcoded English labels (Ready, Running,
Waiting, etc.) and raw scope/state values (workspace, agent, open, completed).
Added badge namespace to en.ts and zh-CN.ts, made StatusChip.tsx i18n-aware.
@vercel

vercel Bot commented Jul 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
holon Ready Ready Preview, Comment Jul 5, 2026 4:10pm

@jolestar jolestar left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Review Summary

Architecture: Clean i18n implementation with well-separated concerns — config.ts (init), I18nProvider.tsx (context + persistence), resolve.ts (language detection), types.ts (type-safe modes), and resources/ (translation files). Good use of react-i18next patterns.

Positives:

  • Type-safe LanguageMode / ResolvedLanguage with clear extension points
  • Resource key parity test (resources.test.ts) ensures en/zh-CN stay in sync
  • resolveBrowserLanguage test covers zh variants, en variants, fallback, and case insensitivity
  • Dashboard metrics converted cleanly: data layer stores i18n keys, component resolves via t()
  • escapeValue: false correct for React; useSuspense: false avoids Suspense boundary requirement
  • localStorage persistence with graceful fallback for SSR/unavailable environments
  • <em>Saving…</em> patterns properly converted to t("common.saving")
  • Test files properly initialize i18next in beforeAll

Non-blocking findings:

  1. Incomplete "Save" button translations — Several SettingsPage buttons translate "Saving…" via t("settings.saving") but leave "Save" / "Save Vision" hardcoded:

    • {runtimeConfigSaving ? t("settings.saving") : "Save"} → should use t("settings.save")
    • {runtimeConfigSaving ? t("settings.saving") : "Save Vision"} → should use t("settings.saveVision")
    • Dynamic labels like `Save ${definition.label}` and `Save ${providerId}` could use i18next interpolation: t("settings.saveProvider", { name }). The translation keys already exist in resources.
  2. Missed hint stringDuckDuckGo and native search do not need API keys. Add keys only for API-backed providers below. in SettingsPage remains hardcoded English.

  3. Minor duplicationreadStoredLanguageMode() in config.ts and readStoredMode() in I18nProvider.tsx do identical work; could share one function.

None of these are blocking — the architecture is sound and the untranslated strings can be addressed in a follow-up. The PR description claim of "zero remaining hardcoded UI strings" is slightly overstated but the core i18n infrastructure is production-ready.

@holonbot holonbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review Summary

This PR adds a comprehensive i18n layer (react-i18next + i18next) to the web-gui, covering most user-facing strings across Dashboard, Agent, Settings, Search, Skills, and Templates pages. The migration introduces a language selector (system / English / Chinese Simplified) and passes TFunction through utility helpers for consistent translation. Overall the architecture is clean — well-separated concerns with namespace-based resource files and a sensible fallback pattern in statusLabel().

Findings

1. Hardcoded English strings remain alongside translated counterparts (warn)
Several strings were missed during the migration pass:

  • "Save" and "Save Vision" buttons still use raw English while the loading variant correctly uses t("settings.saving").
  • "Disabled" appears raw while the adjacent enabled state uses t("settings.enabled") — the key t("settings.disabled") already exists.

2. Mixed useTranslation() vs direct i18next.t() in non-component code (warn)
groupTimelineTurns() and workingActivityLabel() in AgentPage.tsx use i18next.t() directly instead of the React hook. This works at call time but bypasses React's re-render cycle, so these labels won't update when the user switches languages without a full page re-render.

3. statusLabel() fallback robustness (nit)
The badgeKey !== translated check relies on i18next's default behavior of returning the key when no translation exists. Consider using t(badgeKey, { defaultValue: prettify(value) }) for explicit fallback safety.

What looks good

  • Language selector with system detection and manual override is a nice UX touch.
  • The describeStatus / statusLabel refactor to accept TFunction is pragmatic and avoids unnecessary React context dependency in pure functions.
  • Translation resource files are well-organized by namespace.
  • The fallback prettify() path in statusLabel handles unknown badge values gracefully.

@holonbot

holonbot Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Holon Run Report

@jolestar
jolestar merged commit e6e890d into main Jul 5, 2026
5 checks passed
@jolestar
jolestar deleted the feat/web-gui-i18n-phase0 branch July 5, 2026 16:19
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