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

93 lines
3.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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` 通过