You are here:  Home » Python » – 多语言国际化服务 – 国际化(Python教程)(参考资料)

gettext– 多语言国际化服务

源代码: Lib / gettext.py


gettext模块为您的Python模块和应用程序提供国际化(I18N)和本地化(L10N)服务。它同时支持GNU gettext消息目录API和更高级别的基于类的API,可能更适合Python文件。下面介绍的界面允许您用一种自然语言编写模块和应用程序消息,并提供已翻译消息的目录,以便在不同的自然语言下运行.

还给出了一些关于本地化你的Python模块和应用程序的提示.

GNU gettext API

gettext模块定义了以下API,与GNU gettext的API。如果使用此API,则会影响整个应用程序的全局翻译。如果您的应用程序是单语的,那么这通常就是您想要的,语言的选择取决于您的用户的名称。如果要本地化Python模块,或者如果应用程序需要动态切换语言,则可能需要使用基于类的API .

gettext.bindtextdomaindomain, localedir=None

domain绑定到语言环境目录localedir。更具体地说,gettext将使用路径(在Unix上)查找给定域的二进制.mo文件:localedir/language/LC_MESSAGES/domain.mo,其中languages在环境变量LANGUAGE,LC_ALL, LC_MESSAGESLANG中分别搜索

如果localedir被省略或None,则当前绑定为domain回复。[1]

gettext.bind_textdomain_codesetdomain, codeset=None

绑定domaincodeset,更改lgettext(), ldgettext(), lngettext()ldngettext()函数返回的字节字符串的编码。如果codeset被省略,则返回当前绑定.

gettext.textdomaindomain=None

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

gettext.gettext//(message

根据当前的globaldomain,language和locale目录返回message的本地化翻译。此函数通常在本地命名空间中别名为_()(参见下面的示例).

gettext.dgettextdomain, message

喜欢gettext(),但是在指定的domain.

gettext.ngettextsingular, plural, n

看看信息像gettext(),但考虑复数形式。如果找到翻译,请将复数公式应用于n,并返回结果消息(somelanguages有两个以上的复数形式)。如果没有找到翻译,如果singular为1则返回n;返回plural否则

Plural公式取自目录标题。它是一个C或Pythonexpression有一个自由变量n;表达式求值为目录中复数的索引。请参阅GNU gettext文档,了解在.po文件中使用的精确语法以及各种语言的公式.

gettext.dngettextdomain, singular, plural, n

喜欢 ngettext(),但在指定的domain.

gettext.lgettextmessage
gettext.ldgettextdomain, message
gettext.lngettextsingular, plural, n
gettext.ldngettextdomain, singular, plural, n

相当于没有l字首(gettext(), dgettext(), ngettext()dngettext()),但如果没有使用bind_textdomain_codeset().

警告

在Python 3中应该避免使用这些函数,因为它们返回了编码的字节。使用返回Unicode字符串的替代方法要好得多,因为大多数Python应用程序都希望将人类可读的文本操作为字符串而不是字节。此外,如果翻译的字符串存在编码问题,则可能会出现意外的与Unicode相关的异常。可能是l*()函数将在以后的Pythonversions中被弃用,因为它们存在固有的问题和限制.

注意GNU gettext 也定义了dcgettext()方法,但这被认为没用,所以它目前没有实现.

以下是此API的典型用法示例:

import gettextgettext.bindtextdomain("myapplication", "/path/to/my/language/directory")gettext.textdomain("myapplication")_ = gettext.gettext# ...print(_("This is a translatable string."))

基于类的API

的基于类的APIgettext模块比GNU gettext API提供更多的灵活性和更大的便利性。它是本地化Python应用程序和模块的推荐途径。gettext定义实现GNU .moformatfiles,并具有返回字符串的方法。这个“翻译”类的实例也可以自己安装在内置的命名空间中作为函数_().

gettext.finddomain, localedir=None, languages=None, all=False

该函数实现了标准的.mo文件搜索算法。它domain,与textdomain()相同。可选localedirbindtextdomain()可选languages是一个字符串列表,其中每个字符串是一个语言代码.

如果没有给出localedir,则使用默认的系统区域设置目录。[2]如果提供languages没有给出,然后搜索以下环境变量:LANGUAGE, LC_ALL, LC_MESSAGESLANG。返回非空值的第一个用于languages变量。环境变量应该包含一个冒号分隔的语言列表,它将在冒号上拆分以产生预期的语言代码字符串列表.

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

localedir/language/LC_MESSAGES/domain.mo

第一个这样的文件名由find()返回。如果没有找到这样的文件,则返回None。如果给出all,则返回所有文件名的列表,按照它们出现在语言列表或环境变量中的顺序.

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

根据Translationsdomain, localedir返回一个languages实例,首先传递给find()以获取相关的列表.mo文件路径。缓存具有相同.mo文件名的实例。实例化的实例是class_,否则GNUTranslations。类的构造函数必须使用单个文件对象参数。如果提供,codeset将改变用于编码lgettext()lngettext()methods.

如果找到多个文件,则后面的文件将用作早期文件的后备。要允许设置后备,copy.copy()用于从缓存中克隆每个翻译对象;实际的实例数据仍然与缓存共享.

如果找不到.mo文件,这个函数会提升OSError如果fallback是假的(这是默认值),如果NullTranslations为真,则返回fallback实例.

在版本3.3中更改:IOError曾经被提升代替OSError.

gettext.installdomain, localedir=None, codeset=None, names=None

这个安装功能_()在Python的内置命名空间中,基于domain, localedircodeset传递给函数translation().

对于names参数,请参阅翻译对象的描述install() 方法。

如下所示,您通常会在应用程序中标记用于转换的字符串,通过将它们包装在对_()函数的调用中,如下所示:

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

为方便起见,您需要_()函数安装在Python’sbuiltins命名空间中,因此可以在你的应用程序的所有模块中轻松访问.

NullTranslations class

翻译类实际上是将原始资源文件消息字符串转换为已翻译的消息字符串。所有翻译类使用的基类是NullTranslations;这提供了可用于编写自己的专用翻译类的基本接口。这里的方法是NullTranslations

class gettext.NullTranslationsfp=None

带一个可选的文件对象 fp,被基类忽略。初始化“受保护”实例变量_info_charset由派生类设置,以及_fallback,通过add_fallback()。然后它叫self._parse(fp)如果fp不是None.

_parsefp

在基类中没有操作,这个方法需要文件对象fp,并从文件中读取数据,初始化其消息目录。如果你有一个不支持的消息目录文件格式,你应该覆盖这个方法来解析你的格式.

add_fallback (fallback )

添加fallback作为当前翻译对象的后备对象。如果翻译对象无法为给定的消息提供翻译,则应查阅后备.

gettext (message )

如果设置了后退,则将gettext()转发给后备。否则,返回message。在派生类中重写.

ngettext (singular, plural, n)

如果设置了后退,则转发ngettext()到了后退。否则,如果singular是1,则返回n;返回plural否则。在派生类中过度.

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

等效于gettext()ngettext(),但是翻译作为在首选系统编码中编码的字节字符串返回如果在派生类中没有使用set_output_charset().Overridden显式设置编码.

Warning

在Python 3中应避免使用这些方法。请参阅lgettext()功能。

info

返回“受保护”_info变量。

charset

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

output_charset

返回用于在lgettext()lngettext().

set_output_charsetcharset

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

installnames=None

这个方法安装gettext()进入内置命名空间,将其绑定到_.

如果names给定参数,它必须是一个包含你想要安装在内置命名空间中的函数名称的序列,而不是_()。支持的名称是"gettext", "ngettext","lgettext""lngettext".

请注意,这只是制作_()功能可用于您的应用程序。因为它影响整个应用程序全局,特别是内置命名空间,本地化模块永远不应该安装_()。相反,他们应该使用这个代码来制作_()可用于他们的模块:

import gettextt = gettext.translation("mymodule", ...)_ = t.gettext

这只将_()放在模块的全局命名空间中,因此只影响该模块中的调用.

GNUTranslations class

gettext模块提供了一个来自NullTranslationsGNUTranslations的附加类。这个类重写了_parse(),能够以big-endian和little-endian格式读取GNU gettext 格式.mo文件.

GNUTranslations解析翻译目录中的可选元数据。与GNU gettext 的约定包括元数据作为空字符串的翻译。这个元数据在 RFC 822 – style key: value对,并且应该包含Project-Id-Version键。如果找到了密钥Content-Type,那么charset属性用于初始化“受保护的”_charset实例变量,如果找不到则默认为None。如果指定了charset编码,则使用此编码将从目录中读取的所有messageid和消息字符串转换为Unicode,否则假定为ASCII编码.

由于消息ID也被读作Unicode字符串,所有*gettext()方法都会将消息ID假设为Unicode字符串,而不是字节字符串.

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

如果.mo文件的幻数无效,主要版本号是否符合预期,或者在读取文件时出现其他问题,实例化GNUTranslations类可以引发OSError.

class gettext.GNUTranslations

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

gettextmessage

查找message目录中的id并返回相应的messagestring,作为Unicode字符串。如果目录中没有messageid,并设置了后备,查找转发到thefallback的gettext()方法。否则,message id返回.

ngettext (singular, plural, n )

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

如果在目录中找不到消息ID,并且指定了回退,则请求将转发到回退的ngettext()方法。否则,当n为1 singular时,返回plural在所有其他情况下都会回复.

这是一个例子:

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}
lgettextmessage
lngettextsingular, plural, n

相当于 gettext()ngettext(),但是翻译是以首选系统编码编码的字节字符串返回的,没有使用set_output_charset().

警告

在Python 3中应避免使用这些方法。请参阅lgettext()函数的警告

Solaris消息目录支持

Solaris操作系统定义自己的二进制文件.mo文件格式,但是因为没有文件可以找到此格式,此时不支持.

目录构造函数

GNOME使用的是gettextJames Henstridge的模块,但是这个版本的API略有不同。其记录的用法是:

import gettextcat = gettext.Catalog(domain, localedir)_ = cat.gettextprint(_("hello world"))

为了兼容这个旧模块,函数Catalog()是上述translation()函数的分析.

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

国际化你的程序和模块

国际化(I18N)指的是操作通过该程序使多种语言成为可能。本地化(L10N)是指您的程序一旦国际化,就会适应当地语言和文化习惯。为了向您的Python程序提供多语言消息,您需要执行以下步骤:

  1. 准备您的程序或模块通过专门标记可翻译的字符串
  2. 在您标记的文件上运行一套工具来生成原始消息目录
  3. 创建消息目录的语言特定翻译
  4. 使用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"不是.

有一些工具可以提取用于翻译的字符串。原始GNU gettext 仅支持C或C ++源代码,但其扩展版本为 xgettext 扫描用多种语言编写的代码,包括Python,以查找标记为可译的字符串。Babel是一个Pythoninternationalization库,包含pybabel脚本提取和编译消息目录。FrançoisPinard的节目叫 xpot 做类似的工作,并作为po-utils包的一部分提供.

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

xgettext , pygettext ,和类似的工具生成.po作为消息目录的文件。它们是结构化的可读文件,包含源代码中的每个标记字符串,以及forsestrings的翻译版本的占位符.

这些副本.po然后将文件移交给为自己支持的自然语言编写翻译的个人翻译。他们将已完成的语言版本发送回<language-name>.po使用.mo msgfmt 程序将文件编译成机器可读的二进制目录文件。.mo使用gettext用于实际翻译处理的模块atrun-time.

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

定位你的模块

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

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

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

本地化您的应用程序

如果您正在本地化您的应用程序,您可以将_()函数全局安装到内置命名空间中,通常在你的应用程序的主驱动文件中。这将使所有特定于应用程序的文件只使用_("...")而无需在每个文件中明确安装它.

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

import gettextgettext.install("myapplication")

如果需要设置locale目录,可以将其传递到install()函数:

import gettextgettext.install("myapplication", "/usr/share/locale")

动态更改语言

如果您的程序需要同时支持多种语言,您可能需要创建多个翻译实例,然后在主题之间切换,如下所示:

import gettextlang1 = gettext.translation("myapplication", languages=["en"])lang2 = gettext.translation("myapplication", languages=["fr"])lang3 = gettext.translation("myapplication", languages=["de"])# start by using language1lang1.install()# ... time goes by, user selects language 2lang2.install()# ... more time goes by, user selects language 3lang3.install()

延迟翻译

在大多数编码情况下,字符串在它们被编码的位置被翻译。但是,有时候,您需要标记字符串以进行翻译,但是将实际翻译推迟到以后。一个典型的例子是:

animals = ["mollusk",           "albatross",           "rat",           "penguin",           "python", ]# ...for a in animals:    print(a)

在这里,你想要将animals列表中的字符串标记为可翻译,但实际上你并不想翻译它们直到它们被打印出来.

这是你可以处理这种情况的一种方法:

def _(message): return messageanimals = [_("mollusk"),           _("albatross"),           _("rat"),           _("penguin"),           _("python"), ]del _# ...for a in animals:    print(_(a))

这是有效的,因为_()的虚拟定义只返回stringunchanged。这个虚拟定义将暂时覆盖内置命名空间中的_()的任何定义(直到del命令)。Takecare,虽然如果你在localnamespace中有_()的先前定义.

注意第二次使用_()不会将“a”标识为可以转移给 gettext program,因为参数不是字符串文字.

处理此问题的另一种方法是使用以下示例:

def N_(message): return messageanimals = [N_("mollusk"),           N_("albatross"),           N_("rat"),           N_("penguin"),           N_("python"), ]# ...for a in animals:    print(_(a))

在这种情况下,您使用函数N_()标记可翻译字符串,这不会与_()的任何定义冲突。但是,您需要教授您的消息提取程序,以便翻译字符串。标有N_(). xgettext , pygettext , pybabel extract xpot 通过使用-k来支持命令行开关。这里N_()的选择是完全随意的;它可能很容易被MarkThisStringForTranslation().

致谢

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

  • Peter Funk
  • James Henstridge
  • JuanDavidIbáñezPalomar
  • Marc-AndréLemburg
  • MartinvonLüwis
  • FrançoisPinard
  • Barry Warsaw
  • Gustavo Niemeyer

脚注

[1] 默认的语言环境目录是系统相关的;例如,在RedHat Linuxit上是/usr/share/locale,但在Solaris上它是/usr/lib/localegettext模块不会尝试支持这些系统相关的默认设置;相反它的默认值是​​sys.prefix/share/locale。因此,最好拨打bindtextdomain()应用程序的开头有一个明确的绝对路径.
[2] 请参阅bindtextdomain()以上。

评论被关闭。