22.1. gettext — 多语言国际化服务 — Python 文档
22.1. 获取文本 — 多语言国际化服务
gettext 模块为 Python 模块和应用程序提供国际化 (I18N) 和本地化 (L10N) 服务。 它支持 GNU gettext
消息目录 API 和更高级别的基于类的 API,后者可能更适合 Python 文件。 下面描述的接口允许您用一种自然语言编写模块和应用程序消息,并提供用于在不同自然语言下运行的翻译消息目录。
还提供了一些有关本地化 Python 模块和应用程序的提示。
22.1.1. GNU 获取文本应用程序接口
gettext 模块定义了以下 API,它与 GNU gettext API 非常相似。 如果您使用此 API,您将在全局范围内影响整个应用程序的翻译。 如果您的应用程序是单语的,并且语言的选择取决于用户的语言环境,那么这通常就是您想要的。 如果您正在本地化 Python 模块,或者您的应用程序需要即时切换语言,您可能希望改用基于类的 API。
- gettext.bindtextdomain(domain[, localedir])
将 域 绑定到语言环境目录 localedir。 更具体地说,gettext 将使用路径(在 Unix 上)为给定域查找二进制
.mo
文件:localedir/language/LC_MESSAGES/domain.mo
,其中搜索 languages对于环境变量LANGUAGE
、LC_ALL
、LC_MESSAGES
和 [X227] ]LANG
分别。如果省略 localedir 或
None
,则返回 domain 的当前绑定。 1
- gettext.bind_textdomain_codeset(domain[, codeset])
将 domain 绑定到 codeset,更改由 gettext() 系列函数返回的字符串的编码。 如果省略 codeset,则返回当前绑定。
2.4 版中的新功能。
- gettext.textdomain([domain])
- 更改或查询当前全局域。 如果domain为
None
,则返回当前全局域,否则全局域设置为domain,返回。
- gettext.gettext(message)
- 根据当前的全局域、语言和区域设置目录返回 message 的本地化翻译。 该函数在本地命名空间中通常别名为
_()
(参见下面的示例)。
- gettext.lgettext(message)
等效于 gettext(),但如果没有其他编码被明确设置为 bind_textdomain_codeset(),则翻译以首选系统编码返回。
2.4 版中的新功能。
- gettext.dgettext(domain, message)
- 类似于 gettext(),但在指定的 域 中查找消息。
- gettext.ldgettext(domain, message)
等效于 dgettext(),但如果没有其他编码被明确设置为 bind_textdomain_codeset(),则翻译以首选系统编码返回。
2.4 版中的新功能。
- gettext.ngettext(singular, plural, n)
像 gettext(),但考虑复数形式。 如果找到翻译,将复数公式应用于 n,并返回结果消息(某些语言有两个以上的复数形式)。 如果没有找到翻译,如果 n 为 1,则返回 singular; 否则返回 复数 。
复数公式取自目录标题。 它是一个具有自由变量 n 的 C 或 Python 表达式; 表达式计算为目录中复数的索引。 有关
.po
文件中使用的精确语法和各种语言的公式,请参阅 GNU gettext 文档。2.3 版中的新功能。
- gettext.lngettext(singular, plural, n)
等效于 ngettext(),但如果没有其他编码被明确设置为 bind_textdomain_codeset(),则翻译以首选系统编码返回。
2.4 版中的新功能。
- gettext.dngettext(domain, singular, plural, n)
类似于 ngettext(),但在指定的 域 中查找消息。
2.3 版中的新功能。
- gettext.ldngettext(domain, singular, plural, n)
等效于 dngettext(),但如果没有其他编码被明确设置为 bind_textdomain_codeset(),则翻译以首选系统编码返回。
2.4 版中的新功能。
请注意,GNU gettext 还定义了一个 dcgettext()
方法,但这被认为没有用,因此目前尚未实现。
以下是此 API 的典型用法示例:
import gettext
gettext.bindtextdomain('myapplication', '/path/to/my/language/directory')
gettext.textdomain('myapplication')
_ = gettext.gettext
# ...
print _('This is a translatable string.')
22.1.2. 基于类的 API
gettext 模块的基于类的 API 为您提供了比 GNU gettext API 更大的灵活性和更大的便利性。 这是本地化 Python 应用程序和模块的推荐方法。 gettext
定义了一个“翻译”类,它实现了 GNU .mo
格式文件的解析,并具有返回标准 8 位字符串或 Unicode 字符串的方法。 这个“翻译”类的实例也可以作为函数 _()
安装在内置命名空间中。
- gettext.find(domain[, localedir[, languages[, all]]])
该函数实现了标准的
.mo
文件搜索算法。 它需要一个 domain,与 textdomain() 相同。 可选的 localedir 与 bindtextdomain() 中的一样 可选的 languages 是一个字符串列表,其中每个字符串都是一个语言代码。如果未给出 localedir,则使用默认的系统语言环境目录。 2 如果没有给出 languages,则搜索以下环境变量:
LANGUAGE
, [ X136X]、LC_MESSAGES
和LANG
。 第一个返回非空值的值用于 languages 变量。 环境变量应包含以冒号分隔的语言列表,它将在冒号上拆分以生成预期的语言代码字符串列表。find() 然后扩展和规范化语言,然后遍历它们,搜索由这些组件构建的现有文件:
localedir/language/LC_MESSAGES/domain.mo
find() 返回存在的第一个这样的文件名。 如果没有找到这样的文件,则返回
None
。 如果给出 all,它会返回所有文件名的列表,按照它们在语言列表或环境变量中出现的顺序。
- gettext.translation(domain[, localedir[, languages[, class_[, fallback[, codeset]]]]])
返回基于 domain、localedir 和 languages 的
Translations
实例,这些实例首先传递给 find() ] 获取相关.mo
文件路径的列表。 缓存具有相同.mo
文件名的实例。 实例化的实际类要么是 class_(如果提供),否则是GNUTranslations
。 类的构造函数必须采用单个文件对象参数。 如果提供,codeset 将更改用于编码翻译字符串的字符集。如果找到多个文件,则使用较晚的文件作为较早文件的后备。 为了允许设置回退,copy.copy() 用于从缓存中克隆每个翻译对象; 实际实例数据仍与缓存共享。
如果没有找到
.mo
文件,如果 fallback 为 false(这是默认值),此函数会引发IOError
,并返回一个 NullTranslations 实例,如果回退是真的。在 2.4 版更改: 添加了 codeset 参数。
- gettext.install(domain[, localedir[, unicode[, codeset[, names]]]])
这会在 Python 的内置命名空间中安装函数
_()
,基于传递给函数 的 domain、localedir 和 codeset翻译()。 unicode 标志被传递给生成的翻译对象的 install() 方法。对于 names 参数,请参阅翻译对象的 install() 方法的说明。
如下所示,您通常通过将它们包装在对
_()
函数的调用中来标记应用程序中的候选字符串,如下所示:print _('This string will be translated.')
为方便起见,您希望将
_()
函数安装在 Python 的内置命名空间中,以便在应用程序的所有模块中轻松访问它。在 2.4 版更改: 添加了 codeset 参数。
在 2.5 版更改: 添加了 names 参数。
22.1.2.1. 这空翻译班级
翻译类是真正实现将原始源文件消息字符串翻译成翻译消息字符串的类。 所有翻译类使用的基类是NullTranslations; 这提供了可用于编写自己的专用翻译类的基本接口。 这里是NullTranslations
的方法:
- class gettext.NullTranslations([fp])
接受一个可选的文件对象 fp,它被基类忽略。 初始化由派生类设置的“受保护”实例变量 _info 和 _charset,以及通过 add_fallback()[ 设置的 _fallback 184]。 然后,如果 fp 不是
None
,它会调用self._parse(fp)
。- _parse(fp)
在基类中没有操作,此方法获取文件对象 fp,并从文件中读取数据,初始化其消息目录。 如果您的消息目录文件格式不受支持,则应覆盖此方法以解析您的格式。
- add_fallback(fallback)
添加 fallback 作为当前翻译对象的回退对象。 如果翻译对象无法为给定消息提供翻译,则它应该咨询回退。
- gettext(message)
如果已设置回退,则将
gettext()
转发到回退。 否则,返回翻译后的消息。 在派生类中重写。
- lgettext(message)
如果已设置回退,则将
lgettext()
转发到回退。 否则,返回翻译后的消息。 在派生类中重写。2.4 版中的新功能。
- ugettext(message)
如果已设置回退,则将
ugettext()
转发到回退。 否则,将翻译后的消息作为 Unicode 字符串返回。 在派生类中重写。
- ngettext(singular, plural, n)
如果已设置回退,则将
ngettext()
转发到回退。 否则,返回翻译后的消息。 在派生类中重写。2.3 版中的新功能。
- lngettext(singular, plural, n)
如果已设置回退,则将
lngettext()
转发到回退。 否则,返回翻译后的消息。 在派生类中重写。2.4 版中的新功能。
- ungettext(singular, plural, n)
如果已设置回退,则将
ungettext()
转发到回退。 否则,将翻译后的消息作为 Unicode 字符串返回。 在派生类中重写。2.3 版中的新功能。
- info()
返回“受保护的”
_info
变量。
- charset()
返回“受保护的”
_charset
变量。
- output_charset()
返回“protected”
_output_charset
变量,该变量定义了用于返回已翻译消息的编码。2.4 版中的新功能。
- set_output_charset(charset)
更改“protected”
_output_charset
变量,该变量定义用于返回已翻译消息的编码。2.4 版中的新功能。
- install([unicode[, names]])
如果 unicode 标志为 false,则此方法将
self.gettext()
安装到内置命名空间中,并将其绑定到_
。 如果 unicode 为真,则绑定self.ugettext()
。 默认情况下,unicode 为 false。如果给出了 names 参数,它必须是一个序列,除了
_()
之外,还包含要安装在 builtins 命名空间中的函数的名称。 支持的名称有'gettext'
(根据 unicode 标志绑定到self.gettext()
或self.ugettext()
)、'ngettext'
(绑定到self.ngettext()
或self.ungettext()
根据 unicode 标志)、'lgettext'
和'lngettext'
。请注意,这只是使
_()
功能可用于您的应用程序的一种方式,尽管是最方便的方式。 因为它会影响整个应用程序的全局,特别是内置的命名空间,本地化的模块不应该安装_()
。 相反,他们应该使用此代码使_()
可用于他们的模块:import gettext t = gettext.translation('mymodule', ...) _ = t.gettext
这将
_()
仅放在模块的全局命名空间中,因此仅影响该模块内的调用。在 2.5 版更改: 添加了 names 参数。
22.1.2.2. 这GNUTranslations班级
gettext 模块提供了一个从 NullTranslations 派生的额外类:GNUTranslations
。 此类覆盖 _parse()
以启用以 big-endian 和 little-endian 格式读取 GNU gettext 格式 .mo
文件。 它还将消息 ID 和消息字符串强制转换为 Unicode。
GNUTranslations
从翻译目录中解析出可选的元数据。 GNU gettext 的约定是包含元数据作为空字符串的翻译。 此元数据位于 RFC 822 样式的 key: value
对中,并且应包含 Project-Id-Version
键。 如果找到键 Content-Type
,则使用 charset
属性初始化“受保护的”_charset
实例变量,如果没有找到,则默认为 None
。 如果指定了字符集编码,则从目录中读取的所有消息 ID 和消息字符串都将使用此编码转换为 Unicode。 ugettext()
方法总是返回一个 Unicode,而 gettext() 返回一个编码的 8 位字符串。 对于这两种方法的消息 id 参数,Unicode 字符串或仅包含 US-ASCII 字符的 8 位字符串都是可以接受的。 请注意,方法的 Unicode 版本(即 ugettext()
和 ungettext()
) 是推荐用于国际化 Python 程序的接口。
整个键/值对集合被放入字典并设置为“受保护的”_info
实例变量。
如果 .mo
文件的幻数无效,或者在读取文件时出现其他问题,实例化 GNUTranslations
类可以引发 IOError
。
以下方法从基类实现中被覆盖:
- GNUTranslations.gettext(message)
- 在目录中查找 message id 并返回相应的消息字符串,作为使用目录字符集编码的 8 位字符串(如果已知)。 如果目录中没有 message id 的条目,并且已经设置了回退,则查找将转发到回退的 gettext() 方法。 否则,返回 message id。
- GNUTranslations.lgettext(message)
等效于 gettext(),但如果没有其他编码被明确设置为
set_output_charset()
,则以首选系统编码返回翻译。2.4 版中的新功能。
- GNUTranslations.ugettext(message)
- 在目录中查找 message id 并返回相应的消息字符串,作为 Unicode 字符串。 如果目录中没有 message id 的条目,并且已设置回退,则查找将转发到回退的 ugettext() 方法。 否则,返回 message id。
- GNUTranslations.ngettext(singular, plural, n)
对消息 ID 进行复数形式的查找。 singular 用作在目录中查找的消息 ID,而 n 用于确定使用哪种复数形式。 返回的消息字符串是一个 8 位字符串,使用目录的字符集编码(如果已知)进行编码。
如果在目录中找不到消息 ID,并且指定了回退,则请求将转发到回退的 ngettext() 方法。 否则,当 n 为 1 时,返回 singular,而在所有其他情况下返回 plural。
2.3 版中的新功能。
- GNUTranslations.lngettext(singular, plural, n)
等效于 gettext(),但如果没有其他编码被明确设置为
set_output_charset()
,则以首选系统编码返回翻译。2.4 版中的新功能。
- GNUTranslations.ungettext(singular, plural, n)
对消息 ID 进行复数形式的查找。 singular 用作在目录中查找的消息 ID,而 n 用于确定使用哪种复数形式。 返回的消息字符串是一个 Unicode 字符串。
如果在目录中找不到消息 ID,并且指定了回退,则请求将转发到回退的 ungettext() 方法。 否则,当 n 为 1 时,返回 singular,而在所有其他情况下返回 plural。
下面是一个例子:
n = len(os.listdir('.')) cat = GNUTranslations(somefile) message = cat.ungettext( 'There is %(num)d file in this directory', 'There are %(num)d files in this directory', n) % {'num': n}
2.3 版中的新功能。
22.1.2.3. Solaris 消息目录支持
Solaris 操作系统定义了自己的二进制 .mo
文件格式,但由于找不到有关此格式的文档,因此目前不支持。
22.1.2.4. 目录构造函数
GNOME 使用 James Henstridge 的 gettext 模块的一个版本,但这个版本的 API 略有不同。 其记录在案的用法是:
import gettext
cat = gettext.Catalog(domain, localedir)
_ = cat.gettext
print _('hello world')
为了与这个旧模块兼容,函数 Catalog()
是上述 translation() 函数的别名。
此模块与 Henstridge 的一个区别:他的目录对象支持通过映射 API 进行访问,但这似乎未使用,因此当前不受支持。
22.1.3. 国际化您的程序和模块
国际化 (I18N) 是指使程序能够识别多种语言的操作。 本地化 (L10N) 是指您的程序在国际化后适应当地语言和文化习惯。 为了为您的 Python 程序提供多语言消息,您需要执行以下步骤:
- 通过专门标记可翻译字符串来准备您的程序或模块
- 在标记的文件上运行一套工具以生成原始消息目录
- 创建消息目录的特定语言翻译
- 使用 gettext 模块,以便正确翻译消息字符串
为了为 I18N 准备代码,您需要查看文件中的所有字符串。 任何需要翻译的字符串都应该通过将其包裹在 _('...')
中来标记——即调用函数 _()
。 例如:
filename = 'mylog.txt'
message = _('writing a log message')
fp = open(filename, 'w')
fp.write(message)
fp.close()
在这个例子中,字符串 'writing a log message'
被标记为翻译候选,而字符串 'mylog.txt'
和 'w'
不是。
Python 发行版带有两个工具,可在您准备好源代码后帮助您生成消息目录。 这些可能在二进制发行版中可用,也可能不可用,但它们可以在源发行版中找到,位于 Tools/i18n
目录中。
pygettext 3 程序会扫描您所有的 Python 源代码,寻找您之前标记为可翻译的字符串。 它类似于 GNU gettext 程序,除了它了解 Python 源代码的所有复杂性,但对 C 或 C++ 源代码一无所知。 您不需要 GNU gettext
,除非您还要翻译 C 代码(例如 C 扩展模块)。
pygettext 生成文本 Uniforum 样式的人类可读消息目录 .pot
文件,本质上是结构化的人类可读文件,其中包含源代码中的每个标记字符串,以及翻译字符串的占位符。 pygettext是一个命令行脚本,支持与xgettext类似的命令行界面; 有关其使用的详细信息,请运行:
pygettext.py --help
然后将这些 .pot
文件的副本移交给个人翻译人员,他们为每种支持的自然语言编写特定于语言的版本。 他们将填充的特定语言版本作为 .po
文件发回给您。 使用 msgfmt.py 4 程序(在 Tools/i18n
目录中),您从翻译器中获取 .po
文件并生成机器可读的.mo
二进制目录文件。 .mo
文件是 gettext 模块在运行时用于实际翻译处理的文件。
如何在代码中使用 gettext 模块取决于您是在国际化单个模块还是整个应用程序。 接下来的两节将讨论每种情况。
22.1.3.1. 本地化你的模块
如果您正在本地化您的模块,您必须注意不要进行全局更改,例如 到内置命名空间。 您不应使用 GNU gettext
API,而应使用基于类的 API。
假设您的模块被称为“垃圾邮件”,并且该模块的各种自然语言翻译 .mo
文件位于 GNU gettext 格式的 /usr/share/locale
中。 以下是您将放在模块顶部的内容:
import gettext
t = gettext.translation('spam', '/usr/share/locale')
_ = t.lgettext
如果您的翻译人员在他们的 .po
文件中为您提供了 Unicode 字符串,您应该这样做:
import gettext
t = gettext.translation('spam', '/usr/share/locale')
_ = t.ugettext
22.1.3.2. 本地化您的应用程序
如果您正在本地化您的应用程序,您可以将 _()
函数全局安装到内置命名空间中,通常在应用程序的主驱动程序文件中。 这将使您的所有特定于应用程序的文件只使用 _('...')
,而无需在每个文件中明确安装它。
在简单的情况下,您只需将以下代码添加到应用程序的主驱动程序文件中:
import gettext
gettext.install('myapplication')
如果您需要设置语言环境目录或 unicode 标志,您可以将它们传递给 install() 函数:
import gettext
gettext.install('myapplication', '/usr/share/locale', unicode=1)
22.1.3.3. 即时更改语言
如果您的程序需要同时支持多种语言,您可能需要创建多个翻译实例,然后在它们之间显式切换,如下所示:
import gettext
lang1 = gettext.translation('myapplication', languages=['en'])
lang2 = gettext.translation('myapplication', languages=['fr'])
lang3 = gettext.translation('myapplication', languages=['de'])
# start by using language1
lang1.install()
# ... time goes by, user selects language 2
lang2.install()
# ... more time goes by, user selects language 3
lang3.install()
22.1.3.4. 延期翻译
在大多数编码情况下,字符串在编码的地方被翻译。 然而,有时您需要标记要翻译的字符串,但将实际翻译推迟到以后。 一个经典的例子是:
animals = ['mollusk',
'albatross',
'rat',
'penguin',
'python', ]
# ...
for a in animals:
print a
在这里,您希望将 animals
列表中的字符串标记为可翻译,但您实际上并不想在打印它们之前翻译它们。
这是您可以处理这种情况的一种方法:
def _(message): return message
animals = [_('mollusk'),
_('albatross'),
_('rat'),
_('penguin'),
_('python'), ]
del _
# ...
for a in animals:
print _(a)
这是有效的,因为 _()
的虚拟定义只是返回未更改的字符串。 而这个虚拟定义将暂时覆盖内置命名空间中 _()
的任何定义(直到 del 命令)。 但请注意,如果您在本地命名空间中有 _()
的先前定义。
请注意,第二次使用 _()
不会将“a”识别为可翻译为 pygettext 程序,因为它不是字符串。
处理此问题的另一种方法是使用以下示例:
def N_(message): return message
animals = [N_('mollusk'),
N_('albatross'),
N_('rat'),
N_('penguin'),
N_('python'), ]
# ...
for a in animals:
print _(a)
在这种情况下,您使用函数 N_()
, 5 标记可翻译字符串,这不会与 _()
的任何定义冲突。 但是,您需要教您的消息提取程序查找标有 N_()
的可翻译字符串。 pygettext 和 xpot 都通过使用命令行开关来支持这一点。
22.1.3.5. 获取文本() 对比 lgettext()
在 Python 2.4 中引入了 lgettext() 函数系列。 这些函数的目的是提供一种更符合当前 GNU gettext 实现的替代方法。 与 gettext() 返回使用翻译文件中使用的相同代码集编码的字符串不同,lgettext() 将返回使用首选系统编码编码的字符串,如 返回的locale.getpreferredencoding()。 另请注意,Python 2.4 引入了新函数来显式选择翻译字符串中使用的代码集。 如果显式设置了代码集,即使 lgettext() 也会返回请求代码集中的翻译字符串,正如 GNU gettext 实现中所预期的那样。
22.1.4. 致谢
以下人员为本模块的创建贡献了代码、反馈、设计建议、以前的实现和宝贵的经验:
- 彼得·芬克
- 詹姆斯·亨斯屈奇
- 胡安·大卫·伊巴涅斯·帕洛马尔
- 马克-安德烈·伦堡
- 马丁·冯·洛维斯
- 弗朗索瓦·皮纳尔
- 巴里华沙
- 古斯塔沃·尼迈耶
脚注
- 1
- 默认的语言环境目录是系统相关的; 例如,在 RedHat Linux 上它是
/usr/share/locale
,但在 Solaris 上它是/usr/lib/locale
。 gettext 模块不会尝试支持这些依赖于系统的默认值; 相反,它的默认值为sys.prefix/share/locale
。 因此,最好在应用程序开始时使用显式绝对路径调用 bindtextdomain()。 - 2
- 请参阅上面 bindtextdomain() 的脚注。
- 3
- François Pinard 编写了一个名为 xpot 的程序,它完成了类似的工作。 它作为他的 po-utils 包 的一部分提供。
- 4
- msgfmt.py 与 GNU msgfmt 二进制兼容,但它提供了更简单的全 Python 实现。 有了这个和 pygettext.py,你通常不需要安装 GNU gettext 包来国际化你的 Python 应用程序。
- 5
- 这里
N_()
的选择完全是任意的; 它也可以很容易地变成MarkThisStringForTranslation()
。