app-template/docs/superpowers/specs/2026-09-24-i18n-design.md

3.2 KiB
Raw Blame History

i18n 设计 — 方案 A(zh-CN + en · 路径前缀)

日期:2026-09-24
状态:已批准(用户选 A;追加 / 系统语言检测跳转)

目标

  • 中文:/zh-CN、/zh-CN/dashboard(中文也带前缀,跳 /zh-CN,不留在裸 /)
  • 英文:/en、/en/dashboard
  • 访问 / 时:有 app-lang cookie 按 cookie 跳 /zh-CN 或 /en;否则按 Accept-Language → 中文 /zh-CN、英文 /en
  • 用户手动切换语言写入 cookie,优先于系统语言
  • 文案集中在 @app/i18n;Astro / React 共用同一套静态 messages(无 i18next、无 Context 强绑)

非目标

  • 不改 tRPC / DAO / DB
  • 不引入 i18next / astro-i18next
  • 第一阶段不做复数 ICU、后端按 locale 存文案

包结构

packages/i18n/
  package.json          # @app/dao 风格:纯 TS,无运行时重依赖
  src/
    locales.ts          # LOCALES、DEFAULT_LOCALE、Locale 类型
    cookie.ts           # COOKIE_LANG = "app-lang"
    accept-language.ts  # parsePreferredLocale(header)
    messages/
      zh-CN.ts          # 扁平命名空间对象(权威 key 源)
      en.ts             # 必须 keyof zh-CN
    t.ts                # t(locale, key, params?) 简单 {name} 插值
    index.ts
  AGENTS.md
  • type MessageKey = keyof typeof zhCN
  • t 缺 key 编译期失败(en 与 zh 同构)
  • 无 IO、无 env;与 seo-geo 同为纯函数包

路由与 Middleware(@app/web)

astro.config.ts
  i18n: {
    defaultLocale: "zh-CN",
    locales: ["zh-CN", "en"],
    routing: { prefixDefaultLocale: true, redirectToDefaultLocale: false }
  }

src/middleware.ts
  GET 且 path === "/"
    cookie app-lang=en        → redirect /en
    cookie app-lang=zh-CN     → redirect /zh-CN
    无 cookie:
      Accept-Language 偏好 en → /en
      否则(含 zh)           → /zh-CN
  响应 Vary: Accept-Language

src/pages/
  [locale]/index.astro | dashboard.astro   # 单套动态前缀(locale 校验)
  views/home.astro | dashboard.astro       # 双语共用

不再使用 pages/zh-CN / pages/en 双目录;也不依赖 Astro 文件式 i18n 双份路由。

语言切换:链接到对侧 URL + document.cookie = app-lang=...; path=/; max-age=...
(island LocaleSwitch 或 header 内联均可)

文案分层

层 做法
Astro 布局/页面 t(locale, "nav.home")
React island props 传入 locale,组件内 t(locale, ...) 或传入已解析文案
tools 目录 category 改为稳定 id(text|code|dev|net|image),显示名走 i18n
SEO siteSeo 按 locale 构造;sitemap 每 URL 填 alternates(zh-CN ↔ en)

SEO

  • <html lang>:zh-CN / en
  • hreflang:x-default → /...;zh-CN → /...;en → /en/...
  • lib/seo.ts 提供 getSiteSeo(locale),避免写死单 locale
  • llms.txt / sitemap 至少覆盖双语文档首页(工具页路径可后补 en 副本)

验收

  1. Accept-Language: en 访问 / → 302 /en
  2. Accept-Language: zh-CN 访问 / → 200 中文
  3. cookie app-lang=en 访问 / → 302 /en(覆盖系统语言)
  4. /en 与 / 文案、lang、导航一致切换
  5. yarn typecheck / yarn build 通过