163 lines
5.2 KiB
Markdown
163 lines
5.2 KiB
Markdown
# 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.
|