gettext ——多语种国际化服务

源代码: Lib/gettext.py


这个 gettext 模块为您的Python模块和应用程序提供国际化(i18n)和本地化(l10n)服务。它同时支持GNU gettext 消息目录API和更高级别、基于类的API,可能更适合于Python文件。下面描述的接口允许您用一种自然语言编写模块和应用程序消息,并提供在不同自然语言下运行的翻译消息目录。

还提供了一些关于本地化Python模块和应用程序的提示。

GNU gettext API

这个 gettext 模块定义了以下API,这与GNU非常相似 gettext 应用程序编程接口。如果使用此API,将影响整个应用程序的全局转换。如果您的应用程序是单语言的,并且语言的选择取决于您的用户的语言环境,那么这通常就是您想要的。如果您正在本地化一个Python模块,或者您的应用程序需要动态地切换语言,那么您可能希望使用基于类的API。

gettext.bindtextdomain(domain, localedir=None)

绑定 到区域设置目录 本地语言 . 更具体地说, gettext 将查找二进制 .mo 使用路径的给定域的文件(在Unix上): {localedir}/{language}/LC_MESSAGES/{domain}.mo 在哪里 语言 在环境变量中搜索 LANGUAGELC_ALLLC_MESSAGESLANG 分别。

如果 本地语言 被省略或 None ,然后是当前绑定 返回。 1

gettext.bind_textdomain_codeset(domain, codeset=None)

绑定 代碼集 ,更改由返回的字节字符串的编码 lgettext()ldgettext()lngettext()ldngettext() 功能。如果 代碼集 如果省略,则返回当前绑定。

Deprecated since version 3.8, removed in version 3.10.

gettext.textdomain(domain=None)

更改或查询当前全局域。如果 None ,则返回当前全局域,否则将全局域设置为 ,返回。

gettext.gettext(message)

返回的本地化翻译 消息 ,基于当前全局域、语言和区域设置目录。此函数通常别名为 _() 在本地命名空间中(参见下面的示例)。

gettext.dgettext(domain, message)

类似于 gettext() ,但在指定的 .

gettext.ngettext(singular, plural, n)

类似于 gettext() 但要考虑复数形式。如果找到译文,请将复数公式应用于 n ,并返回结果消息(某些语言有两个以上的复数形式)。如果找不到翻译,返回 单数的 如果 n 1;返回 复数的 否则。

复数公式取自目录标题。它是一个c或python表达式,有一个自由变量 n ;表达式计算为目录中复数的索引。见 the GNU gettext documentation _用于 .po 各种语言的文件和公式。

gettext.dngettext(domain, singular, plural, n)

类似于 ngettext() ,但在指定的 .

gettext.pgettext(context, message)
gettext.dpgettext(domain, context, message)
gettext.npgettext(context, singular, plural, n)
gettext.dnpgettext(domain, context, singular, plural, n)

类似于没有 p 在前缀中(即, gettext()dgettext()ngettext()dngettext() ,但翻译仅限于给定的消息 context .

3.8 新版功能.

gettext.lgettext(message)
gettext.ldgettext(domain, message)
gettext.lngettext(singular, plural, n)
gettext.ldngettext(domain, singular, plural, n)

相当于没有 l 前缀 (gettext()dgettext()ngettext()dngettext() ,但如果没有使用显式设置其他编码,则转换将返回为以首选系统编码编码编码的字节字符串。 bind_textdomain_codeset() .

警告

在Python3中应该避免使用这些函数,因为它们返回编码的字节。最好使用返回Unicode字符串的替代方法,因为大多数Python应用程序都希望将人类可读的文本作为字符串而不是字节来操作。此外,如果翻译后的字符串存在编码问题,则可能会出现意外的与Unicode相关的异常。

Deprecated since version 3.8, removed in version 3.10.

注意,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.'))

基于类的API

基于类的API gettext 模块比GNU更灵活、更方便 gettext 应用程序编程接口。这是本地化Python应用程序和模块的推荐方法。 gettext 定义一个 GNUTranslations 实现GNU分析的类 .mo 格式化文件,并具有返回字符串的方法。此类的实例也可以作为函数安装在内置命名空间中。 _() .

gettext.find(domain, localedir=None, languages=None, all=False)

此函数实现标准 .mo 文件搜索算法。它需要一个 ,与什么相同 textdomain() 拿。可选的 本地语言 就像在 bindtextdomain() . 可选的 语言 是字符串列表,其中每个字符串都是语言代码。

如果 本地语言 未给定,则使用默认的系统区域设置目录。 2 如果 语言 未给出,则搜索以下环境变量: LANGUAGELC_ALLLC_MESSAGESLANG . 第一个返回非空值的用于 语言 变量。环境变量应该包含以冒号分隔的语言列表,这些语言将在冒号上拆分,以生成预期的语言代码字符串列表。

find() 然后扩展和规范化语言,然后迭代这些语言,搜索由这些组件构建的现有文件:

localedir/language/LC_MESSAGES/domain.mo

存在的第一个此类文件名由返回 find() . 如果找不到此类文件,则 None 返回。如果 all 如果给定,它将返回所有文件名的列表,其顺序与它们在语言列表或环境变量中的显示顺序相同。

gettext.translation(domain, localedir=None, languages=None, class_=None, fallback=False, codeset=None)

返回A *Translations 实例基于 本地语言语言 ,首先传递给 find() 获取关联的列表 .mo 文件路径。具有相同的实例 .mo 文件名被缓存。实例化的实际类是 class_ 如果提供,否则 GNUTranslations . 类的构造函数必须采用一个 file object 参数。如果提供, 代碼集 将更改用于在 lgettext()lngettext() 方法。

如果找到多个文件,以后的文件将用作以前的文件的回退。要允许设置回退, copy.copy() 用于从缓存复制每个转换对象;实际实例数据仍与缓存共享。

如果没有 .mo 找到文件,此函数将引发 OSError 如果 fallback 为false(默认值),并返回 NullTranslations 实例如果 fallback 是真的。

在 3.3 版更改: IOError 以前被引发而不是 OSError .

Deprecated since version 3.8, removed in version 3.10: 这个 代碼集 参数。

gettext.install(domain, localedir=None, codeset=None, names=None)

这将安装函数 _() 在python的内置命名空间中,基于 本地语言代碼集 传递给函数的 translation() .

对于 姓名 参数,请参见翻译对象的描述 install() 方法。

如下面所示,您通常通过将字符串封装在对 _() 函数,如下所示:

print(_('This string will be translated.'))

为了方便起见,您需要 _() 函数将安装在python的内置命名空间中,因此它在应用程序的所有模块中都很容易访问。

Deprecated since version 3.8, removed in version 3.10: 这个 代碼集 参数。

这个 NullTranslations

翻译类实际上实现了原始源文件消息字符串到翻译消息字符串的转换。所有翻译类使用的基类是 NullTranslations ;这提供了基本接口,您可以使用它编写自己的专业翻译类。以下是 NullTranslations

class gettext.NullTranslations(fp=None)

选择一个选项 file object fp ,它被基类忽略。初始化“受保护”实例变量 _info_charset 它由派生类以及 _fallback ,通过 add_fallback() . 然后调用 self._parse(fp) 如果 fp 不是 None .

_parse(fp)

基类中没有op,此方法接受文件对象 fp ,并从文件中读取数据,初始化其消息目录。如果有不支持的消息目录文件格式,则应重写此方法以分析格式。

add_fallback(fallback)

添加 fallback 作为当前翻译对象的回退对象。如果转换对象无法为给定消息提供转换,则它应咨询回退。

gettext(message)

如果设置了回退,则向前 gettext() 回退。否则,返回 消息 . 在派生类中重写。

ngettext(singular, plural, n)

如果设置了回退,则向前 ngettext() 回退。否则,返回 单数的 如果 n 1;返回 复数的 否则。在派生类中重写。

pgettext(context, message)

如果设置了回退,则向前 pgettext() 回退。否则,返回翻译后的消息。在派生类中重写。

3.8 新版功能.

npgettext(context, singular, plural, n)

如果设置了回退,则向前 npgettext() 回退。否则,返回翻译后的消息。在派生类中重写。

3.8 新版功能.

lgettext(message)
lngettext(singular, plural, n)

相当于 gettext()ngettext() ,但如果没有使用显式设置编码,则转换将作为用首选系统编码编码的字节字符串返回。 set_output_charset() . 在派生类中重写。

警告

在Python3中应该避免使用这些方法。请参见 lgettext() 功能。

Deprecated since version 3.8, removed in version 3.10.

info()

返回“受保护” _info 变量,包含在消息目录文件中找到的元数据的字典。

charset()

返回消息目录文件的编码。

output_charset()

返回用于返回已翻译邮件的编码 lgettext()lngettext() .

Deprecated since version 3.8, removed in version 3.10.

set_output_charset(charset)

更改用于返回已翻译邮件的编码。

Deprecated since version 3.8, removed in version 3.10.

install(names=None)

此方法安装 gettext() 在内置命名空间中,将其绑定到 _ .

如果 姓名 参数是给定的,它必须是一个序列,其中包含要在内置命名空间中安装的函数的名称以及 _() . 支持的名称是 'gettext''ngettext''pgettext''npgettext''lgettext''lngettext' .

请注意,这只是一种方法,尽管是最方便的方法,使 _() 您的应用程序可用的功能。因为它全局性地影响整个应用程序,特别是内置命名空间,所以本地化模块不应该安装 _() . 相反,他们应该使用此代码 _() 可用于他们的模块:

import gettext
t = gettext.translation('mymodule', ...)
_ = t.gettext

这提出 _() 仅在模块的全局命名空间中,因此只影响此模块内的调用。

在 3.8 版更改: 补充 'pgettext''npgettext' .

这个 GNUTranslations

这个 gettext 模块提供一个派生自 NullTranslationsGNUTranslations . 此类重写 _parse() 启用读取GNU gettext 格式 .mo 大尾数和小尾数格式的文件。

GNUTranslations 从翻译目录中分析可选元数据。这是与GNU的约定 gettext 将元数据包含为空字符串的转换。此元数据位于 RFC 822 风格 key: value 对,并且应该包含 Project-Id-Version 关键。如果钥匙 Content-Type 找到了,然后 charset 属性用于初始化“protected” _charset 实例变量,默认为 None 如果找不到。如果指定了字符集编码,则使用此编码将从目录中读取的所有消息ID和消息字符串转换为Unicode,否则假定为ASCII。

由于消息ID也被读取为Unicode字符串,因此 *gettext() 方法将假定消息ID为Unicode字符串,而不是字节字符串。

整组键/值对被放入字典并设置为“受保护” _info 实例变量。

如果 .mo 文件的幻数无效,主版本号意外,或者在读取文件、实例化 GNUTranslations 课堂可以提高 OSError .

class gettext.GNUTranslations

以下方法将从基类实现中重写:

gettext(message)

抬头看 消息 并返回相应的消息字符串,作为Unicode字符串。如果目录中没有 消息 ID,并且设置了回退,查找将转发到回退的 gettext() 方法。否则, 消息 返回ID。

ngettext(singular, plural, n)

对消息ID进行复数形式的查找。 单数的 用作在目录中查找的消息ID,而 n 用于确定要使用的复数形式。返回的消息字符串是Unicode字符串。

如果在目录中找不到消息ID,并且指定了回退,则请求将转发到回退的 ngettext() 方法。否则,何时 n 是1 单数的 被退回,并且 复数的 在所有其他情况下返回。

下面是一个例子:

n = len(os.listdir('.'))
cat = GNUTranslations(somefile)
message = cat.ngettext(
    'There is %(num)d file in this directory',
    'There are %(num)d files in this directory',
    n) % {'num': n}
pgettext(context, message)

抬头看 context消息 并返回相应的消息字符串,作为Unicode字符串。如果目录中没有 消息 ID和 context ,并且设置了回退,查找将转发到回退的 pgettext() 方法。否则, 消息 返回ID。

3.8 新版功能.

npgettext(context, singular, plural, n)

对消息ID进行复数形式的查找。 单数的 用作在目录中查找的消息ID,而 n 用于确定要使用的复数形式。

如果消息ID context 在目录中找不到,并且指定了回退,请求被转发到回退的 npgettext() 方法。否则,何时 n 是1 单数的 被退回,并且 复数的 在所有其他情况下返回。

3.8 新版功能.

lgettext(message)
lngettext(singular, plural, n)

相当于 gettext()ngettext() ,但如果没有使用显式设置编码,则转换将作为用首选系统编码编码的字节字符串返回。 set_output_charset() .

警告

在Python3中应该避免使用这些方法。请参见 lgettext() 功能。

Deprecated since version 3.8, removed in version 3.10.

Solaris消息目录支持

Solaris操作系统定义自己的二进制文件 .mo 文件格式,但由于在该格式上找不到任何文档,因此目前不支持该格式。

目录构造函数

gnome使用的是 gettext 模块由JamesHenstridge编写,但此版本的API略有不同。其使用记录如下:

import gettext
cat = gettext.Catalog(domain, localedir)
_ = cat.gettext
print(_('hello world'))

为了与这个旧模块兼容,函数 Catalog() 是的别名 translation() 上述功能。

这个模块和Henstridge的一个区别是:他的目录对象支持通过映射API访问,但这似乎是未使用过的,因此目前不受支持。

使程序和模块国际化

国际化(i18n)是指使程序了解多种语言的操作。本地化(l10n)指的是将你的程序国际化后,适应当地语言和文化习惯。要为Python程序提供多语言消息,需要执行以下步骤:

  1. 通过特别标记可翻译字符串来准备程序或模块

  2. 在标记的文件上运行一套工具以生成原始邮件目录

  3. 创建特定于语言的邮件目录翻译

  4. 使用 gettext 模块,以便正确转换消息字符串

为了准备I18N的代码,您需要查看文件中的所有字符串。任何需要翻译的字符串都应通过将其封装在 _('...') ---即对函数的调用 _() . 例如::

filename = 'mylog.txt'
message = _('writing a log message')
with open(filename, 'w') as fp:
    fp.write(message)

在这个例子中,字符串 'writing a log message' 当字符串 'mylog.txt''w' 不是。

有一些工具可以提取用于翻译的字符串。原始GNU gettext 只支持C或C++源代码,但其扩展版本 xgettext 扫描用多种语言(包括python)编写的代码,以查找标记为可翻译的字符串。 Babel _是一个包含 pybabel 用于提取和编译消息目录的脚本。弗朗索瓦皮纳德的节目叫 xpot 做类似的工作,并作为他的一部分 po-utils package _.

(python还包括这些程序的纯python版本,称为 pygettext.pymsgfmt.py ;一些python发行版将为您安装它们。 pygettext.py 类似于 xgettext ,但只理解Python源代码,不能处理其他编程语言,如C或C++。 pygettext.py 支持类似于 xgettext ;有关其使用的详细信息,请运行 pygettext.py --help . msgfmt.py 二进制与GNU兼容 msgfmt . 有了这两个程序,您可能不需要GNU gettext 将您的python应用程序国际化的包。)

xgettextpygettext 和类似工具生成 .po 属于邮件目录的文件。它们是结构化的人类可读文件,包含源代码中的每个标记字符串,以及这些字符串的翻译版本的占位符。

这些复印件 .po 然后将文件移交给为每种支持的自然语言编写翻译的个人翻译人员。他们将已完成的语言特定版本作为 <language-name>.po 编译成机器可读的文件 .mo 二进制目录文件使用 msgfmt 程序。这个 .mo 文件由 gettext 用于在运行时进行实际翻译处理的模块。

如何使用 gettext 代码中的模块取决于您是将单个模块国际化还是将整个应用程序国际化。接下来的两个部分将讨论每个案例。

本地化模块

如果要本地化模块,则必须注意不要对内置命名空间进行全局更改。你不应该使用GNU gettext 而是基于类的API。

假设您的模块被称为“垃圾邮件”,模块的各种自然语言翻译 .mo 文件驻留于 /usr/share/locale 在GNU中 gettext 格式。以下是您将在模块顶部放置的内容:

import gettext
t = gettext.translation('spam', '/usr/share/locale')
_ = t.gettext

本地化应用程序

如果要本地化应用程序,可以安装 _() 在内置命名空间中全局运行,通常在应用程序的主驱动程序文件中。这将允许所有特定于应用程序的文件使用 _('...') 不必在每个文件中显式安装它。

在简单的情况下,您只需要将以下代码位添加到应用程序的主驱动程序文件中:

import gettext
gettext.install('myapplication')

如果需要设置区域设置目录,可以将其传递到 install() 功能:

import gettext
gettext.install('myapplication', '/usr/share/locale')

随时更改语言

如果您的程序需要同时支持多种语言,您可能需要创建多个翻译实例,然后在它们之间显式切换,如:

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()

延期翻译

在大多数编码情况下,字符串在编码的地方被转换。不过,有时您需要标记字符串进行翻译,但将实际翻译推迟到稍后。一个典型的例子是:

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”标识为可翻译为 gettext 程序,因为参数不是字符串文本。

另一种处理方法是使用以下示例:

def N_(message): return message

animals = [N_('mollusk'),
           N_('albatross'),
           N_('rat'),
           N_('penguin'),
           N_('python'), ]

# ...
for a in animals:
    print(_(a))

在这种情况下,使用函数标记可翻译字符串 N_() 不会与任何定义冲突 _() . 但是,您需要教您的消息提取程序查找标记有 N_() . xgettextpygettextpybabel extractxpot 通过使用 -k 命令行开关。选择 N_() 这完全是武断的,本来也可以 MarkThisStringForTranslation() .

确认

以下人员为本模块的创建贡献了代码、反馈、设计建议、以前的实现和宝贵的经验:

  • 彼得芬克

  • 詹姆斯·亨斯特里奇

  • 胡安·大卫·伊布·伊兹·帕洛马尔

  • 马克·安德尔·莱姆伯格

  • 马丁·冯·路易斯

  • 弗朗索瓦·皮纳德

  • 华沙巴里

  • 古斯塔沃·尼迈耶

脚注

1

默认的区域设置目录依赖于系统;例如,在Redhat Linux上,它是 /usr/share/locale 但是在Solaris上 /usr/lib/locale . 这个 gettext 模块不尝试支持这些依赖于系统的默认值;相反,它的默认值是 {sys.base_prefix}/share/locale (见 sys.base_prefix )因此,最好打电话给 bindtextdomain() 在应用程序的开头有一个明确的绝对路径。

2

参见脚注 bindtextdomain() 上面。