国际化与本土化

Internationalization (i18n)是创建具有用户界面的软件的行为,该用户界面可能显示在多个语言或文化上下文中。 Localization (l10n)是将国际化应用程序的用户界面显示在 特定的 语言或文化背景。

Pyramid 提供国际化和本地化子系统,可用于将按钮文本、错误消息和其他软件和模板定义的值转换为应用程序用户的本地语言。

创建翻译字符串

编写软件时,可以将专门的标记插入到Python代码中,这样系统就可以将文本值转换为应用程序用户使用的语言。此标记创建一个 translation string . 翻译字符串是一个行为基本上类似于普通Unicode字符串的对象,除了它还携带与其作业相关的额外信息作为 Pyramid 翻译机器。

使用 TranslationString 等级

创建翻译字符串最原始的方法是使用 pyramid.i18n.TranslationString 可调用的:

1
2
from pyramid.i18n import TranslationString
ts = TranslationString('Add')

这创造了一个 str -像是translationString对象。

注解

对于更熟悉的人 Zope I18N,翻译字符串很像 zope.i18nmessageid.Message 对象。然而,它不是一个子类。对于更熟悉的人 PylonsDjango i18n,使用translationString很像使用相关getText API的“懒惰”版本。

第一个论点 TranslationStringmsgid ;这是必需的。它表示特定本地化提供的翻译映射中的键。这个 msgid 参数必须是字符串。这个 msgid 可以选择包含 替换标记 . 例如:

1
2
from pyramid.i18n import TranslationString
ts = TranslationString('Add ${number}')

在上面的字符串中, ${{number}} 是替换标记。它将被替换为 映射 用于转换字符串。映射可以与替换标记本身同时提供:

1
2
from pyramid.i18n import TranslationString
ts = TranslationString('Add ${number}', mapping={'number':1})

任何数量的替换标记都可以出现在 msgid 值,任何次数。仅可由中的值替换的标记 映射 将在翻译时替换。其他的将不会被插入,并将逐字输出。

翻译字符串通常还应包含 . 域表示一个翻译类别,以将其与同一翻译的其他翻译消除歧义。 msgid 以防发生冲突。

1
2
3
from pyramid.i18n import TranslationString
ts = TranslationString('Add ${number}', mapping={'number':1},
                       domain='form')

上面的转换字符串命名的域 form . 一 translator 函数通常使用域来定位文件系统中包含给定域翻译的正确转换器文件。在这种情况下,如果它试图翻译我们的 msgid 在德语中,它可能会试图从 gettext 文件内 translation directory 像这样:

locale/de/LC_MESSAGES/form.mo

换句话说,它希望从 form.mo 德语翻译文件。

最后,translationString构造函数接受 default 争论。如果A default 提供了参数,它将替换 msgid 作为 默认值 用于转换字符串。什么时候? defaultNone , the msgid 传递给translationString的值用作隐式消息标识符。消息标识符与翻译文件中的翻译相匹配,因此使用与默认文本无关的“不透明”消息标识符创建翻译字符串通常很有用:

1
2
3
from pyramid.i18n import TranslationString
ts = TranslationString('add-number', default='Add ${number}',
                       domain='form', mapping={'number':1})

使用默认文本时,默认文本对象可能包含替换值。

使用 TranslationStringFactory 等级

另一种生成翻译字符串的方法是使用 TranslationStringFactory 对象。此对象是 翻译字符串工厂 . 基本上,翻译字符串工厂预设 domain 任何价值 translation string 使用它生成的。例如:

1
2
3
from pyramid.i18n import TranslationStringFactory
_ = TranslationStringFactory('pyramid')
ts = _('add-number', default='Add ${number}', mapping={'number':1})

注解

我们将翻译字符串工厂分配给了名称 _ . 这是翻译文件生成工具将支持的约定。

转让后 _ 结果是 TranslationStringFactory() ,调用的后续结果 _ 将是 TranslationString 实例。即使是 domain 值未传递给 _ (如果 TranslationString 使用了构造函数,而不是转换字符串工厂)。 domain 结果转换字符串的属性将为 pyramid . 因此,前面的代码示例完全等同于(拼写除外):

1
2
3
from pyramid.i18n import TranslationString as _
ts = _('add-number', default='Add ${number}', mapping={'number':1},
       domain='pyramid')

您可以使用 TranslationStringFactory 班级。例如,如果要创建一个翻译字符串工厂,该工厂将 domain 生成的转换字符串的值 form 你可以这样做:

1
2
3
from pyramid.i18n import TranslationStringFactory
_ = TranslationStringFactory('form')
ts = _('add-number', default='Add ${number}', mapping={'number':1})

通过翻译字符串工厂为应用程序创建唯一的域是最佳实践。使用您自己独特的翻译域可以让其他人重用您的应用程序,而无需将您的翻译文件与其自己的文件合并。相反,他们可以只包括你的包裹 translation directory 通过 pyramid.config.Configurator.add_translation_dirs() 方法。

注解

对于熟悉Zope国际化的人来说,TranslationStringFactory很像 zope.i18nmessageid.MessageFactory 对象。然而,它不是一个子类。

一起工作 gettext 翻译文件

基础 Pyramid 翻译服务是GNU gettext . 一旦应用程序源代码文件和模板被标记为翻译标记,就可以通过创建各种类型的gettext文件来处理翻译。

注解

开发人员必须采取的步骤 gettext message catalog 文件内 Pyramid 应用程序与步骤A非常相似 Pylons 开发人员必须采取同样的措施。见 Pylons Internationalization and Localization documentation 更多信息。

GNU GetText在翻译框架中使用三种类型的文件, .pot 文件夹, .po 文件,以及 .mo 文件夹。

.pot (可移植对象模板)文件

A .pot 文件是由一个程序创建的,该程序搜索项目的源代码,并选择 message identifier 传递给 _() 功能(例如, translation string 结构)。将所有消息标识符的列表放入 .pot 文件,用作创建的模板 .po 文件夹。

.po (可移植对象)文件

中的消息列表 .pot 文件由人翻译为特定语言;结果保存为 .po 文件。

.mo (机器对象)文件

A .po 文件被转换为机器可读的二进制文件,即 .mo 文件。将翻译编译为机器代码使本地化程序启动更快。

使用的工具 gettext 与a相关的翻译文件 Pyramid 应用是 LinguaGettext . Lingua可以从python和chameleon文件中清除i18n引用,并创建 .pot 文件。GetText包括 msgmerge 更新工具 .po 文件来自更新的 .pot 文件和 msgfmt 编写 .po 文件到 .mo 文件夹。

安装Lingua和GetText

用于与工作相关的命令 gettext 翻译文件要正常工作,您需要 LinguaGettext 安装在相同的环境中 Pyramid 已安装。

在Unix上安装

getText通常已经安装在UNIX系统上。您可以通过测试 msgfmt 命令可用。如果它不可用,您可以从操作系统通过打包系统安装它;包名称几乎总是 gettext . 例如,在Debian或Ubuntu系统上运行以下命令:

sudo apt-get install gettext

安装lingua是使用python打包工具完成的。如果 virtual environment 在其中安装了 Pyramid 应用程序在环境变量中生存 $VENV ,您可以这样安装lingua:

$VENV/bin/pip install lingua

在Windows上安装

在Windows上安装GetText有几种方法:它包含在 Cygwin 或者您可以使用 installer from the GnuWin32 或者自己编译。确保已将安装路径添加到 $PATH .

安装lingua是使用python打包工具完成的。如果 virtual environment 在其中安装了 Pyramid 应用程序在环境变量中生存 %VENV% ,您可以这样安装lingua:

%VENV%\Scripts\pip install lingua

从代码和模板提取消息

一旦安装了Lingua,就可以从代码中提取消息目录模板,并 Chameleon 位于您的 Pyramid 应用。你运行一个 pot-create 提取消息的命令:

cd /file/path/to/myapplication_setup.py
mkdir -p myapplication/locale
$VENV/bin/pot-create -o myapplication/locale/myapplication.pot src

邮件目录 .pot 模板将以 myapplication/locale/myapplication.pot .

初始化邮件目录文件

一旦将消息提取到 .pot 文件(见) 从代码和模板提取消息 )开始本地化 .pot 文件,您需要至少生成一个 .po 文件。一 .po 文件表示特定消息集到特定区域设置的翻译。初始化一个 .po 预生成的特定区域设置的文件 .pot 使用模板 msginit 来自GetText的命令:

cd /file/path/to/myapplication_setup.py
cd myapplication/locale
mkdir -p es/LC_MESSAGES
msginit -l es -o es/LC_MESSAGES/myapplication.po

这将创建新的邮件目录 .po 文件在 myapplication/locale/es/LC_MESSAGES/myapplication.po .

一旦文件存在,它就可以由人工翻译完成。有一个工具可以帮助解决这个问题 Poedit .

注意 Pyramid 它本身忽略了一切的存在 .po 文件夹。要使正在运行的应用程序具有可用的翻译,请 .mo 文件必须存在。见 编译消息目录文件 .

更新目录文件

如果向应用程序中添加更多的翻译字符串,或者翻译字符串发生更改,则需要更新现有的 .po 基于对 .pot 文件,以便新的和更改的消息也可以被翻译或重新翻译。

首先,重新生成 .pot 文件按 从代码和模板提取消息 . 然后使用 msgmerge 来自GetText的命令。

cd /file/path/to/myapplication_setup.py
cd myapplication/locale
msgmerge --update es/LC_MESSAGES/myapplication.po myapplication.pot

编译消息目录文件

最后,要为执行实际运行时翻译准备应用程序,请编译 .po 文件到 .mo 使用的文件 msgfmt 来自GetText的命令:

cd /file/path/to/myapplication_setup.py
msgfmt -o myapplication/locale/es/LC_MESSAGES/myapplication.mo \
    myapplication/locale/es/LC_MESSAGES/myapplication.po

这将创建一个 .mo 为每个文件 .po 应用程序中的文件。只要 translation directory 其中 .mo 文件的结尾在应用程序中配置(请参见 添加翻译目录 )这些翻译将提供给 Pyramid .

使用定位器

A localizer 是允许您在应用程序中“手动”执行翻译或复数的对象。你可以使用 pyramid.request.Request.localizer 获取的属性 localizer . 本地化器对象将被配置为生成活动的 locale negotiator 如果没有注册显式区域设置协商器,则为默认的本地化器对象。

1
2
def aview(request):
    localizer = request.localizer

注解

如果需要为区域设置创建本地化程序,请使用 pyramid.i18n.make_localizer() 功能。

执行翻译

A localizer 有一个 translate 方法,它接受 translation string 或者一个Unicode字符串,它返回一个表示转换的Unicode字符串。在应用程序的视图组件中生成翻译可能如下所示:

1
2
3
4
5
6
7
8
9
from pyramid.i18n import TranslationString

ts = TranslationString('Add ${number}', mapping={'number':1},
                       domain='pyramid')

def aview(request):
    localizer = request.localizer
    translated = localizer.translate(ts) # translation string
    # ... use translated ...

这个 request.localizer 属性将是 pyramid.i18n.Localizer 对象绑定到请求所表示的区域设置名称。从其返回的翻译 pyramid.i18n.Localizer.translate() 方法将取决于 domain 提供的翻译字符串的属性以及本地化程序的区域设置。

注解

如果你在用 Chameleon 模板,您不需要这样预先翻译翻译字符串。见 对翻译字符串的变色龙模板支持 .

实行多元化

A localizer 有一个 pluralize 具有以下签名的方法:

1
2
def pluralize(singular, plural, n, domain=None, mapping=None):
    # ...

最简单的情况是 singularplural 作为Unicode文本传递的参数。这将根据数字的区域设置复数规则返回适当的文本。 n 和插值 mapping .

1
2
3
4
def aview(request):
    localizer = request.localizer
    translated = localizer.pluralize('Item', 'Items', 1, 'mydomain')
    # ... use translated ...

但是,为了支持其他语言, singular 参数应为表示 message identifier . 在这种情况下, plural 值被忽略。 domain 应该是 translation domainmapping 应该是用于 重置价值 转换字符串的插值。

价值 n 将用于查找当前语言的适当复数形式,以及 pluralize 将返回消息ID的Unicode转换 singular . 消息文件必须已定义 singular 作为复数形式的翻译。

提供的参数为 singular 可能是 translation string 对象,但已忽略附加的域和映射信息。

1
2
3
4
5
def aview(request):
    localizer = request.localizer
    num = 1
    translated = localizer.pluralize('item_plural', '${number} items',
        num, 'mydomain', mapping={'number':num})

相应的消息目录必须设置语言复数定义和复数替代项。

1
2
3
4
5
6
7
"Plural-Forms: nplurals=3; plural=n==0 ? 0 : n==1 ? 1 : 2;"

msgid "item_plural"
msgid_plural ""
msgstr[0] "No items"
msgstr[1] "${number} item"
msgstr[2] "${number} items"

有关复数的更多信息,请参见 gettext documentation .

获取请求的区域设置名称

您可以通过使用 pyramid.request.Request.locale_name() 请求的属性。

1
2
def aview(request):
    locale_name = request.locale_name

请求的区域设置名称是动态计算的;它将是当前活动的 locale negotiator ,或者 default locale name 如果现场谈判者返回 None . 您可以通过更改 pyramid.default_locale_name 设置。见 默认区域设置名称 .

一次 locale_name() 第一次运行时,区域设置名称存储在请求对象上。后续呼叫 locale_name() 将返回存储的区域设置名称,而不调用 locale negotiator . 为了避免这种缓存,可以使用 pyramid.i18n.negotiate_locale_name() 功能:

1
2
3
4
from pyramid.i18n import negotiate_locale_name

def aview(request):
    locale_name = negotiate_locale_name(request)

您还可以使用 locale_name 的属性 localizer .

1
2
3
def aview(request):
    localizer = request.localizer
    locale_name = localizer.locale_name

获取作为本地化器属性的区域设置名称等同于通过请求 locale_name() 属性。

执行日期格式和货币格式

Pyramid 本身不为不同的区域设置日期和货币格式。然而, Babel 可以帮助您通过 babel.core.Locale 班级。这个 Babel documentation for this class 提供有关如何执行与日期和货币相关的区域设置操作的最少信息。见 安装Lingua和GetText 有关如何安装babel的信息。

这个 babel.core.Locale 类需要 locale name 作为其构造函数的参数。你可以使用 Pyramid 获取要传递到的请求的区域设置名称的API babel.core.Locale 构造函数。见 获取请求的区域设置名称 . 例如:

1
2
3
4
5
from babel.core import Locale

def aview(request):
    locale_name = request.locale_name
    locale = Locale(locale_name)

对翻译字符串的变色龙模板支持

当A translation string 作为文本呈现的主题 Chameleon 模板渲染器,如果存在合适的翻译,它将自动转换为请求用户的语言。这对于变色龙模板渲染器的zpt和文本变体都是正确的。

例如,在变色龙zpt模板中,下面每个示例中由“some_translation_string”表示的翻译字符串将在呈现之前经过翻译:

1
<span tal:content="some_translation_string"/>
1
<span tal:replace="some_translation_string"/>
1
<span>${some_translation_string}</span>
1
<a tal:attributes="href some_translation_string">Click here</a>

i18n 变色龙的名称空间也将参考 Pyramid 翻译。请参阅https://chameleon.readthedocs.io/en/latest/reference.html translation-i18n。

注解

与变色龙在室外使用不同 Pyramid ,使用时 在内部 Pyramid ,它不支持使用 zope.i18n 翻译框架。使用的应用程序 Pyramid 应该使用本章中记录的特性,而不是 zope.i18n .

第三方 Pyramid 模板呈现器可能无法提供这种现成的支持,可能需要特殊的代码来实现等效功能。对于这些,您可以始终使用中描述的更手动的翻译工具 执行翻译 .

Mako金字塔i18n支持

里面有一个配方 Pyramid Community Cookbook 已命名 Mako Internationalization 它解释了如何将惯用i18n支持添加到 Mako 模板。

金贾2金字塔I18N支架

附加件 pyramid_jinja2 为一个脚手架提供了一个如何在金字塔中使用Jinja2国际化的示例。请参阅文档部分 Internalization (i18n)pcreate template i18n .

“检测”可用语言

其他系统提供的API返回一组“可用语言”,如调用API时磁盘上所有翻译目录中所有语言的联合所示。

是设计出来的 Pyramid 不提供这样的API。相反,应用程序本身负责了解“可用语言”。其基本原理是:任何特定的应用程序部署都必须始终知道应该将其翻译成哪种语言,而不管磁盘上有哪些翻译文件。

原因如下:由于翻译存在于已注册的翻译目录集中的特定语言中,因此此特定部署希望允许翻译到该语言,所以这不是一个给定的。例如,有些翻译可能存在,但它们可能不完整或不正确。或者可能存在对某种语言的翻译,但并非所有翻译域都有。

任何重要的应用程序部署都将始终需要能够有选择地选择只允许某些语言,即使这组语言小于在注册的翻译目录中检测到的所有语言。考虑到这一点,最简单的方法是让应用程序完全负责知道哪些语言被允许翻译,而不是依靠框架从翻译目录文件信息中提取这些信息。

您可以通过使用 pyramid.settings

允许部署人员修改应用程序的 .ini 文件:

1
2
3
4
[app:main]
use = egg:MyProject
# ...
available_languages = fr de en ru

然后作为自定义代码的一部分 locale negotiator

1
2
3
4
5
from pyramid.settings import aslist

def my_locale_negotiator(request):
    languages = aslist(request.registry.settings['available_languages'])
    # ...

这只是一个建议。您可以根据需要创建自己的“可用语言”配置方案。

激活翻译

默认情况下,A Pyramid 应用程序不执行翻译。要打开翻译,必须:

添加翻译目录

gettext 基础机械是否在 Pyramid 翻译机器。翻译目录是组织起来对 gettext . 翻译目录通常包括语言目录列表,每个目录本身都包含 LC_MESSAGES 目录。各 LC_MESSAGES 目录应包含一个或多个 .mo 文件夹。各 .mo 文件表示 message catalog ,用于为应用程序提供翻译。

添加一个 translation directory 注册其所有组成部分 message catalog 您的 Pyramid 可用于翻译服务的应用程序。这包括所有 .mo 在所有文件中找到的文件 LC_MESSAGES 翻译目录中每个区域设置目录中的目录。

您可以使用 pyramid.config.Configurator.add_translation_dirs() 在应用程序启动期间。例如:

1
2
3
from pyramid.config import Configurator
config.add_translation_dirs('my.application:locale/',
                            'another.application:locale/')

在翻译目录中添加的邮件目录 add_translation_dirs() 如果两个翻译目录都包含同一区域设置的翻译,并且 translation domain .

设置区域设置

默认区域设置协商器 (见 默认的区域设置协商器 )正在使用中,您可以通知 Pyramid 在需要执行任何翻译之前,通过执行以下任何操作来获取当前区域设置名称:

  • 设置 _LOCALE_ 请求属性到有效的区域设置名称(通常直接在视图代码中),例如, request._LOCALE_ = 'de' .
  • 确保有效的区域设置名称值位于 request.params 名为的键下的字典 _LOCALE_ . 这通常是通过 _LOCALE_ 查询字符串中的值,或与请求关联的表单发布正文中的值。例如,访问 http://my.application?_LOCALE_=de .
  • 确保有效的区域设置名称值位于 request.cookies 名为的键下的字典 _LOCALE_ . 这通常是设置 _LOCALE_ 先前响应中的cookie,例如, response.set_cookie('_LOCALE_', 'de') .

注解

如果此区域设置协商方案不适合特定应用程序,则可以配置自定义 locale negotiator 根据需要在该应用程序中运行。见 使用自定义区域设置协商器 .

现场谈判者

A locale negotiator 通知操作 localizer 告诉它什么 locale name 与特定请求相关。区域设置协商器是接受请求并返回 locale name . 咨询时间 pyramid.i18n.Localizer.translate()pyramid.i18n.Localizer.pluralize() 被调用。当 locale_name() 被访问或何时访问 negotiate_locale_name() 被调用。

默认的区域设置协商器

大多数应用程序都可以使用默认的区域设置协商器,它不需要额外的编码或配置。

名为 default_locale_negotiator 使用以下步骤确定区域设置名称。

  • 首先,谈判代表寻找 _LOCALE_ 请求对象的属性(可能直接由视图代码或侦听器设置 event
  • 然后它寻找 request.params['_LOCALE_'] 价值。
  • 然后它寻找 request.cookies['_LOCALE_'] 价值。
  • 如果通过请求找不到区域设置,则返回到使用 default locale name (见 与本地化相关的部署设置
  • 最后,如果未显式设置默认区域设置名称,则使用区域设置名称 en .

使用自定义区域设置协商器

现场协商有时是一个充满政策和复杂的过程。如果中描述的(简单)默认区域设置协商方案 激活翻译 不适合您的应用程序,您可以创建一个 locale negotiator . 随后,您可以通过将新创建的区域设置协商器添加到应用程序的配置中来覆盖默认的区域设置协商器。

区域设置协商器只是一个可调用的,它接受一个请求并返回一个 locale nameNone 如果无法确定区域设置。

下面是一个简单的区域协商器的实现:

1
2
3
def my_locale_negotiator(request):
    locale_name = request.params.get('my_locale')
    return locale_name

如果现场谈判者返回 None ,表示 Pyramid 应该使用默认的应用程序区域设置名称。

您可以通过传递一个可以充当谈判者的对象(或 dotted Python name 指的是物体)。 locale_negotiator 论证 Configurator 应用程序启动期间的实例。例如:

1
2
from pyramid.config import Configurator
config = Configurator(locale_negotiator=my_locale_negotiator)

或者,使用 pyramid.config.Configurator.set_locale_negotiator() 方法。

例如:

1
2
3
from pyramid.config import Configurator
config = Configurator()
config.set_locale_negotiator(my_locale_negotiator)