Skip to content

Latest commit

 

History

History
310 lines (246 loc) · 8.96 KB

File metadata and controls

310 lines (246 loc) · 8.96 KB

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)


1) Architecture Overview

Localization is implemented in the frontend with i18next + react-i18next and browser language detection.

flowchart TB
    subgraph Browser
        User["User"]
        LS["localStorage<br/>i18nextLng"]
        Nav["navigator.language"]
    end

    subgraph ClientApp["React Client"]
        Detector["i18next-browser-languagedetector"]
        I18n["i18n init<br/>client/src/i18n/index.ts"]
        NS["Namespace bundles<br/>common/nav/dashboard/..."]
        UI["Pages + components<br/>useTranslation()"]
        Format["format.ts<br/>locale-aware date/number/model-name"]
    end

    User --> UI
    LS --> Detector
    Nav --> Detector
    Detector --> I18n
    I18n --> NS
    NS --> UI
    I18n --> Format
Loading

Key runtime facts

  • supportedLngs: ["en", "zh", "vi"]
  • fallbackLng: "en"
  • nonExplicitSupportedLngs: true (e.g. vi-VN resolves to vi)
  • Detection order: localStoragenavigator

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

Active namespaces:

  • common
  • nav
  • dashboard
  • sessions
  • activity
  • analytics
  • workflows
  • settings
  • kanban
  • errors
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"
    }
    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"
    }
Loading

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
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
Loading

4) Language Detection and Switching Flow

The sidebar language controls call i18n.changeLanguage() and UI updates reactively through useTranslation.

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)
    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
Loading
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_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
Loading

5) Date and Number Localization Behavior

Formatting utilities are centralized in client/src/lib/format.ts.

  • enen-US
  • zhzh-CN
  • vivi-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).

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"]
Loading

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:

npm run test:client

Recommended test matrix

Area What to verify Example
Resource parity Same key coverage across en/zh/vi 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 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
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

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 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
Loading

Operational checklist

  • Confirm all namespaces exist for en, zh, vi
  • 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