☰
BongoCat 本地化文案书写规范:跨语言 JSON catalog 的省略号约定与自动化门禁
2026/10/2 6:57:27 网站建设 项目流程
  • 桌面应用

【免费下载链接】BongoCat

🐱 BongoCat — A cross-platform interactive desktop pet that brings fun to your desktop!

项目地址:https://gitcode.com/gh_mirrors/bong/BongoCat
点击查看免费下载

本文解读 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❌ 拉丁写法,不采用

规范的三个理由值得逐条展开:

  1. 同一个 key 由所有语言共用,写法必须与语言无关。……只在中文排版里成立、...只在拉丁文里成立,任何一种都会让另一种语言的用户看到"错误"的省略号。
  2. …是单个字符、占一个码位,中英文字体都能正确渲染,宽度稳定;……是两个字符,在非中文字体下还可能被拆成两个独立字形。
  3. 文案里没有需要区分"三个点"和"六个点"的语义,长度差异纯属排版习惯,不是信息差异。

以 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 与校验脚本,一次合规的文案变更应当按以下顺序完成:

  1. 定位领域路径:在crates/bongocat-i18n/locales/下 6 份 JSON 的同一领域路径(如models.import.step)同时新增或修改 key——不允许只改一侧;
  2. 省略号写单个…(U+2026):任何进行时、等待态的文案收尾统一用单个字符,禁止……与...;
  3. 占位符对齐:若文案需要插值,使用%{name}语法并确保 6 份 catalog 的占位符集合与en-US完全一致;
  4. 本地跑校验:python3 tools/validate-locales.py,确认输出validated 6 locale(s), N key(s) each,无 error;
  5. 跑 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!

项目地址:https://gitcode.com/gh_mirrors/bong/BongoCat
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询