# 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<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
```

**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`
