- 桌面应用
【免费下载链接】BongoCat
🐱 BongoCat — A cross-platform interactive desktop pet that brings fun to your desktop!
本文解读 BongoCat 仓库中 本地化文案书写规范 的技术内涵。该规范约束
crates/bongocat-i18n/locales/*.json下全部 UI 文案的书写方式,核心只有一条:省略号一律写成单个…(U+2026)。读完本文,你将理解这条约定为何必须与语言无关、它如何被tools/validate-locales.py机械强制,以及新增/修改多语言文案时的完整实战流程与配套校验。
规范定位与适用范围
BongoCat 的 UI 文案统一存放在crates/bongocat-i18n/locales/目录,每种语言一个 JSON 文件。当前仓库实际携带 6 份 catalog:en-US、zh-CN、zh-TW、ar-SA、vi-VN、pt-BR(清单见 crates/bongocat-i18n/src/lib.rs 中的SHIPPED_LOCALES)。
本规范是一份规范性约定,有两个明确边界:
- 它只约束
crates/bongocat-i18n/locales/*.json里的 UI 文案怎么写,不涉及目标架构;架构以 docs/technical-design.md 和docs/adr/下的决策记录为准; - catalog 的结构、key 命名与占位符规则,由 docs/adr/0012-json-localization.md 单独定义,本文与 ADR 互为补充而非覆盖。
规则一律遵守、不因语言而异:中英文、阿拉伯语、越南语、葡萄牙语都适用同一套写法。
核心规则:省略号一律单个…(U+2026)
规则一句话:省略号写成单个…(U+2026),一个字符,视觉上是三个点。其余任何写法都违反规范:
| 写法 | 点数 | 是否允许 |
|---|---|---|
…(U+2026 ×1) | 3 | ✅ 唯一允许的写法 |
……(U+2026 ×2) | 6 | ❌ 中文排版习惯,不采用 |
...(ASCII 句点 ×3) | 3 | ❌ 拉丁写法,不采用 |
规范的三个理由值得逐条展开:
- 同一个 key 由所有语言共用,写法必须与语言无关。
……只在中文排版里成立、...只在拉丁文里成立,任何一种都会让另一种语言的用户看到"错误"的省略号。 …是单个字符、占一个码位,中英文字体都能正确渲染,宽度稳定;……是两个字符,在非中文字体下还可能被拆成两个独立字形。- 文案里没有需要区分"三个点"和"六个点"的语义,长度差异纯属排版习惯,不是信息差异。
以 crates/bongocat-i18n/locales/zh-CN.json 中的模型导入流程为例,中文侧每个进行时态字符串都以单个…收尾:
{ "models": { "import": { "step": { "choosing": "正在打开文件选择器…", "importing": "正在导入模型…", "capturing": "正在截取模型封面…" } } } }英文侧(crates/bongocat-i18n/locales/en-US.json)同样是一个…,绝不写 ASCII 的...:
{ "models": { "import": { "step": { "choosing": "Opening the file picker…", "importing": "Importing model…", "capturing": "Capturing cover…" } } } }在仓库真实 catalog 里可以看到这条规则被严格贯彻:zh-TW.json写"正在開啟檔案選取器…"、ar-SA.json写جارٍ فتح منتقي الملفات…、pt-BR.json写Abrindo o seletor de arquivos…,6 份 catalog 在models.import.step.choosing等同一 key 下全部使用单个 U+2026。甚至带占位符的字符串同样遵守,例如更新流程的downloading:"正在下载… %{percent}%"(zh-CN)与Downloading… %{percent}%(en-US)——省略号与%{percent}%占位符之间用一个空格分隔。
为什么"与语言无关"是硬性要求:同一 key 服务多种语言
要理解这条规则为什么如此严格,需要看到 catalog 的解析机制。bongocat-i18n的locale_code(crates/bongocat-i18n/src/lib.rs)实现了 RFC 4647 的 language-subtag fallback:按 primary subtag 匹配已发布的 catalog,因此机器实际报告的 tag 几乎总不是 catalog 自身的名字——ar-EG落到ar-SA、pt-PT落到pt-BR、vi落到vi-VN、en-GB落到en-US,而不是各自回退英文。zh是唯一按书写体系(script subtag)分流的语言:hant与TW/HK/MO判为繁体落zh-TW,其余zh标签判为简体落zh-CN。
这意味着一份 catalog 的同一个字符串会被多种语言环境的用户共同阅读。省略号若是语言专属写法,就必然让另一批读者看到排版错误。单个…是唯一在中英文、阿拉伯文、越南文、葡萄牙文字体下都渲染正确且宽度稳定的选择。
机械强制:validate-locales.py把规则变成门禁
规范明确指出这条规则"不依赖 review 记忆"——写错就会在门禁上变红。具体实现是 tools/validate-locales.py,其核心是一行正则:
ELLIPSIS_RUN = re.compile(r"\u2026{2,}|\.{2,}")它匹配"连续两个及以上的…"或"连续两个及以上的 ASCII.",check_ellipsis对每个 locale 的每个值逐一扫描,一旦命中就抛出错误并报出 key 与实际写法。文档中给出的真实报错样式为:
error: zh-CN: models.import.step.importing spells an ellipsis as '……'; use a single '…' (U+2026) in every locale新增或修改文案后,本地跑一次即可验证:
python3 tools/validate-locales.py脚本在检查省略号之前会先把嵌套 JSON 展平为点号分隔的扁平 key(flatten),并顺带完成一系列配套校验:
- 文件清单校验:
crates/bongocat-i18n/locales/下的文件必须与EXPECTED_LOCALES(en-US、zh-CN、zh-TW、ar-SA、vi-VN、pt-BR)完全一致,多一个或少一个文件都直接失败; - 结构校验:顶层必须是对象、key 非空、值为非空字符串、不允许空对象、不允许重复扁平 key、
_version是保留 key; - key 集合一致性:每种语言与
en-US的扁平 key 集合必须完全相同(缺 key 或多余 key 都报错); - 占位符一致性:每种语言同一 key 的
%{name}占位符集合必须与en-US一致(placeholder_names对每个值做排序比较),防止漏掉插值参数。
正常通过时输出:
validated 6 locale(s), <N> key(s) each这套"key 集合一致 + 占位符一致 + 省略号一致"的三重校验,正是 docs/adr/0012-json-localization.md 中"测试/CI 必须检查 JSON 可解析、语言 key 集合相同、值为非空字符串且占位符集合一致"约束的落地实现。
配套约束:key 命名、占位符与回退(ADR-0012)
省略号规范不是孤立的一条,它与 ADR-0012 定义的 catalog 结构约束共同构成完整规则集:
- key 命名:翻译 key 使用小写
snake_case,按navigation、settings、models、shortcuts、diagnostics、about、actions、status、errors等领域分层,字段名表达具体上下文;JSON 源文件使用真正嵌套的领域结构,不得使用点号分隔的扁平 key; - 插值:使用
rust-i18n的%{name}语法,所有语言保持相同的占位符集合; - 回退:找不到语言或 key 时回退到
en-US(即DEFAULT_LOCALE); - 新文案先入 JSON:新文案必须先加入 JSON,再由 Rust 使用 key,禁止在
.rs源码中新增自然语言翻译文本; - 单一 catalog owner:
bongocat-i18n是唯一调用rust_i18n::i18n!的 crate,UI 通过text(locale, key)facade 在使用点直接引用稳定的领域路径,不内嵌翻译文本。
Rust 侧配套实现可在 crates/bongocat-i18n/src/lib.rs 中看到:text带缓存查找、format_text负责%{name}插值、platform_text支持按平台后缀(如..status_icon.label.macos)覆盖文案。而 crates/bongocat-i18n/src/tests/mod.rs 中的测试基建(LOCALES常量、messages/messages_on_disk双路展平、Catalog::resolves解析模拟)则保证语言清单只在一处枚举,新增语言不会让某条比较测试悄悄少覆盖一种语言。
例外与边界
规范同时划清了三条边界:
- 文档、源码注释、
CHANGELOG、memory 记录属于自然语言,不受本规范约束。但引用 UI 文案时照抄 catalog 里的写法,否则文档与界面会不一致。 - 代码里的
..、...不属于文案——Rust 语法、路径、范围表达式(如1..3、0..len)不受影响。 - 更新日志的书写是另一套独立规范:docs/changelog-conventions.md 只约束
CHANGELOG.md与CHANGELOG.zh-CN.md的分段标题(emoji 词表),与本规范互不覆盖——文档本身也通过交叉引用明确了两者的边界。
新增 / 修改文案的完整检查清单
结合规范、ADR-0012 与校验脚本,一次合规的文案变更应当按以下顺序完成:
- 定位领域路径:在
crates/bongocat-i18n/locales/下 6 份 JSON 的同一领域路径(如models.import.step)同时新增或修改 key——不允许只改一侧; - 省略号写单个
…(U+2026):任何进行时、等待态的文案收尾统一用单个字符,禁止……与...; - 占位符对齐:若文案需要插值,使用
%{name}语法并确保 6 份 catalog 的占位符集合与en-US完全一致; - 本地跑校验:
python3 tools/validate-locales.py,确认输出validated 6 locale(s), N key(s) each,无 error; - 跑 crate 测试:
cargo test -p bongocat-i18n,覆盖 crates/bongocat-i18n/src/tests/ 下的 catalog 一致性、覆盖率、格式化与平台覆盖四组用例。
若需要新增一种语言,则是一次"数据加注册"的改动(详见 docs/adr/0012-json-localization.md):写一份与en-US同 key、同占位符的 JSON,然后在build.rs的CATALOGS、语言枚举及其code/catalog_locale/from_system_locale/resolve、settings_language_display_name、以及 tools/validate-locales.py 的EXPECTED_LOCALES与 crates/bongocat-i18n/src/tests/mod.rs 的LOCALES中各加一项。语言下拉中每种语言用自身文字书写(endonym),例如Português而不是Português (Brasil)。
总结:BongoCat 的本地化文案规范通过"单个 U+2026"这一条与语言无关的硬性约定,加上validate-locales.py的机械门禁和 ADR-0012 的 key/占位符约束,把多语言 UI 文案的书写质量从"靠 review 记忆"提升为"靠校验脚本保证"。这套做法对任何维护多语言 catalog 的项目都有直接借鉴价值:凡是多语言共用的字符串,排版习惯必须取所有语言的公约数,并用自动化校验锁定它。
- 桌面应用
【免费下载链接】BongoCat
🐱 BongoCat — A cross-platform interactive desktop pet that brings fun to your desktop!
相关推荐
三步存下在线视频与音频:猫抓资源嗅探完整指南
三步存下在线视频与音频:猫抓资源嗅探完整指南 在某个页面想回看一段课程视频却找不到下载按钮时,资源嗅探扩展 cat catch(猫抓)会把页面里的视频、音频文件
音视频Easydict 编码规范指南:跨语言代码质量、Swift 实践与本地化约束全解析
Easydict 编码规范指南:跨语言代码质量、Swift 实践与本地化约束全解析 本篇技术指南围绕 Easydict(开源 macOS 词典翻译应用)仓库中的
桌面应用AI 应用gitsigns.nvim 提交信息规范与 commitlint 自动化:从格式约定到本地校验
gitsigns.nvim 提交信息规范与 commitlint 自动化:从格式约定到本地校验 本篇指南围绕 gitsigns.nvim 仓库的 提交信息规范文
开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考