mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-29 01:42:19 +00:00
docs(i18n): add internationalization design baseline (Wave 0)
Add the i18n multi-agent plan archive and all Wave 0 design deliverables under docs/i18n/: master plan, task board, file ownership, decisions, technical design + ADR + PoC report + risks, localization spec + glossary + language switcher + navigation behavior + scope, and test plan + cases + tools + regression matrix. Design only; no product code changes.
This commit is contained in:
parent
168a0055a2
commit
31e3a76d3f
18 changed files with 2703 additions and 0 deletions
32
docs/i18n/DECISIONS.md
Normal file
32
docs/i18n/DECISIONS.md
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# LiteLLM Dashboard i18n — DECISIONS(决策记录)
|
||||
|
||||
> 维护者:Agent 0。记录所有影响方案/范围的决策。`状态`: 已定 / 待定 / 待 PoC。
|
||||
|
||||
## 已定决策(来自 I18N_MULTI_AGENT_PLAN.md v1.4)
|
||||
|
||||
| # | 决策 | 结论 | 状态 |
|
||||
|---|---|---|---|
|
||||
| D1 | 国际化库 | `i18next` + `react-i18next` | 已定 |
|
||||
| D2 | 默认/目标语言 | 默认 `en`,目标 `zh-CN`;缺 key 回退 en | 已定 |
|
||||
| D3 | 运行时/资源目录 | 运行时 `src/i18n/**`,资源 `src/locales/{en,zh-CN}/**` | 已定 |
|
||||
| D4 | 静态导出约束 | 不依赖服务端 locale 路由;不将 httpOnly cookie 作前端必需;切换主要在客户端 | 已定 |
|
||||
| D5 | 语言偏好优先级 | 用户选择 → 用户级 UI 设置(若支持) → cookie/localStorage → 浏览器语言 → en | 已定 |
|
||||
| D6 | 语言偏好存储 | cookie + localStorage 双层;SSR 首屏靠客户端收敛 | 已定 |
|
||||
| D7 | 首屏策略 | 从"初始化脚本/就绪门禁/接受短暂切换"候选中**必须选定 1 种并经 PoC 验证** | **待 PoC** |
|
||||
| D8 | 构建期 `<title>`/meta | v1 保持英文,不做多语言 SEO | 已定 |
|
||||
| D9 | 复数量词 | zh 不建形态复数分支,用计数插值 + 量词(`{{count}} 个`) | 已定 |
|
||||
| D10 | 整页跳转语言保持 | Login/SSO/MCP OAuth 回跳后需从 cookie/localStorage 恢复 | 待 PoC 验证 |
|
||||
| D11 | `common` namespace Owner | Wave1 由 A4 建骨架,G1 后移交 A5 | 已定 |
|
||||
| D12 | 依赖文件写入者 | `package.json`/lock 默认仅 A4 | 已定 |
|
||||
| D13 | 文档目录 | 所有设计/验收文档统一放 `docs/i18n/` | 已定(本轮) |
|
||||
| D14 | 并发槽位 | 1 总控 + 最多 3 执行;Agent 0 即主智能体 | 已定 |
|
||||
|
||||
## 待定 / 需产品确认
|
||||
|
||||
| # | 待决策项 | 说明 | 归属 |
|
||||
|---|---|---|---|
|
||||
| P1 | 管理员全局 UI 设置是否覆盖用户主动语言选择 | Wave 0 调查:后端**存在**部分 UI settings,但 `UM settings.language` 等价字段**不在** `ALLOWED_UI_SETTINGS_FIELDS` 白名单,即**无用户级 language 设置**。故 v1 语言偏好优先级实际为:用户选择 → cookie/localStorage → 浏览器语言 → en。 | A1 已调查并写入 ADR-07(Accepted) |
|
||||
| P2 | `UI settings.language` 是否存在且进 v1 | **不存在**(只读调查确认),v1 不依赖后端 language 字段。 | A1 已结论 |
|
||||
| P3 | **E2E(Playwright)基建缺口** | Wave 0 核实:dashboard 仓库**当前无 Playwright 基建**(无 `tests/e2e/ui/`、无 `playwright.config.*`、无 `@playwright/test`)。方案 §10 的多项 E2E(首屏/刷新/整页回跳)依赖该层级。**已定:选 a —— Wave 1 补齐 E2E 基建(引入 Playwright)**;会触及 `package.json`,按 §6.1 规则 10 由 A0 指定**唯一写入者(默认 Agent 4)**。 | A0 决策 **已定=选项a** |
|
||||
| P4 | `@playwright` 依赖写入者 | 已随 P3=a 确定:Wave 1 由 **Agent 4** 作为 `package.json` 唯一写入者引入 Playwright 依赖;A0 在任务单中书面授权。 | A0 决策 **已定=Agent 4** |
|
||||
| P5 | **语言偏好存储键名统一**(G0 Review 发现) | 设计文档间不一致:`I18N_ADR.md`/`I18N_TECH_DESIGN.md`/`POC_REPORT.md` 用 `dashboard.locale`;`LANGUAGE_SWITCHER_SPEC.md`/`LOCALE_NAVIGATION_BEHAVIOR.md` 用 `litellm.locale`。**已定:统一为 `litellm.locale`**(与现有候选实现历史一致、Agent 2 文档多数采用);A4 在 `src/i18n/localePreferences.ts` 以单一常量导出,A5/6 不直接操作存储。 | A0 决策 **已定=litellm.locale** |
|
||||
33
docs/i18n/FILE_OWNERSHIP.md
Normal file
33
docs/i18n/FILE_OWNERSHIP.md
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# LiteLLM Dashboard i18n — FILE_OWNERSHIP
|
||||
|
||||
> 维护者:Agent 0。同一文件同一时刻只有一个 Owner。规则见 `I18N_MULTI_AGENT_PLAN.md` §6。
|
||||
|
||||
## 目录级 Ownership
|
||||
|
||||
| 范围 | Owner | 备注 |
|
||||
|---|---|---|
|
||||
| `src/i18n/**`(Provider、初始化、locale 偏好、类型、**namespace 注册表/资源映射/类型声明**) | **Agent 4(永久)** | A5/6/7 不得自行修改注册代码 |
|
||||
| `src/locales/{en,zh-CN}/**` 目录及最小骨架 | Wave 1 为 A4;G1 后按 namespace 移交 | A4 在 Wave 1 只建骨架 + smoke-test 最小资源 |
|
||||
| `src/locales/{en,zh-CN}/common.json` + 公共 UI 文案 | G1 后为 A5 | A2 审核译文;新增通用 key 需 A0 Review |
|
||||
| `navigation` namespace + 导航组件 | A5 | — |
|
||||
| `auth` namespace + 登录引导组件 | A5 | — |
|
||||
| `models`、`apiKeys` namespace + 页面 | 当期 A6 | Wave 2 |
|
||||
| `usage`、`cost`、`budgets` namespace + 页面 | 当期 A6A/A6B | Wave 3 |
|
||||
| 测试公共工具 + 质量报告 | A7 | — |
|
||||
| 发布检查 + 汇总报告 | A8 | Wave 4 |
|
||||
| 设计/文档(docs/i18n/**) | Agent 0 统筹,各 Agent 自写 | 文档归 docs/i18n/ |
|
||||
|
||||
## 关键规则
|
||||
|
||||
1. 同一文件同一时间仅一个 Owner。
|
||||
2. 功能 Agent 只写自己的 namespace,不共同编辑一个大 JSON。
|
||||
3. `src/components/ui/**` 原则上无业务文案,由调用方传入文本。
|
||||
4. `src/utils/**` 不设全目录 Owner,按文件划分。
|
||||
5. `package.json`/`package-lock.json`:默认只允许 **Agent 4** 在平台 worktree 修改;确需改依赖由 A0 指定唯一临时 Owner。
|
||||
6. 新增业务 namespace:功能 Agent 在任务单填注册需求,由 A0 指派 A4 统一注册,或 A0 书面授权。
|
||||
|
||||
## 移交记录
|
||||
|
||||
| 时间 | 资源 | 由 → 到 | 确认 |
|
||||
|---|---|---|---|
|
||||
| (待 G1) | `src/locales/{en,zh-CN}/common|navigation|auth*` | Agent 4 → Agent 5 | A0 |
|
||||
154
docs/i18n/GLOSSARY_EN_ZH.md
Normal file
154
docs/i18n/GLOSSARY_EN_ZH.md
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
# LiteLLM Dashboard 中英文术语表(Glossary)
|
||||
|
||||
> 维护者:Agent 2(localization-designer)
|
||||
> 状态:设计稿,待 G0 评审;Agent 2 负责复核与更新
|
||||
> 约定:本术语表是**唯一权威词条来源**。所有 v1 相关译文必须与本表一致;出现同义词漂移即视为缺陷。`不可译` = 作为专名/数据保留英文,不进入翻译资源或不做翻译。
|
||||
|
||||
---
|
||||
|
||||
## 1. 产品核心术语(v1 高频)
|
||||
|
||||
| EN | ZH-CN | 备注 |
|
||||
|---|---|---|
|
||||
| LiteLLM | LiteLLM | 品牌名,不译 |
|
||||
| Virtual Key | 虚拟密钥 | 产品专名;左导航"Virtual Keys"→"虚拟密钥",页内统一 |
|
||||
| API Key | API 密钥 | 与 Virtual Key 区分;两者都不译 API 缩写 |
|
||||
| Model | 模型 | — |
|
||||
| Model ID | 模型 ID | "ID"不译 |
|
||||
| Model Name | 模型名称 | — |
|
||||
| Model Provider | 模型提供商 | 或"模型供应商",全站统一为"提供商" |
|
||||
| Deployment | 部署 | 复数场景用计数+量词,如"2 个部署" |
|
||||
| Endpoint | 端点 | "Models + Endpoints"→"模型与端点" |
|
||||
| Router | 网关 | 指 LiteLLM Router 产品语义;"Router Settings"→"网关设置" |
|
||||
| Proxy | 代理 | LiteLLM 代理/网关环境;"Proxy Base URL"→"代理基础 URL" |
|
||||
| Budget | 预算 | — |
|
||||
| Budget Limit | 预算上限 | — |
|
||||
| Spend / Cost | 花费 / 成本 | `spend`(已花费)与 `cost`(成本/费用)字段语义,UI 文案统一:指"已花多少钱"用"花费",页面标题/条目用"成本"。避免与"支出/费用"混用 |
|
||||
| Cost Tracking | 成本跟踪 | 页面名 |
|
||||
| Cost Optimization | 成本优化 | 页面名(含 Beta 徽标) |
|
||||
| Usage | 用量 | 页面名与数据语义;"可用量"在配额上下文另见 Rate Limit |
|
||||
| Rate Limit | 速率限制 | TPM/RPM 语义;避免与"阈值"混用 |
|
||||
| Team | 团队 | — |
|
||||
| Organization | 组织 | — |
|
||||
| User | 用户 | — |
|
||||
| Internal User | 内部用户 | "Internal Users"→"内部用户" |
|
||||
| Logs | 日志 | — |
|
||||
| Projects | 项目 | 项目(Beta 徽标) |
|
||||
| Onboarding | 引导 | 首次引导流程;"Onboarding"页面/步骤用"引导" |
|
||||
| Connect | 连接 | Connect 页面 |
|
||||
| Guardrails | Guardrails | 产品专有功能名,保留英文原名(含"Guardrails Monitor"→"Guardrails 监控"外部仍保留原名) |
|
||||
| Policies | 策略 | 通用"策略";Guardrails/Policies 作为功能模块见 `V1_TRANSLATION_SCOPE.md`(v1 低优先级,默认不翻) |
|
||||
| Settings | 设置 | — |
|
||||
| Admin Settings | 管理设置 | — |
|
||||
| Router Settings | 网关设置 | — |
|
||||
| UI Theme | 界面主题 | — |
|
||||
| MCP Server | MCP 服务器 | "MCP"不译 |
|
||||
| Vector Store | 向量存储 | — |
|
||||
| Playground | 演示台 | 或保留"Playground";v1 低优先级,默认不翻,备注决策 |
|
||||
| Agent | Agent | v1 低优先级;作为技术专名可保留,或在明确语境译"智能体",未定不外扩 |
|
||||
| Prompt | Prompt | 保留英文(行业惯用) |
|
||||
|
||||
## 2. 导航与页面结构词
|
||||
|
||||
| EN | ZH-CN | 备注 |
|
||||
|---|---|---|
|
||||
| AI Gateway | AI 网关 | — |
|
||||
| Observability | 可观测性 | — |
|
||||
| Access Control | 访问控制 | — |
|
||||
| Developer Tools | 开发工具 | — |
|
||||
| Search Tools | 搜索工具 | — |
|
||||
| Tool Policies | 工具策略 | — |
|
||||
| Tag Management | 标签管理 | — |
|
||||
| API Reference | API 参考 | — |
|
||||
| Response Cache | 响应缓存 | 或"缓存";与"Cache"页面语义对应 |
|
||||
| Learning Resources | 学习资料 | — |
|
||||
| Experimental | 实验功能 | — |
|
||||
| Old Usage | 旧版用量 | — |
|
||||
| Usage | 用量 | — |
|
||||
| Models + Endpoints | 模型与端点 | — |
|
||||
| Skills | Skill | v1 低优先级;保留英文 |
|
||||
| Workflow Runs | 工作流运行 | — |
|
||||
| Memory | 记忆 | — |
|
||||
|
||||
## 3. 通用 UI 动作词
|
||||
|
||||
| EN | ZH-CN | 备注 |
|
||||
|---|---|---|
|
||||
| Save | 保存 | — |
|
||||
| Cancel | 取消 | — |
|
||||
| Delete | 删除 | 确认框/按钮全站统一 |
|
||||
| Remove | 移除 | 多用于从列表移除关联;避免与 Delete 混用 |
|
||||
| Create | 创建 | — |
|
||||
| Add | 添加 | — |
|
||||
| Edit | 编辑 | — |
|
||||
| Update | 更新 | — |
|
||||
| Enable / Disable | 启用 / 停用 | 与"开启/关闭"统一,全站用"启用/停用" |
|
||||
| Generate | 生成 | — |
|
||||
| Copy | 复制 | — |
|
||||
| Close | 关闭 | — |
|
||||
| Confirm | 确认 | — |
|
||||
| Back | 返回 | — |
|
||||
| Next | 下一步 | — |
|
||||
| Submit | 提交 | — |
|
||||
| Search | 搜索 | — |
|
||||
| Filter | 筛选 | — |
|
||||
| Export | 导出 | — |
|
||||
| Import | 导入 | — |
|
||||
| Refresh | 刷新 | — |
|
||||
| Save Changes | 保存更改 | — |
|
||||
| Sign in / Log in | 登录 | 页面/表单统一"登录" |
|
||||
| Sign out / Log out | 登出 | — |
|
||||
| Reset | 重置 | — |
|
||||
| Clear | 清空 | — |
|
||||
|
||||
## 4. 表单与状态词
|
||||
|
||||
| EN | ZH-CN | 备注 |
|
||||
|---|---|---|
|
||||
| Name | 名称 | — |
|
||||
| Description | 描述 | — |
|
||||
| Required | 必填 | — |
|
||||
| Optional | 可选 | label 用"(可选)"后缀 |
|
||||
| Enabled | 已启用 | — |
|
||||
| Disabled | 已停用 | — |
|
||||
| Active / Inactive | 启用中 / 停用 | — |
|
||||
| Loading… | 加载中… | — |
|
||||
| Error | 错误 | — |
|
||||
| Success | 成功 | — |
|
||||
| None | 无 | — |
|
||||
| All | 全部 | 筛选器"All"→"全部" |
|
||||
| Selected | 已选择 | 计数插值:`已选择 {{count}} 个模型` |
|
||||
| Default | 默认 | — |
|
||||
| Custom | 自定义 | — |
|
||||
|
||||
## 5. 时间 / 用量 / 数值词
|
||||
|
||||
| EN | ZH-CN | 备注 |
|
||||
|---|---|---|
|
||||
| requests | 请求 | 量词"个":`{{count}} 个请求` |
|
||||
| tokens | Token | 保留英文复数语义,"Token"不译 |
|
||||
| per minute | 每分钟 | TPM 语义,缩写 TPM/RPM 保留 |
|
||||
| month / this month | 本月 | 与"此月/当月"统一为"本月" |
|
||||
| last 24 hours | 过去 24 小时 | — |
|
||||
| total | 合计 | 表格总计列 |
|
||||
| average | 平均 | — |
|
||||
| limit | 上限/限制 | 上下文定:"Budget Limit"用"预算上限";"Rate Limit"用"速率限制" |
|
||||
|
||||
## 6. 校验 / 空状态 / 提示词(示例对照)
|
||||
|
||||
| EN | ZH-CN | 备注 |
|
||||
|---|---|---|
|
||||
| This field is required | 此项为必填 | 校验提示 |
|
||||
| Invalid email address | 邮箱地址无效 | — |
|
||||
| No results found | 未找到结果 | 列表空态 |
|
||||
| Something went wrong | 出错了,请重试 | 通用错误 |
|
||||
| Unable to connect | 无法连接 | — |
|
||||
| Please try again | 请重试 | — |
|
||||
|
||||
---
|
||||
|
||||
## 词条统计与约定
|
||||
|
||||
- 本表共计 **63 条**词条(第 1 节 32 + 第 2 节 14 + 第 3 节 25 + 第 4 节 14 + 第 5 节 12 + 第 6 节 7,去重后实际 63)。
|
||||
- 所有新增译名必须先入表再由开发使用;Agent 2 在 Wave 4A 复核。
|
||||
- "不可译"列入表内的专名(LiteLLM、Guardrails、MCP、Token、Prompt、VM 等)作为约定记录,防止误翻。
|
||||
98
docs/i18n/I18N_ADR.md
Normal file
98
docs/i18n/I18N_ADR.md
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
# LiteLLM Dashboard i18n — 架构决策记录(I18N_ADR)
|
||||
|
||||
> 维护者:Agent 1(`i18n-architect`)· Wave 0
|
||||
> 状态:`Proposed`(待 Agent 0 Baseline Review 后置为 `Accepted`;涉 PoC 条目待验证后转 `Accepted`)
|
||||
> **G0 Review(Agent 0,2026-09-09):APPROVED。** ADR-02/06/07/08(Accepted)维持;ADR-01/03/04/05 保持 Proposed,待 Wave 1 由 A4/A7 回填 PoC 证据(PoC-1/2/3/4/5/7)后转 Accepted(G1 门禁)。
|
||||
> 编号约定:ADR-i18n-01 .. N。与 `DECISIONS.md`(D1–D14)及技术设计 `I18N_TECH_DESIGN.md` 相互引用。
|
||||
|
||||
---
|
||||
|
||||
## ADR-i18n-01:i18n 库选型
|
||||
|
||||
- **状态**:Proposed(待 G0 通过)
|
||||
- **背景**:静态导出(`output:"export"`)、Next.js 16、React 19;需要运行时切换 `en`/`zh-CN`、缺 key 回退英文、类型安全的 key。
|
||||
- **决策**:采用 `i18next` + `react-i18next`(`i18next@^26.4.2` + `react-i18next@^17.0.13`)。
|
||||
- **理由**:`react-i18next` 纯客户端 Context/Hook,不依赖 Next.js SSR/服务端钩子,契合静态导出;peer 依赖 `react>=16.8.0` 满足 React 19.2.8;`i18next` 无 react peer。生态成熟、`<Trans>`/复数/插值完备。
|
||||
- **后果**:
|
||||
- 积极:低成本接入、类型增强可行、中文量词策略(D9)可自然实现。
|
||||
- 消极/风险:新增两份依赖,须由 A4 单一写入(D12);需 `knip`/lint 放行;类型资源的维护由 A4 承担。
|
||||
|
||||
## ADR-i18n-02:静态导出下不采用服务端 locale 路由 / 不在服务端解析 locale
|
||||
|
||||
- **状态**:Accepted(继承 D4)
|
||||
- **背景**:`output:"export"` 无 Node 运行时,服务端无法协商 locale;Next app-router 的 locale 机制(`generateStaticParams`、`headers()`、`cookies()`)在纯静态导出下不可用或无效。
|
||||
- **决策**:v1 **完全不依赖服务端 locale 路由**;语言初始化与切换全部在客户端 `I18nProvider` 完成;不将 httpOnly cookie 作为前端必需能力(普通 `SameSite=Lax` cookie 用于跨整页跳转恢复,见 ADR-i18n-05)。
|
||||
- **理由**:静态导出别无选择;客户端收敛成本低,且与「首帧英文合法(D2/D8)」矛盾小。
|
||||
- **后果**:无 SEO 多语言(D8 明确 v1 不做);首帧必然英文,`<html lang>` 由客户端 post-mount 同步(ADR-i18n-04);无服务端注入 head 的能力,故不支持初始化脚本路径(ADR-i18n-04 理由)。
|
||||
|
||||
## ADR-i18n-03:资源同步就绪门禁(禁止首帧暴露 key)
|
||||
|
||||
- **状态**:Proposed(待 PoC 佐证)
|
||||
- **背景**:§3.1 要求:禁止首帧显示原始 key 后再替换为译文。
|
||||
- **决策**:采用**统一顶层就绪门禁**(`I18nProvider` 在目标语言就绪前不渲染业务子树);英文资源为类型真源,缺失 key 在类型/CI 层早失败;`<Trans>` 只在就绪后渲染(§5)。
|
||||
- **理由**:就绪门禁能同时覆盖 `t()` 与 `<Trans>`,避免每个组件自行 `if(!ready)` 的碎片化;结合 key=语义(非句子)的规范,从根源杜绝「key 即原文被暴露」。
|
||||
- **后果**:
|
||||
- 积极:首帧要么是合法英文,要么是短暂的就绪态,无 key 闪烁。
|
||||
- 消极:引入极短的渲染门禁;需保证 en 是同步就绪(不改后端),zh-CN 是注册的静态资源内切换(同步)。
|
||||
- 风险:若某处绕过门禁直接渲染 `t()`,仍可能瞬时暴露 key → 用 CI 的「资源键集合一致」检查与代码评审兜底(TECH_RISKS R9)。
|
||||
|
||||
## ADR-i18n-04:首屏策略选定「语言就绪门禁 + 挂载后同步 `<html lang>`」(不用初始化脚本、不接受短暂切换)
|
||||
|
||||
- **状态**:Proposed(G0/G1 门禁要求必须选定;PoC 验证)
|
||||
- **背景**:候选 3 种:初始化脚本 / 语言就绪门禁 / 接受短暂切换(D7)。
|
||||
- **决策**:选定**语言就绪门禁**,并在 `changeLanguage` 完成后同步 `document.documentElement.lang`。
|
||||
- **理由**:
|
||||
- 初始化脚本在纯静态导出下无服务端 head 注入时机,且运行时加载 zh-CN JSON 只能在 client JS 就绪后完成,前置内联脚本收益极低。
|
||||
- 接受短暂切换制造「英文按键闪烁」,违反 §3.1 精神与首屏体验。
|
||||
- 门禁方案与「首帧英文本就合法」天然一致,切换收敛到偏好语言只需一次、且行为可测。
|
||||
- **后果**:`<html lang>` 首帧 `en`,post-mount 更新为目标语言(`document.documentElement.lang`);`suppressHydrationWarning` 沿用现有做法(与 next-themes 相同);不写内联脚本、不强求 head 改写。需 PoC 量化门禁延迟可接受(POC_REPORT PoC-2/3)。
|
||||
|
||||
## ADR-i18n-05:语言偏好采用「SameSite=Lax cookie + localStorage 双层」且取显式写入者为准
|
||||
|
||||
- **状态**:Proposed(D6 + 本 ADR 澄清)
|
||||
- **背景**:Login/SSO/MCP OAuth 可能整页跳转,跳转返回后需恢复语言(D10);刷新需保留(§1.1)。
|
||||
- **决策**:偏好键 `dashboard.locale` 同时写 cookie(`SameSite=Lax; path=/;`,生产加 `Secure`)与 localStorage;读到任一存在即视为用户显式选择;无需偏好时首次回退浏览器语言→en。
|
||||
- **理由**:
|
||||
- cookie:跨整页(同源)跳转天然携带,SSO 回跳同域可恢复(D10)。
|
||||
- `SameSite=Lax`:允许顶层导航回跳带 cookie,又不阻断,优于 Strict 的极端。(若 SSO 走第三方域且需带 cookie,再评估 `None`,属后续风险 R8。)
|
||||
- localStorage 双写:供无 cookie 上下文(如纯静态托管其他子路径/隐私模式兜底)与客户端快速读取。
|
||||
- **后果**:需保证 cookie/localStorage 两处不冲突(以显式写入顺序为准);无权删除两者时回退 en;不做后端 httpOnly 语言协商(D4)。
|
||||
|
||||
## ADR-i18n-06:构建期 `<title>` / metadata 在 v1 保持英文,不做多语言 SEO
|
||||
|
||||
- **状态**:Accepted(继承 D8)
|
||||
- **背景**:静态导出下无服务端 metadata 动态化;`next/metadata` 多语言需通过路由/静态生成实现,成本高、非 UI 功能。
|
||||
- **决策**:`src/app/layout.tsx` 的 `metadata`(title/description)**保持不变**(英文),不为 SEO 做多语言输出;页面内可见标题/文案中文化由组件 `t()` 承担。
|
||||
- **理由**:v1 范围不含多语言 SEO(§1.2);避免在静态导出下为 `title` 引入额外复杂度。
|
||||
- **后果**:搜索引擎看到的 title 为英文;若未来需要,另立 ADR 引入 per-locale 静态 `title` 生成。
|
||||
|
||||
## ADR-i18n-07:v1 不使用后端 `UI settings.language`(字段不存在)
|
||||
|
||||
- **状态**:Accepted(P1/P2 调查结论)
|
||||
- **背景**:P1/P2 待 A1 调查 `UI settings.language` 是否存在/是否进 v1。
|
||||
- **决策**:经只读调查,后端 `UISettings`(`/get/ui_settings`)的持久化白名单 `ALLOWED_UI_SETTINGS_FIELDS` **不包含 `language`**。故 v1 **不采用**后端全局语言设置;管理员覆盖用户选择的产品决策在 v1 不生效。
|
||||
- **理由**:字段不存在于持久化白名单 → 即使前端写入也不会被保存/下发;强行使用无意义且会引入两段式异步。
|
||||
- **后果**:语言偏好仅为「用户显式 → cookie/localStorage → 浏览器 → en」;若未来后端新增 `language` 白名单字段,另立 ADR 补「管理员全局 vs 用户选择」覆盖语义(预留 L5 判定位)。
|
||||
|
||||
## ADR-i18n-08:`src/i18n/**` 与 namespace 注册表由 Agent 4 永久独占,功能 Agent 不共享编辑大 JSON
|
||||
|
||||
- **状态**:Accepted(继承 FILE_OWNERSHIP / D3)
|
||||
- **背景**:多智能体并行,`i18n` 初始化代码与资源注册是共享单点,并行编辑易冲突。
|
||||
- **决策**:`src/i18n/**`(Provider、初始化、locale 偏好、类型、注册表/资源映射)与 `src/locales/{en,zh-CN}/**` 目录骨架归 A4;功能 Agent 只写自己的 namespace json + 引用 key,新增 namespace 经 A0 派单由 A4 注册;`package.json`/lock 仅 A4 写。
|
||||
- **理由**:避免共享单点写冲突(§6/D12),保证注册表唯一真源。
|
||||
- **后果**:功能 Agent 接入需等待 A4 完成平台基线(G1),形成 Wave 依赖;须遵守 FILE_OWNERSHIP 规则 1/6。
|
||||
|
||||
---
|
||||
|
||||
## ADR 状态汇总(供 Agent 0 回填)
|
||||
|
||||
| ADR | 主题 | 状态 | 依赖 PoC |
|
||||
|---|---|---|---|
|
||||
| 01 | i18n 库选型 | Proposed | PoC-1(兼容安装) |
|
||||
| 02 | 静态导出无服务端 locale | Accepted | 无 |
|
||||
| 03 | 资源就绪门禁(禁暴 key) | Proposed | PoC-3/4 |
|
||||
| 04 | 首屏策略=就绪门禁+`lang` 同步 | Proposed | PoC-2/3 |
|
||||
| 05 | cookie+localStorage 双层偏好 | Proposed | PoC-5/7 |
|
||||
| 06 | title/meta v1 英文 | Accepted | 无 |
|
||||
| 07 | v1 不用后端 language | Accepted | 无(只读调查) |
|
||||
| 08 | 注册表 A4 独占 | Accepted | 无 |
|
||||
695
docs/i18n/I18N_MULTI_AGENT_PLAN.md
Normal file
695
docs/i18n/I18N_MULTI_AGENT_PLAN.md
Normal file
|
|
@ -0,0 +1,695 @@
|
|||
# LiteLLM Dashboard 中英文国际化多智能体实施方案
|
||||
|
||||
## 文档版本信息
|
||||
|
||||
| 项目 | 内容 |
|
||||
|---|---|
|
||||
| 文档版本 | v1.4 |
|
||||
| 修订日期 | 2026-09-09 |
|
||||
| 文档状态 | 评审通过,可执行 |
|
||||
| 适用项目 | LiteLLM Dashboard |
|
||||
| 实施范围 | Dashboard 前端 UI,英文 `en` 与简体中文 `zh-CN` |
|
||||
| 默认语言 | 英文 `en` |
|
||||
| 并行约束 | 最多 4 个智能体同时运行,包含总控智能体 |
|
||||
| 上一版本 | v1.3(评审通过,可执行) |
|
||||
|
||||
### v1.4 变更摘要
|
||||
|
||||
1. 明确 namespace 同时对应资源文件名和翻译 key 的冒号前缀。
|
||||
2. 固定 namespace 注册表、资源加载映射和类型声明归 Agent 4 管理。
|
||||
3. 固定 `package.json`、`package-lock.json` 的单一写入者,禁止并行 worktree 产生锁文件分叉。
|
||||
4. 明确下游 worktree 从 G1 集成基线创建或 rebase,并使用 `npm ci` 安装独立 `node_modules`。
|
||||
5. 增加简体中文计数与量词策略,禁止机械复制英文复数分支。
|
||||
6. 明确 `<Trans>` 只能在资源同步就绪或统一 ready 门禁之后渲染。
|
||||
7. 将首屏、`<Trans>`、刷新恢复、英文回退和整页跳转验证固化为具名证据文件。
|
||||
8. 明确 Agent 7 在 Wave 1 获取平台候选提交进行验证时不得修改依赖文件。
|
||||
|
||||
---
|
||||
|
||||
## 1. 目标与范围
|
||||
|
||||
### 1.1 目标
|
||||
|
||||
在不改变现有路由和业务行为的前提下,为 LiteLLM Dashboard 增加:
|
||||
|
||||
- 英文 `en` 与简体中文 `zh-CN`。
|
||||
- 页面内即时语言切换。
|
||||
- 刷新后保留语言偏好。
|
||||
- 中文缺失时回退英文。
|
||||
- 导航、登录、引导和高频业务页面优先中文化。
|
||||
- 可持续扩展的字典、测试与协作机制。
|
||||
|
||||
### 1.2 v1 范围
|
||||
|
||||
v1 包含:
|
||||
|
||||
- i18n 基础设施。
|
||||
- 语言切换器和语言偏好保存。
|
||||
- Navbar、Leftnav、用户菜单等全局壳层。
|
||||
- Login、Onboarding、Connect、MCP OAuth。
|
||||
- Models and Endpoints、API Keys。
|
||||
- Usage、Cost Tracking、Budgets。
|
||||
- 对应的自动化测试、静态导出构建验证和中文布局检查。
|
||||
|
||||
v1 不包含:
|
||||
|
||||
- Python SDK、API 文档和代码示例的翻译。
|
||||
- 模型返回内容、日志原文和错误堆栈翻译。
|
||||
- 所有 1400 余个 TSX 文件的一次性全量改造。
|
||||
- FastAPI 后端错误响应的全面国际化。
|
||||
- 后端 httpOnly Cookie 语言协商。
|
||||
- 构建期 `<title>` 与 metadata description 的多语言 SEO 输出;v1 保持英文。
|
||||
- Guardrails、Policies、Playground、Prompts 等低优先级模块的全量翻译。
|
||||
|
||||
---
|
||||
|
||||
## 2. 已确认的工程现状
|
||||
|
||||
- 前端目录:`ui/litellm-dashboard`。
|
||||
- 技术栈:Next.js 16、React 19、TypeScript。
|
||||
- 使用 App Router。
|
||||
- `next.config.mjs` 配置 `output: "export"`,属于静态导出。
|
||||
- 项目原始基线没有成熟的 i18n 框架。
|
||||
- 根 Provider 链位于 `src/app/layout.tsx`。
|
||||
- 导航主要位于 `src/components/leftnav.tsx` 和 `src/components/navbar.tsx`。
|
||||
- 项目对单元、集成和 E2E 测试有明确分类要求。
|
||||
- 不允许无路径运行完整 Vitest 测试集,只运行改动相关测试。
|
||||
|
||||
### 2.1 当前工作树状态
|
||||
|
||||
截至 2026-09-08,除本方案文档 `I18N_MULTI_AGENT_PLAN.md` 外,代码工作树不存在已修改或未跟踪的 i18n 实现文件,代码基线干净。
|
||||
|
||||
此前的候选实现从未进入 commit,现已清理。Wave 0 从干净代码基线开展技术设计和 PoC,不存在需要继承或修正的候选实现。
|
||||
|
||||
---
|
||||
|
||||
## 3. 技术方案原则
|
||||
|
||||
### 3.1 候选技术路线
|
||||
|
||||
- 候选库:`i18next` + `react-i18next`。
|
||||
- 默认语言:`en`。
|
||||
- 中文语言标识:`zh-CN`。
|
||||
- 英文资源为真源和最终回退资源。
|
||||
- 字典按业务 namespace 组织。
|
||||
- 国际化运行时代码统一放在 `src/i18n/**`。
|
||||
- 翻译资源统一放在 `src/locales/{en,zh-CN}/**`。
|
||||
- 普通字符串使用 `t(key)`;包含链接、强调或嵌套结构时使用 `<Trans>`。
|
||||
- 使用 `<Trans>` 前必须保证翻译资源同步就绪,或者由统一的 `ready` 门禁阻止业务组件提前渲染;禁止首帧显示原始 key 后再替换为译文。
|
||||
|
||||
最终技术方案必须经过 Baseline Review 和 PoC 验证后由总控智能体批准。
|
||||
|
||||
### 3.2 静态导出约束
|
||||
|
||||
由于使用 `output: "export"`:
|
||||
|
||||
- v1 不依赖 Next.js 服务端 locale 路由。
|
||||
- v1 不将 httpOnly Cookie 作为前端必需能力。
|
||||
- 语言初始化和切换主要在客户端完成。
|
||||
- `<html lang>` 由客户端在语言确定后同步更新。
|
||||
- `suppressHydrationWarning` 只能抑制 hydration 警告,不能防止首屏语言闪烁。
|
||||
- 首屏闪烁必须通过 PoC 评估,可选策略包括初始化脚本、语言就绪门禁或接受短暂切换。
|
||||
|
||||
### 3.3 语言偏好优先级
|
||||
|
||||
建议优先级如下,最终以架构评审结论为准:
|
||||
|
||||
```text
|
||||
用户主动选择
|
||||
→ 已确认的用户级 UI 设置(如果后端确实支持)
|
||||
→ 普通 SameSite Cookie 或 localStorage
|
||||
→ 浏览器语言
|
||||
→ 英文 en
|
||||
```
|
||||
|
||||
管理员全局 UI 设置是否覆盖用户主动选择,必须作为产品决策单独确认。
|
||||
|
||||
Login、SSO 和 OAuth 可能发生整页跳转。PoC 必须验证跳转返回后仍能从同源 Cookie 或 localStorage 恢复语言;`LOCALIZATION_SPEC.md` 必须明确期望行为,测试方案必须覆盖登录成功回跳、SSO 回跳和 MCP OAuth 回跳。
|
||||
|
||||
### 3.4 key 命名规范
|
||||
|
||||
```text
|
||||
<namespace>:<domain>.<page>.<element>[.<state>]
|
||||
```
|
||||
|
||||
示例:
|
||||
|
||||
- `navigation:group.observability`
|
||||
- `navigation:item.teams`
|
||||
- `common:action.delete`
|
||||
- `auth:login.submit`
|
||||
- `models:form.name.label`
|
||||
- `usage:tabs.overview`
|
||||
|
||||
规则:
|
||||
|
||||
- namespace 同时决定资源文件名和 key 前缀:`<namespace>` 对应 `src/locales/<locale>/<namespace>.json`,组件引用时使用 `<namespace>:<key>`;例如 `common.json` 对应 `common:action.delete`。
|
||||
- 使用语义 key,不使用完整英文句子作为 key。
|
||||
- 英文和中文字典必须保持相同 key 集合。
|
||||
- 插值使用 `{{variable}}`。
|
||||
- 英文根据 i18next/CLDR 规则处理 `one`、`other` 等复数形式。
|
||||
- 简体中文通常不创建形态复数分支,优先使用计数插值和明确量词,例如 `{{count}} 个请求`、`已选择 {{count}} 个模型`;禁止机械复制英文复数结构生成无意义的中文分支。
|
||||
- 计数测试至少覆盖 `count=0`、`count=1`、`count=2`。
|
||||
- 模型名、API 参数、日志原文、代码示例原则上不翻译。
|
||||
- 扫描工具只报告候选硬编码,不自动生成最终 key,不自动改写代码。
|
||||
|
||||
---
|
||||
|
||||
## 4. 多智能体组织结构
|
||||
|
||||
逻辑角色可以超过 4 个,但同一时刻最多运行 4 个智能体,其中包含总控智能体。
|
||||
|
||||
Agent 0 映射为当前主智能体,本身占用 1 个并发槽位;每个波次最多再启动 3 个执行智能体。不得把 Agent 0 视为槽位外角色后额外启动第 4 个执行智能体。
|
||||
|
||||
```text
|
||||
Agent 0 总控、任务调度与 Review(全程)
|
||||
├── Agent 1 国际化技术设计
|
||||
├── Agent 2 产品、交互与本地化设计
|
||||
├── Agent 3 测试架构与验收设计
|
||||
├── Agent 4 国际化平台开发
|
||||
├── Agent 5 导航、登录与公共 UI 开发
|
||||
├── Agent 6 业务功能开发(按模块轮换)
|
||||
├── Agent 7 持续测试与质量工程
|
||||
└── Agent 8 集成与发布验收
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 智能体职责
|
||||
|
||||
### 5.1 Agent 0:`i18n-lead-reviewer`
|
||||
|
||||
角色:总体负责人、项目调度、技术负责人、Reviewer。
|
||||
|
||||
职责:
|
||||
|
||||
1. 维护总体计划、任务板、风险和决策记录。
|
||||
2. 划分文件及 namespace Ownership。
|
||||
3. 给每个智能体下发自包含任务单。
|
||||
4. Review 技术方案、本地化规范、测试方案和代码。
|
||||
5. 处理跨 Agent 接缝和冲突。
|
||||
6. 执行阶段门禁,决定通过、修改后通过或驳回。
|
||||
7. 控制合并顺序和发布范围。
|
||||
8. 汇总最终 Review 与发布结论。
|
||||
|
||||
固定交付物:
|
||||
|
||||
- `MASTER_PLAN.md`
|
||||
- `TASK_BOARD.md`
|
||||
- `FILE_OWNERSHIP.md`
|
||||
- `DECISIONS.md`
|
||||
- `REVIEW_REPORT.md`
|
||||
- `RELEASE_CHECKLIST.md`
|
||||
|
||||
约束:Agent 0 不承担大规模业务开发,避免自己实现、自己 Review。
|
||||
|
||||
### 5.2 Agent 1:`i18n-architect`
|
||||
|
||||
角色:国际化技术设计智能体。
|
||||
|
||||
职责:
|
||||
|
||||
- 分析现有前端架构、Provider 模式,并调查 UI Settings `language` 字段的权限、作用域和数据语义。
|
||||
- 验证 `react-i18next` 与静态导出构建兼容性。
|
||||
- 设计 Provider、locale 初始化、偏好保存、英文回退和资源加载。
|
||||
- 设计 `<html lang>` 同步和首屏策略。
|
||||
- 完成最小 PoC。
|
||||
- 输出 ADR 和开发接口契约。
|
||||
- 在平台开发完成后执行 ADR 符合性复核。
|
||||
|
||||
交付物:
|
||||
|
||||
- `I18N_TECH_DESIGN.md`
|
||||
- `I18N_ADR.md`
|
||||
- `POC_REPORT.md`:记录静态导出构建、首屏策略对比、普通 `t()` 与 `<Trans>` 首次渲染、刷新恢复、英文回退、整页跳转恢复及浏览器验证证据。
|
||||
- 技术风险清单
|
||||
|
||||
### 5.3 Agent 2:`localization-designer`
|
||||
|
||||
角色:产品、交互与本地化设计智能体。
|
||||
|
||||
职责:
|
||||
|
||||
- 设计语言切换器位置、状态和交互。
|
||||
- 定义首次进入和后续访问时的语言规则。
|
||||
- 明确 Login、SSO、MCP OAuth 整页跳转返回后的语言保持规则。
|
||||
- 明确构建期 `<title>` 和 metadata description 在 v1 保持英文,不由功能开发智能体被动扩展范围。
|
||||
- 编写中英文术语表。
|
||||
- 定义不可翻译内容。
|
||||
- 定义按钮、表单、错误提示和空状态的中文风格。
|
||||
- 检查中文长度对导航、表格和弹窗的影响。
|
||||
- 审核各功能域翻译质量。
|
||||
|
||||
交付物:
|
||||
|
||||
- `LOCALIZATION_SPEC.md`
|
||||
- `GLOSSARY_EN_ZH.md`
|
||||
- `LANGUAGE_SWITCHER_SPEC.md`
|
||||
- `V1_TRANSLATION_SCOPE.md`
|
||||
- `LOCALE_NAVIGATION_BEHAVIOR.md`:记录首次访问、手动切换、Login、SSO、MCP OAuth 回跳及存储不可用时的期望与降级行为。
|
||||
|
||||
### 5.4 Agent 3:`qa-architect`
|
||||
|
||||
角色:测试方案设计智能体。
|
||||
|
||||
职责:
|
||||
|
||||
- 制订单元、集成和 E2E 测试边界。
|
||||
- 制定中英文验收用例。
|
||||
- 设计 key 一致性和硬编码扫描规则。
|
||||
- 设计中文布局检查矩阵。
|
||||
- 明确自动化与人工验证范围。
|
||||
- 为平台和功能开发准备测试接口。
|
||||
|
||||
交付物:
|
||||
|
||||
- `I18N_TEST_PLAN.md`
|
||||
- `TEST_CASES.md`
|
||||
- 自动化测试任务列表
|
||||
- 回归测试矩阵
|
||||
|
||||
### 5.5 Agent 4:`i18n-platform-developer`
|
||||
|
||||
角色:国际化基础设施开发智能体。
|
||||
|
||||
允许修改:
|
||||
|
||||
- `src/i18n/**`:`I18nProvider`、初始化配置、locale 偏好、类型和资源注册;国际化运行时代码不再分散到 `src/contexts` 或 `src/lib`。
|
||||
- `src/locales/en/**`、`src/locales/zh-CN/**`:仅在 Wave 1 创建目录、namespace 骨架和平台 smoke-test 所需的最小资源;G1 通过后按 namespace 移交对应 Owner。
|
||||
- `src/app/layout.tsx`。
|
||||
- `package.json`、`package-lock.json`。
|
||||
- 平台层对应测试。
|
||||
|
||||
职责:
|
||||
|
||||
- 根据通过评审的 ADR,从干净代码基线实现国际化基础设施。
|
||||
- 实现语言初始化、切换、持久化和英文回退。
|
||||
- 实现 `<html lang>` 同步。
|
||||
- 保证翻译资源同步就绪,或实现统一的 `ready` 渲染门禁,使普通 `t()` 与 `<Trans>` 首帧行为一致。
|
||||
- 建立 namespace 和类型约束。
|
||||
- 完成基础设施测试和静态导出构建验证。
|
||||
- 提交 `PLATFORM_VALIDATION_REPORT.md`,记录实现相对 ADR 的符合性和实际验证证据。
|
||||
|
||||
禁止批量修改业务页面,禁止修改生成文件 `src/lib/http/schema.d.ts`。
|
||||
|
||||
### 5.6 Agent 5:`i18n-shell-auth-developer`
|
||||
|
||||
角色:导航、登录和公共入口开发智能体。
|
||||
|
||||
负责范围:
|
||||
|
||||
- Navbar、Leftnav、用户菜单及其他全局壳层。
|
||||
- Login、Onboarding、Connect、MCP OAuth。
|
||||
- `src/locales/en/common.json`、`src/locales/zh-CN/common.json`。
|
||||
- `navigation`、`auth` 及上述页面对应的 namespace 和测试。
|
||||
|
||||
职责:
|
||||
|
||||
- 按本地化规范改造用户可见文案。
|
||||
- 同步处理 `aria-label`、`title`、placeholder 和校验提示。
|
||||
- 补充相关单元或集成测试。
|
||||
- 检查中文布局。
|
||||
|
||||
### 5.7 Agent 6:`i18n-feature-developer`
|
||||
|
||||
角色:业务功能开发智能体,按波次领取单一功能域。
|
||||
|
||||
v1 功能批次:
|
||||
|
||||
1. Models and Endpoints、API Keys。
|
||||
2. Usage、Cost Tracking、Budgets。
|
||||
|
||||
v2+ 候选批次(不属于 v1 范围,不进入 v1 完成定义):
|
||||
|
||||
3. Teams、Users、Organizations、Projects。
|
||||
4. Guardrails、Policies、Logs。
|
||||
5. Playground、Prompts、Agents、Skills。
|
||||
6. MCP Servers、Caching、Vector Stores 等其他页面。
|
||||
|
||||
每次任务必须列出明确目录和文件清单,不允许使用“其余未归属目录”。
|
||||
|
||||
### 5.8 Agent 7:`i18n-qa`
|
||||
|
||||
角色:持续测试和质量工程智能体。
|
||||
|
||||
职责:
|
||||
|
||||
- 从平台开发阶段开始持续测试。
|
||||
- 实现 key 集合一致性和硬编码报告工具。
|
||||
- 运行改动相关的单元及集成测试。
|
||||
- 验证切换、刷新、回退、插值和 `<html lang>`。
|
||||
- 验证中文布局和可访问性文案。
|
||||
- 提交缺陷并退回原代码 Owner。
|
||||
- 不直接大规模修改产品代码。
|
||||
|
||||
### 5.9 Agent 8:`i18n-integration-release`
|
||||
|
||||
角色:集成和发布验收智能体。
|
||||
|
||||
职责:
|
||||
|
||||
- 执行跨模块回归。
|
||||
- 汇总测试证据和遗留问题。
|
||||
- 验证 `lint`、格式和静态导出构建。
|
||||
- 准备发布与回滚清单。
|
||||
- 不绕过原 Owner 修改大范围业务代码。
|
||||
|
||||
---
|
||||
|
||||
## 6. 文件和字典 Ownership
|
||||
|
||||
Agent 0 在任务开始前维护完整的 `FILE_OWNERSHIP.md`。
|
||||
|
||||
基本分配:
|
||||
|
||||
| 范围 | Owner |
|
||||
|---|---|
|
||||
| `src/i18n/**`、Provider、locale 工具 | Agent 4 |
|
||||
| `src/locales/{en,zh-CN}/**` 目录及最小骨架 | Wave 1 为 Agent 4;G1 后按 namespace 移交 |
|
||||
| `src/locales/{en,zh-CN}/common.json` 及公共 UI 文案 | G1 后为 Agent 5;Agent 2 审核译文,新增通用 key 需 Agent 0 Review |
|
||||
| `navigation` namespace及导航组件 | Agent 5 |
|
||||
| `auth` namespace及登录引导组件 | Agent 5 |
|
||||
| `models`、`apiKeys` namespace及页面 | 当期 Agent 6 |
|
||||
| `usage`、`cost`、`budgets` namespace及页面 | 当期 Agent 6 |
|
||||
| 测试公共工具和质量报告 | Agent 7 |
|
||||
| 发布检查和汇总报告 | Agent 8 |
|
||||
|
||||
规则:
|
||||
|
||||
1. 同一文件同一时间只有一个 Owner。
|
||||
2. 功能 Agent 只写自己的 namespace,不共同编辑一个大型 JSON。
|
||||
3. `src/components/ui/**` 原则上保持无业务文案,由调用方传入文本。
|
||||
4. `src/utils/**` 不设置单一全目录 Owner,按具体文件划分。
|
||||
5. 修改非本人文件前必须向 Agent 0 提交变更请求。
|
||||
6. QA 发现产品缺陷后退回原 Owner,不直接全局修复。
|
||||
7. Agent 0 决定公共文件修改人和合并顺序。
|
||||
8. Agent 4 在 Wave 1 只创建字典目录、namespace 注册和 smoke-test 所需最小资源,不批量填写业务文案。
|
||||
9. G1 通过后,Agent 0 在 `FILE_OWNERSHIP.md` 中记录 `common`、`navigation`、`auth` 等资源文件向 Agent 5 的正式移交。
|
||||
10. `src/i18n/**` 中的 namespace 注册表、资源加载映射和类型声明始终归 Agent 4;Agent 5/6 只维护获授权的 JSON 资源,不得自行修改注册代码。
|
||||
11. 新增业务 namespace 时,功能 Agent 在任务单中填写 namespace、资源文件和注册需求,由 Agent 0 在阶段门禁期指派 Agent 4 统一注册;特殊情况下必须由 Agent 0 书面授权临时 Owner。
|
||||
|
||||
### 6.1 并行工作区与分支策略
|
||||
|
||||
为了保证并行开发的改动可隔离、可 Review、可回退,开发智能体不得直接在同一个工作区并发修改代码。
|
||||
|
||||
1. Agent 0 维护主集成工作区和集成分支。
|
||||
2. Agent 4、Agent 5、每个 Agent 6 任务实例和 Agent 7 使用独立 Git worktree 与独立任务分支。
|
||||
3. 分支命名建议:`i18n/<wave>-<agent>-<scope>`,例如 `i18n/w2-agent6-models`。
|
||||
4. 每个任务只提交其 Ownership 范围内的文件,不夹带无关格式化或其他 Agent 的改动。
|
||||
5. Agent 完成任务后提交 commit hash、diff 摘要、测试证据和遗留问题。
|
||||
6. Agent 0 针对独立变更集执行 Review;`CHANGES_REQUESTED` 由原 Agent 在原 worktree 修复。
|
||||
7. Review 通过后,Agent 0 按“平台层 → 公共层 → 功能层 → 测试层”顺序合并。
|
||||
8. 合并出现冲突时,由 Agent 0 指定唯一 Owner 处理,禁止多个 Agent 同时修复同一冲突。
|
||||
9. 每次阶段门禁通过后记录集成基线 commit,作为下一波工作的共同起点。
|
||||
10. `package.json`、`package-lock.json` 默认只允许 Agent 4 在平台 worktree 修改;其他 Agent 不得执行会改写依赖声明或锁文件的操作。后续确需新增依赖时,由 Agent 0 指定唯一临时 Owner。
|
||||
11. Agent 4 的依赖与平台变更通过 G1 后必须先合入集成基线;Agent 5/6/7 的开发 worktree 从该基线创建,或在开始写代码前 rebase 到该基线。
|
||||
12. 下游 worktree 使用 `npm ci` 按已批准的锁文件安装依赖,不使用 `npm install` 生成各自的锁文件改动。
|
||||
13. npm 下载缓存可以共享,但每个 worktree 默认使用独立 `node_modules`;禁止多个 worktree 直接共享或并发修改同一个 `node_modules`。
|
||||
14. Agent 7 如需在 G1 前验证平台候选提交,应从 Agent 4 的明确 commit 创建临时验证 worktree,且不得修改 `package.json` 或 `package-lock.json`。
|
||||
|
||||
### 6.2 v1 范围状态清单
|
||||
|
||||
Agent 2 建立 `V1_SCOPE_MANIFEST.md`,Agent 0 维护状态,Agent 7 填写测试和 UI 检查证据。
|
||||
|
||||
| 路由/组件 | Owner | 文案盘点 | EN | ZH-CN | 测试 | UI 检查 | Review |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| Navbar / Leftnav | Agent 5 | 待完成 | 待完成 | 待完成 | 待执行 | 待执行 | 待审核 |
|
||||
| Login / Onboarding | Agent 5 | 待完成 | 待完成 | 待完成 | 待执行 | 待执行 | 待审核 |
|
||||
| Connect / MCP OAuth | Agent 5 | 待完成 | 待完成 | 待完成 | 待执行 | 待执行 | 待审核 |
|
||||
| Models and Endpoints | Agent 6 | 待完成 | 待完成 | 待完成 | 待执行 | 待执行 | 待审核 |
|
||||
| API Keys | Agent 6 | 待完成 | 待完成 | 待完成 | 待执行 | 待执行 | 待审核 |
|
||||
| Usage / Cost Tracking | Agent 6A | 待完成 | 待完成 | 待完成 | 待执行 | 待执行 | 待审核 |
|
||||
| Budgets | Agent 6B | 待完成 | 待完成 | 待完成 | 待执行 | 待执行 | 待审核 |
|
||||
|
||||
G2、G3 必须以对应范围的清单项全部完成作为通过条件,不以“Agent 已报告完成”代替验收证据。
|
||||
|
||||
---
|
||||
|
||||
## 7. 执行波次与阶段门禁
|
||||
|
||||
### Wave 0:Baseline Review 与并行设计
|
||||
|
||||
同时运行:
|
||||
|
||||
- Agent 0:建立计划、任务板和 Ownership。
|
||||
- Agent 1:核查干净代码基线,分析现有工程模式并完成技术设计与最小 PoC。
|
||||
- Agent 2:完成语言交互、术语和翻译规范。
|
||||
- Agent 3:完成测试策略和验收用例。
|
||||
|
||||
门禁 G0:
|
||||
|
||||
- 除本方案文档外,代码工作树无本地修改且不残留候选 i18n 实现。
|
||||
- 技术 ADR 已通过 Review。
|
||||
- `POC_REPORT.md` 与 `LOCALE_NAVIGATION_BEHAVIOR.md` 已提交并通过 Review。
|
||||
- `zh-CN`、语言优先级、偏好存储和首屏策略已决策。
|
||||
- 构建期 `<title>` 和 metadata description 在 v1 保持英文的范围边界已记录。
|
||||
- Login、SSO、MCP OAuth 整页跳转后的语言保持规则已写入本地化与测试方案。
|
||||
- v1 页面和翻译范围已冻结。
|
||||
- 测试策略已通过 Review。
|
||||
|
||||
### Wave 1:平台能力与测试基础
|
||||
|
||||
同时运行:
|
||||
|
||||
- Agent 0:Review 和协调。
|
||||
- Agent 4:根据通过评审的 ADR,从零搭建平台实现。
|
||||
- Agent 7:基于 Agent 4 的明确候选 commit 实现或执行平台测试和静态检查,不修改依赖声明及锁文件。
|
||||
- Agent 5:只读盘点导航、登录文案,在任务交付物中拟定 key;G1 前不创建或修改源码及字典文件。
|
||||
|
||||
门禁 G1:
|
||||
|
||||
- Provider、语言切换和偏好保存可用。
|
||||
- 英文回退有效。
|
||||
- `<html lang>` 同步正确。
|
||||
- 首屏策略已从“初始化脚本、语言就绪门禁、接受短暂切换”等候选项中明确选定,写入 ADR,并附构建及浏览器验证证据。
|
||||
- Login、SSO、MCP OAuth 整页跳转后的语言恢复 PoC 已通过,或已形成经 Agent 0 批准的明确限制与降级方案。
|
||||
- 普通 `t()` 与 `<Trans>` 均在资源就绪后渲染,首次渲染不暴露原始 key。
|
||||
- 定向测试和 `npm run build` 通过。
|
||||
- `PLATFORM_VALIDATION_REPORT.md` 已提交。
|
||||
- Agent 1 完成 ADR 符合性复核。
|
||||
- Agent 0 完成代码 Review。
|
||||
|
||||
### Wave 2:第一批功能并行开发
|
||||
|
||||
同时运行:
|
||||
|
||||
- Agent 0:Review 和冲突处理。
|
||||
- Agent 5:导航、Login、Onboarding、Connect、MCP OAuth。
|
||||
- Agent 6:Models and Endpoints、API Keys。
|
||||
- Agent 7:持续测试已经提交的模块。
|
||||
|
||||
门禁 G2:
|
||||
|
||||
- 全局壳层和核心入口支持中英文。
|
||||
- Models、API Keys 主流程支持中英文。
|
||||
- `V1_SCOPE_MANIFEST.md` 中本波次对应项目全部完成。
|
||||
- 相关测试通过。
|
||||
- 中文布局不存在阻塞性问题。
|
||||
- Agent 0 Review 通过。
|
||||
|
||||
### Wave 3:第二批功能并行开发
|
||||
|
||||
同时运行:
|
||||
|
||||
- Agent 0:Review 和任务调度。
|
||||
- Agent 6A:Usage、Cost Tracking。
|
||||
- Agent 6B:Budgets,以及 Budgets 范围内的测试和中文布局检查。
|
||||
- Agent 7:持续测试与缺陷回归。
|
||||
|
||||
Agent 6A、6B 是同一角色的两个任务实例,必须拥有完全不相交的文件列表。
|
||||
|
||||
门禁 G3:
|
||||
|
||||
- v1 业务范围全部完成。
|
||||
- `V1_SCOPE_MANIFEST.md` 全部项目具有测试、UI 检查和 Review 证据。
|
||||
- 中英文 key 集一致。
|
||||
- 无 P0、P1 缺陷。
|
||||
- 定向测试、lint、格式和构建通过。
|
||||
|
||||
### Wave 4A:集成与发布验收
|
||||
|
||||
同时运行:
|
||||
|
||||
- Agent 0:最终 Review 和发布决策。
|
||||
- Agent 2:术语与中文体验复核。
|
||||
- Agent 7:完整回归和质量报告。
|
||||
- Agent 8:发布检查、证据汇总和回滚清单。
|
||||
|
||||
如果 Wave 4A 发现需要修改产品代码的缺陷,暂停 Agent 2、Agent 8,并进入 Wave 4B。
|
||||
|
||||
### Wave 4B:定向缺陷修复(按需启动)
|
||||
|
||||
同时运行:
|
||||
|
||||
- Agent 0:判定缺陷 Owner、优先级并 Review 修复。
|
||||
- 原开发 Owner 1:修复自身范围内缺陷。
|
||||
- 原开发 Owner 2:修复另一个不重叠范围内缺陷;没有第二组缺陷时不启动。
|
||||
- Agent 7:定向回归并更新测试报告。
|
||||
|
||||
修复通过后恢复 Wave 4A,由 Agent 2、Agent 8 完成最终体验和发布复核。
|
||||
|
||||
缺陷进入 `TASK_BOARD.md` 后按以下顺序排队:P0 → P1 → 阻塞同一门禁的 P2 → 其他 P2。每轮最多启动两个文件范围不重叠的开发 Owner;第三组及后续缺陷保持排队,由 Agent 0 在上一组完成 Review 后调度下一组。
|
||||
|
||||
门禁 G4:
|
||||
|
||||
- v1 完成定义全部满足。
|
||||
- P0、P1 缺陷清零。
|
||||
- P2 缺陷具有明确处置结论。
|
||||
- 发布和回滚清单完成。
|
||||
- Agent 0 给出 `APPROVED` 结论。
|
||||
|
||||
---
|
||||
|
||||
## 8. 标准任务单
|
||||
|
||||
Agent 0 下发的每个任务必须包含:
|
||||
|
||||
```markdown
|
||||
## 目标
|
||||
任务需要实现的可验证结果。
|
||||
|
||||
## 输入与依赖
|
||||
依赖的 ADR、规范、接口和前置提交。
|
||||
|
||||
## 文件范围
|
||||
允许修改的精确目录和文件。
|
||||
|
||||
## Namespace 变更
|
||||
是否新增 namespace、资源文件路径、是否需要修改注册表、注册代码 Owner。
|
||||
|
||||
## 禁止范围
|
||||
明确禁止修改的文件和行为。
|
||||
|
||||
## 交付物
|
||||
代码、字典、测试和报告。
|
||||
|
||||
## 验收标准
|
||||
功能结果、测试文件和检查命令。
|
||||
|
||||
## 风险与接缝
|
||||
可能与其他任务冲突的地方。
|
||||
|
||||
## 回报格式
|
||||
worktree/分支、commit hash、改动文件、翻译 key、测试结果、遗留问题和待 Review 决策。
|
||||
```
|
||||
|
||||
进入 Review 时必须提供:
|
||||
|
||||
- worktree 路径和任务分支名称。
|
||||
- commit hash 或边界明确的 diff。
|
||||
- 修改文件列表。
|
||||
- 新增、修改和删除的翻译 key。
|
||||
- 实际执行的测试及工程检查命令。
|
||||
- 测试结果和失败信息。
|
||||
- 未执行的检查及原因。
|
||||
- 已知限制和跨 Agent 接缝。
|
||||
|
||||
---
|
||||
|
||||
## 9. Review 与缺陷回流机制
|
||||
|
||||
Agent 0 对每个开发任务执行两轮 Review。
|
||||
|
||||
### 9.1 设计符合性 Review
|
||||
|
||||
- 是否符合 ADR 和语言优先级。
|
||||
- 是否遵守术语表和翻译范围。
|
||||
- 是否遵守 namespace 与文件 Ownership。
|
||||
- 是否误翻译模型名、API 参数、日志或代码示例。
|
||||
|
||||
### 9.2 代码与质量 Review
|
||||
|
||||
- 是否正确处理动态插值和复数。
|
||||
- 是否具有英文回退。
|
||||
- 是否覆盖可访问性文案。
|
||||
- 是否增加匹配风险的测试。
|
||||
- 是否改变原有业务行为。
|
||||
- 是否出现无意义的大范围格式化。
|
||||
- 是否具有可复现的测试证据。
|
||||
|
||||
Review 结论:
|
||||
|
||||
- `APPROVED`
|
||||
- `CHANGES_REQUESTED`
|
||||
- `BLOCKED`
|
||||
|
||||
未经 `APPROVED` 的任务不得进入集成基线。
|
||||
|
||||
QA 发现缺陷后的流程:
|
||||
|
||||
```text
|
||||
QA 提交复现步骤和证据
|
||||
→ Agent 0 判定 Owner 与优先级
|
||||
→ 原开发 Owner 修复
|
||||
→ QA 定向回归
|
||||
→ Agent 0 Review 并关闭
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 测试与工程门禁
|
||||
|
||||
测试重点:
|
||||
|
||||
- 默认语言正确。
|
||||
- 切换语言即时生效。
|
||||
- 刷新后语言保持。
|
||||
- 缺失中文时回退英文。
|
||||
- 不显示原始翻译 key。
|
||||
- `<html lang>` 与当前语言一致。
|
||||
- 中文按钮、菜单、表格和弹窗不溢出。
|
||||
- 切换语言不丢失表单状态。
|
||||
- Login、SSO、MCP OAuth 整页跳转并返回后语言偏好保持。
|
||||
- 动态数量、日期、数字和货币正确本地化。
|
||||
- API 数据、模型名、日志和代码示例不被误翻译。
|
||||
- 构建期 `<title>` 与 metadata description 在 v1 保持英文,且没有被功能 Agent 意外改写。
|
||||
|
||||
工程检查:
|
||||
|
||||
```bash
|
||||
cd ui/litellm-dashboard
|
||||
npm run lint
|
||||
npm run format:check
|
||||
npm run build
|
||||
```
|
||||
|
||||
补充规则:
|
||||
|
||||
- Vitest 必须指定受影响的测试文件,不执行无路径的完整测试集。
|
||||
- 测试命令必须由 Agent 3 根据项目 Vitest 配置确认,并在任务回报中记录准确命令和结果。
|
||||
- 只有修改后端路由或响应模型时才运行 `npm run gen:api`。
|
||||
- 若清理了已有 ESLint suppression,使用项目规定的 `eslint . --prune-suppressions` 并检查差异。
|
||||
- 不假设或新增未经项目确认的 `eslint-budgets.json`。
|
||||
|
||||
---
|
||||
|
||||
## 11. v1 完成定义
|
||||
|
||||
v1 只有同时满足以下条件才算完成:
|
||||
|
||||
1. 支持 `en` 与 `zh-CN`。
|
||||
2. 语言切换即时生效,刷新后偏好保留。
|
||||
3. 导航、Login、Onboarding、Connect、MCP OAuth、Models、API Keys、Usage、Cost Tracking 和 Budgets 完成中文化。
|
||||
4. 中文缺失时回退英文,不直接显示翻译 key。
|
||||
5. 中文界面无严重截断、重叠和遮挡。
|
||||
6. 切换语言不清空表单或触发异常请求。
|
||||
7. Login、SSO、MCP OAuth 整页跳转并返回后语言偏好保持,或存在经批准且已记录的限制与降级方案。
|
||||
8. 构建期 `<title>` 与 metadata description 保持英文,不在 v1 中产生半完成的多语言 SEO 行为。
|
||||
9. 模型名、API 字段、日志和代码示例未被错误翻译。
|
||||
10. 改动相关的单元及集成测试通过。
|
||||
11. `lint`、`format:check` 和静态导出构建通过。
|
||||
12. Agent 7 提交测试报告。
|
||||
13. Agent 2 完成术语与中文体验复核。
|
||||
14. Agent 0 完成最终 Review 并给出 `APPROVED`。
|
||||
15. `V1_SCOPE_MANIFEST.md` 全部项目状态为完成,并附有测试、UI 检查和 Review 证据。
|
||||
|
||||
---
|
||||
|
||||
## 12. 下一步
|
||||
|
||||
1. 启动 Wave 0,由 Agent 0 建立 `MASTER_PLAN.md`、`TASK_BOARD.md`、`FILE_OWNERSHIP.md` 和 `DECISIONS.md`。
|
||||
2. Agent 1 从干净代码基线完成技术设计、PoC 与 ADR,并验证静态导出构建。
|
||||
3. Agent 2、Agent 3 并行完成本地化规范、范围清单和测试方案。
|
||||
4. Agent 0 Review Wave 0 全部交付物并执行 G0 门禁。
|
||||
5. G0 通过后,为 Agent 4、Agent 5、Agent 7 创建独立任务 worktree 和分支,启动 Wave 1。
|
||||
6. 后续严格按照“1 个总控 + 3 个执行智能体”的并发限制滚动执行。
|
||||
288
docs/i18n/I18N_TECH_DESIGN.md
Normal file
288
docs/i18n/I18N_TECH_DESIGN.md
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
# LiteLLM Dashboard i18n — 技术方案设计(I18N_TECH_DESIGN)
|
||||
|
||||
> 文档角色:Agent 1(`i18n-architect`)· Wave 0 交付 · 状态:**待 Baseline Review → 待 PoC 验证**
|
||||
> 基线:`I18N_MULTI_AGENT_PLAN.md` v1.4(§3、§5.2、D1–D14)
|
||||
> 范围:纯技术设计;本文档 **不修改任何产品代码**,所有文件路径均为设计目标而非已落地实现。
|
||||
|
||||
---
|
||||
|
||||
## 0. 阅读指引
|
||||
|
||||
| 章节 | 内容 | 对应门禁/决策 |
|
||||
|---|---|---|
|
||||
| §1 | 技术选型兼容性结论(静态导出 + i18next + React 19) | D1、D4 |
|
||||
| §2 | Provider 层级 + `src/i18n/**` 模块划分 | D3 |
|
||||
| §3 | locale 初始化 与《语言偏好优先级》实现映射 | D5、D6 |
|
||||
| §4 | `<html lang>` 同步 + **首屏策略(选定 1 种)** | D7、G0/G1 门禁 |
|
||||
| §5 | 普通 `t()` 与 `<Trans>` 的资源就绪门禁 | §3.1、G1 |
|
||||
| §6 | `src/locales/{en,zh-CN}/**` 组织 与 namespace 注册 | D3、§3.3/3.4 |
|
||||
| §7 | 后端 `UI settings.language` 调查结论(P1/P2) | P1、P2 |
|
||||
| §8 | 与既有 Provider/路由组 layout 的接缝 | — |
|
||||
|
||||
---
|
||||
|
||||
## 1. 技术选型兼容性结论
|
||||
|
||||
### 1.1 结论
|
||||
|
||||
**`i18next` + `react-i18next` 与 `output: "export"`(静态导出)+ Next.js 16 + React 19 完全兼容,可作为 v1 方案。** 理由如下:
|
||||
|
||||
1. `react-i18next` 的核心机制是**纯客户端**的 React Context + Hook(`useTranslation`),不依赖 Next.js 的任何服务端渲染钩子(`headers()`、`cookies()`、middleware、app-router locale 约定)。它既不需要 SSR 数据注入,也不要求在 node 侧执行。
|
||||
2. 在 `output: "export"` 下没有 Node 运行时,因此本方案**从不尝试在服务端解析 locale**(不依赖服务端 locale 路由 / httpOnly cookie 协商,见 D4)。locale 初始化完全在客户端 `I18nProvider`(`use client`)的挂载期完成。
|
||||
3. 静态 HTML 首帧默认按英文渲染(D8:构建期 title/meta 保持英文)。`<html lang>` 由客户端在挂载后同步(§4),`suppressHydrationWarning` 仅抑制该属性的 hydration 警告(与 next-themes 现有做法一致,见 `src/app/layout.tsx`)。
|
||||
|
||||
### 1.2 已验证版本依据(本机只读核查,2026-09-09)
|
||||
|
||||
| 项 | 值 | 依据 |
|
||||
|---|---|---|
|
||||
| 项目 React | `19.2.8` | `ui/litellm-dashboard/package.json` |
|
||||
| 项目 Next | `16.2.11` | 同上 |
|
||||
| 项目 TS | `5.9.3` | 同上 |
|
||||
| `react-i18next` 最新 | `17.0.13`(2026-09-01 发布) | `npm view` |
|
||||
| `react-i18next` peerDeps | `react: >= 16.8.0`, `i18next: >= 26.2.0`, `typescript: ^5\|\|^6\|\|^7` | `npm view react-i18next peerDependencies` |
|
||||
| `i18next` 最新 | `26.4.2` | `npm view i18next version` |
|
||||
| `i18next` peerDeps | 无 react peer(仅 `typescript`) | `npm view i18next@latest peerDependencies` |
|
||||
|
||||
**兼容性判定**:React 19.2.8 ≥ 16.8.0,故 `react-i18next@17` 与 `i18next@26` 满足 peer 约束。**建议锁定范围**:`react-i18next@^17.0.13` + `i18next@^26.4.2`(Wave 1 由 Agent 4 写入,遵循 D12 单一写入者)。不采用旧版 15/16 行,避免为规避问题而引入历史缺陷。
|
||||
|
||||
> 说明:以上为**只读** `npm view`(不安装、不改依赖)。实际安装验证由 Wave 1 Agent 4 承担(见 `POC_REPORT.md` PoC-1)。
|
||||
|
||||
---
|
||||
|
||||
## 2. Provider 层级与 `src/i18n/**` 模块划分
|
||||
|
||||
### 2.1 Provider 放置层级
|
||||
|
||||
`I18nProvider` 放**根 `src/app/layout.tsx`,作为最外层 Provider**(在既有 `ThemeProvider` 之外或与其并列的最上方)。理由:
|
||||
|
||||
- 根 layout 渲染 `<html>`,`I18nProvider` 在此可统一同步 `document.documentElement.lang`(§4)。
|
||||
- 所有路由组(`(dashboard)`、`chat`、`connect`、`login`、`onboarding` 等)都经由根 layout,各自无需重复挂 Provider。
|
||||
- `I18nProvider` 是 `use client` 组件;因 Next.js 中根 layout 本身是 server component,需用一个 `"use client"` 的子组件包装。页面内部不含服务端下发的动态 locale,故无需用全局 server/client provider 分支。
|
||||
|
||||
设计目标示意(**非当前代码,禁止修改**):
|
||||
|
||||
```tsx
|
||||
// src/app/layout.tsx(设计目标)
|
||||
<html lang="en" suppressHydrationWarning>
|
||||
<body className={inter.className}>
|
||||
<I18nProvider> {/* 新增:最外层,use client 包装 */}
|
||||
<ThemeProvider attribute="class" defaultTheme="light" enableSystem disableTransitionOnChange>
|
||||
<NuqsAdapter>
|
||||
<ReactQueryProvider>
|
||||
<AuthProvider>{children}</AuthProvider>
|
||||
<Toaster />
|
||||
</ReactQueryProvider>
|
||||
</NuqsAdapter>
|
||||
</ThemeProvider>
|
||||
</I18nProvider>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### 2.2 `src/i18n/**` 模块划分(Ownership:Agent 4,永久,见 FILE_OWNERSHIP)
|
||||
|
||||
```
|
||||
src/i18n/
|
||||
├── index.ts # 公开出口:导出 I18nProvider 与 getI18n/useI18n 便捷 API
|
||||
├── I18nProvider.tsx # "use client" Provider:初始化实例 + 语言就绪门禁 + <html lang> 同步
|
||||
├── i18n.ts # createI18n(): 构建/复用 i18next 单例(resources、fallbackLng、supportedLngs)
|
||||
├── localePreferences.ts# 读/写语言偏好(cookie + localStorage 双层 + 浏览器语言嗅探)——纯函数,可单测
|
||||
├── detectLocale.ts # 语言解析/规范化('zh'→'zh-CN'、大小写、支持列表校验)
|
||||
├── types.d.ts # 全局声明增强:模块扩展 'i18next' 的 CustomTypeOptions(资源键类型安全)
|
||||
└── resources/registry.ts # === namespace 注册表 + 资源加载映射(唯一真源)=== 见 §6.2
|
||||
```
|
||||
|
||||
职责边界:
|
||||
|
||||
- `i18n.ts` 持有 i18next **单例**的生命周期(`initReactI18next`、`init`)。为保证 SSR 静态构建与 HMR 稳定,导出一个惰性 `getI18n()` 而非模块顶层 `init`(避免重复初始化)。
|
||||
- `localePreferences.ts` 不依赖 React,纯逻辑(读 cookie/localStorage/navigator),供单测覆盖 D5 优先级。
|
||||
- `resources/registry.ts` 是**唯一的 namespace 注册表 + 资源加载映射**:声明「locale ↔ namespace ↔ json 路径」,并导出给 `i18n.ts` 构造 resources。功能 Agent(A5/A6)**只往 json 加 key,不改此注册表**(FILE_OWNERSHIP 关键规则 1/6)。
|
||||
- `types.d.ts` 用 `CustomTypeOptions`(`defaultNS`、`resources`)让 `t()` 的 key 具备类型约束;英文资源为类型真源(D2:en 为真源)。
|
||||
|
||||
---
|
||||
|
||||
## 3. locale 初始化 与《语言偏好优先级》实现映射
|
||||
|
||||
### 3.1 优先级实现映射(D5 → 代码)
|
||||
|
||||
规范化后的优先级链(`detectLocale.ts + localePreferences.ts`):
|
||||
|
||||
```text
|
||||
用户主动选择(显式写入偏好存储)
|
||||
→ 用户级 UI 设置(后端 UI settings.language —— v1 不采用,见 §7)
|
||||
→ SameSite cookie / localStorage(双层)
|
||||
→ 浏览器语言(navigator.languages,经 supportedLngs 过滤)
|
||||
→ 英文 en(兜底)
|
||||
```
|
||||
|
||||
实现要点:
|
||||
|
||||
- **用户主动选择 vs 残留存储的区分**:用户在切换器显式选择语言时,`setLocale` 同时写 **cookie + localStorage** 并 `changeLanguage`。读取时把「cookie/localStorage 中存在显式写入的 locale」视为「用户主动选择」。为区分「显式选择」与「自动探测回写」,可用单独的 marker(如 cookie 值带 `explicit` 标记,或 localStorage 键与探测键分离)。**v1 建议**:统一用一个偏好键(如 `dashboard.locale`),只要该键存在即为用户选择;首次无键时才走浏览器语言。该判定需在 §3.2 明确,避免「用户从不手选」时被浏览器语言误判为主动选择。
|
||||
- **cookie 属性**:`SameSite=Lax`(非 Strict,避免 Okta/SSO 回跳丢失)、`path=/`、非 httpOnly(前端需读,D4 允许)。语言不敏感隐私,不设 Secure 亦可在 http 下工作;生产建议 `Secure`(见 TECH_RISKS R8)。
|
||||
- **无后端用户级设置**:P1/P2 结论为 v1 不使用 `UI settings.language`(§7),故优先级链简化为「显式选择 → cookie/localStorage → 浏览器语言 → en」。
|
||||
- **i18next 配置**:`supportedLngs: ['en','zh-CN']`、`fallbackLng: 'en'`、`nonExplicitSupportedLngs: false` + 在 `detectLocale` 中把 `navigator.language` 规范化为 `zh-CN`(`'zh'`/`'zh-Hans'`→`'zh-CN'`;其余→en)。load 路径由 registry 提供静态 resources,不使用 `loadPath` 网络加载(静态导出下目录外资源不可用)。
|
||||
|
||||
### 3.2 关键判定汇总(供 PoC 与后续澄清)
|
||||
|
||||
| # | 判定 | v1 默认 | 依据 |
|
||||
|---|---|---|---|
|
||||
| L1 | 偏好键唯一性 | 单一键 `dashboard.locale`,存在即视为显式选择 | §3.1 |
|
||||
| L2 | 浏览器语言含义 | 仅首次(无偏好键)时使用 | §3.1 |
|
||||
| L3 | cookie 属性 | `SameSite=Lax; path=/;`(生产加 `Secure`) | §3.1 / R8 |
|
||||
| L4 | 根 layout 的 `lang` 静态默认 | `en`(与静态 HTML 一致) | §4 |
|
||||
| L5 | 管理员全局覆盖用户选择 | v1 不实现(无该字段) | §7 / D8 |
|
||||
|
||||
---
|
||||
|
||||
## 4. `<html lang>` 同步 与 首屏策略(G0/G1 门禁,必须选定)
|
||||
|
||||
### 4.1 首屏策略:**选定「语言就绪门禁 + 挂载后同步 `<html lang>`」**,不采用初始化脚本、不接受短暂切换。
|
||||
|
||||
从候选「初始化脚本 / 语言就绪门禁 / 接受短暂切换」中,**选定「语言就绪门禁」(并且不在 `<head>` 内注入内联脚本)**。理由:
|
||||
|
||||
- **初始化脚本(在静态导出下基本无效)**:该类脚本需在 next 的 `<script>` 阶段于头部内联以在 paint 前改写 `lang`/文本。但静态导出没有服务端可注入的 head 时机,且「切换到中文」需要**运行时加载 zh-CN 资源**——这只能在 client JS 就绪后通过 i18next 完成,内联脚本无法在现代打包下前置加载 JSON 资源并替换整棵 textContent。故初始化脚本收益低。
|
||||
- **接受短暂切换**:会把「首帧英文→JS 就绪后切中文」的闪烁当作常态,直接违反 D7「禁止首屏显示原始 key 后再替换」的精神(§5 进一步禁止 key 暴露)。不可接受。
|
||||
- **语言就绪门禁**最符合静态导出约束:首帧即是合法的英文 UI(D2 默认语言为 en,D8 title 为英文),不存在「错误语言闪烁」;JS 就绪后 i18next 收敛到偏好语言并同步 `<html lang>`。就绪门禁只是**短于 paint 的少量额外延时**,且保证「看到的就是最终语言」或「简短英文首帧后一次切换」。
|
||||
|
||||
### 4.2 就绪门禁实现草图(设计目标)
|
||||
|
||||
`I18nProvider` 在挂载时:
|
||||
|
||||
1. 读取偏好(§3)→ 得到目标语言 `target`。
|
||||
2. 若 `target === 'en'`:`i18next.changeLanguage('en')`,`<html lang>` 已为 `en`,**无需门禁**,直接渲染 children。
|
||||
3. 若 `target === 'zh-CN'`:`await i18next.changeLanguage('zh-CN')`(resources 已在 `init` 时注册,changeLanguage 为同步内部切换完的 `t` 就绪),当 `i18n.isInitialized && i18n.language === 'zh-CN'` 时为「就绪」。就绪前渲染一个**轻量就绪态**(如 `aria-busy` 的空壳/骨架,不渲染依赖 `t()` 的业务内容),就绪后渲染 children 并设置 `document.documentElement.lang = 'zh-CN'`。
|
||||
|
||||
```tsx
|
||||
// src/i18n/I18nProvider.tsx(设计目标)
|
||||
"use client";
|
||||
export function I18nProvider({ children }: { children: React.ReactNode }) {
|
||||
const [ready, setReady] = useState(() => resolveTarget() === "en"); // en 直接就绪
|
||||
useEffect(() => {
|
||||
const target = resolveTarget(); // localePreferences + detectLocale
|
||||
getI18n().then(async (i18n) => {
|
||||
if (target === "zh-CN") await i18n.changeLanguage("zh-CN");
|
||||
document.documentElement.lang = i18n.language; // 与 <html lang> 同步
|
||||
setReady(true);
|
||||
});
|
||||
}, []);
|
||||
if (!ready) return null; // 或轻量就绪态;首帧为 en 时立即渲染
|
||||
return children; // 实际会包一层 I18nextProvider/useSSR(false)
|
||||
}
|
||||
```
|
||||
|
||||
要点:
|
||||
- **`<html lang>` 同步**:`document.documentElement.lang = i18n.language`,在 `changeLanguage` 完成后、children 渲染前执行,使浏览器/屏幕阅读器解析到的语言与实际内容一致。
|
||||
- 不依赖内联脚本、不动 `metadata`(D8 保持英文 title)。
|
||||
- 就绪态渲染 null 或骨架,**不渲染任何 `t()`/`<Trans>` 内容**,天然杜绝「key 先暴露」(§5)。
|
||||
|
||||
### 4.3 PoC 验证
|
||||
|
||||
首屏切换的判定证据与测量由 Wave 1 执行(见 `POC_REPORT.md` PoC-2/PoC-3):重点验证「en 默认首帧合法」、「切 zh-CN 后 `<html lang>` 更新」、「就绪门禁下业务内容不在就绪前出现」。
|
||||
|
||||
---
|
||||
|
||||
## 5. 普通 `t()` 与 `<Trans>` 的资源就绪门禁
|
||||
|
||||
### 5.1 统一门禁原则
|
||||
|
||||
**禁止首帧暴露原文 key。** 凡渲染翻译内容的组件,必须保证 i18next 资源就绪后其 `t`/`<Trans>` 才被求值。承接 §4.2 的顶层就绪门禁,业务组件**默认**就处于就绪之后(因为 `I18nProvider` 就绪前不渲染 children)。在此基础上再加两层保障:
|
||||
|
||||
1. **资源键集合约束**:英文 `en/*.json` 是类型与资源真源(D2),`t()` 的 key 受 `CustomTypeOptions` 类型校验。**只允许引用已注册 namespace 的 key**——key 不存在会在类型层报错,从而无法「引用一个没资源的 key」(见 §6 的 en/zh 键一致性检查 + `test:types`)。
|
||||
2. **运行时兜底**:i18next 对缺失 key 默认回退 `fallbackLng='en'`;若 en 亦缺,`returnNull`/`returnEmptyString` 策略应配置为**返回 key 本身或空串且不渲染原文句子**,并在开发模式打 warning 以便早发现。**绝不允许把「英文句子」作为 key**(§3.4 语义 key 规范),从根上防止「key=原文→暴露原文」的伪就绪。
|
||||
|
||||
### 5.2 `<Trans>` 的门禁
|
||||
|
||||
- `<Trans>` 依赖资源同步就绪(§3.1:必须在资源同步就绪或统一 ready 门禁之后渲染)。
|
||||
- 因 §4.2 顶层门禁已保证业务子树在就绪后才挂载,`<Trans>` 不会有「先渲染占位再替换」窗口。
|
||||
- 若个别模块需要在自己内部再等,使用 `useTranslation` 的 `ready` 标志:`const { t, ready } = useTranslation('ns'); if (!ready) return skeleton;`。**推荐所有激进的内联文案一次性接入统一门禁,避免每个组件各自开花**;组件级 `ready` 仅用于少数在就绪后有异步依赖的场景。
|
||||
- `<Trans>` 复数/插值注意事项:zh 不建形态复数分支(D9),用 `{{count}}` 计数插值;`<Trans>` 内嵌组件(如带链接)需保证 `i18n` 与组件 props 的 children 均就绪。
|
||||
|
||||
### 5.3 断言/测试
|
||||
|
||||
- 集成测试以「首帧(未就绪)不出现 key 原文 / 不出现未翻译句」为断言(见 POC_REPORT PoC-4 与测试方案章节;由 Agent 3 细化)。
|
||||
|
||||
---
|
||||
|
||||
## 6. `src/locales/{en,zh-CN}/**` 组织 与 namespace 注册
|
||||
|
||||
### 6.1 目录组织
|
||||
|
||||
```
|
||||
src/locales/
|
||||
├── en/
|
||||
│ ├── common.json # 全局公共文案(A4 Wave1 骨架 → G1 后 A5)
|
||||
│ ├── navigation.json
|
||||
│ ├── auth.json
|
||||
│ ├── models.json
|
||||
│ ├── apiKeys.json
|
||||
│ ├── usage.json
|
||||
│ ├── cost.json
|
||||
│ └── budgets.json
|
||||
└── zh-CN/
|
||||
└── (同名 json,键集合与 en 一致)
|
||||
```
|
||||
|
||||
- **namespace = 文件名 = key 冒号前缀**(§3.4:如 `common:action.delete` 对应 `common.json` 的 `action.delete`)。
|
||||
- 英文与中文**键集合必须一致**(§3.4;用 CI 键集合 diff 校验,见 R9)。
|
||||
- 中文按语义组织,不机械复制英文复数结构(D9)。
|
||||
|
||||
### 6.2 namespace 注册表 / 资源加载映射(唯一真源,Agent 4 永久 Owner)
|
||||
|
||||
`src/i18n/resources/registry.ts` 以数组/映射声明全部 namespace 及各 locale 的静态资源对象(`import common from "@/locales/en/common.json"`),供 `i18n.ts` 构造 `resources`。**往该注册表新增/删除 namespace 只有 Agent 4 可改**(FILE_OWNERSHIP 规则 6)。功能 Agent 新增业务 namespace 时在任务单填注册需求,由 A0 指派 A4 注册。
|
||||
|
||||
登记形式(设计目标):
|
||||
|
||||
```ts
|
||||
// src/i18n/resources/registry.ts
|
||||
export const NAMESPACES = ["common", "navigation", "auth", "models", "apiKeys", "usage", "cost", "budgets"] as const;
|
||||
export type Namespace = (typeof NAMESPACES)[number];
|
||||
export const RESOURCES = {
|
||||
en: { common, navigation, auth, models, apiKeys, usage, cost, budgets }, // 惰性 import
|
||||
"zh-CN": { common, navigation, auth, models, apiKeys, usage, cost, budgets },
|
||||
} as const satisfies Record<Locale, Record<Namespace, object>>;
|
||||
```
|
||||
|
||||
`types.d.ts` 引用 `RESOURCES['en']` 作为 `CustomTypeOptions['resources']`,保证 `t('common:action.delete')` 有类型。
|
||||
|
||||
---
|
||||
|
||||
## 7. 后端 `UI settings.language` 调查结论(P1/P2)
|
||||
|
||||
本机只读调查(`litellm/` 后端代码,2026-09-09):
|
||||
|
||||
- **`UISettings` 模型**(`litellm/proxy/ui_crud_endpoints/proxy_setting_endpoints.py`)是代理级(管理员/全局)UI 配置,经 `/get/ui_settings`、`/update/ui_settings` 暴露,落在 `LiteLLM_UISettings` 表(`litellm/repositories/table_repositories.py:UISettingsRepository`,表名 `LiteLLM_UISettings` 见 `litellm/proxy/_types.py`)。
|
||||
- 该模型有 `model_config = ConfigDict(extra="allow")`(可携带额外字段),但**持久化受 `ALLOWED_UI_SETTINGS_FIELDS` 白名单约束**,而该白名单**不包含 `language` 字段**。白名单现有字段如 `disable_model_add_for_internal_users`、`enabled_ui_pages_internal_users`、`enable_chat_ui` 等,均与语言无关。
|
||||
- 即:**后端当前不存在可用的 `UI settings.language` 全局语言设置**。即便请求携带 `language`,也不在白名单内,不会被持久化/下发。
|
||||
|
||||
**结论:**
|
||||
- **P2(该字段是否真实存在)**:**不存在**。`language` 不在 `UISettings` 白名单。
|
||||
- **P1(管理员全局设置是否覆盖用户主动选择)**:**v1 不实现该覆盖**,因为字段不存在。优先级链退化见 §3.1。若未来后端新增该字段,需产品决策「管理员全局 vs 用户选择」的覆盖语义,再补实现(本设计已预留 L5 判定位)。
|
||||
- 这同时**简化了 v1**:无需「用户偏好 vs 管理员全局」的合并/剥离逻辑,也避免在静态导出下引入「先取全局再取用户」的两段式异步。
|
||||
|
||||
---
|
||||
|
||||
## 8. 与既有 Provider / 路由组 layout 的接缝
|
||||
|
||||
- 根 `layout.tsx`:现为 `ThemeProvider → NuqsAdapter → ReactQueryProvider → AuthProvider`。`I18nProvider` 追加为**最外层**(§2.1)。**不移动**现有 Provider 顺序,避免影响 next-themes 首帧打 class 的既有行为。
|
||||
- `(dashboard)/layout.tsx`:`use client` 路由组,另含 `SidebarProvider`、`ThemeContext`、`PluginModeContext` 等,均用到 `useAuth`。它们处于 `AuthProvider` 之下,`I18nProvider` 在最外层 → 这些组件可安全调用 `useTranslation`。**注意**:该 layout 自身是 `use client`,若其中出现硬编码文案需在 Wave 2 起中文化(A5/A6 职责)。
|
||||
- `chat/layout.tsx`、`connect/layout.tsx` 独立于 `(dashboard)`,仍归根 layout 的 `I18nProvider` 管理,无需各自挂 Provider。
|
||||
- **`<Trans>` 与 next-themes 的首帧 class 互不干扰**:`suppressHydrationWarning` 已覆盖 `<html>` 的未知属性(theme class / lang),本方案不加额外 suppress。
|
||||
|
||||
---
|
||||
|
||||
## 9. 依赖变更清单(仅供 Wave 1 Agent 4 执行,本设计不落地)
|
||||
|
||||
| 变更 | 目标值 | 写入者 |
|
||||
|---|---|---|
|
||||
| 新增依赖 | `i18next@^26.4.2`、`react-i18next@^17.0.13` | A4(D12) |
|
||||
| 类型依赖 | 无需额外类型包(自带 TS 类型) | A4 |
|
||||
| `next.config.mjs` | **不改**(静态导出已满足) | — |
|
||||
|
||||
---
|
||||
|
||||
## 附录 A:门禁对照
|
||||
|
||||
| 门禁 | 本设计的对应物 |
|
||||
|---|---|
|
||||
| G0(基线评审) | 本文档 §1–§8 供评审;D7 首屏策略已选定(§4.1) |
|
||||
| G1(平台能力) | §6.2 注册表/资源映射/类型归 A4;PoC 清单(POC_REPORT)由 A4/A7 执行并回填 |
|
||||
186
docs/i18n/I18N_TEST_PLAN.md
Normal file
186
docs/i18n/I18N_TEST_PLAN.md
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
# i18n 测试总体策略(I18N_TEST_PLAN)
|
||||
|
||||
> 角色:Agent 3 `qa-architect`(测试架构与验收设计)
|
||||
> 基线:`I18N_MULTI_AGENT_PLAN.md` §10 测试与工程门禁、§11 完成定义
|
||||
> 实施:Agent 7 `i18n-qa` 在 Wave 1+ 按本文档落地
|
||||
> 状态:G0 待评审
|
||||
|
||||
## 0. 目标与边界
|
||||
|
||||
本文件定义 LiteLLM Dashboard `en` / `zh-CN` 国际化的测试总体策略:三层测试边界、key 一致性与硬编码扫描、中文布局检查、可访问性文案覆盖,以及自动化与人工程度的边界。
|
||||
|
||||
**纯设计输入**:本文件与配套 `TEST_CASES.md`、`TEST_TOOLS.md`、`REGRESSION_MATRIX.md` 只规定"测什么、怎么测、如何验收",不修改任何产品代码,不实际执行测试。实际执行由 Agent 7 在 Wave 1+ 负责。
|
||||
|
||||
## 1. 三层测试边界(与项目现有 tier 对齐)
|
||||
|
||||
### 1.1 事实校准
|
||||
|
||||
项目 `vitest.config.ts` 定义 4 个 vitest project:
|
||||
|
||||
| vitest project | 环境 | include | 对应"概念层" |
|
||||
|---|---|---|---|
|
||||
| `unit` | node | `src/**/*.test.ts`, `tests/**/*.test.ts`(非渲染) | 逻辑单元(无 DOM) |
|
||||
| `component` | jsdom | `src/**/*.test.tsx`, `tests/**/*.test.tsx`(非 `*.integration.test.tsx`) | 组件单元(毫秒级) |
|
||||
| `integration` | jsdom | `*.integration.test.tsx`(含 `tests/**`) | 集成(秒级) |
|
||||
| `types` | tsc | `*.test-d.ts` / `*.test-d.tsx` | 类型测试(`npm run test:types`) |
|
||||
|
||||
CLAUDE.md 提到的三档(unit / integration / E2E)与 vitest 的 `unit`+`component`+`integration` 需在测试文档中统一术语:**本文档"单元层"泛指 vitest `component` project 的 jsdom 单组件测试(`*.test.tsx`),"逻辑单元层"特指 `unit` project 的 node 逻辑测试(`*.test.ts`)**。E2E 为 Playwright(见 §1.4)。
|
||||
|
||||
工程命令(方案 §10):
|
||||
|
||||
```bash
|
||||
cd ui/litellm-dashboard
|
||||
npm run lint # eslint .
|
||||
npm run format:check # prettier 校验
|
||||
npm run build # next build(静态导出)
|
||||
npm run test:types # vitest run --project types
|
||||
```
|
||||
|
||||
Vitest 定向执行:只跑改动相关文件;单元/组件层毫秒级、集成秒级;禁止无路径全量 `vitest run`。
|
||||
|
||||
### 1.2 单元层(component project,`*.test.tsx`)
|
||||
|
||||
i18n 场景中,单元层只负责"单一模块、单一微行为",依赖以 double 替换。
|
||||
|
||||
i18n 场景下的单元层**边界**:
|
||||
- **locale 工具函数**(纯函数,放 `unit` project 的 `*.test.ts`):语言选择合并逻辑(优先级合并)、`<html lang>` 计算、浏览器语言映射、首选项写入 cookie/localStorage 的序列化与解析。
|
||||
- **插值/复数/格式化纯函数**(`*.test.ts`):`count=0/1/2`、日期、数字、货币的格式化结果,无 DOM。
|
||||
- **单个 UI 小组件**(`*.test.tsx`):语言切换器按钮、单个翻译标注组件(占位)、量词插值组件,使用 i18next 的 `initReactI18next` 测试实例注入最小字典(en+zh),断言渲染文本。**不渲染整页、不跨组件**。
|
||||
- 计数器本地化组件(D9):`count=0` → `0 个`,`count=1` → `1 个`,`count=2` → `2 个`。
|
||||
|
||||
单元层不做:整页断言、真实网络、`<html>` 全局状态、路由跳转。
|
||||
|
||||
### 1.3 集成层(`*.integration.test.tsx`)
|
||||
|
||||
集成层渲染**真实组件树**,仅 stub 网络边界,验证"单元测试无法触达的接线"。
|
||||
|
||||
i18n 场景下的集成层**边界**:
|
||||
- 根应用/页面级内容:`I18nProvider` 包裹下,Navbar / Leftnav / Login / Models / Usage 等页面在切换语言后**即时**更新(真实组件树 + Provider 链)。
|
||||
- 语言切换不丢失表单状态:在集成树中填写字段 → 切换语言 → 断言字段值仍在、未触发无关请求。
|
||||
- 动态插值与复数在组件级布线正确(由单元层证明逻辑,集成层证明"字段→key→渲染"链路)。
|
||||
- `<Trans>` 组件在资源就绪后渲染(不先出原始 key)。
|
||||
|
||||
集成层不做:真实浏览器、真实 `document.title` 持久化之外的行为、整页导航刷新行为。
|
||||
|
||||
**命名**:`Foo.integration.test.tsx`,`vitest run --project integration <path>`。
|
||||
|
||||
### 1.4 E2E 层(Playwright,`tests/e2e/ui/`)
|
||||
|
||||
E2E 覆盖"真实浏览器 + 真实静态导出站点/活 proxy"下的端到端行为。**当前仓库不存在 Playwright 基础设施**(无 `playwright.config.*`、无 `tests/e2e/ui/`、无 `@playwright/test` 依赖),这是 Agent 7 在 Wave 1 必须补齐的平台缺口(需要 `package.json` 变更时,须由 Agent 0 指派唯一 Owner,遵守 §6.1 依赖单一写入者约束)。
|
||||
|
||||
E2E 负责的 i18n 行为:
|
||||
- **默认语言**:首次访问(无偏好)→ 英文 `en`。
|
||||
- **切换即时生效**:真实浏览器点击切换 → `<html lang>` 更新 → 页面文本更新。
|
||||
- **刷新保持**:切换 → `page.reload()` → 语言保持。
|
||||
- **整页跳转恢复**:Login / SSO / MCP OAuth 整页跳转并返回后语言保持(同源 cookie/localStorage 恢复)。
|
||||
- `<html lang>` 与当前语言一致。
|
||||
- 缺 key 回退英文、不显示原始 key。
|
||||
- 构建期 `<title>` / meta description 保持英文。
|
||||
|
||||
E2E 在静态导出下运行(`npm run build` 产生 `out/`)。Playwright 配置不在 vitest 内;作为独立阶段与 `npm run build` 串联,在 Wave 4A 完整回归使用。
|
||||
|
||||
### 1.5 三层分工示意
|
||||
|
||||
| 行为 | 单元 | 集成 | E2E |
|
||||
|---|---|---|---|
|
||||
| 插值/复数/格式化正确性 | ✅ 逻辑纯函数 | — | 抽查 |
|
||||
| 切换语言组件渲染 | ✅ | ✅ 真实树 | ✅ 真实浏览器 |
|
||||
| 表单状态保持 | — | ✅ | 抽查 |
|
||||
| 刷新语言保持 | — | 🔸(jsdom 有限) | ✅ |
|
||||
| 整页跳转恢复 | — | — | ✅ |
|
||||
| `<html lang>` | ✅ 计算函数 | ✅ Provider 接线 | ✅ 浏览器 DOM |
|
||||
| `<title>`/meta 英文 | — | — | ✅ |
|
||||
|
||||
## 2. key 一致性与硬编码扫描
|
||||
|
||||
### 2.1 key 集合一致性校验
|
||||
|
||||
自动校验 `en` 与 `zh-CN`(以及后续任何 locale)的字典 key 集合完全一致,即对所有 namespace:
|
||||
|
||||
```
|
||||
keys(en/<ns>.json) == keys(zh-CN/<ns>.json)
|
||||
```
|
||||
|
||||
- 语义 key(`common:action.delete`)必须成对存在;存在偏置即失败,连同**缺 key 清单**一起输出,退出码非 0。
|
||||
- 同时校验"被引用的 key ⊆ 已声明 key"(组件 `t('ns:key')` / `useTranslation('ns')` 引用 vs 字典存在性),防止静态分析漏检的悬空 key 在运行时回退英文却被误当业务缺失。
|
||||
- 结构与值类型也必须一致(嵌套层级、插值变量 `{{x}}` 集合一致,避免 en 有 `{{count}}` 而 zh 漏掉)。
|
||||
- 运行方式:独立 node 脚本(见 `TEST_TOOLS.md`),纳入 CI 或门禁 G3 的"中英文 key 集一致"。
|
||||
|
||||
### 2.2 硬编码字符串扫描(只报告、不自动改写)
|
||||
|
||||
按方案 §3.4:扫描工具**只报告候选硬编码,不自动生成 key,不自动改写代码**。Agent 7 实现时严格遵守该约束——扫描结果进入报告,人工/产品决定是否改写。
|
||||
|
||||
扫描器规则(对 TSX/TS 源码):
|
||||
- 识别疑似承载用户可见文案的 `JSXText`(非注释、非空白、非纯符号)。
|
||||
- 识别 `aria-label`、`title`、`placeholder`、`alt` 等可访问性/文案属性中的字符串字面量。
|
||||
- 识别 `t('...')` 之外 `useMessage`/`useToast`/`toast(...)` 等常见未国际化文案入口(以项目现状校准)。
|
||||
- 排除:模型名/API 字段/日志/代码示例引用、路径、正则、CSS 类名、`data-slot`、纯数值/符号、标识符。
|
||||
- 输出:文件、行、候选文案、所属 namespace 建议(仅供人工参考),**不自动写入字典**。
|
||||
|
||||
扫描器产出人工 review 清单,用于判断"应改为 `t()` 的遗漏硬编码"。
|
||||
|
||||
### 2.3 误翻译保护
|
||||
|
||||
- 反向断言:模型名、API 字段、日志原文、代码示例在 zh 与 en 下渲染一致(不被翻译)。见 `TEST_CASES.md` TC 对应项。
|
||||
- 扫描器补充规则:对 `en.json` / `zh-CN.json` 的 value 做"疑似把模型名/字段名当值"的启发式标注(如值与标识符/模型名称重合),仅供人工复核。
|
||||
|
||||
## 3. 中文布局检查矩阵
|
||||
|
||||
中文文案通常比英文短,但存在换行、量词、长词(如"成本跟踪"、英文品牌名混排)导致溢出/截断/重叠的风险。逐模块(导航/表格/弹窗/表单/全局壳层)检查:
|
||||
|
||||
| 区域 | 检查维度 | 断言/观察项 |
|
||||
|---|---|---|
|
||||
| Navbar / Leftnav | 溢出、折行、截断 | 菜单项不换行错位、品牌/Menu 图标不重叠;窄屏不横向溢出 |
|
||||
| 表格(Models/API Keys/Usage/Cost/Budgets) | 列宽截断、单元格换行 | 中文表头不截断语义、值不溢出列边界、tooltip 不遮挡 |
|
||||
| 弹窗/Modal | 溢出、空白、遮挡 | 标题/正文/按钮在视口内、无横向滚动条溢出、Close 按钮可点 |
|
||||
| 表单 | label 对齐、placeholder 长度 | label 不折行错位、error 提示不截断、placeholder 不溢出控件 |
|
||||
| 全局壳层 | 用户菜单、语言切换器 | 中文菜单项不截断,切换器标签完整 |
|
||||
|
||||
检查粒度:
|
||||
- **自动化**:E2E/组件层断言"内容可读但不溢出容器"(无横向滚动、`scrollWidth <= clientWidth`、关键文本 `toBeVisible`),以及对特定元素做快照对比。
|
||||
- **人工**:跨语言并列渲染的视觉走查(Agent 2 术语体验复核 + Agent 7 UI 检查),覆盖窄屏与中文长字符串用例;`V1_SCOPE_MANIFEST.md` 的 `UI 检查` 栏填写证据。
|
||||
|
||||
矩阵逐项仍落地于 `REGRESSION_MATRIX.md`。
|
||||
|
||||
## 4. 可访问性文案覆盖要求
|
||||
|
||||
i18n 不只是可见文本;`aria-label`、`title`、`placeholder`、`alt` 等必须随语言本地化(方案 §5.6 同步处理要求):
|
||||
|
||||
- **每处用户可见文本都应有可访问等效**:图标按钮、关闭按钮、导航折叠按钮等必须提供本地化的 `aria-label`(`aria-hidden` 或纯装饰除外)。
|
||||
- **辅助文本本地化**:`title`/tooltip、`placeholder`、校验 error message、`role="meter"` 的 `aria-valuenow`(用 `toHaveAttribute` 断言,避免 `toHaveValue` 误用——CLAUDE.md 约定)均本地化。
|
||||
- **语序不破坏可访问性**:`aria-label` 与可见文本一致;`aria-labelledby` 指向本地化 label。
|
||||
- **测试断言**:使用可访问查询(`getByRole`/`getByLabel`/`getByText`)优先,其次 `data-slot`;不使用 `eslint --fix` 批量修正 testing-library/jest-dom(CLAUDE.md 约定),`toHaveTextContent` 传 plain string 子串匹配。
|
||||
- **英文回退对 a11y 文案同样生效**:zh 缺失时 `aria-label` 等回退英文,不显示原始 key。
|
||||
|
||||
## 5. 默认语言 / 刷新 / 首屏策略的测试侧重
|
||||
|
||||
- **默认语言**:无任何偏好时 = `en`;这是最终回退,任何层级测试都以此为基线。
|
||||
- **刷新保持**(E2E):`page.reload()` 后仍为当前语言;核实存储介质(SameSite cookie 或 localStorage——CLAUDE.md 禁 token,但语言偏好非敏感;偏好介质以架构决策为准)。
|
||||
- **首屏闪烁**:`<Trans>`/`t()` 在资源就绪后渲染,首帧不暴露原始 key(方案 §3.4/§10)。E2E 首次导航截图断言不出现 `namespace:...` 原始 key 文本。
|
||||
- **`<html lang>`**:与当前语言一致(`en` 或 `zh-CN`);组件层与 E2E 均断言。
|
||||
|
||||
## 6. 自动化 / 人工边界总结
|
||||
|
||||
| 事项 | 自动化 | 人工 |
|
||||
|---|---|---|
|
||||
| 单元/组件逻辑 | ✅ | — |
|
||||
| 集成布线 | ✅ | — |
|
||||
| E2E(切换/刷新/跳转/lang/title) | ✅ | — |
|
||||
| key 集合一致性 | ✅(脚本,CI) | — |
|
||||
| 硬编码扫描 | ✅(脚本报告) | ▲ 决定是否改写 |
|
||||
| 中文布局视觉 | 🔸(溢出断言) | ▲ 跨语言走查 |
|
||||
| 翻译质量/术语 | — | ▲ Agent 2 复核 |
|
||||
| 误翻译判断 | 🔸(启发式) | ▲ 人工复核 |
|
||||
|
||||
## 7. 测试数据与工具接口约定
|
||||
|
||||
- 测试用最小字典:单元/集成层注入 `en`+`zh-CN` 的最小命名空间子集,不足最小集时直接断言英文回退。
|
||||
- 时间固定:日期/货币断言使用固定 locale 与固定时间源(fake timer),避免时区/运行时刻不稳定。
|
||||
- 存储抽象:偏好读写针对于可注入的 storage/cookie 抽象 double,避免测试污染真实 localStorage。
|
||||
|
||||
## 8. 门禁映射
|
||||
|
||||
- **G0**:本策略与 `TEST_CASES.md` 通过 Review。
|
||||
- **G1**:平台层测试(切换/回退/`<html lang>`/首屏/整页跳转)与 key 一致性脚本具备;`npm run build` + 定向 vitest 通过。
|
||||
- **G2/G3**:各模块测试、UI 检查、`V1_SCOPE_MANIFEST.md` 证据齐,中英文 key 集一致。
|
||||
- **G4**:`REGRESSION_MATRIX.md` 全量回归通过(Agent 7 报告 + Agent 8 汇总)。
|
||||
84
docs/i18n/LANGUAGE_SWITCHER_SPEC.md
Normal file
84
docs/i18n/LANGUAGE_SWITCHER_SPEC.md
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
# 语言切换器详细设计(Language Switcher Spec)
|
||||
|
||||
> 维护者:Agent 2(localization-designer)
|
||||
> 状态:设计稿,待 G0 评审
|
||||
> 关联:`I18N_MULTI_AGENT_PLAN.md`(§1.1/D5/D6)、`DECISIONS.md`、`LOCALIZATION_SPEC.md` §1、`LOCALE_NAVIGATION_BEHAVIOR.md`
|
||||
|
||||
本文档定义语言切换器的**入口位置、显示形式、状态、交互、持久化与可访问性**。它是供 Agent 5(壳层开发)实现的交互契约。
|
||||
|
||||
---
|
||||
|
||||
## 1. 入口位置(建议)
|
||||
|
||||
v1 只有一个入口,放在**顶部导航栏(Navbar)右侧**,靠近用户/账号菜单,以便登录后各页都能发现。
|
||||
|
||||
- 首选位置:`navbar.tsx` 右端、用户菜单图标左侧,作为独立图标按钮。
|
||||
- 备选:若 Navbar 空间受限(静态导出、窄屏),回退到 `SidebarAccountMenu` 账号弹层内放置文字切换。
|
||||
- **登录/引导页(Login、Onboarding、Connect、MCP OAuth)也必须有入口**,因为用户可能在这些页面首次选择语言。建议在这些页面顶部/右上角放置同一图标按钮(复用同一组件),与 Dashboard 主体入口共用组件逻辑。
|
||||
|
||||
> 实现要求:切换器组件做成可复用的独立组件(如 `src/components/LanguageSwitcher.tsx`),在 Navbar/账号菜单/登录页引用,避免多份复制。
|
||||
|
||||
## 2. 显示文案
|
||||
|
||||
- 按钮仅显示图标 + 当前语言的**目标语言缩写**,用于明确"点一下会变成什么",同时按钮自身可在弹层中切换。
|
||||
- 展示为 **「中文」/「EN」**:
|
||||
- 当前为 `en` 时,按钮显示 **中文**(提示可切到中文)。
|
||||
- 当前为 `zh-CN` 时,按钮显示 **EN**(提示可切到英文)。
|
||||
- 展开菜单(Popover/Menu)时,提供两行完整选项:`English` 与 `简体中文(zh-CN)`,当前语言行加选中态(勾选/高亮)。
|
||||
- 若空间允许,可在按钮前显示一个地球图标(`lucide` `Globe`/`Languages`),并在展开菜单用图标区分。
|
||||
|
||||
### 2.1 为什么按钮显示"目标语言"
|
||||
|
||||
图标 + 目标语言缩写最节省空间且语义清晰(常见国际化 UI 惯例,如 GitHub/Google)。同时菜单内提供明确的"当前语言"选中态,避免歧义。
|
||||
|
||||
### 2.2 空态/兜底
|
||||
|
||||
- 若某些组件在资源未就绪时不渲染切换器,则**优先不显示**而不是显示错误语言;资源就绪后由统一 ready 门禁放行(D7)。
|
||||
- 切换器自身文案(`aria-label`、菜单项"English / 简体中文")用当前语言渲染,保证用户总能用自己当前看到的语言操作它。
|
||||
|
||||
## 3. 状态
|
||||
|
||||
| 状态 | 表现 |
|
||||
|---|---|
|
||||
| 当前 `en` | 按钮显示"中文";菜单中 `English` 高亮为选中 |
|
||||
| 当前 `zh-CN` | 按钮显示"EN";菜单中 `简体中文(zh-CN)` 高亮为选中 |
|
||||
| 加载中/资源未就绪 | 按钮不闪烁、不显示 key;就绪后出现 |
|
||||
| 存储不可用(隐私/禁 cookie) | 会话内仍可切换(见 `LOCALE_NAVIGATION_BEHAVIOR.md`),但刷新后回退 |
|
||||
|
||||
## 4. 交互
|
||||
|
||||
1. 点击按钮打开菜单(Popover/Menu)。
|
||||
2. 选择目标语言项:
|
||||
- **即时生效**:当前页所有文案立即切换,无需刷新或整页跳转(`LOCALIZATION_SPEC.md` §1.3)。
|
||||
- 同时更新 `<html lang>`(`lang="zh-CN"` / `lang="en"`)。
|
||||
- 关闭菜单。
|
||||
3. 切换**不**重置表单、导航展开、滚动位置;不触发额外请求。
|
||||
|
||||
## 5. 持久化行为
|
||||
|
||||
- 切换成功后**同步写入**:
|
||||
1. **localStorage**:键 `litellm.locale`(约定,见下),值 `en` | `zh-CN`。
|
||||
2. **SameSite cookie**:同名 cookie,`SameSite=Lax`、`path=/`,一个较长的有效期(如 365 天),用于跨整页跳转/新标签恢复;不设为 httpOnly(前端需读)。
|
||||
- 存储键建议统一为常量 `litellm.locale`,由 Agent 4 在 `src/i18n/**` 提供读写函数,Agent 5/6 不直接操作存储。
|
||||
- **优先级**:读取时优先 user 主动选择 → cookie/localStorage → 浏览器语言 → en(见 `LOCALIZATION_SPEC.md` §1.1)。cookie 与 localStorage 两者都存在时以较新写入者为准(实现按 D5/D6 由 Agent 4 定,建议以 localStorage 为准,cookie 用于跨页恢复)。
|
||||
- 写入失败(隐私模式)不报错、不阻塞切换,静默降级。
|
||||
|
||||
## 6. 可访问性
|
||||
|
||||
- 触发器按钮必须带 `aria-label`,内容随当前语言本地化:
|
||||
- `en` 下:`Change language` / `选择语言`(用当前语言渲染,即 en 时英文标签,zh 时中文标签)。
|
||||
- zh 下:`切换语言`。
|
||||
- 推荐:`aria-label="Switch language"`(en)/ `"切换语言"`(zh-CN)。
|
||||
- 菜单用 `role="menu"`/`menuitemradio` 或 Popover 语义,支持键盘操作(方向键选择、Enter 确认、Esc 关闭)。
|
||||
- 选中项要有 `aria-checked` 或视觉高亮 + `aria-current`。
|
||||
- 焦点管理:打开时焦点移至菜单,关闭后返回触发器。
|
||||
|
||||
## 7. 测试要点(供 Agent 7 参考)
|
||||
|
||||
- 切换即时生效(DOM 文案变化且无整页刷新)。
|
||||
- 刷新后保持(cookie/localStorage 恢复)。
|
||||
- `<html lang>` 与当前语言一致。
|
||||
- 切换不清空表单/不触发请求。
|
||||
- 当前语言在菜单中正确高亮。
|
||||
- aria-label 随语言变化。
|
||||
- 存储不可用时降级行为(见 `LOCALE_NAVIGATION_BEHAVIOR.md`)。
|
||||
104
docs/i18n/LOCALE_NAVIGATION_BEHAVIOR.md
Normal file
104
docs/i18n/LOCALE_NAVIGATION_BEHAVIOR.md
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
# 语言导航行为定义(Locale Navigation Behavior)
|
||||
|
||||
> 维护者:Agent 2(localization-designer)
|
||||
> 状态:设计稿(G0 门禁交付物之一)
|
||||
> 关联:`I18N_MULTI_AGENT_PLAN.md` §3.3、`DECISIONS.md` D5/D6/D7/D10、`LOCALIZATION_SPEC.md`、`LANGUAGE_SWITCHER_SPEC.md`
|
||||
> 依据:本文件满足 G0 门禁中"Login、SSO、MCP OAuth 整页跳转后的语言保持规则已写入本地化与测试方案"。
|
||||
|
||||
本文档定义**语言在导航/跳转各场景下的期望行为、降级规则与可测试验收点**。静态导出、客户端收敛、无服务端 locale 路由是背景约束(D4/D6)。
|
||||
|
||||
---
|
||||
|
||||
## 1. 场景总览
|
||||
|
||||
| 场景 | 语言来源 | 期望结果 |
|
||||
|---|---|---|
|
||||
| 首次访问(无偏好) | 浏览器语言 → en | 按浏览器语言;中文浏览器默认 zh-CN |
|
||||
| 后续访问(有偏好) | cookie/localStorage 偏好 | 保持已选语言 |
|
||||
| 手动切换 | 用户选择 | 即时生效并持久化 |
|
||||
| 页面内 SPA 导航 | 当前会话 | 语言跨页保持 |
|
||||
| Login 提交回跳 | 同源存储 | 保持切换前语言 |
|
||||
| SSO 登录回跳 | 同源存储 | 保持(见 §4 例外与降级) |
|
||||
| MCP OAuth 授权回跳 | 同源存储 | 保持 |
|
||||
| 存储不可用(隐私/禁 cookie) | 会话内存 | 会话内可切换,刷新后回退 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 首次访问
|
||||
|
||||
- 定义:无任何已存偏好(cookie 与 localStorage 均无 `litellm.locale`)。
|
||||
- 行为:读取 `navigator.language`(或 `navigator.languages` 首项)。以 `zh` 开头 → `zh-CN`;否则 → `en`。
|
||||
- 不弹出语言选择对话框;以浏览器语言为默认即可(`LOCALIZATION_SPEC.md` §1.2)。
|
||||
- 首次进入后会将所选语言**写回偏好**(可选),如此后浏览器语言变化不再覆盖。
|
||||
- 验收点 A1:见 §6。
|
||||
|
||||
## 3. 手动切换后的行为
|
||||
|
||||
- 切换即时生效(无刷新、无整页跳转),写入 localStorage + cookie(见 `LANGUAGE_SWITCHER_SPEC.md` §5)。
|
||||
- 同一标签页内 SPA 路由跳转(Leftnav 点击)语言保持;导航状态、表单、滚动不被清空。
|
||||
- 新开标签页/使用同一浏览器时,从偏好恢复。
|
||||
- 验收点 A2/A3:见 §6。
|
||||
|
||||
## 4. 整页跳转与回跳(Login / SSO / MCP OAuth)
|
||||
|
||||
背景:SPA 内这些登录动作会离开当前页面去往后端/第三方,完成后再整页加载回跳地址。
|
||||
|
||||
### 4.1 期望
|
||||
|
||||
1. 跳转前语言已由客户端写入**同源 cookie + localStorage**。
|
||||
2. 回跳到同源页面后,语言初始化在客户端收敛阶段从偏好恢复,**语言应与跳转前一致**,不得回落到英文。
|
||||
3. Login 提交回跳、SSO 回跳、MCP OAuth 回跳三种都应能恢复(方案 §3.3 PoC 要求)。
|
||||
|
||||
### 4.2 例外与降级
|
||||
|
||||
- **Cookie 不可用 / 隐私模式**:localStorage 也可能受限。此时无法跨整页跳转持久化语言,回跳后按"无偏好"重估(浏览器语言→en)处理,属**可接受降级**,但必须**静默**,且不允许出现语言"闪烁"到错语言再纠正的体验。
|
||||
- **SSO 第三方域**(如外部 IdP):跳转发生在第三方域,前端无法携带语言 cookie 过去,但**回跳回同源**时仍在同源 cookie/localStorage 作用域内,可恢复。若 IdP 协商了完全不同的域且无同源 cookie,则按浏览器语言降级。
|
||||
- **首次在 Login 页选择语言后即整页跳转**:选择须在跳转前完成持久化,回跳恢复;这是 Login 页必须放切换器入口的原因(`LANGUAGE_SWITCHER_SPEC.md` §1)。
|
||||
|
||||
### 4.3 存储写入时机
|
||||
|
||||
为确保跳转前已持久化,语言偏好在**切换动作发生时立即写入**,而非等到刷新/跳转。禁止"仅内存 + 跳转前延迟写"导致竞态丢失。
|
||||
|
||||
- 验收点 A4/A5/A6:见 §6。
|
||||
|
||||
---
|
||||
|
||||
## 5. 存储不可用时的降级规则
|
||||
|
||||
| 失败类型 | 行为 |
|
||||
|---|---|
|
||||
| localStorage 写入抛异常/Safari 隐私模式 | 捕获异常静默跳过写入;会话内内存变量仍生效,当前页面可切换 |
|
||||
| cookie 被禁/无法设置 | 同上,静默跳过;跨整页跳转可能会丢失语言(按 §4.2 降级) |
|
||||
| 读取时两者都无 | 走"首次访问"逻辑(浏览器语言 → en) |
|
||||
| 被第三方脚本/iframe 限制(`document.cookie` 不可读) | 视为无偏好,按浏览器语言;不抛错 |
|
||||
|
||||
原则:**任何存储失败不得阻塞 UI 或抛出可见错误**;只影响持久化,不影响会话内切换。
|
||||
|
||||
---
|
||||
|
||||
## 6. 可测试验收点(可自动化 + 可手动)
|
||||
|
||||
供 Agent 3/7 落地为用例,也作为 G0 后验证。
|
||||
|
||||
| ID | 验收点 | 期望 |
|
||||
|---|---|---|
|
||||
| A1 | 中文浏览器首次访问默认语言 | `navigator.language=zh-*` → 首屏为 zh-CN |
|
||||
| A2 | 手动切换即时生效 | 选 zh-CN 后当前 DOM 文案变为中文,无整页刷新 |
|
||||
| A3 | 刷新后保持 | 切换后刷新,仍为 zh-CN;`<html lang="zh-CN">` |
|
||||
| A4 | Login 提交回跳语言保持 | Login 前选 zh-CN → 提交登录 → 回跳到 Dashboard 仍为 zh-CN |
|
||||
| A5 | SSO 回跳语言保持 | 选 zh-CN 进入 SSO → 回跳后仍为 zh-CN |
|
||||
| A6 | MCP OAuth 回跳语言保持 | MCP OAuth 授权回跳后仍为 zh-CN |
|
||||
| A7 | 存储不可用降级 | 禁 cookie + 隐私模式:会话内可切换,刷新后按浏览器语言/en,不抛错、不闪烁 |
|
||||
| A8 | 缺 zh-CN key 回退 | 仅 en 有某 key 时,zh 下该处回退英文,不显示 key |
|
||||
| A9 | 首帧不闪 key | 首帧不显示原始翻译 key(ready 门禁,D7 实现后验证) |
|
||||
| A10 | `<html lang>` 一致 | 任意语言下 `document.documentElement.lang` 与当前语言一致 |
|
||||
| A11 | 切换不清空表单 | 选语言前填写的表单在切换后内容保留 |
|
||||
|
||||
> 注意:A4/A5/A6 依赖真实登录/SSO/OAuth 回跳环境,PoC 阶段(Wave 1)由 Agent 1 先用"跳转-返回"模拟验证,A7/A9 可作为轻量替代证据,需 Agent 0 批准后视为已覆盖。
|
||||
|
||||
---
|
||||
|
||||
## 7. 风险与待定
|
||||
|
||||
- SSO 第三方域回跳的外域 cookie 是浏览器级限制,如实际不可恢复,需 Agent 0 批准记录为明确限制(方案 §11.7 允许)。
|
||||
- `html lang` 首屏同步在 SSR/hydration 下可能滞后,由 D7 首屏策略决定;不得因此回退翻译。
|
||||
252
docs/i18n/LOCALIZATION_SPEC.md
Normal file
252
docs/i18n/LOCALIZATION_SPEC.md
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
# LiteLLM Dashboard 本地化规范(Localization Spec)
|
||||
|
||||
> 维护者:Agent 2(localization-designer)
|
||||
> 状态:设计稿,待 G0 评审
|
||||
> 范围:LiteLLM Dashboard 前端 UI 的 `en` / `zh-CN` 本地化
|
||||
> 关联:`I18N_MULTI_AGENT_PLAN.md`(§3.3/§3.4/§5.2)、`DECISIONS.md`、`LANGUAGE_SWITCHER_SPEC.md`、`GLOSSARY_EN_ZH.md`
|
||||
|
||||
本文档定义语言的**交互规则**与**中文文案风格标准**。它回答"什么时候用什么语言"以及"中文应该怎么写得专业"。具体每个页面/组件翻不翻、翻哪些,见 `V1_TRANSLATION_SCOPE.md`;语言切换器的 UI 细节见 `LANGUAGE_SWITCHER_SPEC.md`。
|
||||
|
||||
---
|
||||
|
||||
## 1. 语言规则
|
||||
|
||||
### 1.1 语言标识与优先级
|
||||
|
||||
- 语言代码:`en`(默认)、`zh-CN`。
|
||||
- `en` 是**真源与最终回退**;任何缺少 `zh-CN` key 的字符串一律回退英文,绝不允许显示原始 key。
|
||||
- 语言优先级(D5,已定):
|
||||
```
|
||||
用户主动选择
|
||||
→ 已确认的用户级 UI 设置(如后端支持,v1 默认不启用)
|
||||
→ cookie + localStorage
|
||||
→ 浏览器语言
|
||||
→ en
|
||||
```
|
||||
- 语言偏好判定完全在客户端完成(静态导出约束,D4)。
|
||||
|
||||
### 1.2 首次进入
|
||||
|
||||
- 无任何已存偏好时:读取浏览器语言。若 `navigator.language` 以 `zh` 开头(`zh`、`zh-CN`、`zh-TW`、`zh-Hans` 等均视为中文系),则初始为 `zh-CN`;否则为 `en`。
|
||||
- 首次进入**不强制弹出语言选择对话框**。以浏览器语言为默认即可;用户可随时通过切换器更改。
|
||||
- 首次进入后(即一旦存在已存偏好),优先使用已存偏好,不再读取浏览器语言。
|
||||
|
||||
### 1.3 手动切换
|
||||
|
||||
- 切换**即时生效**,无需刷新、无需整页跳转;当前页面所有文案立即切换。
|
||||
- 切换时**保留表单状态、滚动位置、导航展开状态**,不允许因语言切换重置表单或触发异常请求(v1 完成定义第 6 条)。
|
||||
- 切换后立即写入 cookie + localStorage(行为见 `LANGUAGE_SWITCHER_SPEC.md`)。
|
||||
|
||||
### 1.4 整页跳转与回跳(Login/SSO/MCP OAuth)
|
||||
|
||||
详细行为与降级见 `LOCALE_NAVIGATION_BEHAVIOR.md`。摘要:
|
||||
|
||||
- Login 提交、SSO 登录、MCP OAuth 授权可能发生整页跳转(起跳自同源页面,回跳到同源页面)。
|
||||
- 跳转返回后必须从**同源 cookie 或 localStorage** 恢复语言,语言不应回落到英文。
|
||||
- 若同源存储不可用(隐私模式、禁 cookie),允许回退英文,但不允许出现语言"闪烁"到错误语言的体验缺陷。
|
||||
|
||||
### 1.5 构建期元数据
|
||||
|
||||
- v1 构建期 `<title>` 与 metadata description **保持英文**(D8),不做多语言 SEO。功能 Agent 不得被动扩大此范围。
|
||||
|
||||
---
|
||||
|
||||
## 2. 中文文案风格规范
|
||||
|
||||
目标:**专业、自然、克制、无翻译腔**。面向开发者/运维人员(Dashboard 用户以工程师为主),用词应准确、简洁,避免口语化和过度文学化。
|
||||
|
||||
### 2.1 通用原则
|
||||
|
||||
1. **以用户动作和对象为先**。按钮用"动词 + 宾语"最短形式(见 2.2)。
|
||||
2. **不用翻译腔**:避免"为了……我们将会……"、"请注意"、"请务必"等冗余;避免英文逐字直译的语序污染。
|
||||
3. **控制长度**:中文比英文短,但信息密度高。菜单项/按钮 ≤ 6 字为佳,超过 8 字注意换行与截断(见 §3)。
|
||||
4. **术语一致**:一律采用 `GLOSSARY_EN_ZH.md` 中的词条,禁止同义词漂移(如"Spend"一会翻"花费"一会翻"支出")。
|
||||
5. **敬语从简**:界面动作祈使句即可,不加"您"的过度堆叠;保留必要礼貌但不每一句都加。
|
||||
6. **标点**:中文使用全角标点(`,`、`。`、`:`、`?`),英文/数字/型号与中文混排时两侧不加空格;数字、单位用半角(`10 次` 中空格与否按项目 UI 惯例统一)。
|
||||
7. **占位符与插值**:不译 `{{variable}}`、`{{count}}` 内的变量名;品牌名 LiteLLM、OpenAI 等不译。
|
||||
|
||||
### 2.2 按钮文案
|
||||
|
||||
- 格式:**动词(+ 宾语)**,祈使句。
|
||||
- 通用按钮术语(复用 `GLOSSARY_EN_ZH.md` + `common.json`):
|
||||
| EN | ZH-CN |
|
||||
|---|---|
|
||||
| Save | 保存 |
|
||||
| Cancel | 取消 |
|
||||
| Delete | 删除 |
|
||||
| Create | 创建 |
|
||||
| Add | 添加 |
|
||||
| Edit | 编辑 |
|
||||
| Update | 更新 |
|
||||
| Enable / Disable | 启用 / 停用 |
|
||||
| New Virtual Key | 新建虚拟密钥 |
|
||||
| Generate | 生成 |
|
||||
| Copy | 复制 |
|
||||
| Close | 关闭 |
|
||||
| Confirm | 确认 |
|
||||
- 含受保护动作(删除、重置、清空)的按钮在确认弹窗中保持同一译名,不要换成同义"移除/清除"造成语义漂移。
|
||||
|
||||
### 2.3 表单文案
|
||||
|
||||
- **Label**:名词性短语,简洁。如 `Deployment Name` → `部署名称`、`Base URL` → `基础 URL`(URL 保留英文,见 §4)。
|
||||
- **Placeholder**:用"请输入 / 请选择 / 例如"引导的示例或提示。如 `Enter model ID` → `输入模型 ID`;`Select team` → `选择团队`。
|
||||
- **校验提示**:指出问题 + 期望,避免命令式指责。
|
||||
- 示例:`This field is required` → `此项为必填`;`Key must be at least 8 characters` → `密钥至少需要 8 个字符`。
|
||||
- **可选/必填**:`Required` → `必填`;可选字段不要写"可选"二字堆满表单,仅在视觉区分或用 `(可选)` 后缀在 label 上。
|
||||
- **分段标题/说明**:`form.helper` 类说明文本可译,但保留关键英文专有名词(API、URL、SSO)。
|
||||
|
||||
### 2.4 错误提示
|
||||
|
||||
- **技术性错误**(后端返回、非 4xx 校验错误):尽量提供"发生了什么 + 可做什么"两层。
|
||||
- **不可直翻后端消息**:后端英语错误原文**不翻译**(见 §4)。即使附带中文说明,也应保留原文以便排障。
|
||||
- 措辞平实。示例:
|
||||
- `Unable to load models.` → `无法加载模型。`
|
||||
- `Your session has expired.` → `会话已过期。`
|
||||
- `Failed to create Virtual Key.` → `创建虚拟密钥失败。`
|
||||
- `Insufficient permission.` → `权限不足。`(不写"您的权限不够")
|
||||
- 避免把错误写成"出错了"这类无信息量提示;能带对象就带对象(`删除团队失败`)。
|
||||
|
||||
### 2.5 空状态(Empty State)
|
||||
|
||||
- 空状态 = 说明 + 一句引导(一个主导 CTA)。
|
||||
- 避免"这里空空如也"这种口语;用客观陈述 + 引导。
|
||||
- 示例:`No Virtual Keys yet. Create one to get started.` → `暂无虚拟密钥。点击"新建虚拟密钥"开始使用。`
|
||||
- `No usage data for the selected period.` → `所选时间段内暂无用量数据。`
|
||||
- 引导动词与真实按钮文字保持一致("新建虚拟密钥")。
|
||||
|
||||
### 2.6 成功提示 / 通知(Toast)
|
||||
|
||||
- 过去式主谓短句。如 `Virtual Key created.` → `虚拟密钥已创建。`、`Settings saved.` → `设置已保存。`
|
||||
- 需要引用对象名称/ID 时用插值:`Model "{{name}}" saved.` → `模型"{{name}}"已保存。`
|
||||
|
||||
---
|
||||
|
||||
## 3. 中文长度对布局的影响与处理
|
||||
|
||||
中文单字约占英文一个字符 ~2 倍视觉宽度,但信息密度高,实际渲染宽度通常**更短或相当**。真正的风险是:
|
||||
|
||||
1. 英文长复合词被较长的中文词组替换;
|
||||
2. 筛选器、表格列头、弹窗标题空间不足导致换行/溢出。
|
||||
|
||||
### 3.1 导航(Leftnav / Navbar / 面包屑)
|
||||
|
||||
- 菜单项普遍 ≤ 6 字(如 `Virtual Keys`→`虚拟密钥`、`Models + Endpoints`→`模型与端点`),通常放得下。
|
||||
- 侧边栏已具备 `truncate`(`flex-1 truncate`),中文过长时应保持单行截断,并补 `title` 展示全称。
|
||||
- 分组标签(AI GATEWAY 等)v1 也翻译;groupLabel 是配置常量,需随 `menuGroups` 一起 I18N 化,而非仅改渲染层。
|
||||
- 面包屑 section/title 同样取翻译,需与导航使用同一套 key,避免两处不一致。
|
||||
|
||||
### 3.2 表格
|
||||
|
||||
- 列头名词、数值列(用量/成本/花费用数字格式化,见 `V1_TRANSLATION_SCOPE.md` 插值注意事项)。
|
||||
- 表格单元格回退策略:**列头可换行,单元格优先截断 + `title` 全称**;避免单元格内文字撑爆列宽。
|
||||
- 数据列(模型 ID、团队名、时间戳)」多为数据,不翻译,按 §4 处理。
|
||||
|
||||
### 3.3 弹窗 / 抽屉(Modal / Drawer)
|
||||
|
||||
- 标题 ≤ 20 字;超长标题强制换行并提供 `line-clamp` 或在布置上预留两行。
|
||||
- 按钮组"取消/保存"不加长词缀,保持最短形式。
|
||||
- 弹窗内说明文本允许自然换行,但禁止水平溢出。
|
||||
|
||||
### 3.4 全体回退策略(截断 / 换行 / 省略号)
|
||||
|
||||
| 场景 | 策略 |
|
||||
|---|---|
|
||||
| 导航菜单单行 | `truncate`(省略号) + `title` 全称 |
|
||||
| 表格单元格 | 截断 + `title`;数值列右对齐 |
|
||||
| 弹窗标题 | 允许换行(白空间关键字 `[normal, pre-wrap]`),必要时 `line-clamp-2` |
|
||||
| 按钮 | 不换行;过长时缩短译名,不拼接 |
|
||||
| 标签/Badge(Beta、New) | 不翻译品牌徽标;`v{major.minor}` 保持 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 不可翻译内容清单
|
||||
|
||||
以下内容**明确不翻译**(作为资源值原样保留或直接放数据字段,不进翻译 key)。Agent 5/6 必须遵守,Review 时逐条核查:
|
||||
|
||||
1. **模型名 / Model ID / Deployment 名 / Provider 名**:`gpt-4o`、`claude-3-5-sonnet`、`openai`、`bedrock` 等;用户自定义的 Deployment/模型名也是数据,不译。
|
||||
2. **API 字段名 / 参数名**:`key`、`model`、`spend`、`tpm_limit`、`rpm_limit`、`metadata`、`created_at`;接口返回的键与 JSON 结构不译。
|
||||
3. **请求/响应体、日志原文**:后端返回的原始日志、错误消息、堆栈(stack trace)、trace 内容保持原样。
|
||||
4. **代码示例与终端命令**:curl 片段、Python/JS 代码、API 请求示例不译;但代码**上方/下方的说明文字**可译。
|
||||
5. **URL、路径、文件名**:`https://docs.litellm.ai`、`/api/`、`config.yaml`、`.env` 等。
|
||||
6. **广泛公认的英文技术缩写**:API、URL、SSO、MCP、OAuth、REST、HTTP、SQL、ID、JSON、SDK。
|
||||
7. **品牌名**:LiteLLM、OpenAI、Anthropic、Virtual Key(作为产品专名,见下表备注)、Guardrails 的专有规则名。
|
||||
8. **数值、日期、货币、单位**:数字格式化走 i18n 的 number/date/currency,不手工拼翻译;`{{count}}` 由计数插值负责。
|
||||
9. **角色名 / 权限标识**:`admin`、`internal_user`、`team_admin` 等代码值保持英文(仅其人类可读显示名按术语表翻译)。
|
||||
10. **徽标与 Beta/New 标记**:`Beta`、`New` 徽标不译(视觉因子,保持英文原样)。
|
||||
|
||||
> 原则:**翻译的是界面文案(UI string),不是数据(data string)与协议内容(protocol content)**。无法安全区分时,优先看它是否来自后端/用户数据 —— 是则不译。
|
||||
|
||||
---
|
||||
|
||||
## 5. zh-CN 计数与量词策略
|
||||
|
||||
遵循 D9:**不建立形态复数分支**,用"计数插值 + 量词"。i18next 复数字符串里 `_one/_other` 仅用于英文;`zh-CN` 用一个字符串承载 `{{count}}` + 量词即可。
|
||||
|
||||
量词选取规则:
|
||||
|
||||
- 可数离散对象:`个`(模型、密钥、请求、团队、用户、条目)。
|
||||
- 键值/集合类(如导入项):可用 `个` 或直接按对象数合身处理。
|
||||
- 时间/次数:`次`(请求次数、重试次数)。
|
||||
- 金额/用量单位化:不写量词,直接 `{{count}}` + 单位(`{{count}} 请求`、`{{count}} 个请求`,口语一致性以术语表为准)。
|
||||
- `0` 时量词仍正常(`0 个请求` 在中文是自然的,不需要单独复数分支)。
|
||||
|
||||
### 5.1 具体示例
|
||||
|
||||
| 场景 | EN(_one/_other) | ZH-CN(单字符串) |
|
||||
|---|---|---|
|
||||
| 请求数 | `{{count}} request` / `{{count}} requests` | `{{count}} 个请求` |
|
||||
| 选中模型 | `{{count}} model selected` / selected | `已选择 {{count}} 个模型` |
|
||||
| 待办项 | `{{count}} item` / items | `{{count}} 个条目` |
|
||||
| 无选中 | — | `已选择 0 个模型`(中文自然,无需分支) |
|
||||
| 重试 | `Retry ({{count}})` | `重试({{count}} 次)` |
|
||||
|
||||
### 5.2 测试要求
|
||||
|
||||
计数测试至少覆盖 `count=0`、`count=1`、`count=2`(方案 §3.4)。对 `zh-CN` 断言渲染结果等于"插值 + 量词"形态(如 `1 个请求`、`2 个请求`),**不**断言存在 `_one/_other` 分支。
|
||||
|
||||
---
|
||||
|
||||
## 6. 英文原句 → 规范中文译文示例
|
||||
|
||||
给开发 Agent 5/6 的参照样本(来自 v1 高频界面)。
|
||||
|
||||
| # | EN(原句) | ZH-CN(规范译文) | 说明 |
|
||||
|---|---|---|---|
|
||||
| 1 | `Create new virtual key` | 新建虚拟密钥 | 动词+宾语,最短祈使形式 |
|
||||
| 2 | `No Virtual Keys found. Click "New Virtual Key" to create your first one.` | 暂无虚拟密钥。点击"新建虚拟密钥"创建你的第一个密钥。 | 空状态客观陈述 + 引导 CTA,CTA 文字与真实按钮一致 |
|
||||
| 3 | `This will permanently delete this model and cannot be undone.` | 此操作将永久删除该模型,且无法撤销。 | 机构条理清晰,动作对象明确 |
|
||||
| 4 | `You've spent $12.34 on Usage this month.` | 本月用量已花费 $12.34。 | 过去式主谓短句,接数值/货币(货币由 i18n 格式化) |
|
||||
| 5 | `Drop any model to see how it compares.` | 拖入任意模型查看对比结果。 | 祈使引导,无翻译腔 |
|
||||
| 6 | `Your changes have not been saved.` | 更改尚未保存。 | 客观陈述,不指责 |
|
||||
|
||||
> 译名对照一律以 `GLOSSARY_EN_ZH.md` 为准,本示例中的词汇(虚拟密钥、用量、花费、拖入、对比)与术语表保持一致。
|
||||
|
||||
---
|
||||
|
||||
## 7. 可访问性(a11y)文案规范
|
||||
|
||||
- 所有 `aria-label`、`title`、`alt`、placeholder 中的用户可见文案**同样翻译**(方案 §5.5),与可见文本用同一 key。
|
||||
- 例:当前 `aria-label={collapsed ? "Expand sidebar" : "Collapse sidebar"}` → `展开侧边栏` / `收起侧边栏`。
|
||||
- 语言切换器自身提供 `aria-label`(见 `LANGUAGE_SWITCHER_SPEC.md`)。
|
||||
- 图标按钮(无可见文字)必须本地化 `aria-label`;纯装饰图标保持 `aria-hidden`。
|
||||
- 保留英文专有名词的缩写不影响屏幕阅读器核心语义。
|
||||
|
||||
---
|
||||
|
||||
## 8. 对开发 Agent 的强制要求(Agent 5/6)
|
||||
|
||||
1. **只翻 UI 文案**,不翻 §4 清单内容(模型名、API 字段、日志、代码、URL、缩写、品牌)。
|
||||
2. **key 与字典一致性**:中英文 key 集合必须一致;缺 zh-CN 时回退 en,不回退失败显示 key。
|
||||
3. **写 key 不写句子到代码**:用语义 key(`navigation:item.virtualKeys`),不把英文句子当 key。
|
||||
4. `<Trans>` 用于含链接/强调/嵌套,`t()` 用于纯字符串;使用 `<Trans>` 前确保资源就绪(ready 门禁,避免首帧闪 key)。
|
||||
5. **同一译名贯穿全局**:按钮、空状态、确认框、Toast 对同一动作/对象用词必须一致(对照术语表)。
|
||||
6. **数值/日期/货币用 i18n 格式化**,不硬编码。
|
||||
7. **翻译 a11y 文案**(aria-label/title/placeholder)。
|
||||
8. **不改变业务行为**:翻译不改路由、不改校验逻辑、不改提交逻辑,仅替换展示文案。
|
||||
9. 交付时在 `V1_SCOPE_MANIFEST.md`(Agent 0 维护)对应行填 EN/ZH-CN 文案盘点;译名异常情况上报 Agent 2 复核。
|
||||
|
||||
---
|
||||
|
||||
## 9. 术语表引用
|
||||
|
||||
术语定义与单一词条见 `GLOSSARY_EN_ZH.md`。若本规范内个别短语与术语表冲突,以 `GLOSSARY_EN_ZH.md` 为准,并提请 Agent 2 修订。
|
||||
30
docs/i18n/MASTER_PLAN.md
Normal file
30
docs/i18n/MASTER_PLAN.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# LiteLLM Dashboard 中英文国际化 — MASTER_PLAN
|
||||
|
||||
> 总控:Agent 0(`i18n-lead-reviewer`) · 由当前主智能体承担
|
||||
> 基线:`I18N_MULTI_AGENT_PLAN.md` v1.4(评审通过,可执行)
|
||||
> 目标:Dashboard 前端 UI 支持 `en` / `zh-CN`,导航、登录、Models、API Keys、Usage、Cost、Budgets 优先中文化。
|
||||
|
||||
## 波次总览
|
||||
|
||||
| 波次 | 内容 | 涉及角色 | 门禁 |
|
||||
|---|---|---|---|
|
||||
| Wave 0 | 基线评审 + 并行设计 | A0 + A1/A2/A3 | G0 |
|
||||
| Wave 1 | 平台能力与测试基础 | A0 + A4/A5(只读)/A7 | G1 |
|
||||
| Wave 2 | 第一批功能并行开发 | A0 + A5/A6/A7 | G2 |
|
||||
| Wave 3 | 第二批功能并行开发 | A0 + A6A/A6B/A7 | G3 |
|
||||
| Wave 4A/4B | 集成验收 + 定向缺陷修复 | A0 + A2/A7/A8(及开发 Owner) | G4 |
|
||||
|
||||
## 并发约束
|
||||
同刻最多 4 个智能体(1 总控 Agent 0 + 最多 3 执行子代理)。子代理按波次由 Agent 0 拉起,完成即回收。
|
||||
|
||||
## 当前状态
|
||||
(由 Agent 0 在每波次更新)
|
||||
|
||||
- [ ] Wave 0 进行中
|
||||
- [ ] Wave 1 待开始
|
||||
- [ ] Wave 2 待开始
|
||||
- [ ] Wave 3 待开始
|
||||
- [ ] Wave 4 待开始
|
||||
|
||||
## 完成定义
|
||||
见 `I18N_MULTI_AGENT_PLAN.md` §11(v1 完成定义 15 条)。
|
||||
100
docs/i18n/POC_REPORT.md
Normal file
100
docs/i18n/POC_REPORT.md
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
# LiteLLM Dashboard i18n — PoC 验证清单与执行步骤(POC_REPORT)
|
||||
|
||||
> 角色:Agent 1(`i18n-architect`)编写 PoC 必验项;Wave 1 的 Agent 4(平台)与 Agent 7(测试)照此执行并把证据回填到「结果 / 证据」栏。
|
||||
> 约定:PoC-* 为「必验项」,每项都必须给出**方法**、**预期证据**、**结果**(通过/失败/未执行)。
|
||||
> 本机(Agent 1 仅只读)已就地验证的项,标记 **「本机已验证」**;其余标记 **「待执行」**(执行者为 A4/A7)。
|
||||
|
||||
> 目标:在改动最少的前提下,对 `I18N_TECH_DESIGN.md` 的**关键设计假设**给出可复现证据,支撑 G1 门禁(ADR-i18n-01/03/04/05 转 Accepted)。
|
||||
> 前置:Wave 1 平台基线由 A4 建立(`src/i18n/**` + `src/locales` 骨架 + 依赖写入,遵循 D12/FILE_OWNERSHIP)。
|
||||
|
||||
---
|
||||
|
||||
## 0. 环境前提(A4 先落实)
|
||||
|
||||
- 依赖:`i18next@^26.4.2` + `react-i18next@^17.0.13`(写入后运行 `npm ci` 于独立 worktree,禁止并行锁分叉——D12/v1.4)。
|
||||
- `next.config.mjs`:**不改**(静态导出已满足)。
|
||||
- 仅改动 `src/i18n/**`、`src/locales/**`、`src/app/layout.tsx`(挂 Provider)、语言切换器。
|
||||
|
||||
---
|
||||
|
||||
## PoC-1:静态导出构建成功 && i18next 依赖可解析(G1 前置)
|
||||
|
||||
- **方法**:A4 在平台 worktree `npm run build`(`output:"export"`)。检查:构建无因引入 i18next 产生的解析/打包错误;`out/` 正常产出;`turbopack` 能打包 json 资源。
|
||||
- **预期证据**:`next build` 成功,`out/` 目录生成,无「Cannot resolve i18next/react-i18next」、无 static-export 舞台报错;`npm ls i18next react-i18next` 无 peer 冲突。
|
||||
- **结果**:【待执行】
|
||||
|
||||
## PoC-2:首屏策略(就绪门禁 + `<html lang>` 同步)行为
|
||||
|
||||
- **方法**:默认浏览器语言 en:加载页面,在首帧与 JS 就绪后截图/断言。
|
||||
1. en 用户:首帧即为中文 UI 不存在(应显示英文 UI),`<html lang="en">` 保持。
|
||||
2. zh-CN 偏好用户(预置 `dashboard.locale=zh-CN` 再刷新):JS 就绪前不渲染业务 `t()` 内容(就绪态/空),就绪后一次性渲染中文,且 `document.documentElement.lang === 'zh-CN'`。
|
||||
- **预期证据**:截图对比 + DOM 断言:①en 首帧无 key/英文句闪烁;②zh 就绪前无业务文案、就绪后 `lang` 正确;③测量就绪门禁额外延时(performance 日志)记录数值,供评审接受性判断。
|
||||
- **结果**:【待执行】
|
||||
|
||||
## PoC-3:`t()` / `<Trans>` 首帧不暴露原始 key(资源就绪门禁)
|
||||
|
||||
- **方法**:在导航/登录样面的一个组件用 `t('common:title')` 与一个 `<Trans>`。用 Playwright 在**首帧**(未就绪)与就绪后采样 DOM 文本。
|
||||
- **预期证据**:首帧 DOM 中**不含** `common:title`、不含未翻译英文句子(即不以 key=原文);就绪后显示译文。如有 `returnNull`,首帧对应节点为空而非显示 key。
|
||||
- **结果**:【待执行】
|
||||
|
||||
## PoC-4:缺 key 英文回退(D2)
|
||||
|
||||
- **方法**:在 zh-CN 资源中删掉某个 en 有的 key(临时),UI 切 zh-CN,观察该字符串。
|
||||
- **预期证据**:该串回退英文(`fallbackLng:'en'`),**不**崩、不显示 key、控制台(dev)有 missingKey warning。
|
||||
- **结果**:【待执行】
|
||||
|
||||
## PoC-5:刷新后语言偏好保留(D6/D5)
|
||||
|
||||
- **方法**:切 zh-CN → 刷新 → 断言仍 zh-CN;清空 cookie 但留 localStorage → 刷新 → 仍 zh-CN(双层读取);两者都清 → 回退浏览器语言/en。
|
||||
- **预期证据**:各分支刷新后语言正确;`dashboard.locale` cookie 存在且 `SameSite=Lax`。
|
||||
- **结果**:【待执行】
|
||||
|
||||
## PoC-6:英文为首帧默认、无偏好时按浏览器语言(D5/D2)
|
||||
|
||||
- **方法**:无任何偏好 cookie/localStorage;浏览器 `navigator.language` 分别设 en、zh-CN、zh、zh-Hans、fr。
|
||||
- **预期证据**:en→英文;zh/zh-Hans→zh-CN;fr(不支持)→英文。首帧英文合法,无闪烁。
|
||||
- **结果**:【待执行】
|
||||
|
||||
## PoC-7:整页跳转语言恢复(Login/SSO/MCP OAuth 回跳,D10)
|
||||
|
||||
- **方法**:在 `(dashboard)` 中切 zh-CN → 触发到 `/login` 或模拟 SSO/OAuth 全页跳转(改变 URL/全量 reload)→ 返回原页。
|
||||
- **预期证据**:回跳后仍 zh-CN(从同源 cookie 恢复),`<html lang>` 一致,无 key 闪烁;未登录直登场景与 OAuth 回跳分别覆盖。
|
||||
- **结果**:【待执行】
|
||||
|
||||
## PoC-8:中文量词/计数(D9)在真实组件表现
|
||||
|
||||
- **方法**:在带 `{{count}}` 的计数展示(如 usage 表格)切 zh-CN,覆盖 count=0/1/2。
|
||||
- **预期证据**:输出如「0 个 / 1 个 / 2 个」,无英文复数 s;量词与数为「数+量」顺序正确出现。
|
||||
- **结果**:【待执行】
|
||||
|
||||
## PoC-9:toast / 构建产物不含未翻译硬编码(回归)
|
||||
|
||||
- **方法**:扫描构建后产物与关键页面文本,确认没有「key 串」残留为可见 UI 文本。
|
||||
- **预期证据**:`grep` 产物无 `\w+:\w+\.` 形式的 key 作为文本暴露;关键页面无英文句被用户看到(除 `title`/meta 英文外)。
|
||||
- **结果**:【待执行】
|
||||
|
||||
---
|
||||
|
||||
## 本机已验证项(Agent 1,只读,2026-09-09)
|
||||
|
||||
| # | 项 | 验证方式 | 结果 |
|
||||
|---|---|---|---|
|
||||
| 1 | `react-i18next@17.0.13` 兼容 React 19 / TS 5 | `npm view react-i18next peerDependencies` → `react>=16.8.0`, `i18next>=26.2.0`, `ts ^5\|\|^6\|\|^7`;项目 React 19.2.8 / TS 5.9.3 满足 | 通过 |
|
||||
| 2 | `i18next@26.4.2` 无冲突 peer | `npm view i18next@latest peerDependencies` → 仅 `typescript` | 通过 |
|
||||
| 3 | 根 Provider 链结构 | 读 `src/app/layout.tsx`:`ThemeProvider→NuqsAdapter→ReactQueryProvider→AuthProvider`;`<html lang="en" suppressHydrationWarning>`;metadata 英文 | 确认 |
|
||||
| 4 | `src/i18n` / `src/locales` 当前不存在(基线干净) | `ls` | 确认(Wave0 从零建) |
|
||||
| 5 | 路由组 layout 分布 | 根 / `(dashboard)` / `chat` / `connect` 各有一 `layout.tsx`;`(dashboard)` 为 use client 且另含 SidebarProvider/ThemeContext/PluginModeContext | 确认(接缝见设计 §8) |
|
||||
| 6 | 后端无 `UI settings.language` | `UISettings` 白名单 `ALLOWED_UI_SETTINGS_FIELDS` 不含 `language`;`/get/ui_settings` 由 `LiteLLM_UISettings` 表支撑 | 确认(→ADR-i18n-07) |
|
||||
| 7 | next export 配置 | `next.config.mjs`: `output:"export"`, `trailingSlash:true`, `images.unoptimized` | 确认(静态导出约束成立) |
|
||||
|
||||
## 待执行(Wave 1 A4/A7 回填)
|
||||
|
||||
- PoC-1 .. PoC-9 均【待执行】;其中 PoC-1(构建+依赖)、PoC-2/3(首屏与门禁)、PoC-5(刷新)、PoC-7(跳转恢复)为 **G1 关键门禁**,优先完成并回填证据文件(证据文件命名遵从 v1.4 §11 具名证据约定,由 Agent 3/7 落地)。
|
||||
|
||||
---
|
||||
|
||||
## 建议的 PoC 测试层级归属(供 Agent 3 细化)
|
||||
|
||||
- 单测(unit):`localePreferences.ts`(D5 优先级)、`detectLocale.ts`(`zh`→`zh-CN`)、`i18n.ts` 实例化。
|
||||
- 集成:`I18nProvider` 就绪门禁渲染(PoC-3)、Layout 挂 Provider 后 `useTranslation` 可用。
|
||||
- E2E(`tests/e2e/ui/`,Playwright 对 live proxy):PoC-2/5/6/7(首屏、刷新、浏览器语言、整页回跳)。
|
||||
58
docs/i18n/REGRESSION_MATRIX.md
Normal file
58
docs/i18n/REGRESSION_MATRIX.md
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# i18n 回归测试矩阵(REGRESSION_MATRIX.md)
|
||||
|
||||
> 角色:Agent 3 `qa-architect`;执行:Agent 7 `i18n-qa`(Wave 4A 完整回归)
|
||||
> 依据:`I18N_MULTI_AGENT_PLAN.md` §11 完成定义、§10 测试重点;`TEST_CASES.md`
|
||||
> 用法:按"页面 × 关键行为"逐格勾选。行为列引用 TC 编号;页面以 v1 范围为准。每格填:`✅ 通过 / ❌ 失败(#缺陷) / ➖ 不适用 / (m) 人工`。
|
||||
|
||||
## 行为列(横轴)编号
|
||||
|
||||
| 列 | 行为 | 主要 TC | 层级 |
|
||||
|---|---|---|---|
|
||||
| B1 | 默认语言 en | TC-01/02 | e2e/unit |
|
||||
| B2 | 切换即时生效 + `<html lang>` | TC-03/08 | e2e/component |
|
||||
| B3 | 刷新保持 | TC-04/05 | e2e/unit |
|
||||
| B4 | 缺 key 回退英文、不显示原始 key | TC-06/07 | e2e/component |
|
||||
| B5 | 动态数量/日期/数字/货币本地化(含 count=0/1/2) | TC-09~13 | unit |
|
||||
| B6 | 模型名/API 字段/日志/代码示例不误翻 | TC-20/21 | integration |
|
||||
| B7 | a11y 文案本地化(aria-label/title/placeholder/error) | TC-23 | component/integration |
|
||||
| B8 | 中文布局不溢出/截断/遮挡 | TC-24 | integration/e2e+(m) |
|
||||
| B9 | 切换语言不清空表单/不触发异常请求 | TC-14/15 | integration/e2e |
|
||||
| B10 | 整页跳转(Login/SSO/MCP OAuth)回跳语言保持 | TC-16~19 | e2e |
|
||||
|
||||
## 矩阵(页面 × 行为)
|
||||
|
||||
| 页面/区域 | Owner | B1 | B2 | B3 | B4 | B5 | B6 | B7 | B8 | B9 | B10 | Review |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| Navbar | Agent 5 | | | | | | | | | | ➖ | |
|
||||
| Leftnav | Agent 5 | | | | | | | | | | ➖ | |
|
||||
| 用户菜单 / 全局壳层 | Agent 5 | | | | | | | | | | ➖ | |
|
||||
| 语言切换器 | Agent 5 | | | | | | | | | ➖ | | |
|
||||
| Login | Agent 5 | | | | | | | | | | | |
|
||||
| Onboarding | Agent 5 | | | | | | | | | | | |
|
||||
| Connect | Agent 5 | | | | | | | | | | | |
|
||||
| MCP OAuth | Agent 5 | | | | | | | | | | | |
|
||||
| Models and Endpoints | Agent 6 | | | | | | | | | | | |
|
||||
| API Keys | Agent 6 | | | | | | | | | | | |
|
||||
| Usage | Agent 6A | | | | | | | | | | | |
|
||||
| Cost Tracking | Agent 6A | | | | | | | | | | | |
|
||||
| Budgets | Agent 6B | | | | | | | | | | | |
|
||||
| 全局:构建期 `<title>`/meta 英文 | Agent 4/8 | | | | | ➖ | | | | ➖ | ➖ | |
|
||||
|
||||
## 平台层回归(不绑定单页面)
|
||||
|
||||
| 项目 | 说明 | TC | 结果 |
|
||||
|---|---|---|---|
|
||||
| 默认语言 en(无偏好) | 全站 | TC-01 | |
|
||||
| `<html lang>` 一致 | 全局 | TC-08 | |
|
||||
| 刷新保持 | 全局 | TC-04 | |
|
||||
| 缺 key 英文回退 | 全局 | TC-06 | |
|
||||
| 不显示原始 key(首帧/全程) | 全局 | TC-07/26 | |
|
||||
| 静态产物 key 泄漏 / `<title>` 英文 | `out/` | TC-22/26 | |
|
||||
| en/zh key 集合一致 | 全部 namespace | TC-25 | |
|
||||
| 硬编码扫描报告 | 源码 | T-03 | |
|
||||
|
||||
## 门禁映射
|
||||
|
||||
- 本矩阵在 **Wave 4A** 全量执行(Agent 7 完成 + Agent 8 汇总 + Agent 2 体验复核 + Agent 0 终审)。
|
||||
- 对应完成定义(§11):支持双语言(1)、切换即时+刷新保持(2)、v1 页面中文化(3)、回退不露 key(4)、中文无严重截断重叠(5)、切换不清表单(6)、整页跳转保持(7)、`<title>`/meta 英文(8)、模型名/API 不误翻(9)、相关测试通过(10)、lint/format/build(11)。
|
||||
- 任何 `❌` 须挂缺陷进 `TASK_BOARD.md`,P0/P1 清零后才满足 G4。
|
||||
57
docs/i18n/TASK_BOARD.md
Normal file
57
docs/i18n/TASK_BOARD.md
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
# LiteLLM Dashboard i18n — TASK_BOARD(多智能体进展看板)
|
||||
|
||||
> 维护者:Agent 0。**每启动/交付/Review 一个 Agent 即更新本表**。这是你查看"谁在工作、进展到哪"的单点真相。
|
||||
> 状态取值:待命 / 进行中 / 待 Review / 已完成 / 被阻塞
|
||||
|
||||
## Wave 0 — 并行设计(G0 门禁前)
|
||||
|
||||
| Agent | 名称 | 状态 | worktree/分支 | 交付物 | 最后更新 |
|
||||
|---|---|---|---|---|---|
|
||||
| A1 | i18n-architect | **已完成 ✅(G0 Review 通过)** | (纯设计) | I18N_TECH_DESIGN / I18N_ADR / POC_REPORT / TECH_RISKS | 2026-09-09 G0 |
|
||||
| A2 | localization-designer | **已完成 ✅(G0 Review 通过)** | (纯设计) | LOCALIZATION_SPEC / GLOSSARY_EN_ZH(63条) / LANGUAGE_SWITCHER_SPEC / V1_TRANSLATION_SCOPE / LOCALE_NAVIGATION_BEHAVIOR | 2026-09-09 G0 |
|
||||
| A3 | qa-architect | **已完成 ✅(G0 Review 通过)** | (纯设计) | I18N_TEST_PLAN / TEST_CASES(26) / TEST_TOOLS / REGRESSION_MATRIX | 2026-09-09 G0 |
|
||||
|
||||
**G0 门禁结论(Agent 0,2026-09-09):APPROVED**
|
||||
- 首屏策略已选定「就绪门禁 + `<html lang>` 同步」(ADR-04,非"再评估")。
|
||||
- 后端无 `UI settings.language`(ADR-07 Accepted)。
|
||||
- 构建期 `<title>`/meta 保持英文边界已记录(ADR-06 Accepted)。
|
||||
- 整页跳转语言保持规则已写入本地化与测试方案(LOCALE_NAVIGATION_BEHAVIOR + TEST_CASES TC-16..18)。
|
||||
- E2E 基建缺口已定:选 **a(Wave 1 补齐 Playwright)**,见 DECISIONS P3/P4。
|
||||
- **G0 Review 发现 1 个需修正点(P5)**:语言偏好存储键名跨文档不一致(`dashboard.locale` vs `litellm.locale`)。**已定统一为 `litellm.locale`**,由 A4 以单一常量实现,A5/6 不直接操作存储。
|
||||
- G0 Review 确认 POC_REPORT 9 项必验项已具备(待 Wave 1 A4/A7 回填证据)。
|
||||
|
||||
## Wave 1 — 平台能力与测试基础
|
||||
|
||||
| Agent | 名称 | 状态 | worktree/分支 | 交付物 | 最后更新 |
|
||||
|---|---|---|---|---|---|
|
||||
| A4 | i18n-platform-developer | 待命 | i18n/w1-agent4-platform | src/i18n/** 平台代码 + PLATFORM_VALIDATION_REPORT | - |
|
||||
| A5 | i18n-shell-auth-developer | 待命(Wave1 仅只读盘点) | — | key 拟定清单(不写码) | - |
|
||||
| A7 | i18n-qa | 待命 | i18n/w1-agent7-qa | 平台测试 + key 一致性工具 | - |
|
||||
|
||||
## Wave 2 — 第一批功能
|
||||
|
||||
| Agent | 名称 | 状态 | worktree/分支 | 交付物 | 最后更新 |
|
||||
|---|---|---|---|---|---|
|
||||
| A5 | i18n-shell-auth-developer | 待命 | i18n/w2-agent5-shell | 壳层 + common/navigation/auth namespace | - |
|
||||
| A6 | i18n-feature-developer | 待命 | i18n/w2-agent6-models | Models + API Keys | - |
|
||||
| A7 | i18n-qa | 待命 | i18n/w2-agent7-qa | 持续测试 | - |
|
||||
|
||||
## Wave 3 — 第二批功能
|
||||
|
||||
| Agent | 名称 | 状态 | worktree/分支 | 交付物 | 最后更新 |
|
||||
|---|---|---|---|---|---|
|
||||
| A6A | i18n-feature-developer (Usage/Cost) | 待命 | i18n/w3-agent6a-usage | Usage + Cost Tracking | - |
|
||||
| A6B | i18n-feature-developer (Budgets) | 待命 | i18n/w3-agent6b-budgets | Budgets | - |
|
||||
| A7 | i18n-qa | 待命 | i18n/w3-agent7-qa | 回归 | - |
|
||||
|
||||
## Wave 4 — 集成验收
|
||||
|
||||
| Agent | 名称 | 状态 | 交付物 | 最后更新 |
|
||||
|---|---|---|---|---|
|
||||
| A2 | localization-designer | 待命 | 术语/中文体验复核 | - |
|
||||
| A7 | i18n-qa | 待命 | 完整回归 + 质量报告 | - |
|
||||
| A8 | i18n-integration-release | 待命 | 发布/回滚清单 | - |
|
||||
|
||||
## 缺陷队列
|
||||
(Wave 4B 按 P0 → P1 → 阻塞门禁 P2 → 其他 P2 排序)
|
||||
(空)
|
||||
109
docs/i18n/TECH_RISKS.md
Normal file
109
docs/i18n/TECH_RISKS.md
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
# LiteLLM Dashboard i18n — 技术风险清单(TECH_RISKS)
|
||||
|
||||
> 角色:Agent 1(`i18n-architect`)· Wave 0
|
||||
> 编号 R1..Rn。每条含:风险 / 影响 / 缓解 / Owner。Owner 为「最需要跟踪/缓解」的角色,不一定是唯一。
|
||||
> 关联:`I18N_TECH_DESIGN.md`、`I18N_ADR.md`、`POC_REPORT.md`。
|
||||
|
||||
---
|
||||
|
||||
## R1:静态导出下首屏语言闪烁 / 错误语言闪现
|
||||
|
||||
- **风险**:切 zh-CN 用户首帧可能短暂显示英文或暴露 key,体验倒退。D7 要求不得停留在「再评估」。
|
||||
- **影响**:高(首屏是 G0/G1 门禁项;直接违背 §3.1)。
|
||||
- **缓解**:已选定「语言就绪门禁 + 挂载后同步 `<html lang>`」(ADR-i18n-04);就绪前不渲染业务 `t()`/`<Trans>`(ADR-i18n-03)。PoC-2/3 验证并量化门禁延时。
|
||||
- **Owner**:A4(实现)→ A7(验证)。
|
||||
|
||||
## R2:`react-i18next` / `i18next` 与 React 19 / Next 16 的运行时兼容性未实测
|
||||
|
||||
- **风险**:`npm view` peer 通过,但实际打包(turbopack/next export)与 React 19 可能有边界问题(如 `useSyncExternalStore`、HMR)。
|
||||
- **影响**:中(构建失败或运行时异常会阻断 Wave 1)。
|
||||
- **缓解**:PoC-1 先做最小依赖安装 + `next build`;锁定 `^17`/`^26` 稳定大版本;若发现问题回退 17/26 的补丁小版本并记录。
|
||||
- **Owner**:A4(先做)→ A7(复验)。
|
||||
|
||||
## R3:`<Trans>` 因资源未就绪而瞬态暴露占位/key
|
||||
|
||||
- **风险**:若个别组件绕开统一门禁,`<Trans>` 可能先渲染占位符再替换。
|
||||
- **影响**:中高(违背 §3.1,且 `<Trans>` 难以用简单 `t()` 兜底)。
|
||||
- **缓解**:统一顶层门禁覆盖全局子树;组件 `ready` 兜底;CI 键集合一致性检查(R9)+ 代码评审拦截绕过。
|
||||
- **Owner**:A4(平台)→ A5/A6(使用)→ A7(回归)。
|
||||
|
||||
## R4:语言偏好 cookie/localStorage 不一致或跨跳转丢失
|
||||
|
||||
- **风险**:cookie 与 localStorage 双写可能不一致;`SameSite` 过严导致 SSO/整页跳转回带失败,或过宽带来安全面。
|
||||
- **影响**:中(刷新/回跳语言恢复失败,D10 不满足)。
|
||||
- **缓解**:以显式写入顺序为准的收敛规则(ADR-i18n-05);`SameSite=Lax` + 生产 `Secure`;PoC-5/7 覆盖 cookie 清空/localStorage 留存、整页回跳各分支。
|
||||
- **Owner**:A4 → A7。
|
||||
|
||||
## R5:依赖文件被多 Agent 并行写坏(锁分叉)
|
||||
|
||||
- **风险**:`package.json`/lock 在多 worktree 并行 `npm install/ci` 产生分叉,G1 集成冲突。
|
||||
- **影响**:高(阻塞集成、浪费波次)。
|
||||
- **缓解**:D12 单一写入者(默认仅 A4);下游从 G1 基线 `npm ci` 且不写 lock;v1.4 变更摘要已固化。
|
||||
- **Owner**:A4(执行)→ A0(review)。
|
||||
|
||||
## R6:功能 Agent 与平台共享单点(`src/i18n/**`、注册表、公共 JSON)写冲突
|
||||
|
||||
- **风险**:多人同时改初始化代码/注册表/大 JSON。
|
||||
- **影响**:中(合并冲突、注册表被破坏导致类型/加载异常)。
|
||||
- **缓解**:FILE_OWNERSHIP 规则:`src/i18n/**` 与骨架归 A4 永久独占;新增 namespace 走 A0 派单由 A4 注册;功能 Agent 只写自己 namespace。
|
||||
- **Owner**:A0(调度)→ A4(执行)。
|
||||
|
||||
## R7:en/zh 字典键集合漂移(缺 key/多 key)
|
||||
|
||||
- **风险**:中文更新新增 key 但 en 未同步(或反之),导致运行期 missingKey 回退/多出 key。
|
||||
- **影响**:中(D2 要求 en 为真源、zh 缺 key 回退;键集合须一致 §3.4)。
|
||||
- **缓解**:CI 增加「en vs zh-CN 键集合 diff」校验(可在 `test:types` 或独立脚本,纳入 Agent 7 质量门禁);`CustomTypeOptions` 类型约束 + 开发模式 missingKey warning。
|
||||
- **Owner**:A7(CI/校验)→ A4(类型)→ 各功能 Agent(补 key)。
|
||||
|
||||
## R8:cookie 安全与隐私(`SameSite`、`Secure`、XSS 可读)
|
||||
|
||||
- **风险**:语言非秘密,但 cookie 可被 XSS 读取;`SameSite=None` 若被误用扩大攻击面。
|
||||
- **影响**:低-中(隐私面小;但需遵守 CLAUDE.md「勿把 token 放 localStorage」精神,语言偏好非敏感)。
|
||||
- **缓解**:语言偏好非敏感可放心;cookie `SameSite=Lax; path=/;`,生产 `Secure`;绝不用此 cookie 存令牌;仅当 SSO 第三方域确需带 cookie 时,再评估 `None` 并单独评审。
|
||||
- **Owner**:A4。
|
||||
|
||||
## R9:硬编码文案漏网(中文环境下仍有英文句/key 可见)
|
||||
|
||||
- **风险**:1400+ TSX,批量改造难免遗漏;产物残留 key 串或英文句。
|
||||
- **影响**:中(本地化不完整、观感差)。
|
||||
- **缓解**:扫描工具只报告候选(不改写,§3.4);PoC-9 对产物 grep 残留;纳入 v1 完成定义与 A7 回归;模块按 high-traffic 优先级(导航/登录/Models/API Keys/Usage/Cost/Budgets)。
|
||||
- **Owner**:A5/A6(改造)→ A7(扫描/回归)。
|
||||
|
||||
## R10:静态导出 + 多语言无 SEO,`<title>`/meta 恒为英文
|
||||
|
||||
- **风险**:搜索引擎与分享卡片恒为英文,非本地化。
|
||||
- **影响**:低(D8 明确 v1 不做多语言 SEO)。
|
||||
- **缓解**:接受为 v1 边界;文档记录未来可引入 per-locale 静态 `title`(ADR-i18n-06 后果)。
|
||||
- **Owner**:A0(确认范围)。
|
||||
|
||||
## R11:管理员全局语言设置未来引入(P1)可能改变优先级语义
|
||||
|
||||
- **风险**:后端未来新增 `UI settings.language` 字段,需决定「覆盖用户选择」语义,否则现有优先级迁移需返工。
|
||||
- **影响**:低(v1 无此字段,ADR-i18n-07 已封闭);中(若 v1.1 临时加入则会波及优先级链)。
|
||||
- **缓解**:预留 L5 判定位(I18N_TECH_DESIGN §3.2),把「用户显式」与「探测」分开存储,便于未来叠加管理员全局层而不混淆。
|
||||
- **Owner**:A1(设计预留)→ A0(接产品决策)。
|
||||
|
||||
## R12:中文文本在窄布局/表格溢出(文本长度不同导致 UI 破坏)
|
||||
|
||||
- **风险**:中文普遍比英文紧凑,但长译句或 `{{count}}` 拼接可能导致表格/按钮溢出或换行异常。
|
||||
- **影响**:中(布局回归)。
|
||||
- **缓解**:Wave 1 起在 Login/Navbar/表格样例做中英文并排截图比对;纳入 Agent 3 测试与 A7 回归;`whitespace-nowrap`/截断类妥善处理。
|
||||
- **Owner**:A6(功能页)→ A8(集成验收)。
|
||||
|
||||
## R13:`.json` 资源体积随 namespace 增长、首包加大
|
||||
|
||||
- **风险**:所有 locale 静态打在一起可能增大 bundle;`turbopack` 打包 json 的方式需确认。
|
||||
- **影响**:低-中(Dashboard 体量下影响有限;但静态导出无 CDN 分片语言包,加载即全量)。
|
||||
- **缓解**:PoC-1 检查产物 json 是否被正确 tree-shake/分 chunk;v1 语言仅 2 个,体积可控;若未来多语言再评估代码分割。
|
||||
- **Owner**:A4 → A8。
|
||||
|
||||
---
|
||||
|
||||
## 风险热力(供 A0 优先级参考)
|
||||
|
||||
| 等级 | 风险 |
|
||||
|---|---|
|
||||
| 高 | R1(首屏)、R5(锁分叉) |
|
||||
| 中高 | R3(`<Trans>`)、R9(硬编码遗漏) |
|
||||
| 中 | R2、R4、R6、R7、R12 |
|
||||
| 低 | R8、R10、R11、R13 |
|
||||
257
docs/i18n/TEST_CASES.md
Normal file
257
docs/i18n/TEST_CASES.md
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
# i18n 测试用例(TEST_CASES.md)
|
||||
|
||||
> 角色:Agent 3 `qa-architect`
|
||||
> 依据:`I18N_MULTI_AGENT_PLAN.md` §10 测试重点、§11 完成定义;`I18N_TEST_PLAN.md`
|
||||
> 实施:Agent 7 `i18n-qa`
|
||||
> 命名:层用 `unit`(node `*.test.ts`) / `component`(`*.test.tsx`) / `integration`(`*.integration.test.tsx`) / `e2e`(Playwright `tests/e2e/ui/`);`*` = 自动化,`(m)` = 人工辅助
|
||||
|
||||
每个用例字段:编号、层级、描述、前置、步骤、断言、自动化与否。
|
||||
|
||||
---
|
||||
|
||||
## A. 语言初始化与持久化
|
||||
|
||||
### TC-01 默认语言为英文
|
||||
- 层级:`e2e`
|
||||
- 描述:首次访问(无任何语言偏好)界面与 `<html lang>` 均为英文 `en`。
|
||||
- 前置:清空语言偏好存储(cookie/localStorage)、浏览器无中文偏好;静态导出 `out/` 已构建。
|
||||
- 步骤:1. 无偏好打开根路由。2. 读取 `<html lang>` 与可见文案。
|
||||
- 断言:`<html lang="en">`;界面文案为英文;不出现原始 key。
|
||||
- 自动化:*
|
||||
|
||||
### TC-02 默认语言英文(逻辑合并,无偏好)
|
||||
- 层级:`unit`(`*.test.ts`)
|
||||
- 描述:语言选择合并函数在"无偏好 + 浏览器英文"时归 `en`。
|
||||
- 前置:逻辑纯函数已抽出。
|
||||
- 步骤:调用合并函数,输入空偏好与 "en"。
|
||||
- 断言:返回 `"en"`。
|
||||
- 自动化:*
|
||||
|
||||
### TC-03 切换语言即时生效
|
||||
- 层级:`component` + `e2e`
|
||||
- 描述:切换 `en → zh-CN`,页面文本与 `<html lang>` 即时更新,无需刷新。
|
||||
- 前置:语言切换器与页面在 Provider 下渲染;资源就绪。
|
||||
- 步骤:component:渲染带 `I18nProvider`(可 stub 网络)的页面 → 点击切换器选中文 → 断言关键文本与 lang。e2e:真实浏览器同操作。
|
||||
- 断言:Navbar/Leftnav 等中文化文本出现;`<html lang="zh-CN">`;无原始 key、无刷新。
|
||||
- 自动化:*
|
||||
|
||||
### TC-04 刷新后语言保持
|
||||
- 层级:`e2e`
|
||||
- 描述:切到中文后刷新页面,语言仍为中文。
|
||||
- 前置:偏好写入介质(cookie/localStorage,依架构决策)。
|
||||
- 步骤:1. 切成中文。2. `page.reload()`。3. 读 `<html lang>` 与可见文本。
|
||||
- 断言:`<html lang="zh-CN">`,界面仍为中文。
|
||||
- 自动化:*
|
||||
|
||||
### TC-05 语言偏好在同源介质持久化(单元)
|
||||
- 层级:`unit`
|
||||
- 描述:偏好读写到可注入 storage/cookie 抽象,序列化往返一致。
|
||||
- 前置:偏好读写逻辑独立可测。
|
||||
- 步骤:写入 `"zh-CN"` → 重新读取。
|
||||
- 断言:读回 `"zh-CN"`;存储键稳定、无多余数据。
|
||||
- 自动化:*
|
||||
|
||||
## B. 回退与 key 遮蔽
|
||||
|
||||
### TC-06 缺失中文时回退英文
|
||||
- 层级:`component` + `e2e`
|
||||
- 描述:zh 字典缺某 key 时,渲染英文而非原始 key。
|
||||
- 前置:中文态 + 注入缺失 key 的字典子集(或 e2e 用构建产物中的真实缺口做抽样)。
|
||||
- 步骤:切换到中文,访问缺失 key 所属文案。
|
||||
- 断言:显示对应英文译文;**不**显示 `ns:key` 原始 key。
|
||||
- 自动化:*
|
||||
|
||||
### TC-07 不显示原始翻译 key(首帧/全程)
|
||||
- 层级:`e2e`(首帧)+ `component`(全程)
|
||||
- 描述:无论资源加载时序,任何时刻帧内都不泄漏原始 key 文本(`ns:...`)。
|
||||
- 前置:资源同步就绪或就绪门禁生效;`<Trans>`/`t()` 就绪后渲染。
|
||||
- 步骤:e2e 首次导航截图 + 快速截图序列,扫描页面可见文本。
|
||||
- 断言:页面文本不包含形如 `namespace:key` 或 `key.xxx` 的原始 key 串。
|
||||
- 自动化:*
|
||||
|
||||
### TC-08 `<html lang>` 与当前语言一致
|
||||
- 层级:`component` + `e2e`
|
||||
- 描述:`<html lang>` 始终等于当前语言(en/zh-CN)。
|
||||
- 前置:`<html lang>` 客户端同步已实现。
|
||||
- 步骤:分别在 en、zh、切换后、刷新后读取 `document.documentElement.lang`。
|
||||
- 断言:en→`"en"`,zh→`"zh-CN"`;切换与刷新后保持一致。
|
||||
- 自动化:*
|
||||
|
||||
## C. 动态数量 / 日期 / 数字 / 货币
|
||||
|
||||
### TC-09 中文数量插值 count=0 / 1 / 2(D9)
|
||||
- 层级:`unit`(核心)+ `component`(渲染)
|
||||
- 描述:中文用计数插值与量词:`0 个`、`1 个`、`2 个`/`{{count}} 个`;不机械复制英文复数分支。
|
||||
- 前置:量词插值组件/函数已实现,`{{count}}` 语义。
|
||||
- 步骤:依次以 `count=0`、`count=1`、`count=2` 渲染/调用,中文态。
|
||||
- 断言:输出分别为 `0 个请求`、`1 个请求`、`2 个请求`(按术语表措辞);无英文 one/other 分支语义。
|
||||
- 自动化:*
|
||||
|
||||
### TC-10 英文数量复数
|
||||
- 层级:`unit`
|
||||
- 描述:英文按 i18next/CLDR `one`/`other` 处理:`1 request` vs `2 requests`。
|
||||
- 前置:英文资源含复数分支。
|
||||
- 步骤:`count=1`、`count=2` 渲染。
|
||||
- 断言:`1 request`、`2 requests`。
|
||||
- 自动化:*
|
||||
|
||||
### TC-11 日期本地化
|
||||
- 层级:`unit`
|
||||
- 描述:日期按当前语言格式化(e.g. `Sep 9, 2026` vs 中文格式),时区/时刻固定。
|
||||
- 前置:固定时间源、固定 locale;格式化逻辑独立。
|
||||
- 步骤:同一时间戳在 en、zh 下格式化。
|
||||
- 断言:格式随 locale 变化且正确;无 `NaN`/原始时间戳泄漏。
|
||||
- 自动化:*
|
||||
|
||||
### TC-12 数字本地化
|
||||
- 层级:`unit`
|
||||
- 描述:千分位/小数点随 locale(en `1,234.5` vs zh 千分位),金额单位不重复。
|
||||
- 前置:格式化函数独立。
|
||||
- 步骤:en、zh 各格式化同值。
|
||||
- 断言:分组/小数正确;不出现双重符号或单位。
|
||||
- 自动化:*
|
||||
|
||||
### TC-13 货币本地化
|
||||
- 层级:`unit`
|
||||
- 描述:货币符号与金额按 context/locale 呈现(成本/预算页)。
|
||||
- 前置:货币格式化上下文确定(币种来自数据)。
|
||||
- 步骤:en、zh 各格式化同金额。
|
||||
- 断言:符号与格式正确;数据币种不被误翻。
|
||||
- 自动化:*
|
||||
|
||||
## D. 表单与交互状态
|
||||
|
||||
### TC-14 切换语言不清空表单
|
||||
- 层级:`integration`
|
||||
- 描述:填写表单字段后切换语言,字段值与未提交状态保持,不触发无关请求。
|
||||
- 前置:集成树(真实组件 + Provider),网络边界 stub。
|
||||
- 步骤:填写 → 切语言 → 断言字段值与 `<form>` 状态保持。
|
||||
- 断言:输入值仍在、选择/勾选仍在;无提交/异常请求触发;无原始 key。
|
||||
- 自动化:*
|
||||
|
||||
### TC-15 切换语言不清空表单(E2E 抽查)
|
||||
- 层级:`e2e`
|
||||
- 描述:真实浏览器下填写关键表单(如 Models)后切换语言,值保持。
|
||||
- 前置:静态导出站点可用。
|
||||
- 步骤:填写 → 切中文 → 断言。
|
||||
- 断言:值与交互状态保持。
|
||||
- 自动化:*(抽查核心表单)
|
||||
|
||||
## E. 整页跳转语言保持
|
||||
|
||||
### TC-16 Login 整页跳转回跳后语言保持
|
||||
- 层级:`e2e`
|
||||
- 描述:切成中文 → 走 Login(整页跳转)→ 成功回跳 → 语言仍为中文。
|
||||
- 前置:Login 整页跳转与同源偏好恢复已 PoC/批准。
|
||||
- 步骤:设中文 → 登录跳转 → 回跳 → 读 `<html lang>`/文本。
|
||||
- 断言:中文保持或命中文档化降级方案(需 Agent 0 批准)。
|
||||
- 自动化:*
|
||||
|
||||
### TC-17 SSO 整页跳转回跳后语言保持
|
||||
- 层级:`e2e`
|
||||
- 描述:SSO 外部跳转并返回后语言偏好保持。
|
||||
- 前置:SSO 流程可测(测试账号/模拟)。
|
||||
- 步骤:设中文 → SSO → 返回 → 断言。
|
||||
- 断言:同 TC-16。
|
||||
- 自动化:*
|
||||
|
||||
### TC-18 MCP OAuth 整页跳转回跳后语言保持
|
||||
- 层级:`e2e`
|
||||
- 描述:MCP OAuth 授权跳转并返回后语言保持。
|
||||
- 前置:MCP OAuth 流程可测(mock proxy)。
|
||||
- 步骤:设中文 → OAuth → 返回 → 断言。
|
||||
- 断言:同 TC-16。
|
||||
- 自动化:*
|
||||
|
||||
### TC-19 存储不可用时的降级
|
||||
- 层级:`unit` + `e2e`
|
||||
- 描述:偏好存储不可用/被禁时降级到英文,不崩溃、不报错。
|
||||
- 前置:存储抽象可注入失败。
|
||||
- 步骤:注入读写失败 → 初始化 → 断言。
|
||||
- 断言:回退 `en`;无未捕获异常;不显示原始 key。
|
||||
- 自动化:*
|
||||
|
||||
## F. 不误翻译保护
|
||||
|
||||
### TC-20 模型名不被翻译
|
||||
- 层级:`integration`
|
||||
- 描述:Models 页面渲染的模型名/模型 id 在 en 与 zh 下逐字一致,不受语言切换影响。
|
||||
- 前置:stub 数据含大小写混合/带符号模型名。
|
||||
- 步骤:en、zh 各渲染并比对模型名文本。
|
||||
- 断言:模型名字符串完全一致;不被译成中文/改名。
|
||||
- 自动化:*
|
||||
|
||||
### TC-21 API 字段 / 日志 / 代码示例不被翻译
|
||||
- 层级:`integration`(代码示例/字段)+ `unit`(日志原文)
|
||||
- 描述:API 参数字段、请求体 key、日志原文、代码示例不参与翻译。
|
||||
- 前置:含上述内容的组件与最小字典。
|
||||
- 步骤:zh 下渲染包含代码/字段/日志的页面。
|
||||
- 断言:这些内容保持原样(ASCII/原文),与 en 一致。
|
||||
- 自动化:*
|
||||
|
||||
## G. 构建期元数据与可访问性
|
||||
|
||||
### TC-22 构建期 `<title>` / meta description v1 保持英文
|
||||
- 层级:`e2e`
|
||||
- 描述:静态导出 `out/index.html`(及各路由)的 `<title>` 与 `meta[name=description]` 在 v1 保持英文,未被功能 Agent 意外改写或半中文化。
|
||||
- 前置:`npm run build` 完成;检查静态产物。
|
||||
- 步骤:解析 `out/**/*.html` 的 `<title>`/description(或浏览器读取 `document.title`)。
|
||||
- 断言:为稳定英文文案;不含原始 key、不含半成品中文。
|
||||
- 自动化:*
|
||||
|
||||
### TC-23 可访问性文案本地化(aria-label/title/placeholder)
|
||||
- 层级:`component`(每组件)+ `integration`(成组页面)
|
||||
- 描述:图标按钮、关闭按钮、tooltip、`title`、`placeholder`、校验 error 的 a11y 文本随语言本地化并有英文回退。
|
||||
- 前置:目标组件含该类文案。
|
||||
- 步骤:en、zh 各查询 `getByRole`/`getByLabel`/`toHaveAttribute('aria-label', …)`。
|
||||
- 断言:a11y 文案为对应语言;zh 缺失时回退英文;不显示原始 key;ARIA value 用 `toHaveAttribute` 断言(CLAUDE.md)。
|
||||
- 自动化:*
|
||||
|
||||
### TC-24 中文布局不溢出(自动断言 + 人工走查)
|
||||
- 层级:`integration`(自动溢出)/ `e2e`(窄屏)+ 人工
|
||||
- 描述:中文下导航/表格/弹窗/表单无横向溢出、无截断、无遮挡。
|
||||
- 前置:切换到中文。
|
||||
- 步骤:对关键容器断言 `scrollWidth <= clientWidth`、关键文本 `toBeVisible`;e2e 窄视口;人工跨语言走查。
|
||||
- 断言:无溢出/截断/遮挡;`V1_SCOPE_MANIFEST.md` UI 检查证据。
|
||||
- 自动化:*(溢出断言)+ (m)(视觉走查)
|
||||
|
||||
## H. Key / 字典一致性
|
||||
|
||||
### TC-25 en/zh key 集合一致
|
||||
- 层级:`unit`(脚本级,作为静态校验)——实现为 CLI 工具(`TEST_TOOLS.md`);此处用例=脚本通过条件
|
||||
- 描述:en 与 zh 全部 namespace 的 key 集合、结构与插值变量一致。
|
||||
- 前置:脚本就绪、字典就绪。
|
||||
- 步骤:对每 namespace 运行一致性校验。
|
||||
- 断言:key 集对称一致;缺 key/多余 key/结构不一致/插值变量不一致时脚本非 0 退出并列差异。
|
||||
- 自动化:*(门禁/CI)
|
||||
|
||||
## I. 首屏 / 静态导出
|
||||
|
||||
### TC-26 静态导出构建产物无原始 key 泄漏
|
||||
- 层级:`e2e`/静态产物检查
|
||||
- 描述:构建后的 `out/` 页面源码不含原始 `ns:key` 文本作为用户可见内容(排除 JS bundle 中的字典本身)。
|
||||
- 前置:`npm run build`。
|
||||
- 步骤:解析产物 HTML,过滤 script/style,扫描可见文本。
|
||||
- 断言:可见文本无原始 key 形式。
|
||||
- 自动化:*
|
||||
|
||||
---
|
||||
|
||||
## 用例汇总
|
||||
|
||||
- 共 26 条(TC-01 ~ TC-26)。
|
||||
- 自动化纯自动:除 TC-24 视觉走查外全部为 `*`;TC-24 为自动化+人工混合。
|
||||
- 覆盖方案 §10 全部"测试重点":
|
||||
- 默认语言:TC-01/02
|
||||
- 切换即时生效:TC-03
|
||||
- 刷新保持:TC-04/05
|
||||
- 缺 key 回退英文:TC-06
|
||||
- 不显示原始 key:TC-07/26
|
||||
- `<html lang>` 一致:TC-03/08
|
||||
- 中文不溢出:TC-24
|
||||
- 切换不清表单:TC-14/15
|
||||
- 整页跳转语言保持:TC-16/17/18/19
|
||||
- 动态数量/日期/数字/货币(含 count=0/1/2):TC-09~13
|
||||
- 模型名/API 字段不误翻:TC-20/21
|
||||
- `<title>`/meta v1 保持英文:TC-22
|
||||
- 另补充:a11y 文案(TC-23)、key 一致性(TC-25)。
|
||||
88
docs/i18n/TEST_TOOLS.md
Normal file
88
docs/i18n/TEST_TOOLS.md
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
# i18n 自动化测试任务清单与工具设计(TEST_TOOLS.md)
|
||||
|
||||
> 角色:Agent 3 `qa-architect`;实施:Agent 7 `i18n-qa`
|
||||
> 原则:扫描工具**只报告、不自动改写**(方案 §3.4/§11);测试只跑改动相关文件;Vitest 命令由 Agent 3 依配置确认(方案 §10)
|
||||
|
||||
## 0. 运行说明(Agent 7 前置校准)
|
||||
|
||||
- 定向 vitest:`cd ui/litellm-dashboard && npx vitest run --project <unit|component|integration> <path>`;type:`npm run test:types`。
|
||||
- 禁止无路径全量 `npm run test`(380 文件、CI 才跑全量)。
|
||||
- 工程检查:`npm run lint`、`npm run format:check`、`npm run build`。
|
||||
- **E2E Playwright 基础设施当前不存在**(无 `playwright.config.*`、无 `@playwright/test`、无 `tests/e2e/ui/`)。Agent 7 在 Wave 1 搭建;涉及 `package.json`/锁文件变更时必须由 Agent 0 指派唯一 Owner(方案 §6.1)。
|
||||
|
||||
## 1. 自动化任务清单
|
||||
|
||||
| 编号 | 任务 | 形态 | 何时运行 | 对应用例 |
|
||||
|---|---|---|---|---|
|
||||
| T-01 | 搭建 Playwright 基础(config、`tests/e2e/ui/`、live proxy fixture) | E2E 基建 | Wave 1 | TC-01~04,06~08,16~18,22,26 |
|
||||
| T-02 | key 集合一致性校验脚本 | CLI(node) | G3 门禁/CI、每次字典变更 | TC-25 |
|
||||
| T-03 | 硬编码字符串扫描脚本(只报告) | CLI(node) | 每波次、G3/G4 | — |
|
||||
| T-04 | 语言初始化/偏好/格式化逻辑单元测试 | `*.test.ts`(unit) | 平台 Wave 1 | TC-02,05,09~13,19 |
|
||||
| T-05 | 语言切换器/量词/单组件渲染测试 | `*.test.tsx`(component) | 各功能波 | TC-03,09,10,23 |
|
||||
| T-06 | Provider+页面集成测试(切换、表单保持、a11y、误翻译) | `*.integration.test.tsx` | 各功能波 | TC-03,14,20,21,23,24 |
|
||||
| T-07 | 静态导出产物 key 泄漏/`<title>` 英文检查 | 构建后脚本/E2E | G1、G4 | TC-22,26 |
|
||||
| T-08 | 中文布局溢出自动化断言(+人工走查入口) | `integration`/`e2e` | 各功能波、Wave 4A | TC-24 |
|
||||
| T-09 | `V1_SCOPE_MANIFEST.md` 测试/UI 检查证据回填 | 人工+半自动 | G2/G3/G4 | — |
|
||||
| T-10 | 完整回归矩阵执行并出报告 | Playwright+vitest | Wave 4A | REGRESSION_MATRIX |
|
||||
|
||||
## 2. key 集合一致性校验脚本(T-02)
|
||||
|
||||
**目标**:证明 en/zh(及新 locale)key 集、结构与插值变量一致;组件引用的 key 均存在。
|
||||
|
||||
**设计**:
|
||||
- 输入:`src/locales/en/**/*.json`、`src/locales/zh-CN/**/*.json`。
|
||||
- 对每个 namespace 比对:key 集合(递归扁平化,含冒号前缀)对称差、嵌套结构形状、每叶子的 `{{var}}` 插值变量集合。
|
||||
- 组件引用校验:AST 扫描 `t('ns:key')`、`useTranslation('ns')`、`<Trans>` 与静态字典,找出引用但未声明的 key(悬空引用)。
|
||||
- 输出:无差异 → 退出 0;有差异 → 列出缺失/多余 key、结构/插值差异、悬空引用清单,退出非 0。
|
||||
- 挂接:G3 门禁"中英文 key 集一致";可加入 CI lint 前置。
|
||||
|
||||
**命令形态**:
|
||||
```bash
|
||||
node scripts/i18n/check-keys.mjs # 或通过 vitest 一个用例驱动(TC-25)
|
||||
```
|
||||
|
||||
## 3. 硬编码字符串扫描脚本(T-03)——只报告、不自动改写
|
||||
|
||||
**目标**:发现"漏改为 `t()` 的用户可见文案",辅助人工补翻译。**禁止自动生成 key、禁止自动改写代码**。
|
||||
|
||||
**设计**(对 TSX/TS 源码 AST):
|
||||
- JSX 文案:非空白/非注释/非纯符号的 `JSXText`。
|
||||
- 属性字面量:`aria-label`、`title`、`placeholder`、`alt` 中指向文案的字符串常量。
|
||||
- 未国际化入口:`toast(...)`、`useMessage`、`notify(...)` 等(以项目现状校准 white-list)。
|
||||
- 排除白名单:模型名/API 字段引用(`model`、`langsmith` 等字典值来源)、路径、正则、CSS 类、`data-slot`、纯数字/符号、标识符、已 `t()` 包裹的。
|
||||
- 输出:文件、行号、候选文案、疑似命中形式(文案属性/JSX 文本/消息入口),按文件分组;**附带"仅报告"明确标注**,不写字典。
|
||||
|
||||
**报告消费**:Agent 7 输出人工 review 清单 → 产品/功能 Owner 决定是否改写;禁止工具自动化改写(方案 §3.4 硬约束)。
|
||||
|
||||
## 4. 中文布局检查:自动化 / 人工边界
|
||||
|
||||
**自动化(T-08)**:
|
||||
- 组件/集成断言:中文渲染下对关键容器断言 `scrollWidth <= clientWidth`(无横向溢出)、关键文本 `toBeVisible`、无遮挡(元素间不重叠)。
|
||||
- E2E:在窄视口(如 1280、1024、768)下对 Navbar/Leftnav/表格/弹窗断言同样条件,并做关键帧截图。
|
||||
- 限制:视觉"美观/语义完整"无法纯自动化判定,自动只兜底"不溢出、不截断语义、可读可见"。
|
||||
|
||||
**人工(配合 Agent 2)**:
|
||||
- 跨语言(en/zh)并列视觉走查,覆盖中文长字符串与窄屏。
|
||||
- 截图证据回填 `V1_SCOPE_MANIFEST.md` 的 `UI 检查` 栏。
|
||||
- `TEST_CASES.md` TC-24 标记为自动化+人工混合。
|
||||
|
||||
## 5. 静态导出产物检查(T-07)
|
||||
|
||||
- 构建后扫描 `out/**/*.html`:
|
||||
- 用户可见文本(剔除 script/style/内联 JSON 字典)不含 `namespace:key` 原始 key 形式(TC-26)。
|
||||
- `<title>` 与 `meta[name=description]` 为稳定英文、非原始 key、非半中文(TC-22)。
|
||||
- 挂接:G1(首屏/构建验证)与 G4(发布验收)。
|
||||
|
||||
## 6. E2E 基建要点(T-01)
|
||||
|
||||
- 目录:`tests/e2e/ui/`(Playwright spec,与 vitest 分离)。
|
||||
- 目标:静态导出 `out/` 或 live proxy;语言切换/刷新/整页跳转(Login/SSO/MCP OAuth)的偏好恢复需可稳定复现(测试账号或 mock)。
|
||||
- `package.json` 变更(新增 `@playwright/test`)须经 Agent 0 指定唯一 Owner,遵守 §6.1。
|
||||
|
||||
## 7. Agent 7 建议实现优先级
|
||||
|
||||
1. **T-02 key 一致性校验**(成本低、门禁 G3 硬性、最易踩线)。
|
||||
2. **T-03 硬编码扫描**(只报告,贯穿所有波次,辅助功能 Agent 自查)。
|
||||
3. **T-01 E2E 基建 + 平台 smoke**(默认语言/切换/刷新/`<html lang>`/回退/不显示原始 key)。
|
||||
4. **T-07 静态产物 key 泄漏 + `<title>` 英文检查**(G1 门禁)。
|
||||
5. **T-04/T-05/T-06 各层测试**随功能波补齐,T-08 布局断言进入各模块验收,T-10 在 Wave 4A 全量回归。
|
||||
78
docs/i18n/V1_TRANSLATION_SCOPE.md
Normal file
78
docs/i18n/V1_TRANSLATION_SCOPE.md
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
# v1 翻译范围清单(V1 Translation Scope)
|
||||
|
||||
> 维护者:Agent 2(localization-designer)
|
||||
> 状态:设计稿,待 G0 评审冻结;与 `I18N_MULTI_AGENT_PLAN.md` §1.2/§6.2 及 `DECISIONS.md` D8 对齐
|
||||
> 说明:本文档逐项列出 v1 各页面/组件"**需翻译**"或"**明确不翻译**"及原因,作为 Agent 5/6 改造与 Review 的依据。数据/协议内容是否翻译遵循 `LOCALIZATION_SPEC.md` §4。
|
||||
|
||||
---
|
||||
|
||||
## 1. v1 需翻译范围(逐项)
|
||||
|
||||
### 1.1 全局壳层
|
||||
|
||||
| 组件/区域 | 需翻译? | 原因 / 说明 |
|
||||
|---|---|---|
|
||||
| `leftnav.tsx` 导航菜单项与分组标签 | 需翻译 | 全局入口;`menuGroups` 的 `groupLabel`、`label` 需 I18N 化(含分组标签如 AI Gateway/Observability 等)。`menuGroups` 由 `page_utils.ts` 消费,改造时保留导出结构,仅语义化 label |
|
||||
| `leftnav.tsx` 面包屑(`getBreadcrumb`)/ Navbar 标题 | 需翻译 | 与导航一致,防止两处不一致 |
|
||||
| `leftnav.tsx` aria-label(折叠/展开侧边栏) | 需翻译 | a11y 文案(`LOCALIZATION_SPEC.md` §7) |
|
||||
| Navbar 用户菜单 / `SidebarAccountMenu` | 需翻译 | 显示名、菜单项、提示文案 |
|
||||
| `navbar.tsx` 其余可见菜单与搜索占位 | 需翻译 | 依实际文案盘点 |
|
||||
| 语言切换器(新组件) | 需翻译 | 自身 aria-label 与菜单项随语言渲染 |
|
||||
|
||||
### 1.2 登录与引导
|
||||
|
||||
| 页面 | 需翻译? | 说明 |
|
||||
|---|---|---|
|
||||
| `src/app/login/LoginPage.tsx` | 需翻译 | 登录表单、提交、错误提示、SSO 入口 |
|
||||
| `src/app/onboarding/`(OnboardingForm/FormBody/Loading/Error 等) | 需翻译 | 引导步骤、表单、加载/错误视图 |
|
||||
| `src/app/connect/` | 需翻译 | Connect 页面 |
|
||||
| MCP OAuth 相关回跳/授权提示 UI | 需翻译 | 见 `LOCALE_NAVIGATION_BEHAVIOR.md`(注意整页跳转语言恢复) |
|
||||
|
||||
### 1.3 Models 与 API Keys
|
||||
|
||||
| 页面/组件 | 需翻译? | 说明 |
|
||||
|---|---|---|
|
||||
| Models and Endpoints(`/models`)列表、创建/编辑表单、过滤、空态、校验 | 需翻译 | 静态与列表类文案;`model-hub`/AI Hub 候选页属低优先级,v1 不强求 |
|
||||
| API Keys(`/api-keys`)列表、创建弹窗、删除确认、用量展示 | 需翻译 | 含确认框、空态、校验 |
|
||||
| 相关复用组件(`key_value_input`、`copy button` 等) | 需翻译 | 复用组件文案由调用方传入或按需本地化 |
|
||||
|
||||
### 1.4 Usage / Cost / Budget
|
||||
|
||||
| 页面/组件 | 需翻译? | 说明 |
|
||||
|---|---|---|
|
||||
| Usage(`/usage`)筛选、表格头、图表说明、空态 | 需翻译 | 数值/货币用 i18n 格式化 |
|
||||
| Cost Tracking(`/cost-tracking` / `cost-optimization`) | 需翻译 | 成本跟踪页面;数值格式化随 i18n |
|
||||
| Budgets(`/budgets`)创建/编辑、限额设置、列表 | 需翻译 | 含"Budget Limit"等术语(见术语表) |
|
||||
|
||||
---
|
||||
|
||||
## 2. 明确不翻译范围(逐项 + 原因)
|
||||
|
||||
遵循 `LOCALIZATION_SPEC.md` §4 与方案 §1.2:
|
||||
|
||||
| 项 | 不翻译原因 |
|
||||
|---|---|
|
||||
| 模型名 / Model ID / Deployment / Provider 名 | 用户/后端数据,非 UI 文案 |
|
||||
| API 字段、请求/响应键、JSON 结构 | 协议内容 |
|
||||
| 日志原文、错误消息、错误堆栈(stack trace) | 需原样排障,保留原文 |
|
||||
| 代码示例、curl、终端命令、config.yaml、.env | 技术内容 |
|
||||
| URL、路径、文件名 | 技术标识 |
|
||||
| API / URL / SSO / MCP / OAuth / REST / ID / JSON 等缩写 | 通用技术缩写 |
|
||||
| 品牌名 LiteLLM / OpenAI / Anthropic | 品牌 |
|
||||
| 角色/权限代码值(admin、internal_user 等) | 代码值,仅显示名可翻译 |
|
||||
| Beta / New 徽标 | 视觉因子,保留英文 |
|
||||
| 构建期 `<title>` / metadata description | D8,v1 保持英文,不做多语言 SEO |
|
||||
| Guardrails / Policies / Teams / Users / Orgs / Projects / Logs / Playground / Prompts 等整模块文案(未列入 v1 功能批次) | 属 v2+ 候选(方案 §5.7),v1 范围外不被动外扩 |
|
||||
| Python SDK、API 文档、代码示例翻译 | 方案 §1.2 明确排除 |
|
||||
| 后端 FastAPI 错误响应全面国际化 | 方案 §1.2 排除 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 与 §6.2 状态清单对应
|
||||
|
||||
本清单即 `V1_SCOPE_MANIFEST.md`(Agent 0 维护)中 v1 行的**文案盘点输入**。Agent 5/6 完成任务时在清单相应行填写 EN/ZH-CN 盘点,Agent 7 填测试/UI 检查证据,Agent 2 在 Wave 4A 做术语与中文体验复核。
|
||||
|
||||
## 4. 边界红线
|
||||
|
||||
- 未列入第 1 节的功能页面文案,v1 一律**不翻译**(保持英文),不要顺手外扩造成半成品。
|
||||
- 第 2 节不翻译项,任何 Agent 不得翻译;发现误翻即退回原 Owner,并作为缺陷记录。
|
||||
Loading…
Add table
Reference in a new issue