# Internationalization (i18n) Architecture and Usage This guide documents how localization works in the Agent Dashboard, including architecture, resources, runtime behavior, testing, and rollout. **Supported languages:** English (`en`), Chinese (`zh`), Vietnamese (`vi`), Korean (`ko`) --- ## 1) Architecture Overview Localization is implemented in the frontend with `i18next` + `react-i18next` and browser language detection. ```mermaid flowchart TB subgraph Browser User["User"] LS["localStorage
i18nextLng"] Nav["navigator.language"] end subgraph ClientApp["React Client"] Detector["i18next-browser-languagedetector"] I18n["i18n init
client/src/i18n/index.ts"] NS["Namespace bundles
common/nav/dashboard/..."] UI["Pages + components
useTranslation()"] Format["format.ts
locale-aware date/number/model-name"] end User --> UI LS --> Detector Nav --> Detector Detector --> I18n I18n --> NS NS --> UI I18n --> Format ``` **Key runtime facts** - `supportedLngs`: `["en", "zh", "vi", "ko"]` - `fallbackLng`: `"en"` - `nonExplicitSupportedLngs`: `true` (e.g. `vi-VN` resolves to `vi`) - Detection order: `localStorage` → `navigator` --- ## 2) Resource and Namespace Strategy Translation resources are stored per language and namespace: - `client/src/i18n/locales/en/*.json` - `client/src/i18n/locales/zh/*.json` - `client/src/i18n/locales/vi/*.json` - `client/src/i18n/locales/ko/*.json` Active namespaces: - `common` - `nav` - `dashboard` - `sessions` - `activity` - `analytics` - `workflows` - `settings` - `kanban` - `errors` ```mermaid erDiagram LANGUAGE ||--o{ NAMESPACE : contains NAMESPACE ||--o{ KEY : defines KEY ||--o{ TRANSLATION : maps_to LANGUAGE { string code "en|zh|vi" string locale "en-US|zh-CN|vi-VN|ko-KR" } NAMESPACE { string name "common|nav|dashboard|..." string file_path "locales/{lang}/{namespace}.json" } KEY { string id "dot.notation.or.leaf" string type "string|pluralized" } TRANSLATION { string value "localized text" } ``` **Strategy notes** - Keep namespace boundaries page/domain focused. - Keep key parity across `en`, `zh`, `vi` files for the same namespace. - Keep fallback behavior deterministic by ensuring `en` is always complete. --- ## 3) Key Naming Conventions Use stable semantic keys, not English sentence literals. ### Convention rules 1. Use namespace-scoped keys: `namespace:key` 2. Use lower camelCase key segments 3. Keep terminology consistent across locales (for example, keep `Agent` / `Subagent` terms stable where required) 4. Use suffixes for plurals when needed (e.g. `_plural`) 5. Group nested concepts by domain (e.g. `time.justNow`, `time.mAgo`) ### Examples - `nav:dashboard` - `nav:languageNames.vi` - `common:time.justNow` - `common:time.mAgo` - `kanban:agentCount` - `kanban:agentCount_plural` ```mermaid classDiagram class I18nConfig { +supportedLngs: ["en","zh","vi"] +fallbackLng: "en" +defaultNS: "common" +detectionOrder: ["localStorage","navigator"] } class NamespaceResource { +languageCode +namespace +jsonFilePath +keys[] } class ReactComponent { +useTranslation(namespace) +t(key, params) } class SidebarLanguageSwitch { +SUPPORTED_LANGUAGES +normalizeLanguage() +changeLanguage() } class FormatUtils { +getCurrentLocale() +formatTime() +formatDateTime() +fmtCostFull() +formatModelName() } I18nConfig --> NamespaceResource ReactComponent --> I18nConfig ReactComponent --> NamespaceResource SidebarLanguageSwitch --> I18nConfig FormatUtils --> I18nConfig ``` --- ## 4) Language Detection and Switching Flow The sidebar language controls call `i18n.changeLanguage()` and UI updates reactively through `useTranslation`. ```mermaid sequenceDiagram participant U as User participant SB as Sidebar.tsx participant I as i18next participant LD as LanguageDetector participant NS as Locale Resources participant UI as React Components U->>SB: Click language button (EN/ZH/VI/KO) SB->>I: changeLanguage("vi") I->>NS: Resolve namespace bundles NS-->>I: Return translations I->>LD: Persist i18nextLng in localStorage I-->>UI: Trigger rerender UI->>UI: Re-evaluate t(...) keys UI-->>U: Localized labels displayed ``` ```mermaid stateDiagram-v2 [*] --> Detecting Detecting --> Loaded_en: localStorage/navigator resolves en Detecting --> Loaded_zh: localStorage/navigator resolves zh Detecting --> Loaded_vi: localStorage/navigator resolves vi Detecting --> Loaded_ko: localStorage/navigator resolves ko Detecting --> Loaded_en: unsupported locale -> fallback en Loaded_en --> Loaded_zh: user switches to zh Loaded_en --> Loaded_vi: user switches to vi Loaded_zh --> Loaded_en: user switches to en Loaded_zh --> Loaded_vi: user switches to vi Loaded_vi --> Loaded_en: user switches to en Loaded_vi --> Loaded_zh: user switches to zh Loaded_en --> Loaded_ko: user switches to ko Loaded_ko --> Loaded_en: user switches to en ``` --- ## 5) Date and Number Localization Behavior Formatting utilities are centralized in `client/src/lib/format.ts`. - `en` → `en-US` - `zh` → `zh-CN` - `vi` → `vi-VN` `formatTime`, `formatDateTime`, and `fmtCostFull` use locale-aware `toLocale*` APIs. Timestamp parsing normalizes timezone-less SQLite datetime strings to UTC before display formatting. `formatModelName` converts raw model identifiers (e.g. `claude-opus-4-7-20260101`, `claude-opus-4-7[1m]`) into human-friendly display names (e.g. "Claude Opus 4.7", "Claude Opus 4.7 (1M)"). This is locale-independent (brand names are proper nouns) and is applied across all UI surfaces except the Settings page (which shows raw patterns for pricing rule configuration). ```mermaid flowchart LR A["Raw timestamp / numeric value"] --> B["parseDate() normalization"] B --> C["getCurrentLanguage()"] C --> D{"Language"} D -->|en| E["Locale en-US"] D -->|zh| F["Locale zh-CN"] D -->|vi| G["Locale vi-VN"] E --> H["toLocaleTimeString / toLocaleString"] F --> H G --> H H --> I["Localized date/time/number output"] ``` --- ## 6) Testing Strategy Use client tests to verify translation correctness, fallback behavior, and locale formatting: - `client/src/i18n/__tests__/i18n.test.ts` - `client/src/lib/__tests__/format.test.ts` - `client/src/components/__tests__/Sidebar.test.tsx` Run: ```bash npm run test:client ``` ### Recommended test matrix | Area | What to verify | Example | |---|---|---| | Resource parity | Same key coverage across `en/zh/vi/ko` | Missing key detection in CI | | Locale fallback | Unknown locales fall back to `en` | `vi-VN` resolves to `vi` | | Terminology consistency | Canonical terms stay stable | `Agent`/`Subagent` expectations | | Date/number formatting | Locale-specific output shape | `zh-CN`, `vi-VN`, `ko-KR` formatting | | Runtime switching | UI rerenders without reload | Sidebar language toggle | --- ## 7) Troubleshooting | Symptom | Likely cause | Resolution | |---|---|---| | UI stays in old language after switch | Cached key or stale component state | Confirm `i18n.changeLanguage(...)` is called and component uses `useTranslation` | | Unexpected fallback to English | Unsupported locale code | Ensure code normalizes to `en|zh|vi|ko` and key exists in target namespace | | Missing text on one page | Namespace file key missing | Add key to all language files for that namespace | | Date/time looks wrong | Locale mapping or timezone parse issue | Verify `getCurrentLocale()` and `parseDate()` behavior | | Inconsistent term translation | Manual translation drift | Enforce glossary and update locale tests | --- ## 8) Rollout Checklist ```mermaid gantt title i18n rollout plan dateFormat YYYY-MM-DD axisFormat %m/%d section Resource Preparation Lock key inventory :a1, 2026-01-01, 3d Fill en/zh/vi/ko namespace files :a2, after a1, 5d section Runtime Integration Wire detection + persistence :b1, after a2, 2d Validate sidebar switching :b2, after b1, 2d Validate locale formatting :b3, after b1, 2d section Verification Add/refresh i18n tests :c1, after b2, 3d Run regression suite :c2, after c1, 2d section Release Staged release + monitoring :d1, after c2, 2d Post-release translation audit :d2, after d1, 3d ``` ### Operational checklist - [ ] Confirm all namespaces exist for `en`, `zh`, `vi`, `ko` - [ ] Confirm key parity across all locale JSON files - [ ] Confirm language switching works in collapsed and expanded sidebar modes - [ ] Confirm fallback behavior for region tags (e.g., `vi-VN`, `zh-CN`) - [ ] Confirm date/time/currency formatting for all supported languages - [ ] Confirm client tests pass before release - [ ] Confirm docs references are updated (`README`, `ARCHITECTURE`, `docs/README`) --- ## References - `client/src/i18n/index.ts` - `client/src/components/Sidebar.tsx` - `client/src/lib/format.ts` - `client/src/i18n/__tests__/i18n.test.ts` - `client/src/lib/__tests__/format.test.ts`