Git 源码本地化(l10n)全流程指南:从 po/XX.po 翻译到核心协作规范
发布时间:2026/9/5 16:18:33 作者:尧图编辑部 阅读量:1,286
全流程指南:从 po/XX.po 翻译到核心协作规范)
Git 源码本地化l10n全流程指南从 po/XX.po 翻译到核心协作规范【免费下载链接】gitGit Source Code Mirror - This is a publish-only repository but pull requests can be turned into patches to the mailing list via GitGitGadget (https://gitgitgadget.github.io/). Please follow Documentation/SubmittingPatches procedure for any of your improvements.项目地址: https://gitcode.com/GitHub_Trending/gi/git本篇技术指南以 Git 源码树中 po/README.md 为核心系统讲解 Git 核心本地化l10n的完整工作流语言代码规范、PO/POT 文件的动态生成与更新、fuzzy 翻译处理、git 过滤属性clean filter提交规范以及 C/Shell/Perl 三种语言中标记可翻译字符串的接口。读完本文你可以独立完成一份po/XX.po翻译文件的初始化、更新、校验与提交准备并理解上游如何从源码中提取、合并与编译本地化消息。核心概念语言代码与语言团队po/目录保存 Git 核心的翻译文件。本指南中用XX作为语言代码占位符如po/XX.po指代某个具体语言的翻译文件。需要注意语言代码并不总是两个字母而是有两种形式llISO 639 双字母语言代码如de德语ll_CCISO 639 语言代码 ISO 3166 国家/地区代码如zh_CN简体中文。当前仓库中实际收录的翻译文件可在 po/ 目录下看到例如 po/zh_CN.po、po/de.po、po/pt_PT.po 等其命名即遵循上述ll或ll_CC规范。为已有语言贡献翻译作为语言 XX 的贡献者第一步是检查 po/TEAMS 文件确认你的语言是否已存在专门dedicated的翻译仓库。如果存在fork 该专门仓库并开始工作。po/TEAMS按语言字段字母序维护各语言团队的仓库与负责人信息例如德语团队维护在专门仓库中、简体中文zh_CN与繁体中文zh_TW各有独立的 leader 与成员列表。另外需要留意如果你使用的 Git 发行版如 Ubuntu 等发行版自带版本的翻译与 Git 官方版本差异很大通常是因为这些发行版有自己的 l10n 工作流——错误的翻译应当通过发行版自己的工作流报告与修复而不是提交到上游。创建新的语言翻译如果你是语言 XX 的首位贡献者fork 本仓库准备并或更新翻译文件po/XX.po请 l10n 协调者coordinator从你的分支拉取pull。如果同一语言有多位贡献者请先在彼此间协调并推举一名团队负责人使 l10n 协调者只需针对每个语言与一个人对接。翻译流程的数据流整个本地化协作的数据流如下图所示原文档 po/README.md 中的示意图------------------- ------------------ | Git source code | ----(2)--- | L10n coordinator | | repository | ---(5)---- | repository | ------------------- ------------------ | | ^ (1) (3) (4) V v | ---------------------------------- | Language Team XX | ----------------------------------各步骤含义可翻译字符串在源码中被标记见后文“标记可翻译字符串”一节语言团队可以随时开始翻译迭代即使 l10n 窗口尚未开启从源码 master 分支拉取步骤 1运行make po-update PO_FILEpo/XX.po更新消息文件翻译po/XX.poL10n 协调者从源码拉取并宣布 l10n 窗口开启步骤 2语言团队从 l10n 协调者仓库拉取针对协调者的树开始新一轮翻译迭代步骤 3运行git pull --rebase从协调者处拉取运行make po-update PO_FILEpo/XX.po更新消息文件翻译po/XX.po用git rebase -i压缩琐碎的 l10n 提交语言团队向 l10n 协调者发送 pull request步骤 4协调者检查并合并随后协调者请求上游源码仓库拉取结果步骤 5。动态生成的 POT 模板文件POT 文件是 l10n 贡献者创建或更新翻译文件的模板。历史上曾存在由 l10n 协调者生成并纳入版本控制的po/git.pot文件但该文件已从代码树中移除如今两个 POT 文件都按需动态生成po/git.pot完整消息模板包含 5000 多条消息供 l10n 贡献者为各语言准备翻译。贡献者使用它但不应修改它。手动生成命令make po/git.potpo/git-core.pot核心翻译core translation模板。核心翻译是为一个新语言完成翻译所需的“最小工作量集合”——完整模板有 5000 多条消息对新语言贡献者而言是全量翻译并不轻松因此上游提供这一最小集合作为起点。手动生成命令make po/git-core.pot从构建系统看这两个目标的实现位于顶层 Makefilepo/git.pot目标Makefile以.build/pot/git.header与所有已本地化文件的中间 PO$(LOCALIZED_ALL_GEN_PO)为依赖通过$(MSGCAT)合并生成po-update目标Makefile依赖po/git.pot先通过check_po_file_envvar宏校验PO_FILE必须匹配po/%.po模式若文件不存在则提示改用make po-init随后执行$(MSGMERGE) $(MSGMERGE_FLAGS) $(PO_FILE) po/git.potpo/git-core.pot目标Makefile仅依赖“核心”消息集合LOCALIZED_C_CORE_GEN_PO即注释中所说“用翻译全部文件来界定 core 只是粗糙的启发式”的产物此外还有一个check-pot目标Makefile用于强制重建全部中间 POT 产物常用来发现遗漏的新字符串。初始化一个新的 XX.po 文件由语言团队执行。如果你的语言尚没有po/XX.po第一次添加翻译的命令是make po-init PO_FILEpo/XX.po其中 XX 为 locale如de、is、pt_BR、zh_CN等。从 Makefile 可以看到po-init目标的实际行为它依赖po/git-core.pot若目标文件已存在则报错退出error: $(PO_FILE) exists already否则调用msginit --inputpo/git-core.pot --output$(PO_FILE)生成新文件。因此新生成的po/XX.po基于核心模板只包含最小消息集合是新手语言贡献的良好起点。完成测试见后文后提交结果并请 l10n 协调者拉取。更新一个 XX.po 文件由语言团队执行。分两种情况如果只是修改已有XX.po中的翻译字符串以改进译文直接编辑文件即可如果要把上游源码中新增的可翻译字符串传播到po/XX.po运行make po-update PO_FILEpo/XX.po该命令会依次完成调用make po/git.pot生成新的完整模板调用msgmerge --add-location --backupoff -U po/XX.po po/git.pot更新你的po/XX.po其中--add-location选项会写入位置注释行如#: builtin/commit.c:123帮助翻译工具快速定位翻译上下文。这与 Makefile 中po-update目标的实现一一对应$(MSGMERGE) $(MSGMERGE_FLAGS) $(PO_FILE) po/git.pot其中MSGMERGE_FLAGS即--add-location --backupoff -U这类参数。Fuzzy模糊翻译Fuzzy 翻译是指被注释#, fuzzy标记的译文它提示你因为msgid已改变该翻译已过期。使用msgfmt编译时fuzzy 翻译会被忽略。fuzzy 标记可以手工打上但大多数情况下是在运行msgmerge更新XX.po时自动标记的。修正相应翻译后必须去掉注释中的fuzzy标记否则该条目在编译时仍会被跳过。测试你的修改语言团队在创建或更新XX.po之后执行。提交前回到顶层目录执行make在具备 GNU gettext 的系统上即 Solaris 之外这会用msgfmt --check编译你修改过的 PO 文件。--check选项会标出许多常见错误例如缺失的 printf 格式串、翻译消息与原文在首尾换行符上是否一致出现偏差等。L10n 协调者还会用辅助程序git-po-helper检查你的贡献git-po-helper check-po po/XX.po git-po-helper check-commits rev-list-opts提交前的 PO 文件整理Git clean filter翻译测试完成后建议提交一个不带位置信息location-less的po/XX.po文件以节省仓库空间并让审阅补丁更友好。第一步检查你的po/XX.po文件配置了哪个 filtergit check-attr filter po/XX.pofilter 配置定义在 po/.gitattributes 中。该文件po/.gitattributes的实际规则是默认对所有*.po应用gettext-no-location提交时同时剥离文件名与行号#: main.c:123整行消失少数维护较少的历史文件el.po、is.po、it.po、ko.po、pl.po、pt_PT.po被显式禁用 filter-filter因为它们仍保留位置注释禁用可避免索引与工作树不一致ca.po、id.po、zh_CN.po、zh_TW.po使用gettext-no-line-number保留文件名、仅去掉行号#: main.c:123变为#: main.c需要 gettext 0.20 及以上。第二步为你的 filter 配置驱动driver。多数语言使用gettext-no-location剥离文件名与行号git config --global filter.gettext-no-location.clean \ msgcat --no-location -部分 PO 文件使用gettext-no-line-number保留文件名、剥离行号。它要求 gettext 0.20 或更高版本唯一收益是当.po文件未通过make po-update从 POT 更新时仍可从位置注释定位源文件git config --global filter.gettext-no-line-number.clean \ msgcat --add-locationfile -设置完成即可以请求 l10n 协调者从你的分支拉取。标记可翻译字符串核心开发者由核心开发者执行。字符串在被翻译之前必须先被标记为可翻译。Git 使用一套封装系统 gettext 库的国际化接口因此 GNU gettext 文档中的大部分建议都适用GNU 系统上终端运行info gettext。通用建议不要标记所有字符串只有会被人直接阅读的字符串porcelain 接口才应翻译。Git 的 plumbing 工具输出主要供程序消费一旦在非 C locale 下翻译会破坏脚本。plumbing 字符串不应翻译因为它们是 Git API 的一部分调整字符串使其易于翻译info (gettext)Preparing Strings中的大部分建议在此适用引用数量number of items的字符串可能需要拆分为单数/复数形式见下文 C 部分Q_()的示例对不清晰或有歧义的内容可用TRANSLATORS注释告知译者如何处理。这些注释会被xgettext(1)提取并写入po/*.po文件。例如 git-am.sh 中的 shell 示例# TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a] # in your translation. The program will only accept English # input at this point. gettext Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all 或 builtin/revert.c 中的 C 示例/* TRANSLATORS: %s will be revert or cherry-pick */ die(_(%s: Unable to write new index file), action_name(opts));Git 为 C、Shell 与 Perl 三类程序提供封装接口。C 语言接口在文件顶部包含builtin.h它会引入gettext.h该头文件定义 gettext 接口若需直接使用gettext.h请先与邮件列表确认。C 接口是标准 GNU gettext 接口的一个子集当前导出_()标记并翻译字符串例如printf(_(HEAD is now at %s), hex);Q_()标记并翻译复数字符串例如printf(Q_(%d commit, %d commits, number_of_commits));它只是ngettext()的封装。N_()作用于静态初始化中的“空操作”直通宏仅做标记。例如 builtin/reset.c 一类用法static const char *reset_type_names[] { N_(mixed), N_(soft), N_(hard), N_(merge), N_(keep), NULL };然后稍后die(_(%s reset is not allowed in a bare repository), _(reset_type_names[reset_type]));此时_()无法在静态期确定翻译串是什么但由于该字符串已用N_()标记消息目录中的查找仍能成功。从源码看N_的定义正是直通宏——gettext.h 中#define N_(msgid) msgid即“为翻译标记 msgid 但不翻译它”。Shell 接口Git 的 gettext shell 接口本质是gettext.sh的封装。在git-sh-setup之后立即导入. git-sh-setup . git-sh-i18n然后使用gettext或eval_gettext函数# 常量界面消息 gettext A message for the user; echo # 需要插值变量 detailsoh noes eval_gettext An error occurred: $details; echo此外还有针对以换行结尾消息的封装即上面代码可写成# 常量界面消息 gettextln A message for the user # 需要插值变量 detailsoh noes eval_gettextln An error occurred: $details这些封装在 git-sh-i18n.sh 中实现gettextln即gettext $1; echoeval_gettextln同理见 git-sh-i18n.sh。更多接口文档见 GNU info 页info (gettext)sh阅读 git-am.sh第一个被翻译的 shell 命令的实例也很有帮助git log --reverse -p --grepi18n git-am.shPerl 接口Git::I18N模块提供Locale::Messages功能的一个有限子集use Git::I18N; print __(Welcome to Git!\n); printf __(The following error occurred: %s\n), $error;模块位于 perl/Git/I18N.pm运行perldoc perl/Git/I18N.pm可查看更多文档。标记字符串的测试策略Git 的测试都在LANGC LC_ALLC下运行因此随着翻译的增加测试本身无需为本地化做任何调整——这也保证了测试基线不受 locale 影响。AI 辅助翻译与 git-po-helperpo/AGENTS.md 描述了 AI 编码助手辅助 Git 本地化的可选工作流更新模板与 PO 文件、翻译po/XX.po、评审翻译质量。这些工作流通常组合使用git-po-helper与 gettext 工具。官方立场是AI 助手是可选的其输出应视为草稿必须由熟悉 Git 与目标语言的贡献者评审。在向编码助手提示时请显式提及该文件例如“Translate po/XX.po with reference to po/AGENTS.md”把 XX 替换为你的语言代码。git-po-helper本身是面向 l10n 协调者与贡献者的辅助程序它自动化检查贡献是否符合项目约定PO 语法、提交信息、允许修改的路径等并可配合 AI 编码代理完成新条目翻译与翻译评审等任务。从 po/AGENTS.md 可看到它提供的具体能力git-po-helper msg-select按条目索引切分大 PO 文件--range 1-50、--head N、--tail N、--since N支持按状态过滤--translated、--untranslated、--fuzzy、--no-obsolete并可输出其内部定义的 GETTEXT JSON 格式--json便于 AI 批处理翻译git-po-helper compare以完整条目上下文展示 PO 变更不同于git diff支持--commit、--since、-r x..y等范围参数--msgid模式用于检测 msgid 是否被篡改无输出即一致git-po-helper msg-cat合并 PO/POT/GETTEXT JSON 输入重复msgid保留文件中首次出现者--unset-fuzzy可清除 fuzzy 标记常用于把翻译后的 JSON 转回 PO。翻译工作流Task 3的核心循环是提取待翻译条目po/l10n-pending.po→ 分批默认每批约 100 条→ 翻译 → 校验git-po-helper compare -q --msgid --assert-no-changes确认 msgid 未被改动、msgfmt --check校验格式→ 用msgcat --use-first合并回po/XX.po→ 循环直到无待翻译条目。评审工作流Task 4则要求只使用git-po-helper compare提取变更禁用git diff/git show因其会破坏 PO 上下文按批产出评分0–3与suggest_msgstr建议最终由git-po-helper agent-run review --report po汇总。其中值得译者借鉴的细节规则包括PO 首条头部条目msgid 的msgstr存放项目、语言、复数规则等元数据不得在翻译时修改文件头部的词表glossary注释块必须阅读并遵循且不得改动占位符必须原样保留仅在需要重排顺序时使用位置参数语法如%1$s、%3$.*2$s未重排时不得引入%n$语言特有的弯引号„、”、«»等绝不可转换为 ASCII 直引号否则 PO 解析器会把 U0022 当作字符串分隔符造成截断与msgfmt --check语法错误未翻译条目不能用grep ^msgstr 查找多行条目会误报应使用msgattrib --untranslated --no-obsolete po/XX.po。提交约定Conventionsl10n 贡献者必须遵守以下约定每个 l10n 提交的标题subject必须以l10n:前缀开头提交标题中不要使用非 ASCII 字符提交标题commit log 首行长度不超过 50 个字符其余行不超过 72 个字符为提交添加Signed-off-bytrailer与其他 Git 提交一致可用以下命令自动添加git commit -s创建提交前用msgfmt或以下命令检查语法git-po-helper check-po XX.po压缩琐碎提交保持历史清晰不要编辑po/目录之外的文件其他子系统git-gui、gitk与 Git 本身各有自己的工作流参见 Documentation/SubmittingPatches 了解如何向这些子系统提交补丁。为新语言贡献还须遵循额外约定按 ISO 639 / ISO 3166 规范初始化正确的XX.po文件名必须基于“核心翻译”Core translation完成最小翻译见前文“动态生成的 POT 文件”与“初始化”两节在 po/TEAMS 文件中按正确格式添加新条目并运行以下命令校验po/TEAMS语法git-po-helper team --check小结一条可复制的上手路径综合本文一个新语言贡献者可执行的最短路径为在 po/TEAMS 中确认无现成团队 →make po-init PO_FILEpo/XX.po基于核心模板初始化 → 逐批翻译并清理#, fuzzy标记 →make触发msgfmt --check与git-po-helper check-po po/XX.po双重校验 → 配置filter.gettext-no-location.clean得到无位置信息的提交 → 以l10n:前缀、git commit -s签名提交 → 向 l10n 协调者发起拉取请求。源码侧的字符串提取、C/Shell/Perl 标记接口与make po-update的 msgmerge 更新机制则保证了模板与翻译之间的持续同步。更多本地化背景如编译期消息目录的安装与运行时 locale 选择可进一步参阅 Documentation/i18n.adoc。【免费下载链接】gitGit Source Code Mirror - This is a publish-only repository but pull requests can be turned into patches to the mailing list via GitGitGadget (https://gitgitgadget.github.io/). Please follow Documentation/SubmittingPatches procedure for any of your improvements.项目地址: https://gitcode.com/GitHub_Trending/gi/git创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考