Files
nianxx-h5/docs/superpowers/plans/2026-05-26-i18n-modules.md
2026-05-26 14:37:32 +08:00

6.5 KiB

I18n Modules Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Add maintainable feature-module internationalization for Chinese, English, and Thai.

Architecture: Use vue-i18n as the app i18n runtime and keep message ownership under src/i18n/modules/<feature>/<locale>.ts. Locale resolution, persistence, document language, and Vant locale synchronization are centralized behind resolveInitialLocale and setLocale.

Tech Stack: Vue 3.4, Vite 5, Vant 4.9, Yarn 1.22, Node test runner, TypeScript.


File Structure

  • Create: src/i18n/types.ts for locale constants, locale types, and type guards.
  • Create: src/i18n/storage.ts for safe browser storage reads and writes.
  • Create: src/i18n/locales.ts for initial locale resolution and browser language mapping.
  • Create: src/i18n/vant.ts for mapping app locales to Vant locale packs.
  • Create: src/i18n/index.ts for vue-i18n creation, message aggregation, and setLocale.
  • Create: src/i18n/modules/common/{zh-CN,en-US,th-TH,index}.ts.
  • Create: src/i18n/modules/home/{zh-CN,en-US,th-TH,index}.ts.
  • Create: src/i18n/modules/quick/{zh-CN,en-US,th-TH,index}.ts.
  • Create: src/i18n/messages.ts for top-level message aggregation.
  • Create: src/i18n/i18n.test.ts for locale and message consistency tests.
  • Modify: src/main.ts to install the i18n plugin.
  • Modify: package.json and yarn.lock by adding vue-i18n@^11.4.4.

Task 1: Add Locale Logic Tests

Files:

  • Create: src/i18n/i18n.test.ts

  • Step 1: Write failing tests for locale resolution and message key consistency

import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { defaultLocale, supportedLocales } from "./types.ts";
import { isSupportedLocale, resolveInitialLocale, resolveLocaleFromNavigator } from "./locales.ts";
import { messages } from "./messages.ts";

function flattenKeys(value: unknown, prefix = ""): string[] {
  if (!value || typeof value !== "object" || Array.isArray(value)) {
    return [prefix];
  }

  return Object.entries(value as Record<string, unknown>).flatMap(([key, nestedValue]) =>
    flattenKeys(nestedValue, prefix ? `${prefix}.${key}` : key),
  );
}

describe("i18n locale model", () => {
  it("supports Chinese, English, and Thai", () => {
    assert.deepEqual([...supportedLocales], ["zh-CN", "en-US", "th-TH"]);
    assert.equal(defaultLocale, "zh-CN");
  });

  it("accepts only supported locale codes", () => {
    assert.equal(isSupportedLocale("zh-CN"), true);
    assert.equal(isSupportedLocale("en-US"), true);
    assert.equal(isSupportedLocale("th-TH"), true);
    assert.equal(isSupportedLocale("en"), false);
    assert.equal(isSupportedLocale("fr-FR"), false);
  });

  it("maps browser languages to supported locales", () => {
    assert.equal(resolveLocaleFromNavigator(["zh-Hans-CN"]), "zh-CN");
    assert.equal(resolveLocaleFromNavigator(["en-GB"]), "en-US");
    assert.equal(resolveLocaleFromNavigator(["th"]), "th-TH");
    assert.equal(resolveLocaleFromNavigator(["fr-FR"]), defaultLocale);
  });

  it("prefers stored locale over browser language", () => {
    assert.equal(resolveInitialLocale({ storedLocale: "th-TH", navigatorLanguages: ["en-US"] }), "th-TH");
  });

  it("falls back to browser language when stored locale is invalid", () => {
    assert.equal(resolveInitialLocale({ storedLocale: "invalid", navigatorLanguages: ["en-US"] }), "en-US");
  });

  it("keeps translation key structure consistent across locales", () => {
    const referenceKeys = flattenKeys(messages["zh-CN"]).sort();

    for (const locale of supportedLocales) {
      assert.deepEqual(flattenKeys(messages[locale]).sort(), referenceKeys, locale);
    }
  });
});
  • Step 2: Run tests and verify the expected failure

Run: corepack yarn test

Expected: FAIL because src/i18n/types.ts, src/i18n/locales.ts, and src/i18n/messages.ts do not exist yet.

Task 2: Implement Locale Model and Message Modules

Files:

  • Create: all src/i18n files except index.ts and vant.ts

  • Step 1: Implement locale constants and resolution

Add supportedLocales, defaultLocale, isSupportedLocale, resolveLocaleFromNavigator, and resolveInitialLocale exactly as tested.

  • Step 2: Implement safe storage helpers

Add localeStorageKey, readStoredLocale, and writeStoredLocale so unavailable browser storage never blocks startup. Cover storage getter failures with a regression test before final verification.

  • Step 3: Implement message modules

Create common, home, and quick modules with matching key shapes for zh-CN, en-US, and th-TH.

  • Step 4: Run tests and verify locale tests pass

Run: corepack yarn test

Expected: PASS for locale model and message key consistency.

Task 3: Install and Wire Runtime I18n

Files:

  • Modify: package.json

  • Modify: yarn.lock

  • Create: src/i18n/vant.ts

  • Create: src/i18n/index.ts

  • Modify: src/main.ts

  • Step 1: Add vue-i18n dependency

Run: corepack yarn add vue-i18n@^11.4.4

Expected: package.json and yarn.lock include vue-i18n.

  • Step 2: Implement Vant locale synchronization

Use Vant's Locale.use(locale, messages) API with local language packs from vant/es/locale/lang/*.mjs.

  • Step 3: Create the i18n plugin

Use createI18n({ legacy: false, locale, fallbackLocale: defaultLocale, messages }), export i18n, setLocale, and getCurrentLocale.

  • Step 4: Register i18n in the app

Modify src/main.ts to call .use(i18n) before mounting.

  • Step 5: Run typecheck

Run: corepack yarn typecheck

Expected: PASS with no TypeScript errors.

Task 4: Final Verification and Commit

Files:

  • Verify all changed files.

  • Step 1: Run unit tests

Run: corepack yarn test

Expected: PASS.

  • Step 2: Run production build

Run: corepack yarn build

Expected: PASS.

  • Step 3: Review diff for scope

Run: git status --short and git diff -- src package.json yarn.lock docs/superpowers/plans/2026-05-26-i18n-modules.md.

Expected: Only i18n implementation files, dependency metadata, the approved plan, and pre-existing user changes appear.

  • Step 4: Commit only implementation-owned files

Stage:

git add -f docs/superpowers/plans/2026-05-26-i18n-modules.md
git add package.json yarn.lock src/main.ts src/i18n

Commit:

git commit -m "feat: add modular i18n foundation"