3.2 KiB
3.2 KiB
i18n 设计 — 方案 A(zh-CN + en · 路径前缀)
日期:2026-09-24
状态:已批准(用户选 A;追加 / 系统语言检测跳转)
目标
- 中文:
/zh-CN、/zh-CN/dashboard(中文也带前缀,跳/zh-CN,不留在裸/) - 英文:
/en、/en/dashboard - 访问
/时:有app-langcookie 按 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 zhCNt缺 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 副本)
验收
Accept-Language: en访问/→ 302/enAccept-Language: zh-CN访问/→ 200 中文- cookie
app-lang=en访问/→ 302/en(覆盖系统语言) /en与/文案、lang、导航一致切换yarn typecheck/yarn build通过