Files
Claude-Code-Monitor/docs/I18N.md
T
nntrivi2001 62425b2f58 feat: Claude Code Monitor — lanes, pipelines and a merged workspace
Internal SmartGift build of a Claude Code monitoring dashboard.

Lanes: a durable unit of parallel agent work, one per working directory,
tracked across session restarts. Managed lanes are git worktrees the
dashboard provisions and can reset or remove behind a three-check destroy
guard and a counted preflight; adopted lanes are directories you already
own and are never destroyable.

Pipelines: a lane moves through pipeline stages. A stage the agent declares
with evidence renders green; a stage inferred from the tool-event stream
renders dashed amber and never counts as done. Detection is forward-only
within a 30-minute window, and never writes the declared stage.

Workspace: one page at /run with a lane grid, the selected lane's pipeline,
and a full Claude console behind a disclosure.
2026-07-30 12:09:11 +07:00

9.2 KiB

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.

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: 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
  • client/src/i18n/locales/ko/*.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|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
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.

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

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

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