16.3. gettext — 翻译消息

未匹配的标注

目的:用于国际化的消息目录API。

gettext模块提供了与GNU gettext 库兼容的纯 Python 实现,用于消息翻译和目录管理。 Python 源分发版提供的工具使您能够从一组源文件中提取消息,构建包含翻译的消息目录,并使用该消息目录在运行时为用户显示适当的消息。

消息目录可用于为程序提供国际化的界面,以适合用户的语言显示消息。它们还可以用于其他消息自定义,包括为不同包装程序或合作伙伴“设置”界面外观。

注意

尽管标准库文档说 Python 随附了所有必需的工具,但即使使用适当的命令行选项,pygettext.py也无法提取包装在ngettext调用中的消息。这些示例改用GNU gettext工具集中的xgettext

翻译工作流程概述

设置和使用翻译的过程包括五个步骤。

  1. 在源代码中标识并标记包含要翻译的消息的文字字符串。

    首先确定程序源中需要翻译的消息,并标记文字字符串,以便提取程序可以找到它们。

  1. 提取消息。

    在源中识别出可翻译字符串后,使用xgettext提取它们并创建一个.pot文件或翻译模板。模板是一个文本文件,其中包含所有已标识字符串的副本以及其翻译的占位符。

  1. 翻译消息。

    将.pot文件的副本提供给翻译器,将扩展名更改为.po.po文件是可编辑的源文件,用作编译步骤的输入。翻译人员应更新文件中的标题文本,并提供所有字符串的翻译。

  1. “翻译”来自翻译的消息目录。

    转换器将已完成的.po文件发送回时,请使用msgfmt将文本文件编译为二进制目录格式。运行时目录查找代码使用二进制格式。

  1. *在运行时加载并激活适当的消息目录。

    最后一步是向应用程序添加几行,以配置和加载消息目录并安装翻译功能。有两种方法可以实现此目的,同时需要进行权衡取舍。

本节的其余部分将从需要的代码修改开始,对这些步骤进行更详细的研究。

从源代码创建消息目录

gettext的工作方式是在翻译数据库中查找文字字符串,然后提取适当的翻译字符串。通常的模式是将适当的查找函数绑定到名称“ _”(单个下划线字符),以使代码不会因调用多个具有较长名称的函数而混乱。

消息提取程序xgettext查找在对目录查找功能的调用中嵌入的消息。它了解不同的源语言,并为每种语言使用适当的解析器。如果查找功能是别名或添加了其他功能,请在提取消息时给xgettext名称以考虑其他符号。

该脚本只有一条消息可供翻译。

gettext_example.py

import gettext

# 设置消息目录访问
t = gettext.translation(
    'example_domain', 'locale',
    fallback=True,
)
_ = t.gettext

print(_('This message is in the script.'))

文本"This message is in the script."是要从目录中替换的消息。启用了回退模式,因此,如果在没有消息目录的情况下运行脚本,则会打印内联消息。

$ python3 gettext_example.py

This message is in the script.

下一步是使用pygettext.pyxgettext提取消息并创建.pot文件。

$ xgettext -o example.pot gettext_example.py

产生的输出文件包含以下内容。

example.pot

#一些描述性标题。
#版权所有(C)套餐的版权持有人
#此文件与PACKAGE软件包使用相同的许可证分发。
#第一作者<EMAIL @ ADDRESS>,年份。
#
#,模糊
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION."
"Report-Msgid-Bugs-To: ."
"POT-Creation-Date: 2018-03-18 16:20-0400."
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE."
"Last-Translator: FULL NAME <EMAIL@ADDRESS>."
"Language-Team: LANGUAGE <LL@li.org>."
"Language: ."
"MIME-Version: 1.0."
"Content-Type: text/plain; charset=CHARSET."
"Content-Transfer-Encoding: 8bit."

#: gettext_example.py:19
msgid "This message is in the script."
msgstr ""

消息目录安装在按语言组织的目录中。域由应用程序或库提供,通常是唯一的值,例如应用程序名称。在这种情况下,gettext_example.py中的域是example_domain。语言值由用户的环境在运行时通过环境变量LANGUAGELC_ALLLC_MESSAGESLANG中的一个来提供,具体取决于其配置和平台。这些示例均以设置为en_US的语言运行。

现在已经准备好模板,下一步是创建所需的目录结构,并将模板复制到正确的位置。这些示例的PyMOTW源树中的locale目录将用作消息目录目录的根目录,但是通常最好使用系统范围可访问的目录,以便所有用户都可以访问消息目录。目录输入源的完整路径为$ localedir / $ language / LC_MESSAGES / $ domain.po,实际目录的文件扩展名为.mo

通过将example.pot复制到locale / en_US / LC_MESSAGES / example.po并进行编辑以更改标题中的值并设置备用消息来创建目录。结果如下所示。

locale/en_US/LC_MESSAGES/example.po

# 来自gettext_example.py的消息。
#版权所有(C)2009 Doug Hellmann
#Doug Hellmann <doug@doughellmann.com>,2016年。
#
msgid ""
msgstr ""
"Project-Id-Version: PyMOTW-3."
"Report-Msgid-Bugs-To: Doug Hellmann <doug@doughellmann.com>."
"POT-Creation-Date: 2016-01-24 13:04-0500."
"PO-Revision-Date: 2016-01-24 13:04-0500."
"Last-Translator: Doug Hellmann <doug@doughellmann.com>."
"Language-Team: US English <doug@doughellmann.com>."
"MIME-Version: 1.0."
"Content-Type: text/plain; charset=UTF-8."
"Content-Transfer-Encoding: 8bit."

#: gettext_example.py:16
msgid "This message is in the script."
msgstr "This message is in the en_US catalog."

该目录是使用msgformat.po文件构建的。

$ cd locale/en_US/LC_MESSAGES; msgfmt -o example.mo example.po

gettext_example.py中的域是example_domain,但文件名为example.pot。要使gettext找到正确的翻译文件,名称必须匹配。

gettext_example_corrected.py

t = gettext.translation(
    'example', 'locale',
    fallback=True,
)

现在,当运行脚本时,将打印目录中的消息,而不是内联字符串。

$ python3 gettext_example_corrected.py

This message is in the en_US catalog.

在运行时查找消息目录

如前所述,包含消息目录的 locale目录 是根据语言来组织的,目录具有以程序的 domain 命名的目录。不同的操作系统定义了自己的默认值,但是gettext并不知道所有这些默认值。它使用默认的语言环境目录sys.prefix +'/ share / locale',但是在大多数情况下,始终显式给出localedir值比依赖于此更安全默认有效。 find()函数负责在运行时查找适当的消息目录。

gettext_find.py

import gettext

catalogs = gettext.find('example', 'locale', all=True)
print('Catalogs:', catalogs)

路径的语言部分来自可用于配置本地化功能的几个环境变量之一(LANGUAGELC_ALLLC_MESSAGESLANG)。使用发现要设置的第一个变量。通过使用冒号()分隔值,可以选择多种语言。要查看其工作原理,请使用第二个消息目录进行一些实验。

$ cd locale/en_CA/LC_MESSAGES; msgfmt -o example.mo example.po
$ cd ../../..
$ python3 gettext_find.py

Catalogs: ['locale/en_US/LC_MESSAGES/example.mo']

$ LANGUAGE=en_CA python3 gettext_find.py

Catalogs: ['locale/en_CA/LC_MESSAGES/example.mo']

$ LANGUAGE=en_CA:en_US python3 gettext_find.py

Catalogs: ['locale/en_CA/LC_MESSAGES/example.mo',
'locale/en_US/LC_MESSAGES/example.mo']

$ LANGUAGE=en_US:en_CA python3 gettext_find.py

Catalogs: ['locale/en_US/LC_MESSAGES/example.mo',
'locale/en_CA/LC_MESSAGES/example.mo']

尽管find()显示了目录的完整列表,但实际上仅加载序列中的第一个用于消息查找。

$ python3 gettext_example_corrected.py

This message is in the en_US catalog.

$ LANGUAGE=en_CA python3 gettext_example_corrected.py

This message is in the en_CA catalog.

$ LANGUAGE=en_CA:en_US python3 gettext_example_corrected.py

This message is in the en_CA catalog.

$ LANGUAGE=en_US:en_CA python3 gettext_example_corrected.py

This message is in the en_US catalog.

多个值

尽管简单的消息替换将满足大多数翻译需求,但gettext会处理复数形式作为特殊情况。根据语言的不同,消息的单数形式和复数形式之间的差异可能仅因单个单词的结尾而有所不同,或者整个句子结构可能有所不同。取决于复数的水平,也可以有不同的形式。为了简化复数的管理(在某些情况下可能),有一组单独的函数用于请求消息的复数形式。

gettext_plural.py

from gettext import translation
import sys

t = translation('plural', 'locale', fallback=False)
num = int(sys.argv[1])
msg = t.ngettext('{num} means singular.',
                 '{num} means plural.',
                 num)

# 仍然需要自己将值添加到消息中。
print(msg.format(num=num))

使用ngettext()访问消息的复数替换。参数是要翻译的消息和项目计数。

$ xgettext -L Python -o plural.pot gettext_plural.py

由于存在要翻译的其他形式,因此替换形式在数组中列出。使用数组可以翻译具有多种复数形式的语言(例如,波兰语的不同形式表示相对数量)。

plural.pot

#一些描述性标题。
#版权所有(C)套餐的版权持有人
#此文件与PACKAGE软件包使用相同的许可证分发。
#第一作者<EMAIL @ ADDRESS>,年份。
#
#,模糊
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION."
"Report-Msgid-Bugs-To: ."
"POT-Creation-Date: 2018-03-18 16:20-0400."
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE."
"Last-Translator: FULL NAME <EMAIL@ADDRESS>."
"Language-Team: LANGUAGE <LL@li.org>."
"Language: ."
"MIME-Version: 1.0."
"Content-Type: text/plain; charset=CHARSET."
"Content-Transfer-Encoding: 8bit."
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;."

#: gettext_plural.py:15
#, python-brace-format
msgid "{num} means singular."
msgid_plural "{num} means plural."
msgstr[0] ""
msgstr[1] ""

除了填写翻译字符串外,还需要告知库复数的形成方式,以便知道如何为任何给定的计数值索引到数组中。 行“复数形式:nplurals = INTEGER;“ plural = EXPRESSION;”。包含两个要手动替换的值。 nplurals是一个整数,指示数组的大小(使用的翻译数),plural是一种C语言表达式,用于在查找时将传入数量转换为数组中的索引译文。文字字符串n被传递给ungettext()的数量替换。

例如,英语包括两种复数形式。数量0被视为复数(“ 0个香蕉”)。 复数形式条目是:

Plural-Forms: nplurals=2; plural=n != 1;

然后,单数转换将在位置0处进行,而复数转换将在位置1处进行。

locale/en_US/LC_MESSAGES/plural.po

#来自gettext_plural.py的消息
#版权所有(C)2009 Doug Hellmann
#此文件在同一许可证下分发
#作为PyMOTW软件包。
#Doug Hellmann <doug@doughellmann.com>,2016年。
#
#,模糊
msgid ""
msgstr ""
"Project-Id-Version: PyMOTW-3."
"Report-Msgid-Bugs-To: Doug Hellmann <doug@doughellmann.com>."
"POT-Creation-Date: 2016-01-24 13:04-0500."
"PO-Revision-Date: 2016-01-24 13:04-0500."
"Last-Translator: Doug Hellmann <doug@doughellmann.com>."
"Language-Team: en_US <doug@doughellmann.com>."
"MIME-Version: 1.0."
"Content-Type: text/plain; charset=UTF-8."
"Content-Transfer-Encoding: 8bit."
"Plural-Forms: nplurals=2; plural=n != 1;"

#: gettext_plural.py:15
#, python-format
msgid "{num} means singular."
msgid_plural "{num} means plural."
msgstr[0] "In en_US, {num} is singular."
msgstr[1] "In en_US, {num} is plural."

在目录编译后运行测试脚本几次将演示如何将不同的N值转换为转换字符串的索引。

$ cd locale/en_US/LC_MESSAGES/; msgfmt -o plural.mo plural.po
$ cd ../../..
$ python3 gettext_plural.py 0

In en_US, 0 is plural.

$ python3 gettext_plural.py 1

In en_US, 1 is singular.

$ python3 gettext_plural.py 2

In en_US, 2 is plural.

应用程序与模块本地化

转换工作的范围定义了gettext的安装方式以及与代码体一起使用的方式。

应用程序本地化

对于应用程序范围的翻译,作者可以使用__ builtins __命名空间在全局范围内安装ngettext()之类的功能,因为它们可以控制应用程序的顶层码。

gettext_app_builtin.py

import gettext

gettext.install(
    'example',
    'locale',
    names=['ngettext'],
)

print(_('This message is in the script.'))

install()函数将gettext()绑定到__ builtins __命名空间中的名称_()。它还添加了ngettext()名称中列出的其他功能。

模块本地化

对于库或单个模块,修改__ builtins __并不是一个好主意,因为它可能会导致与应用程序全局值冲突。相反,请在模块顶部手动导入或重新绑定翻译功能的名称。

gettext_module_global.py

import gettext

t = gettext.translation(
    'example',
    'locale',
    fallback=False,
)
_ = t.gettext
ngettext = t.ngettext

print(_('This message is in the script.'))

切换翻译

较早的示例在程序执行期间都使用单个转换。某些情况,尤其是Web应用程序,需要在不同时间使用不同的消息目录,而无需退出并重置环境。在这种情况下,gettext中提供的基于类的API将更加方便。 API调用本质上与本节中描述的全局调用相同,但是消息目录对象是公开的,可以直接操作,因此可以使用多个目录。

另请参见

-用于gettext的标准库文档
-[locale](pymotw.com/3/locale/index.html#mod...“ locale:POSIX文化本地化API”)–其他本地化工具。
-GNU gettext–此模块的消息目录格式,API等均基于GNU的原始gettext包。目录文件格式是兼容的,并且命令行脚本具有相似的选项(如果不相同)。 GNU gettext手册对文件格式进行了详细说明,并描述了使用它们的工具的GNU版本。
-复数形式–处理多种语言的单词和句子的复数形式。
-Python的国际化– Martin vonLöwis撰写的有关Python应用程序国际化技术的论文。
-Django国际化–关于使用gettext的另一个很好的信息来源,包括实际示例。

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
上一篇 下一篇
Summer
贡献者:2
讨论数量: 0
发起讨论 只看当前版本


暂无话题~