docs: add i18n module design
This commit is contained in:
162
docs/superpowers/specs/2026-05-26-i18n-design.md
Normal file
162
docs/superpowers/specs/2026-05-26-i18n-design.md
Normal file
@@ -0,0 +1,162 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user