给上游项目补一套简体中文翻译:从 45 个 po 到上游 PR

给一个从没做过简体中文的开源项目补翻译,看起来是「把英文换成中文」的体力活。实际做下来,难的是术语一致性、格式校验和 PR 的可审查性——764 条词条,翻错一条可能只是一个词,但术语不统一会让整个配置界面读起来像三个人分别翻的。

这篇以 Wayfire(基于 wlroots 的 Wayland 合成器)为例,记一遍完整流程。它原本只有罗马尼亚语一种翻译,45 个 .po、764 条词条(去重 721 条)。

一、先摸清规模与构建方式

动手前先回答三个问题:

  1. 词条总量与分布:find . -name '*.po' | wc -l、msgattrib --translated 或直接统计 msgid。Wayfire 是 45 个文件、764 条,分散在 core 与每个插件里;
  2. 翻译怎么被构建:看 locale/meson.build。多数项目会自动 find 所有语言目录,新增 zh_CN 不需要改构建脚本——这一步确认了能省掉一个 PR 改动;
  3. 上游有没有术语表或风格指南:没有就自己定一份,写进 PR 描述里。

二、术语表要先定,而且要看代码语义

同一个英文词在不同上下文里的中文可能不同。Wayfire 里先定了几组:

原文 译法 说明
view 窗口 不是「视图」
output 输出 显示器/输出设备
workspace 工作区
activator 激活器 热区/手势触发
Scale 插件 窗口概览 与 Zoom 区分
Zoom 插件 桌面缩放 直译会和 Scale 撞名
Kawase / XWayland / GTK Shell 保留原文 专有名词

Scale 与 Zoom 是典型陷阱:两个插件都直译成「缩放」,配置界面里会出现两个同名条目,用户根本分不清。凡是「功能名」而不是「普通名词」的,要按实际行为命名。

三、生成 po 文件:格式比内容更容易出错

最省事的做法是从模板批量生成,但生成脚本很容易在注释、msgid、msgstr 之间插入空行。msgfmt 仍然能过,但 diff 会变得很难看,上游 review 时会先扣格式分。

几个硬性要求:

  • 文件头不要写不实的生成器标注。模板里如果带 X-Generator: Poedit 3.7 而实际不是 Poedit 生成的,删掉——这是对工具和审查者的误导;
  • 每个文件的 Language:、Plural-Forms: 要正确;
  • msgid 一个字符都不能改,包括末尾空格与转义;
  • 按文件原顺序输出,别重新排序,否则 diff 全红。

生成完先做一次机械校验:

1
find locale/zh_CN -name '*.po' -exec msgfmt -c -o /dev/null {} \;

-c 会检查格式错误(占位符不匹配、msgstr 缺失、复数形式不对)。这一步过了,再谈翻译质量。

四、翻译阶段的三个判断

  1. 短词看上下文,不要按词典:Open 可能是「打开」也可能是「开放」,去 .c/.cpp 里看它出现在按钮还是状态里;
  2. 占位符原样保留:%s、%d、{0} 的顺序和数量都不能动。msgfmt -c 能抓出一部分,但顺序错位它不一定报;
  3. 不要翻译配置键、路径、命令:这些是运行时匹配的字符串,翻了就失效。这个坑在别处已经踩过一次(见「汉化把运行时匹配串一起翻了」那类问题),这里的原则是:只翻给人看的文案。

五、提交 PR:只放翻译文件

上游 PR 的审查成本要尽量低:

  • 一个 PR 只做一件事:45 个 .po、+3988 行,只包含翻译文件;顺手加的 AGENTS.md、构建脚本改动全部不提交;
  • 分支名明确:i18n/zh-cn;
  • PR 描述写清三件事:覆盖的语言、词条总数与完成率、术语表的几个关键决定(比如为什么 Scale 不译成「缩放」);
  • 提交信息用英文:locale: add Simplified Chinese translation;
  • 等 checks:如果仓库没有为该分支配置 CI,PR 页面会显示 no checks reported,这不是失败,但要在描述里说明本地已跑过 msgfmt -c。

六、翻译之外:顺手把打包也做了

这个项目当时还没有 deb/rpm 包,翻译合进去也只是源码。所以同一个分支里另外加了一个 packages.yml:deb 与 rpm 两个 job,把目标发行版仓库里没有的 wlroots 0.20.2、wf-config 0.12.0、wf-utils 一起打进包。

打包细节(debian:testing 基线、brp-compress 与 man page、gh release upload 的目录问题)在「跨平台打包与 CI」那篇里展开,这里只记一条:翻译 PR 和打包 PR 要分开,否则审查者要在 4000 行翻译里找构建脚本。

总结

给上游补翻译,交付物不是「一份中文」,而是一套能被维护的本地化:

  • 术语表先定,功能名按行为译,专有名词保留;
  • msgfmt -c 机械校验必须过,格式问题比错译更影响 review;
  • 只翻给人看的文案,运行时键、路径、命令一律不动;
  • PR 只放翻译文件,描述里写清术语决定;
  • 翻译和打包分成两个 PR。

最后一句:词条数不是工作量,术语一致性才是。764 条翻完可能两天,但把「窗口/视图」「概览/缩放」这些词统一,决定了这套翻译能不能被上游长期维护。