Python代码中的国际化
标准翻译
使用函数ugettext()指定翻译字符串。 按惯例将其导入为较短的别名_以保存输入。
Python的标准库gettext模块将_()安装到全局名称空间中,作为gettext()的别名。 在Django中,我们选择不采取这种做法,原因如下:
- 对于国际字符集(Unicode)支持,ugettext()比gettext()更有用。 有时,您应该使用ugettext_lazy()作为特定文件的默认翻译方法。 如果在全局名称空间中没有_(),开发人员必须考虑哪个是最适合的翻译功能。
- 下划线字符()用于表示Python的交互式shell和doctest测试中的以前的结果。 安装全局()函数会导致干扰。 显式导入ugettext()作为_()可以避免这个问题。
在本例中,文本“welcome to my site”被标记为翻译字符串:
from django.utils.translation import ugettext as _
from django.http import HttpResponse
def my_view(request):
output = _("Welcome to my site.")
return HttpResponse(output)
显然,你可以在不使用别名的情况下对其进行编码。 这个例子与之前的例子相同
from django.utils.translation import ugettext
from django.http import HttpResponse
def my_view(request):
output = ugettext("Welcome to my site.")
return HttpResponse(output)
翻译也适用于计算值。 这个例子与前两个相同:
def my_view(request):
words = ['Welcome', 'to', 'my', 'site.']
output = _(' '.join(words))
return HttpResponse(output)
...和变量。 再次来写这一个相同的例子:
def my_view(request):
sentence = 'Welcome to my site.'
output = _(sentence)
return HttpResponse(output)
(前面两个例子中使用变量或计算值的警告是,Django的翻译字符串检测实用程序django-admin makemessages将无法找到这些字符串,稍后还会介绍makemessage。)
传递给_()或ugettext()的字符串可以占位符,用Python的标准命名字符串插值语法指定。 例:
def my_view(request, m, d):
output = _('Today is %(month)s %(day)s.') % {'month': m, 'day': d}
return HttpResponse(output)
这种技术可以让语言特定的翻译对占位符文本重新排序。 例如,英文翻译可能是“Today is November 26”,而西班牙文翻译可能是“Hoy es 26 de Noviembre。” - 月份和日期占位符交换。
出于这个原因,只要有多个参数,就应该使用命名字符串插值(例如%(day)s)而不是位置插值(例如,%s或%d)。 如果您使用位置插值,翻译将无法重新排列占位符文本。
对译员的标注
如果您想给翻译人员提供有关可翻译字符串的提示,可以在字符串前面的行上添加以Translators关键字为前缀的注释,例如:
def my_view(request):
# Translators: This message appears on the home page only
output = ugettext("Welcome to my site.")
该注释将出现在与位于其下的可翻译结构相关联的结果.po文件中,并且也应该由大多数翻译工具显示。
为了完整起见,这是结果.po文件的相应片段:
#. Translators: This message appears on the home page only
# path/to/python/file.py:123
msgid "Welcome to my site."
msgstr ""
这也适用于模板。有关更多详细信息,请参阅译者注释模板。
将字符串标记为无操作
使用函数django.utils.translation.ugettext_noop()将字符串标记为翻译字符串,而不进行翻译。该字符串稍后将从一个变量转换而来。
如果您的常量字符串应该以源语言存储,因为它们是通过系统或用户进行交换的(例如数据库中的字符串),但应该在最后时间点进行翻译,例如字符串出现时给用户。
多元化
使用函数django.utils.translation.ungettext()指定复数消息。
ungettext有三个参数:单数翻译字符串,复数翻译字符串和对象数量。
当需要将Django应用程序本地化为复数形式的数量和复杂度大于英语所用的两种形式(单数为'object'且count为所有情况的'objects')时,此函数非常有用不同于一个,不管它的价值。)
from django.utils.translation import ungettext
from django.http import HttpResponse
def hello_world(request, count):
page = ungettext(
'there is %(count)d object',
'there are %(count)d objects',
count
) % {
'count': count,
}
return HttpResponse(page)
在这个例子中,对象的数量作为计数传递给翻译语言变量。
请注意,多元化是复杂的,并且在每种语言中工作方式不同 比较计数
到1并不总是正确的规则。 此代码看起来很复杂,但会对某些语言产生不正确的结果:
from django.utils.translation import ungettext
from myapp.models import Report
count = Report.objects.count()
if count == 1:
name = Report._meta.verbose_name
else:
name = Report._meta.verbose_name_plural
text = ungettext(
'There is %(count)d %(name)s available.',
'There are %(count)d %(name)s available.',
count
) % {
'count': count,
'name': name
}
不要试图实现你自己的单数或复数逻辑,它不会是正确的。 在这种情况下,请考虑如下内容:
text = ungettext(
'There is %(count)d %(name)s object available.',
'There are %(count)d %(name)s objects available.',
count
) % {
'count': count,
'name': Report._meta.verbose_name,
}
使用ungettext()时,确保对文字中包含的每个外推变量使用单个名称。 在上面的示例中,请注意我们如何在两个翻译字符串中使用名称Python变量。 这个例子除了在上面提到的某些语言中不正确之外,还会失败:
text = ungettext(
'There is %(count)d %(name)s available.',
'There are %(count)d %(plural_name)s available.',
count
) % {
'count': Report.objects.count(),
'name': Report._meta.verbose_name,
'plural_name': Report._meta.verbose_name_plural
}
运行django-admin compilemessages时会出现错误:
a format specification for argument 'name', as in 'msgstr[0]', doesn't exist in 'msgi\
d'
语境标记
有时单词有几个含义,如英语中的“May”,指的是月份名称和动词。 为了使翻译人员能够在不同的上下文中正确翻译这些单词,如果字符串需要复数形式,可以使用django.utils.translation.pgettext()函数或django.utils.translation.npgettext()函数。 两者都将上下文字符串作为第一个变量。
在生成的.po文件中,字符串将出现的频率与同一字符串的不同上下文标记(该上下文将出现在msgctxt行中)相同,允许翻译器为它们中的每一个提供不同的翻译。
例如:
from django.utils.translation import pgettext
month = pgettext("month name", "May")
或者
from django.db import models
from django.utils.translation import pgettext_lazy
class MyThing(models.Model):
name = models.CharField(help_text=pgettext_lazy(
'help text for MyThing model', 'This is the help text'))
将出现在.po文件中,如下所示:
msgctxt "month name"
msgid "May"
msgstr ""
上下文标记也被trans和blocktrans模板标签支持。
懒惰的翻译
在django.utils.translation中使用懒惰版本的翻译函数(可以通过名称中的懒惰后缀轻松识别)来懒惰地翻译字符串 - 当值被访问时,而不是在被调用时。
这些函数存储对字符串的惰性引用 - 而不是实际的转换。翻译本身将在字符串上下文中使用字符串时完成,例如在模板呈现中。
当这些函数的调用位于模块加载时执行的代码路径中时,这是非常重要的。
这是定义模型,表单和模型表单时很容易发生的事情,因为Django实现了这些,使得它们的字段实际上是类级属性。出于这个原因,请确保在以下情况下使用懒惰翻译。
模型领域和关系
例如,要翻译以下模型中名称字段的帮助文本,请执行以下操作:
from django.db import models
from django.utils.translation import ugettext_lazy as _
class MyThing(models.Model):
name = models.CharField(help_text=_('This is the help text'))
您可以使用其verbose_name选项将ForeignKey,ManyToManyField或OneToOneField关系的名称标记为可翻译的:
class MyThing(models.Model):
kind = models.ForeignKey(ThingKind, related_name='kinds', verbose_name=_('kind'\
))
就像你在verbose_name中做的那样,你应该为关系提供一个小写的详细名称文本,因为Django会在需要时自动标题大小写。
模拟详细名称值
建议始终提供明确的verbose_name和verbose_name_plural选项,而不是依赖于以英语为中心的回退,并且通过查看模型的类名称来确定Django执行的详细名称,这种方式有些天真:
from django.db import models
from django.utils.translation import ugettext_lazy as _
class MyThing(models.Model):
name = models.CharField(_('name'), help_text=_('This is the help text'))
class Meta:
verbose_name = _('my thing')
verbose_name_plural = _('my things')
模型方法的short_description属性值
对于模型方法,您可以使用short_description属性为Django和管理网站提供翻译:
from django.db import models
from django.utils.translation import ugettext_lazy as _
class MyThing(models.Model):
kind = models.ForeignKey(ThingKind, related_name='kinds', verbose_name=_('kind'))
def is_mouse(self):
return self.kind.type == MOUSE_TYPE
is_mouse.short_description = _('Is it a mouse?')
使用懒惰翻译对象
ugettext_lazy()调用的结果可用于Python中任何使用Unicode字符串(类型为unicode的对象)的地方。 如果您尝试在需要字节串(str对象)的地方使用它,则事情将无法按预期工作,因为ugettext_lazy()对象不知道如何将其自身转换为字节串。 你不能在一个字符串中使用一个Unicode字符串,所以这与正常的Python行为一致。 例如:
# This is fine: putting a unicode proxy into a unicode string.
"Hello %s" % ugettext_lazy("people")
# This will not work, since you cannot insert a unicode object
# into a bytestring (nor can you insert our unicode proxy there)
b"Hello %s" % ugettext_lazy("people")
如果您看到输出如“hello <django.utils.functional ...>”,则您已尝试将ugettext_lazy()的结果插入到字符串中。 这是你的代码中的一个错误。
如果您不喜欢长的ugettext_lazy名称,则可以将其作为_(下划线)来使用,如下所示:
from django.db import models
from django.utils.translation import ugettext_lazy as _
class MyThing(models.Model):
name = models.CharField(help_text=_('This is the help text'))
使用ugettext_lazy()和ungettext_lazy()标记模型和实用程序函数中的字符串是常见操作。 当你在代码的其他地方使用这些对象时,应该确保你不会意外地将它们转换为字符串,因为它们应该尽可能晚地进行转换(以便正确的区域设置生效)。 这需要使用下面描述的帮助函数。
懒惰的翻译和复数
当对复数形式的字符串([u] n [p] gettext_lazy)使用惰性转换时,通常不会在字符串定义时知道number参数。 因此,您有权传递**名称而不是整数作为数字参数。 然后在字符串插值过程中,在该字典下查找数字。 这里是一个例子:
from django import forms
from django.utils.translation import ugettext_lazy
class MyForm(forms.Form):
error_message = ungettext_lazy("You only provided %(num)d
argument", "You only provided %(num)d arguments", 'num')
def clean(self):
# ...
if error:
raise forms.ValidationError(self.error_message %
{'num': number})
如果该字符串恰好包含一个未命名的占位符,则可以直接插入数字参数:
class MyForm(forms.Form):
error_message = ungettext_lazy("You provided %d argument",
"You provided %d arguments")
def clean(self):
# ...
if error:
raise forms.ValidationError(self.error_message % number)
连接字符串:string_concat()
标准Python字符串连接(''.join([...]))在包含惰性转换对象的列表上不起作用。
相反,您可以使用django.utils.translation.string_concat(),它会创建一个延迟对象,将其内容连接起来,并只在结果包含在字符串中时将其转换为字符串。 例如:
from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy
# ...
name = ugettext_lazy('John Lennon')
instrument = ugettext_lazy('guitar')
result = string_concat(name, ': ', instrument)
在这种情况下,当结果本身用于字符串时(通常在模板呈现时),结果中的惰性翻译将仅转换为字符串。
懒惰在延迟翻译中的其他用途
对于任何其他您希望延迟翻译的情况,但必须将可转换字符串作为参数传递给另一个函数,您可以自己将此函数包装在惰性调用中。 例如:
from django.utils import six # Python 3 compatibility
from django.utils.functional import lazy
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
mark_safe_lazy = lazy(mark_safe, six.text_type)
然后
lazy_string = mark_safe_lazy(_("<p>My <strong>string!</strong></p>"))
语言的本地化名称
get_language_info()函数提供有关语言的详细信息:
>>> from django.utils.translation import get_language_info
>>> li = get_language_info('de')
>>> print(li['name'], li['name_local'], li['bidi'])
German Deutsch False
字典的name和name_local属性分别包含英语语言和语言本身的名称。 bidi属性仅适用于双向语言。
语言信息的来源是django.conf.locale模块。 类似的访问这些信息可用于模板代码。 见下文。