- 新增 GitHub Actions 工作流实现 PR 时自动审计国际化内容 - 添加 pre-commit 钩子,提交前自动检查相关文件并拦截问题提交 - 编写完整的 i18n 审计脚本、文档与示例报告 - 实现多维度国际化校验:键匹配、占位符、data()误用、硬编码标题等
105 lines
4.4 KiB
Markdown
105 lines
4.4 KiB
Markdown
# i18n 审计流程
|
|
|
|
> 把所有"$lang 是否能正常翻译"的检查收口到一个入口:
|
|
> **新增组件 / 新增翻译 / 拉取新 bundle.json 之后,跑这一个命令就能出报告。**
|
|
|
|
## 快速开始
|
|
|
|
```bash
|
|
# 标准体检(最常用)
|
|
python scripts/i18n_audit.py
|
|
|
|
# 严格模式 — 出现真问题就 exit 1,适合 CI / pre-commit hook
|
|
python scripts/i18n_audit.py --strict
|
|
|
|
# 拉了新 bundle.json(覆盖了 lang-bundle.json),指定新的:
|
|
python scripts/i18n_audit.py --bundle ./lang-bundle.2026-07-04.json
|
|
```
|
|
|
|
报告结束时会输出:
|
|
|
|
```
|
|
健康度评分:
|
|
✓ 99.95% 翻译覆盖,0 个真问题
|
|
或
|
|
! 存在 N 类问题需要修:
|
|
- <类目 1>
|
|
- <类目 2>
|
|
```
|
|
|
|
完整结构化报告写到 `scripts/lang_audit_report_v2.json`,适合 diff 或后续处理。
|
|
|
|
## 它在做什么(7 个检查)
|
|
|
|
| # | 检查维度 | 暴露什么问题 | 真实程度 |
|
|
| --- | --- | --- | --- |
|
|
| 1 | **静态 `$lang(key)` 字面 → bundle leaf** | key 字符串不存在于 bundle | 直接失效 |
|
|
| 2 | **占位符参数匹配** | `key` 含 `{name}` 但调用没传 `{ name: ... }`,运行时会**字面显示 `{name}`** | 直接失效 |
|
|
| 3 | **`data()` 中误用** | `data() { return { x: this.$lang(...) } }` 在 mp/鸿蒙 remote-only 模式下会**永久缓存空串** | 远程首屏失效 |
|
|
| 4 | **bundle ↔ 本地 lang drift** | `lang/zh-cn/...` 有的 key,bundle 没有(可能影响兜底) | 加载策略变更时显现 |
|
|
| 5 | **pages.json 硬编码导航标题** | `navigationBarTitleText: "商品分类"`,运行时**必须 `lang.refresh()` 兜底** | 取决于 refresh() 调用时机 |
|
|
| 6 | **i18n-lazy mixin 使用情况** | 哪些组件没遵循延迟初始化约定 | 标记数据风险点 |
|
|
| 7 | **P2 动态 key** (`verify-p2-keys.js`) | 拼接类 `$lang('prefix.' + var)` 在所有值下都能解析 | 模拟 runtime |
|
|
|
|
## 误报防御(已针对历史坑修过)
|
|
|
|
- **JS 注释剥离** 改用等长空白,正则偏移不偏(以前的 ghost key 大都是这个原因)
|
|
- **`[^'\"]+` 跨换行陷阱** 已用手动 paren-depth 解析替代,不会把整个文件吃进 key
|
|
- **JS 对象简写** `{ appName }` 等同于 `{ appName: appName }`,正则同时识别
|
|
- **`/api/lang/bundle` 真会有的** `tabBar.*` 走 root level,审计对 `common.tabBar.home` 故意不报警
|
|
- **`lang.js` JSDoc 内** 的 `$lang('title')`、`$lang('welcomeText')` 示例被注释剥离规则前置过滤
|
|
|
|
## 文件清单
|
|
|
|
```
|
|
scripts/
|
|
├── i18n_audit.py ← 唯一入口(run this)
|
|
├── audit_lang_bundle_v2.py ← 静态扫描(被 i18n_audit.py 调)
|
|
├── audit_lang_bundle.js ← Node 端 lang 加载工具
|
|
├── verify-p2-keys.js ← 动态 key 验证(被 i18n_audit.py 调)
|
|
├── I18N_AUDIT.md ← 本文档
|
|
└── lang_audit_report_v2.json ← 最近一次报告(自动生成)
|
|
```
|
|
|
|
## 何时该跑
|
|
|
|
1. **拉了新 bundle.json**:`/api/lang/bundle` 返回结果存到 `lang-bundle.json` 后,跑一次。
|
|
2. **新增 / 修改翻译 key**:`lang/zh-cn/...` 或 `lang/en-us/...` 文件改动后,跑一次。
|
|
3. **新增组件**:任何 .vue/.js 引入 `$lang(...)` 后,跑一次。
|
|
4. **修复 i18n bug 后**:跑一次,确认`健康度评分`达 `99.95%`。
|
|
5. **PR 合并前(CI)**:`python scripts/i18n_audit.py --strict`,exit code 控制是否合入。
|
|
|
|
## 想加新的检查怎么办
|
|
|
|
在 `audit_lang_bundle_v2.py` 加一个 section,产生新字段 → 在 `i18n_audit.py::summarize` 里加一行 `print`。`audit_lang_bundle_v2.py` 写好了自动落入结构化报告。
|
|
|
|
## 已知"非 bug 但需注意"(已知风险不需修)
|
|
|
|
- **`pages.json` 中 20 个 `navigationBarTitleText` 硬编码**:由 `lang.refresh()` 自动覆盖,只要 refresh 在页面 mount 后被调到即可。
|
|
- **`components-diy/diy-tab.vue` 的 title 是运行时 DIY 数据**:DIY 数据规范/白名单层面解决,不在审计范围。
|
|
|
|
## 最近一次结果速查
|
|
|
|
```bash
|
|
cat scripts/lang_audit_report_v2.json
|
|
```
|
|
|
|
主要字段:
|
|
|
|
```json
|
|
{
|
|
"code_calls_total": 2675,
|
|
"code_unique_keys": 2083,
|
|
"found_in_bundle": 2082,
|
|
"missing_summary": {"SYNTAX_BUG": 1},
|
|
"missing_classified": {...},
|
|
"placeholder_issues": [],
|
|
"data_lang_issues": {...},
|
|
"hardcoded_navbar_titles": [...],
|
|
"tabbar_missing": [...],
|
|
"local_only_keys": []
|
|
}
|
|
```
|
|
|
|
`placeholder_issues` 为 `[]` 是绿;`data_lang_issues` 为 `{}` 是绿;`missing_summary.SYNTAX_BUG` ≥ 1 通常是 grep 误报,详见 `missing_classified` 字段的 `reason`。
|