# Internationalization Design

## Status
Accepted by user on 2026-05-26.

## Context
The project is a Vue 3 + Vite H5 application using Pinia, Vue Router, Vant, and Tailwind CSS. The current page structure is small, with `home` and `quick` feature folders under `src/pages`. The project needs internationalization that stays readable and maintainable as features grow.

The first supported locales are:

- Simplified Chinese: `zh-CN`
- English: `en-US`
- Thai: `th-TH`

## Goals
- Keep translation files organized by feature module.
- Avoid hard-coded user-facing copy in Vue components.
- Provide one place for locale detection, persistence, switching, and fallback behavior.
- Keep the first implementation simple enough for the current H5 app.
- Make missing or inconsistent translation keys easy to detect.

## Decision
Use `vue-i18n` and organize messages by feature module. Each feature owns the same message shape for every supported locale. Shared copy belongs in a `common` module.

Recommended structure:

```text
src/i18n/
  index.ts
  locales.ts
  storage.ts
  types.ts
  modules/
    common/
      zh-CN.ts
      en-US.ts
      th-TH.ts
      index.ts
    home/
      zh-CN.ts
      en-US.ts
      th-TH.ts
      index.ts
    quick/
      zh-CN.ts
      en-US.ts
      th-TH.ts
      index.ts
```

The aggregated message object should be shaped by module:

```ts
{
  common: { ... },
  home: { ... },
  quick: { ... }
}
```

Components should reference copy with module-prefixed keys:

```ts
t("common.actions.confirm")
t("home.title")
t("quick.form.submit")
```

## Locale Model
Define supported locales in one place:

```ts
export const supportedLocales = ["zh-CN", "en-US", "th-TH"] as const;
export type SupportedLocale = (typeof supportedLocales)[number];
export const defaultLocale: SupportedLocale = "zh-CN";
```

This keeps locale handling type-safe and prevents typo-prone string usage across the app.

## Initial Locale Resolution
Initial locale resolution should follow this priority:

1. User-selected locale stored in `localStorage`.
2. Browser language, matching `zh`, `en`, or `th`.
3. Default locale `zh-CN`.

Use a stable storage key:

```ts
nianxx.locale
```

Invalid or unsupported locale values must fall back to `zh-CN`.

## Locale Switching
Expose a single `setLocale(locale)` function from the i18n module. It should:

- Validate that the locale is supported.
- Update the `vue-i18n` global locale.
- Persist the locale to `localStorage`.
- Update `document.documentElement.lang`.
- Synchronize Vant's built-in locale package.

Language state should not be manually duplicated in feature components.

## Vant Integration
Because the app uses Vant, locale changes must also update Vant's internal copy. The i18n infrastructure should map app locales to Vant locale packs:

- `zh-CN` -> Vant Chinese locale
- `en-US` -> Vant English locale
- `th-TH` -> Vant Thai locale

This should happen during app startup and every manual locale switch.

## Fallback and Error Behavior
- `zh-CN` is the fallback locale.
- Unknown locale input resolves to `zh-CN`.
- `localStorage` failures should not block app startup.
- If locale switching receives an unsupported locale, keep the current locale.
- Missing current-locale messages should fall back to `zh-CN`.

## Module Ownership Rules
- Put shared buttons, state labels, and common errors in `common`.
- Put feature-specific page headings, form labels, placeholders, and business copy in the owning feature module.
- Keep the same key structure across `zh-CN`, `en-US`, and `th-TH`.
- Add a new module when a new feature directory appears under `src/pages`.
- Do not place unrelated feature copy in `common` just to avoid creating a module.

## Testing Strategy
Add a lightweight Node test for i18n infrastructure:

- Supported locale list includes `zh-CN`, `en-US`, and `th-TH`.
- Invalid locale values fall back to `zh-CN`.
- Stored locale takes priority over browser language.
- Browser language maps `zh`, `en`, and `th` to the supported locale codes.
- Translation key structure is consistent across all supported locales for each module.

This test protects maintainability by catching missing English or Thai keys when new Chinese copy is added.

## Alternatives Considered

### Language-Centered Files
Example: `src/locales/zh-CN.ts`, `src/locales/en-US.ts`, `src/locales/th-TH.ts`.

This is simple for very small apps, but files grow quickly and make feature ownership unclear. It was rejected because the project explicitly needs feature-module structure.

### Route-Level Lazy Loading
Each page could lazy-load its own messages when the route is entered.

This can reduce initial bundle size for large applications, but it adds async timing, fallback, and loading complexity. It is not necessary for the first H5 i18n foundation.

## Implementation Scope
The first implementation should include:

- Add `vue-i18n`.
- Add the `src/i18n` structure.
- Register i18n in `src/main.ts`.
- Add `common`, `home`, and `quick` message modules for all three locales.
- Add locale resolution, persistence, switching, and Vant synchronization helpers.
- Add tests for locale resolution and key consistency.

The first implementation should not add a visible language switcher unless requested separately.
