如果你参与过软件的国际化(I18n)和本地化工作,尤其是使用 GNU gettext 框架,那么你一定对 .po
文件(Portable Object 文件)不陌生。这些文件是翻译的载体,将原始的英文消息与各种目标语言的翻译关联起来,相关阅读GNU gettext国际化PO文件格式详解。
最初,翻译工作可能主要围绕使用 Emacs 的 PO mode 或其他 PO 编辑器进行手动编辑。但随着项目变大、翻译文件增多,手动处理会变得效率低下且容易出错。幸好,GNU gettext 工具集不仅仅包含 xgettext
(用于提取字符串模板,详细用法参考《xgettext 用法详解》)和 msgfmt
(用于生成二进制 MO 文件),它还提供了一整套强大的命令行工具,专门用于自动化和批量处理 PO 文件。
这些工具能帮助你合并、过滤、检查、更新和管理 PO 文件,让你的本地化流程更加顺畅。接下来,详细了解这些强大的命令行工具吧!
1. msgcat
: 合并与连接 PO 文件
想象一下,你有多个 PO 文件,也许来自不同的模块,或者你只是想将一些翻译片段合并起来。msgcat
就是你的不二之选。
它的基本作用是将多个 PO 文件串联起来,形成一个单独的输出文件。它还能智能地合并相同的消息条目。
常用选项:
-o file
或--output-file=file
:将结果写入指定文件,而不是标准输出。--sort-output
:按字母顺序对输出的消息条目进行排序。--sort-by-file
:按文件位置对输出的消息条目进行排序。请注意,使用此选项可能会使译者理解消息的上下文变得更加困难。
示例:合并 file1.po
和 file2.po
到 combined.po
:
msgcat -o combined.po file1.po file2.po
如果你想合并多个零散的 compendium 文件到一个文件 update.po
,可以这样做(虽然这个例子后面还涉及到 msgmerge
和 msgattrib
):
msgcat --use-first -o update.po compendium1.po compendium2.po file.po
然后可以结合 msgmerge
和 msgattrib
进行进一步处理。
2. msggrep
: 根据条件过滤消息
有时候你只关心 PO 文件中的一部分消息,比如只处理来自某个源文件的字符串,或者只查找包含特定文本的消息。msggrep
可以根据各种条件从 PO 文件中提取消息子集。
常用选项:
-o file
或--output-file=file
:将结果写入指定文件.--location string
:提取来自指定源文件或位置的消息.-N location
:类似于--location
.-t string
或--text string
:提取包含指定原始字符串 (msgid
) 的消息.
示例:提取来自源文件 src/getopt.c
的消息到 compendium.po
:
msggrep --location src/getopt.c -o compendium.po file.po
示例:提取来自源文件 gnulib-lib/error.c
和 gnulib-lib/getopt.c
的消息:
msggrep -N gnulib-lib/error.c -N gnulib-lib/getopt.c input.po
示例:提取原始字符串中包含“Please specify”的消息:
msggrep --text "Please specify" input.po
3. msgfilter
: 对翻译应用任意命令
这是一个非常强大的工具,它允许你对 PO 文件中的翻译文本(msgstr
部分)应用任何能够读取标准输入并写入标准输出的命令。这对于执行批量文本转换非常有用,比如调整字符编码或执行基于正则表达式的替换。
示例:将德语翻译中的“ß”替换为“ss”(适用于瑞士德语):
msgconv -t UTF-8 de.po | msgfilter sed -e 's/ß/ss/g'
这个例子首先使用 msgconv
将 de.po
转换为 UTF-8 编码,然后通过管道将其输入到 msgfilter
,msgfilter
再将每个翻译文本传递给 sed
命令进行替换。
示例:使用 recode-sr-latin
命令转换西里尔塞尔维亚语翻译:
msgfilter recode-sr-latin < sr.po
4. msguniq
: 移除重复的消息条目
在处理多个来源或多次合并的 PO 文件时,可能会出现同一个原始字符串 (msgid
) 重复出现多次的情况。msguniq
工具可以帮助你找到并合并这些重复的条目,只保留一个唯一的条目。
常用选项:
-o file
或--output-file=file
:将结果写入指定文件.-u
或--unique
:只打印那些只出现一次的消息条目(等同于--less-than=2
).-> number
或--more-than=number
:打印出现次数多于指定次数的消息(默认为 1).
示例:处理 input.po
并将去重后的结果输出到标准输出:
msguniq input.po
5. msgcomm
: 查找共有的消息
如果你想知道两个或多个 PO 文件之间有哪些共同的翻译消息,msgcomm
工具可以帮你实现。
6. msgcmp
: 比较 PO 文件
msgcmp
工具用于比较两个 PO 文件,通常用于检查翻译文件是否与其对应的模板文件 (.pot
) 同步。它可以报告哪些消息在翻译文件中缺失或过时。
7. msgattrib
: 根据属性过滤或修改消息
PO 文件中的每个消息条目都可以有一些属性,比如“模糊”(fuzzy)标记(表示该翻译可能需要复审)或是否已翻译。msgattrib
工具可以根据这些属性来过滤消息,或者修改这些属性。
常用选项:
--translated
:只保留已翻译的条目.--untranslated
:只保留未翻译的条目.--fuzzy
:只保留标记为“模糊”的条目.--no-fuzzy
:排除标记为“模糊”的条目.--obsolete
:只保留已过时的条目.--no-obsolete
:排除已过时的条目.--file file
:根据指定文件中的消息条目进行过滤.--set-fuzzy
:将匹配的消息标记为“模糊”.--clear-fuzzy
:移除匹配消息的“模糊”标记.
示例:从 file.po
中提取所有已翻译且未标记为模糊的条目:
msgattrib --translated --no-fuzzy file.po
示例:结合 msgmerge
更新文件后,移除过时的条目:
msgmerge update.po file.pot | msgattrib --no-obsolete > file.po
8. msgen
: 创建英文翻译目录
对于以英语为母语的项目维护者来说,有时需要一个“英文翻译”,其翻译内容与原始字符串完全相同。msgen
工具可以接受一个 PO 模板文件 (.pot
) 或一个 PO 文件作为输入,并创建一个新的 PO 文件,其中所有未翻译条目的 msgstr
都被填充为其对应的 msgid
。
注意:msginit --no-translator --locale=en
也能完成类似任务,但 msgen
更侧重于消息条目本身的处理。
示例:基于模板文件 package.pot
创建一个英文 PO 文件 en.po
:
msgen package.pot -o en.po
9. msgexec
: 对所有翻译执行命令
msgexec
工具允许你对 PO 文件中的每个翻译文本执行一个指定的命令。这个命令会接收翻译文本作为标准输入。这与 msgfilter
类似,但 msgexec
更像是对每个单独的翻译条目执行操作,而不是对整个文件流进行过滤。
示例:对 PO 文件中的每个翻译文本执行一个脚本 check_translation.sh
:
msgexec check_translation.sh input.po
10. libgettextpo
: 编写自己的 PO 处理程序
如果上述标准工具不能满足你的复杂需求,GNU gettext 还提供了一个 C 语言库 libgettextpo
。这个库提供了用于解析、操作和写入 PO 文件内存结构的函数,让你可以编写自己的定制化 PO 文件处理程序。
头文件是 <gettext-po.h>
。它提供了如 po_file_read
读取文件到内存结构、po_file_write
将内存结构写回文件、po_message_t
数据类型表示一个消息条目、以及访问和修改消息属性(如 msgid
和 msgstr
)的函数(如 po_message_msgid
, po_message_set_msgid
, po_message_msgstr
, po_message_set_msgstr
等)。这个库支持多线程安全地操作不同的数据对象。
总结
GNU gettext 工具集远不止提取和编译翻译文件。msgcat
, msggrep
, msgfilter
, msguniq
, msgcomm
, msgcmp
, msgattrib
, msgen
, msgexec
这些命令行工具,以及 libgettextpo
库,共同构成了一个强大的 PO 文件处理框架。它们能显著提高本地化工作的效率,减少手动错误,让你能更专注于翻译本身的质量。
下次你需要处理大量的 PO 文件时,不妨试试这些工具,它们可能会成为你的得力助手!
希望这篇博客文章对你有所帮助!如果你有任何问题或使用经验,欢迎在下方评论区交流!