1实现原理 · 为什么它能做到
唯一的代码资产是一个纯标准库的审计脚本:用三条正则抽取源码里的 t('key') / i18n.t('key') / i18next.t('key'),与 locale JSON 扁平化后的 key 集合做集合运算。
KEY_PATTERNS = [ re.compile(r"(?<![\w.])t\(\s*['\"]([^'\"]+)['\"]"), re.compile(r"\bi18n\.t\(\s*['\"]([^'\"]+)['\"]"), re.compile(r"\bi18next\.t\(\s*['\"]([^'\"]+)['\"]"), ]
扫描时主动跳过 node_modules 与 .git,并按扩展名白名单收文件,避免在大仓库里做无意义遍历。
if "node_modules" in path.parts or ".git" in path.parts: continue
输出三张差异表:missing_by_locale(代码用了但该 locale 没有)、unused_by_locale(locale 有但代码没用到)、parity_missing(各 locale 之间的缺失,回答「en 有 zh 没有」)。
parity_missing[name] = sorted(other_keys - locale_key_map[name])
复数键被专门豁免:key 本体没直接命中、但存在 _one/_other 等 CLDR 复数后缀变体时不算缺失。
PLURAL_SUFFIXES = ("_zero", "_one", "_two", "_few", "_many", "_other")
locale 名推断有约定:common.json / translation.json / messages.json 这类通用文件名用父目录名当 locale 名(locales/en-US/common.json → en-US)。
if path.name in {"common.json", "translation.json", "messages.json"}:
工作流是「审计 → 修复 → 复验」的闭环,且把缺口归零当作完成条件:审计发现缺失即视为 blocker,改完必须复跑到零。
8) Validate - Re-run the audit until missing/parity issues are zero.
裸字符串检索给了具体的 ripgrep 正则:一条抓 JSX 标签里的可见文案,一条抓 aria-label / title / placeholder 三类可访问性属性。
rg -n --glob '<src>/**/*.{ts,tsx,js,jsx}' "aria-label=\"[^\"]+\"|title=\"[^\"]+\"|placeholder=\"[^\"]+\""
错误处理有一条硬性准则:绝不把原始 error.message 暴露到 UI,只显示本地化文案,原始细节只进日志,并给未知错误码配本地化兜底。
- Never expose raw `error.message` to UI; show localized strings only.
框架选型给了按栈的默认答案,且把「路由是否 locale 感知」(子路径/子域/query 参数)要求在最早期就定下来。
- Choose a framework-appropriate library (e.g., React: react-i18next; Next.js: next-intl; Vue: vue-i18n).
刻意划定「不该翻译」的边界:产品名、API、MCP、Bash 这类技术/品牌词保持原样。
- Some technical/brand terms should remain untranslated (e.g., product name, API, MCP, Bash).
脚本支持 --json 机器可读输出与人类摘要两种模式,便于接入 CI 或人工巡检。
if args.json: json.dump(output, sys.stdout, indent=2, ensure_ascii=False)
2核心能力
3外部依赖
| 类型 | 依赖 |
|---|---|
| cli | python3(执行 scripts/i18n_audit.py;仅标准库 argparse/json/re/pathlib/typing) |
| cli | rg(ripgrep,用于定位裸字符串与可访问性属性) |
| cli | python -m json.tool(校验 locale JSON 合法性) |
| package | i18n 库(由 agent 按栈安装,非脚本调用:react-i18next / next-intl / vue-i18n) |
| api | 宿主 Edit/写文件能力(替换源码字符串、新增 locale 键、更新测试) |
4风险提醒 风险提醒:蓝色 · 知晓即可
- 主要风险是「改坏」而非「被攻击」:批量替换源码字符串与 locale 键不可逆性高。 — Step 5/7 会大范围改写源码与 JSON;护栏是文档级约束,无 dry-run 或变更预览机制,也没有 diff 审查环节(脚本只审计,不生成 patch)。建议在大改动前依靠版本控制兜底。
- 正则抽取有已知盲区,可能给出不完整的审计结论。 — 动态 key(t(var))与自定义包装函数(如 useTranslation 之外的 tr())抽不到;SKILL.md 要求人工复核,但若使用者只信脚本输出,会漏检。脚本也不区分注释里的 t('...')。
- 复数键豁免可能掩盖真实缺键。 — 任何 key 只要存在 _one/_other 之类的变体就不再报缺失,即使该变体集合对目标 locale 并不完整(例如 zh-CN 只提供了 _other 而代码走 count 分支依赖 _one)。脚本不校验变体集合的完整性。
- parity 检查不是两两严格对齐。 — 若两个 locale 同时缺同一个 key,parity_missing 不会报(只有 missing_by_locale 会报,前提是源码用到了该 key)。历史上「locale 文件里有、但只在部分语言里被删掉」的情况能被抓到,而「两边一起缺」只能靠源码侧发现。
- 翻译质量本身不在本 skill 的保障范围。 — SKILL.md 提到可以 AI/专业/人工三种方式生成译文,但没有质量校验机制;脚本只做键位与 JSON 结构检查,语义正确性靠人。
5第二遍独立确认
- [ok] 外部依赖逐条是否真实存在 — python3 见 shebang;rg 两条命令见 SKILL.md Step 4 代码块;python -m json.tool 见 Step 8;i18n 库安装见 Step 2 的 Install packages;宿主写能力见 Step 5 与 Deliverables。无一条来自推测。
- [ok] 脚本是否真的只读(安全结论最关键的一条) — 通读 i18n_audit.py 全文:唯一 I/O 是 path.read_text(encoding='utf-8') 与 path.open(encoding='utf-8') 读 locale、print 与 json.dump(sys.stdout)。无写文件、无删除、无 mkdir。已在 file_writes 中明确把写入归到 agent 侧。
- [ok] 『零网络』是否被 token 扫描的空结果证明 — 扫描零命中即为证据;SKILL.md 正文也未出现任何 URL 或域名(对比同仓其他 skill 常见域名常量)。故 network_calls = []。
- [ok] 复数豁免是否真在 compute_missing 中生效 — compute_missing 内两个 continue 分支:key in locale_keys 与 has_plural_variant(key, locale_keys),后者遍历 PLURAL_SUFFIXES 六个后缀构造 f"{key}{suffix}" 再查集合。代码属实,非文档口号。
- [discrepancy] parity_missing 语义是否与 SKILL.md 表述一致 — 〔第二遍标注:措辞微调〕实现是『其它所有 locale key 并集 − 本 locale key』(other_keys - locale_key_map[name]),语义为「别家有而本家缺」,与 SKILL.md『ensuring en-US/zh-CN coverage』一致,但它不是「两两全等」的严格校验:当同缺一个 key 时两边都不报。第一遍若把 parity 描述成「严格一致校验」会夸大,已在 how_it_works/note 中写明实现语义。
- [ok] 动态 key 的处理是否被承诺为自动 — SKILL.md 明写『Manually verify dynamic keys (`t(var)`).』——正则抽取天然覆盖不到动态 key,文档没有掩盖这一限制,不存在夸大。
- [ok] 元数据(license/stars/last_push) — 取自同 repo 同 commit 的池内既有值(MIT、1385★、2026-09-09T12:33:29Z);本次未联网复核 gh。
6结论
8e220e12dd691eab…d5c4678cb5