Skip to main content

翻译

概述

为了使Django项目可翻译,你必须为你的Python代码和模板添加最少的钩子。这些钩子称为 翻译字符串。他们告诉Django:“如果此文本的翻译是以该语言提供的,则此文本应翻译为最终用户的语言。这是你的责任标记可翻译字符串;系统只能翻译它知道的字符串。

Django然后提供实用程序提取翻译字符串到 message file。此文件是翻译人员提供与目标语言的翻译字符串相当的方便方法。一旦翻译者填写了消息文件,它必须被编译。这个过程依赖于GNU gettext工具集。

一旦这完成,Django负责根据用户的语言偏好,按照每种可用语言即时翻译Web应用程序。

Django的国际化钩子默认是开启的,这意味着在框架的某些地方有一点i18n相关的开销。如果您不使用国际化,您应该花两秒钟在您的设置文件中设置 USE_I18N = False。然后Django将做一些优化,以免加载国际化机制。

注解

还有一个独立但相关的 USE_L10N 设置,控制Django是否应实现格式本地化。有关详细信息,请参阅 格式本地化

注解

确保您已为您的项目激活翻译(最快的方法是检查 MIDDLEWARE 是否包括 django.middleware.locale.LocaleMiddleware)。如果你还没有,请参阅 Django如何发现语言偏好

国际化:在Python代码中

标准翻译

通过使用函数 ugettext() 指定翻译字符串。它的惯例是将其导入为较短的别名 _,以保存输入。

注解

Python的标准库 gettext 模块将 _() 安装到全局命名空间中,作为 gettext() 的别名。在Django,我们选择不遵循这种做法,原因如下:

  1. 对于国际字符集(Unicode)支持,ugettext()gettext() 更有用。有时,您应该使用 ugettext_lazy() 作为特定文件的默认翻译方法。在全局命名空间中没有 _(),开发人员必须考虑哪个是最适当的翻译功能。

  2. 下划线字符(_)用于表示Python的交互式shell和doctest测试中的“上一个结果”。安装全局 _() 功能会导致干扰。将 ugettext() 显式导入为 _() 可避免此问题。

什么功能可以被别名为 _

由于 xgettextmakemessages 使用)的工作原理,只有接受单个字符串参数的函数可以作为 _ 导入:

在本示例中,文本 "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 将无法找到这些字符串。更多内容见之后的 makemessages。)

传递给 _()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 ""

这也适用于模板。有关详细信息,请参阅 模板中翻译员的评论

将字符串标记为no-op

使用函数 django.utils.translation.ugettext_noop() 将字符串标记为翻译字符串,而不翻译它。字符串稍后从变量翻译。

如果您有应该存储在源语言中的常量字符串,因为它们通过系统或用户交换(如数据库中的字符串),但应该在最后可能的时间点翻译,例如当字符串被呈现给用户。

多元化

使用函数 django.utils.translation.ungettext() 指定多个消息。

ungettext 有三个参数:单数翻译字符串,多个翻译字符串和对象数。

当您需要您的Django应用程序可以本地化到语言的 复数形式 的数量和复杂性大于两种形式使用的英语(’对象’为单数和’对象’的所有情况下,count 是不同的从一个,不管其价值)。

例如:

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)

在本示例中,对象的数量作为 count 变量传递到翻译语言。

注意,复数化是复杂的,并且在每种语言中工作不同。将 count 与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() 时,请确保对文本中包含的每个外推变量使用单个名称。在上面的例子中,注意我们如何在两个翻译字符串中使用 name 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 'msgid'

注解

多种形式和po文件

Django不支持在po文件中的自定义复数方程。当所有翻译目录被合并时,仅考虑主Django po文件(在 django/conf/locale/<lang_code>/LC_MESSAGES/django.po 中)的复数形式。所有其他po文件中的多个表单将被忽略。因此,您不应在项目或应用程序po文件中使用不同的复数方程。

上下文标记

有时候,词语有几个意义,例如英语中的 "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 ""

transblocktrans 模板标签也支持上下文标记。

延迟翻译

使用 django.utils.translation 中的翻译函数的惰性版本(可以通过名称中的 lazy 后缀轻松识别)来平移字符串 - 当值被访问时,而不是当它们被调用时。

这些函数存储对字符串的惰性引用 - 而不是实际的翻译。当字符串在字符串上下文中使用时,例如在模板呈现中,翻译本身将被完成。

当对这些函数的调用位于在模块加载时执行的代码路径中时,这是至关重要的。

这是定义模型,形式和模型形式时容易发生的事情,因为Django实现这些,使得他们的字段实际上是类级别的属性。因此,请确保在以下情况下使用延迟翻译:

模型字段和关系 verbose_namehelp_text 选项值

例如,要转换以下模型中 name 字段的帮助文本,请执行以下操作:

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 选项将 ForeignKeyManyToManyFieldOneToOneField 关系的名称标记为可翻译:

class MyThing(models.Model):
    kind = models.ForeignKey(
        ThingKind,
        on_delete=models.CASCADE,
        related_name='kinds',
        verbose_name=_('kind'),
    )

就像你在 verbose_name 中一样,你应该为关系提供一个小写的详细名称文本,Django会在需要时自动定义它。

模型详细名称值

建议始终提供明确的 verbose_nameverbose_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 属性值

对于模型方法,您可以向Django和具有 short_description 属性的管理网站提供翻译:

from django.db import models
from django.utils.translation import ugettext_lazy as _

class MyThing(models.Model):
    kind = models.ForeignKey(
        ThingKind,
        on_delete=models.CASCADE,
        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() 调用的结果可以用于任何你在其他Django代码中使用unicode字符串(str 对象)的地方,但它可能不适用于任意Python代码。例如,以下将不工作,因为 要求 库不处理 ugettext_lazy 对象:

body = ugettext_lazy("I \u2764 Django")  # (unicode :heart:)
requests.post('https://example.com/send', data={'body': body})

您可以通过在将 ugettext_lazy() 对象转换为非Django代码之前将其转换为文本字符串来避免此类问题:

requests.post('https://example.com/send', data={'body': str(body)})

使用 unicode 代替Python 2上的 str,或者使用 six.text_type 来支持Python 2和3。

如果尝试使用一个 ugettext_lazy() 结果,其中一个字节(一个 bytes 对象)是期望的,事情将无法正常工作,因为 ugettext_lazy() 对象不知道如何将自身转换为一个字节。你不能在一个bytestring中使用一个unicode字符串,因此这和普通的Python行为是一致的。例如,将unicode代理放入unicode字符串是好的:

"Hello %s" % ugettext_lazy("people")

但是,您不能将unicode对象插入到bytestring,也不能在那里插入unicode代理:

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 参数。因此,您有权传递键名称而不是传递一个整数作为 number 参数。然后在字符串插值期间,在该键下的字典中查找 number。这里是例子:

from django import forms
from django.utils.translation import ungettext_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})

如果字符串正好包含一个未命名的占位符,则可以直接使用 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(),它创建一个连接其内容的延迟对象,只有当结果包含在字符串中时,and 才会将它们转换为字符串。例如:

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)

在这种情况下,result 中的延迟翻译只有在字符串中使用 result 自身时才会转换为字符串(通常在模板呈现时)。

延迟翻译的其他用途

对于任何其他情况下,你想延迟翻译,但必须将可翻译字符串作为参数传递给另一个函数,你可以将这个函数包装在一个懒惰的调用自己。例如:

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()[源代码]

get_language_info() 功能提供有关语言的详细信息:

>>> from django.utils.translation import activate, get_language_info
>>> activate('fr')
>>> li = get_language_info('de')
>>> print(li['name'], li['name_local'], li['name_translated'], li['bidi'])
German Deutsch Allemand False

字典的 namename_localname_translated 属性分别包含英语,语言本身和当前活动语言的语言名称。 bidi 属性仅对于双向语言为True。

语言信息的来源是 django.conf.locale 模块。类似的访问此信息可用于模板代码。见下文。

Changed in Django 1.9:

已添加 'name_translated' 属性。

国际化:在模板代码中

Django模板 中的翻译使用两个模板标签和与Python代码略有不同的语法。要让您的模板访问这些标签,请将 {% load i18n %} 放在模板的顶部。与所有模板代码一样,此代码需要加载到使用翻译的所有模板中,即使是已从加载了 i18n 代码的其他模板扩展的模板。

trans 模板标记

{% trans %} 模板标签可转换常量字符串(用单引号或双引号括起来)或可变内容:

<title>{% trans "This is the title." %}</title>
<title>{% trans myvar %}</title>

如果存在 noop 选项,则仍会进行变量查找,但会跳过翻译。这在“固定”将来需要翻译的内容时很有用:

<title>{% trans "myvar" noop %}</title>

在内部,内联翻译使用 ugettext() 调用。

如果将模板var(上面的 myvar)传递给标签,标签将首先在运行时将此变量解析为字符串,然后在消息目录中查找该字符串。

不能在 {% trans %} 内的字符串中混合模板变量。如果您的翻译需要带有变量(占位符)的字符串,请改用 {% blocktrans %}

如果您希望检索已翻译的字符串而不显示它,则可以使用以下语法:

{% trans "This is the title" as the_title %}

<title>{{ the_title }}</title>
<meta name="description" content="{{ the_title }}">

实际上,你将使用它来获取一个字符串,你可以在模板的多个地方使用,或者你可以使用输出作为其他模板标签或过滤器的参数:

{% trans "starting point" as start %}
{% trans "end point" as end %}
{% trans "La Grande Boucle" as race %}

<h1>
  <a href="/" title="{% blocktrans %}Back to '{{ race }}' homepage{% endblocktrans %}">{{ race }}</a>
</h1>
<p>
{% for stage in tour_stages %}
    {% cycle start end %}: {{ stage }}{% if forloop.counter|divisibleby:2 %}<br />{% else %}, {% endif %}
{% endfor %}
</p>

{% trans %} 还支持使用 context 关键字的 contextual markers

{% trans "May" context "month name" %}

blocktrans 模板标记

trans 标签相反,blocktrans 标签允许您使用占位符来标记由字面和可变内容组成的复杂句子以进行翻译:

{% blocktrans %}This string will have {{ value }} inside.{% endblocktrans %}

要翻译模板表达式 - 例如访问对象属性或使用模板过滤器,您需要将表达式绑定到本地变量,以在翻译块中使用。例子:

{% blocktrans with amount=article.price %}
That will cost $ {{ amount }}.
{% endblocktrans %}

{% blocktrans with myvar=value|filter %}
This will have {{ myvar }} inside.
{% endblocktrans %}

您可以在单个 blocktrans 标记中使用多个表达式:

{% blocktrans with book_t=book|title author_t=author|title %}
This is {{ book_t }} by {{ author_t }}
{% endblocktrans %}

注解

以前更详细的格式仍然支持:{% blocktrans with book|title as book_t and author|title as author_t %}

blocktrans 标记中不允许使用其他块标记(例如 {% for %}{% if %})。

如果解析其中一个块参数失败,blocktrans将通过使用 deactivate_all() 函数暂时停用当前活动的语言来回退到默认语言。

此标签还提供了复数。使用它:

  • 指定并绑定名称为 count 的计数器值。此值将用于选择合适的复数形式之一。

  • 指定将它们与 {% blocktrans %}{% endblocktrans %} 标签内的 {% plural %} 标签分开的单数和复数形式。

一个例子:

{% blocktrans count counter=list|length %}
There is only one {{ name }} object.
{% plural %}
There are {{ counter }} {{ name }} objects.
{% endblocktrans %}

一个更复杂的例子:

{% blocktrans with amount=article.price count years=i.length %}
That will cost $ {{ amount }} per year.
{% plural %}
That will cost $ {{ amount }} per {{ years }} years.
{% endblocktrans %}

当您同时使用复数特征并将值绑定到局部变量以及计数器值时,请记住,blocktrans 结构在内部转换为 ungettext 调用。这意味着相同的 关于ungettext变量的注释 适用。

反向URL查找不能在 blocktrans 内执行,并且应该预先检索(和存储):

{% url 'path.to.view' arg arg2 as the_url %}
{% blocktrans %}
This is a URL: {{ the_url }}
{% endblocktrans %}

如果您希望检索已翻译的字符串而不显示它,则可以使用以下语法:

{% blocktrans asvar the_title %}The title is {{ title }}.{% endblocktrans %}
<title>{{ the_title }}</title>
<meta name="description" content="{{ the_title }}">

实际上,您将使用它来获取一个字符串,您可以在模板中的多个位置使用,或者您可以使用输出作为其他模板标签或过滤器的参数。

Changed in Django 1.9:

添加了 asvar 语法。

{% blocktrans %} 还支持使用 context 关键字的 contextual markers

{% blocktrans with name=user.username context "greeting" %}Hi {{ name }}{% endblocktrans %}

{% blocktrans %} 支持的另一个功能是 trimmed 选项。此选项将从 {% blocktrans %} 标记的内容的开头和结尾删除换行符,替换行开头和结尾处的任何空格,并使用空格字符将所有行合并为一个,以将它们分隔开。这对缩进 {% blocktrans %} 标记的内容非常有用,而没有缩进字符在PO文件中的相应条目中结束,这使得翻译过程更容易。

例如,以下 {% blocktrans %} 标记:

{% blocktrans trimmed %}
  First sentence.
  Second paragraph.
{% endblocktrans %}

将导致PO文件中的条目 "First sentence. Second paragraph.",与 "\n  First sentence.\n  Second sentence.\n" 相比,如果未指定 trimmed 选项。

传递给标记和过滤器的字符串文字

您可以使用熟悉的 _() 语法将作为参数传递的字符串文字转换为标记和过滤器:

{% some_tag _("Page not found") value|yesno:_("yes,no") %}

在这种情况下,无论是标签和过滤器将看到转换后的字符串,所以他们不需要知道的翻译。

注解

在本示例中,翻译基础结构将传递字符串 "yes,no",而不是单个字符串 "yes""no"。翻译后的字符串需要包含逗号,以便过滤器解析代码知道如何拆分参数。例如,德语翻译者可能将字符串 "yes,no" 翻译为 "ja,nein" (保持逗号完整)。

模板中翻译员的评论

Python代码 一样,这些笔记可以使用注释指定,使用 comment 标签:

{% comment %}Translators: View verb{% endcomment %}
{% trans "View" %}

{% comment %}Translators: Short intro blurb{% endcomment %}
<p>{% blocktrans %}A multiline translatable
literal.{% endblocktrans %}</p>

或与 {# ... #} 单行注释构造

{# Translators: Label of a button that triggers search #}
<button type="submit">{% trans "Go" %}</button>

{# Translators: This is a text of the base template #}
{% blocktrans %}Ambiguous translatable block of text{% endblocktrans %}

注解

为了完整性,这些是所得到的 .po 文件的相应片段:

#. Translators: View verb
# path/to/template/file.html:10
msgid "View"
msgstr ""

#. Translators: Short intro blurb
# path/to/template/file.html:13
msgid ""
"A multiline translatable"
"literal."
msgstr ""

# ...

#. Translators: Label of a button that triggers search
# path/to/template/file.html:100
msgid "Go"
msgstr ""

#. Translators: This is a text of the base template
# path/to/template/file.html:103
msgid "Ambiguous translatable block of text"
msgstr ""

在模板中切换语言

如果要在模板中选择语言,可以使用 language 模板标记:

{% load i18n %}

{% get_current_language as LANGUAGE_CODE %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
<p>{% trans "Welcome to our page" %}</p>

{% language 'en' %}
    {% get_current_language as LANGUAGE_CODE %}
    <!-- Current language: {{ LANGUAGE_CODE }} -->
    <p>{% trans "Welcome to our page" %}</p>
{% endlanguage %}

虽然第一次出现的“欢迎来到我们的页面”使用当前语言,第二次将始终是英语。

其他标签

这些标签还需要 {% load i18n %}

get_available_languages

{% get_available_languages as LANGUAGES %} 返回一个元组列表,其中第一个元素是 language code,第二个是语言名(翻译为当前活动的语言环境)。

get_current_language

{% get_current_language as LANGUAGE_CODE %} 将当前用户的首选语言作为字符串返回。示例:en-us。见 Django如何发现语言偏好

get_current_language_bidi

{% get_current_language_bidi as LANGUAGE_BIDI %} 返回当前语言环境的方向。如果是 True,它是一个从右到左的语言,例如。希伯来语,阿拉伯语。如果 False 是从左到右的语言,例如。英语,法语,德语等。

如果启用 django.template.context_processors.i18n 上下文处理器,则每个 RequestContext 将访问如上定义的 LANGUAGESLANGUAGE_CODELANGUAGE_BIDI

get_language_info

您还可以使用提供的模板标记和过滤器检索有关任何可用语言的信息。要获取有关单一语言的信息,请使用 {% get_language_info %} 标签:

{% get_language_info for LANGUAGE_CODE as lang %}
{% get_language_info for "pl" as lang %}

然后,您可以访问该信息:

Language code: {{ lang.code }}<br />
Name of language: {{ lang.name_local }}<br />
Name in English: {{ lang.name }}<br />
Bi-directional: {{ lang.bidi }}
Name in the active language: {{ lang.name_translated }}
New in Django 1.9:

已添加 name_translated 属性。

get_language_info_list

您还可以使用 {% get_language_info_list %} 模板标签来检索语言列表的信息(例如,LANGUAGES 中指定的活动语言)。有关如何使用 {% get_language_info_list %} 显示语言选择器的示例,请参阅 关于set_language重定向视图的部分

除了 LANGUAGES 样式的元组列表,{% get_language_info_list %} 支持简单的语言代码列表。如果你在你的观点:

context = {'available_languages': ['en', 'es', 'fr']}
return render(request, 'mytemplate.html', context)

您可以在模板中迭代这些语言:

{% get_language_info_list for available_languages as langs %}
{% for lang in langs %} ... {% endfor %}

模板过滤器

为方便起见,还有一些简单的过滤器:

  • {{ LANGUAGE_CODE|language_name }} (“德语”)

  • {{ LANGUAGE_CODE|language_name_local }} (“Deutsch”)

  • {{ LANGUAGE_CODE|language_bidi }} (假)

  • {{ LANGUAGE_CODE|language_name_translated }} (“německy”,当主动语言是捷克语时)

New in Django 1.9:

加入 language_name_translated 过滤器。

国际化:在JavaScript代码中

将翻译添加到JavaScript会产生一些问题:

  • JavaScript代码无权访问 gettext 实现。

  • JavaScript代码无法访问 .po.mo 文件;它们需要由服务器交付。

  • JavaScript的翻译目录应尽可能小。

Django为这些问题提供了一个集成的解决方案:它将翻译成JavaScript,所以你可以从JavaScript中调用 gettext 等。

这些问题的主要解决方案是以下 JavaScriptCatalog 视图,它生成具有模仿 gettext 接口的函数的JavaScript代码库,以及翻译字符串数组。

JavaScriptCatalog 视图

New in Django 1.10.
class JavaScriptCatalog

一个产生具有模拟 gettext 接口的函数的JavaScript代码库以及翻译字符串数组的视图。

属性

domain

包含要在视图输出中添加的字符串的翻译域。默认为 'djangojs'

packages

已安装应用程序中的 application names 列表。这些应用程序应包含 locale 目录。所有这些目录以及在 LOCALE_PATHS 中找到的所有目录(总是包括)被合并到一个目录中。默认为 None,这意味着所有 INSTALLED_APPS 的所有可用翻译都在JavaScript输出中提供。

带默认值的示例:

from django.views.i18n import JavaScriptCatalog

urlpatterns = [
    url(r'^jsi18n/$', JavaScriptCatalog.as_view(), name='javascript-catalog'),
]

自定义包的示例:

urlpatterns = [
    url(r'^jsi18n/myapp/$',
        JavaScriptCatalog.as_view(packages=['your.app.label']),
        name='javascript-catalog'),
]

如果您的根URLconf使用 i18n_patterns(),则 JavaScriptCatalog 也必须由 i18n_patterns() 包装,以便正确生成目录。

示例 i18n_patterns():

from django.conf.urls.i18n import i18n_patterns

urlpatterns = i18n_patterns(
    url(r'^jsi18n/$', JavaScriptCatalog.as_view(), name='javascript-catalog'),
)

翻译的优先级是使得在 packages 参数中出现的包的优先级高于在开始时出现的优先级。这在同一文字的翻译冲突的情况下很重要。

如果在站点上使用多个 JavaScriptCatalog 视图,并且其中一些视图定义了相同的字符串,则最后加载的目录中的字符串优先。

javascript_catalog 视图

javascript_catalog(request, domain='djangojs', packages=None)[源代码]

1.10 版后已移除: javascript_catalog() 已弃用,赞成 JavaScriptCatalog,将在Django 2.0中删除。

这些问题的主要解决方案是 django.views.i18n.javascript_catalog() 视图,它发送一个带有模拟 gettext 接口的函数的JavaScript代码库,以及一个翻译字符串数组。这些翻译字符串取自应用程序或Django核心,根据您在 info_dict 或URL中指定的。还包括 LOCALE_PATHS 中列出的路径。

你这样挂钩:

from django.views.i18n import javascript_catalog

js_info_dict = {
    'packages': ('your.app.package',),
}

urlpatterns = [
    url(r'^jsi18n/$', javascript_catalog, js_info_dict, name='javascript-catalog'),
]

packages 中的每个字符串都应采用Python点分包语法(与 INSTALLED_APPS 中的字符串格式相同),并应引用包含 locale 目录的包。如果指定多个包,则所有这些目录将合并到一个目录中。如果您的JavaScript使用来自不同应用程序的字符串,这将非常有用。

翻译的优先级是使得在 packages 参数中出现的包的优先级高于在开始处出现的包,这在对同一个字面的翻译冲突的情况下是重要的。

默认情况下,视图使用 djangojs gettext域。这可以通过改变 domain 参数来改变。

您可以通过将包放入URL模式来使视图动态:

urlpatterns = [
    url(r'^jsi18n/(?P<packages>\S+?)/$', javascript_catalog, name='javascript-catalog'),
]

这样,您可以将包指定为由URL中的“+”号分隔的包名称列表。如果您的网页使用来自不同应用程式的程式码,且这项变更经常发生,而且您不想提取一个大型目录档案,这项功能就特别实用。作为安全措施,这些值只能是 django.conf 或来自 INSTALLED_APPS 设置的任何程序包。

您还可以将目录拆分为多个URL,并根据需要在您的网站中加载它们:

js_info_dict_app = {
    'packages': ('your.app.package',),
}

js_info_dict_other_app = {
    'packages': ('your.other.app.package',),
}

urlpatterns = [
    url(r'^jsi18n/app/$', javascript_catalog, js_info_dict_app),
    url(r'^jsi18n/other_app/$', javascript_catalog, js_info_dict_other_app),
]

如果在站点上使用多个 javascript_catalog,并且其中一些定义了相同的字符串,则最后加载的目录中的字符串优先。

Changed in Django 1.9:

在Django 1.9之前,目录完全相互覆盖,你一次只能使用一个。

LOCALE_PATHS 设置中列出的路径中找到的JavaScript翻译也始终包括在内。为了与用于Python和模板的翻译查找顺序算法保持一致,LOCALE_PATHS 中列出的目录具有最高优先级,首先出现的优先级高于稍后出现的优先级。

使用JavaScript翻译目录

要使用目录,只需拉动动态生成的脚本,如下所示:

<script type="text/javascript" src="{% url 'javascript-catalog' %}"></script>

这使用反向URL查找来查找JavaScript目录视图的URL。加载目录后,您的JavaScript代码可以使用以下方法:

  • gettext

  • ngettext

  • interpolate

  • get_format

  • gettext_noop

  • pgettext

  • npgettext

  • pluralidx

gettext

gettext 函数的行为类似于Python代码中的标准 gettext 接口:

document.write(gettext('this is to be translated'));

ngettext

ngettext 功能提供了多个词和短语的接口:

var object_count = 1 // or 0, or 2, or 3, ...
s = ngettext('literal for the singular case',
        'literal for the plural case', object_count);

interpolate

interpolate 函数支持动态填充格式字符串。插值语法是从Python借用的,因此 interpolate 函数支持位置和命名插值:

  • 位置插值:obj 包含一个JavaScript数组对象,其元素值然后按照它们出现的相同顺序在其相应的 fmt 占位符中顺序插值。例如:

    fmts = ngettext('There is %s object. Remaining: %s',
            'There are %s objects. Remaining: %s', 11);
    s = interpolate(fmts, [11, 20]);
    // s is 'There are 11 objects. Remaining: 20'
    
  • 命名插值:通过将可选的布尔型 named 参数传递为 true 来选择此模式。 obj 包含JavaScript对象或关联数组。例如:

    d = {
        count: 10,
        total: 50
    };
    
    fmts = ngettext('Total: %(total)s, there is %(count)s object',
    'there are %(count)s of a total of %(total)s objects', d.count);
    s = interpolate(fmts, d, true);
    

你不应该使用字符串插值,但是仍然是JavaScript,所以代码必须重复正则表达式替换。这不像Python中的字符串插值一样快,所以保持它在那些你真正需要它的情况下(例如,与 ngettext 结合产生正确的复数)。

get_format

get_format 功能可以访问配置的i18n格式化设置,并可以检索给定设置名称的格式字符串:

document.write(get_format('DATE_FORMAT'));
// 'N j, Y'

它可以访问以下设置:

这对于保持与Python呈现的值的格式一致性很有用。

gettext_noop

这模拟 gettext 函数,但什么都不做,返回任何传递给它:

document.write(gettext_noop('this will not be translated'));

这对于剔除将来需要翻译的代码部分很有用。

pgettext

pgettext 函数的行为类似于Python变体(pgettext()),提供上下文翻译的词:

document.write(pgettext('month name', 'May'));

npgettext

npgettext 函数的行为类似于Python变体(npgettext()),提供了一个 多元化 上下文译词:

document.write(npgettext('group', 'party', 1));
// party
document.write(npgettext('group', 'party', 2));
// parties

pluralidx

pluralidx 函数以与 pluralize 模板过滤器类似的方式工作,确定给定的 count 是否应该使用字的复数形式:

document.write(pluralidx(0));
// true
document.write(pluralidx(1));
// false
document.write(pluralidx(2));
// true

在最简单的情况下,如果不需要定制复数,则对于所有其他数字,返回针对整数 1falsetrue

然而,在所有语言中,复数并不是这么简单。如果语言不支持复数,则提供空值。

此外,如果围绕复数有复杂的规则,目录视图将呈现条件表达式。这将评估 true (应为pluralize)或 false (应为 pluralize)值。

JSONCatalog 视图

New in Django 1.10.
class JSONCatalog

为了使用另一个客户端库来处理翻译,您可能希望利用 JSONCatalog 视图。它类似于 JavaScriptCatalog,但返回一个JSON响应。

请参阅 JavaScriptCatalog 的文档以了解可能的值和 domainpackages 属性的使用。

响应格式如下:

{
    "catalog": {
        # Translations catalog
    },
    "formats": {
        # Language formats for date, time, etc.
    },
    "plural": "..."  # Expression for plural forms, or null.
}

json_catalog 视图

New in Django 1.9.
json_catalog(request, domain='djangojs', packages=None)

1.10 版后已移除: json_catalog() 已弃用,赞成 JSONCatalog,将在Django 2.0中删除。

为了使用另一个客户端库来处理翻译,您可能希望利用 json_catalog() 视图。它类似于 javascript_catalog(),但返回一个JSON响应。

JSON对象包含i18n格式设置(可用于 get_format 的设置),复数规则(作为GNU gettext Plural-Forms 表达式的 plural 部分)和翻译字符串。翻译字符串取自应用程序或Django自己的翻译,根据通过 urlpatterns 参数或作为请求参数指定。还包括 LOCALE_PATHS 中列出的路径。

该视图连接到您的应用程序,并以与 javascript_catalog() 相同的方式配置(即,domainpackages 参数的行为相同):

from django.views.i18n import json_catalog

js_info_dict = {
    'packages': ('your.app.package',),
}

urlpatterns = [
    url(r'^jsoni18n/$', json_catalog, js_info_dict),
]

响应格式如下:

{
    "catalog": {
        # Translations catalog
    },
    "formats": {
        # Language formats for date, time, etc.
    },
    "plural": "..."  # Expression for plural forms, or null.
}

性能注意事项

各种JavaScript/JSON i18n视图根据每个请求的 .mo 文件生成目录。由于它的输出是恒定的,至少对于给定版本的网站,它是缓存的一个很好的候选者。

服务器端缓存将降低CPU负载。它很容易实现与 cache_page() 装饰。要在翻译更改时触发高速缓存无效,请提供版本相关的密钥前缀,如下面的示例所示,或者将视图映射到版本相关的URL:

from django.views.decorators.cache import cache_page
from django.views.i18n import JavaScriptCatalog

# The value returned by get_version() must change when translations change.
urlpatterns = [
    url(r'^jsi18n/$',
        cache_page(86400, key_prefix='js18n-%s' % get_version())(JavaScriptCatalog.as_view()),
        name='javascript-catalog'),
]

客户端缓存可以节省带宽,并加快网站加载速度。如果您使用的是ETag(USE_ETAGS = True),则已包含在内。否则,您可以申请 条件装饰器。在以下示例中,每当重新启动应用程序服务器时,缓存都将失效:

from django.utils import timezone
from django.views.decorators.http import last_modified
from django.views.i18n import JavaScriptCatalog

last_modified_date = timezone.now()

urlpatterns = [
    url(r'^jsi18n/$',
        last_modified(lambda req, **kw: last_modified_date)(JavaScriptCatalog.as_view()),
        name='javascript-catalog'),
]

您甚至可以预先生成JavaScript目录作为部署过程的一部分,并将其作为静态文件提供。这种激进的技术在 django-statici18n 中实现。

国际化:在URL模式中

Django提供了两种机制来使URL模式国际化:

警告

使用这些功能之一需要为每个请求设置活动语言;换句话说,您需要在您的 MIDDLEWARE 设置中具有 django.middleware.locale.LocaleMiddleware

网址格式中的语言前缀

i18n_patterns(*urls, prefix_default_language=True)[源代码]

此功能可以在根URLconf中使用,Django会自动将当前活动语言代码预置到 i18n_patterns() 中定义的所有URL模式。

prefix_default_language 设置为 False 会从默认语言(LANGUAGE_CODE)中删除前缀。这在将翻译添加到现有网站时很有用,因此当前网址不会更改。

URL模式示例:

from django.conf.urls import include, url
from django.conf.urls.i18n import i18n_patterns

from about import views as about_views
from news import views as news_views
from sitemap.views import sitemap

urlpatterns = [
    url(r'^sitemap\.xml$', sitemap, name='sitemap-xml'),
]

news_patterns = ([
    url(r'^$', news_views.index, name='index'),
    url(r'^category/(?P<slug>[\w-]+)/$', news_views.category, name='category'),
    url(r'^(?P<slug>[\w-]+)/$', news_views.details, name='detail'),
], 'news')

urlpatterns += i18n_patterns(
    url(r'^about/$', about_views.main, name='about'),
    url(r'^news/', include(news_patterns, namespace='news')),
)

定义这些URL模式后,Django会自动将语言前缀添加到由 i18n_patterns 函数添加的URL模式。例:

>>> from django.urls import reverse
>>> from django.utils.translation import activate

>>> activate('en')
>>> reverse('sitemap-xml')
'/sitemap.xml'
>>> reverse('news:index')
'/en/news/'

>>> activate('nl')
>>> reverse('news:detail', kwargs={'slug': 'news-slug'})
'/nl/news/news-slug/'

使用 prefix_default_language=FalseLANGUAGE_CODE='en',URL将是:

>>> activate('en')
>>> reverse('news:index')
'/news/'

>>> activate('nl')
>>> reverse('news:index')
'/nl/news/'
New in Django 1.10:

添加了 prefix_default_language 参数。

警告

i18n_patterns() 只允许在根URLconf中。在包含的URLconf中使用它会抛出 ImproperlyConfigured 异常。

Changed in Django 1.10:

在旧版本中,通过设置 request.urlconf 不支持在根URLconf中使用 i18n_patterns 不同于 ROOT_URLCONF

警告

确保您没有可能与自动添加的语言前缀相冲突的非前缀网址格式。

翻译网址格式

URL模式也可以使用 ugettext_lazy() 功能标记为可翻译。例:

from django.conf.urls import include, url
from django.conf.urls.i18n import i18n_patterns
from django.utils.translation import ugettext_lazy as _

from about import views as about_views
from news import views as news_views
from sitemaps.views import sitemap

urlpatterns = [
    url(r'^sitemap\.xml$', sitemap, name='sitemap-xml'),
]

news_patterns = ([
    url(r'^$', news_views.index, name='index'),
    url(_(r'^category/(?P<slug>[\w-]+)/$'), news_views.category, name='category'),
    url(r'^(?P<slug>[\w-]+)/$', news_views.details, name='detail'),
], 'news')

urlpatterns += i18n_patterns(
    url(_(r'^about/$'), about_views.main, name='about'),
    url(_(r'^news/'), include(news_patterns, namespace='news')),
)

创建翻译后,reverse() 函数将返回活动语言的URL。例:

>>> from django.urls import reverse
>>> from django.utils.translation import activate

>>> activate('en')
>>> reverse('news:category', kwargs={'slug': 'recent'})
'/en/news/category/recent/'

>>> activate('nl')
>>> reverse('news:category', kwargs={'slug': 'recent'})
'/nl/nieuws/categorie/recent/'

警告

在大多数情况下,最好只在语言代码前缀的模式块(使用 i18n_patterns())中使用翻译后的网址,以避免粗心大意的翻译网址与非翻译网址格式发生冲突的可能性。

在模板中反转

如果在模板中反转本地化URL,它们总是使用当前语言。要链接到其他语言的URL,请使用 language 模板标记。它在所包含的模板部分中启用给定的语言:

{% load i18n %}

{% get_available_languages as languages %}

{% trans "View this category in:" %}
{% for lang_code, lang_name in languages %}
    {% language lang_code %}
    <a href="{% url 'category' slug=category.slug %}">{{ lang_name }}</a>
    {% endlanguage %}
{% endfor %}

language 标签期望语言代码作为唯一的参数。

本地化:如何创建语言文件

一旦应用程序的字符串文字已经标记为以后的翻译,翻译本身需要被写(或获得)。这是如何工作。

消息文件

第一步是为新语言创建 message file。消息文件是一个纯文本文件,表示单一语言,包含所有可用的翻译字符串以及如何以给定语言表示它们。消息文件具有 .po 文件扩展名。

Django提供了一个工具,django-admin makemessages,自动创建和维护这些文件。

Gettext实用程序

makemessages 命令(和后面讨论的 compilemessages)使用来自GNU gettext工具集的命令:xgettextmsgfmtmsgmergemsguniq

支持的 gettext 实用程序的最低版本为0.15。

要创建或更新消息文件,请运行此命令:

django-admin makemessages -l de

...其中 de 是要创建的消息文件的 locale name。例如,pt_BR 用于巴西葡萄牙语,de_AT 用于奥地利德语或 id 用于印尼语。

脚本应该从两个地方之一运行:

  • Django项目的根目录(包含 manage.py 的目录)。

  • 您的一个Django应用程序的根目录。

脚本运行在项目源代码树或应用程序源代码树中,并拉出所有标记为要翻译的字符串(请参阅 Django如何发现翻译 并确保 LOCALE_PATHS 配置正确)。它在目录 locale/LANG/LC_MESSAGES 中创建(或更新)消息文件。在 de 示例中,文件将是 locale/de/LC_MESSAGES/django.po

当从项目的根目录运行 makemessages 时,提取的字符串将自动分发到正确的消息文件。也就是说,从包含 locale 目录的应用程序的文件中提取的字符串将进入该目录下的消息文件中。从没有任何 locale 目录的应用程序的文件中提取的字符串将进入在 LOCALE_PATHS 中首先列出的目录下的消息文件中,或者如果 LOCALE_PATHS 为空,则将生成错误。

默认情况下,django-admin makemessages 会检查具有 .html.txt 文件扩展名的每个文件。如果要覆盖默认值,请使用 --extension-e 选项指定要检查的文件扩展名:

django-admin makemessages -l de -e txt

用逗号分隔多个扩展名和/或多次使用 -e--extension:

django-admin makemessages -l de -e html,txt -e xml

警告

从JavaScript源代码创建消息文件 需要使用特殊的’djangojs’域, -e js

使用Jinja2模板?

makemessages 不理解Jinja2模板的语法。要从包含Jinja2模板的项目中提取字符串,请改用 Babel 中的 Message Extracting

这里有一个示例 babel.cfg 配置文件:

# Extraction from Python source files
[python: **.py]

# Extraction from Jinja2 templates
[jinja2: **.jinja]
extensions = jinja2.ext.with_

请确保您列出了您使用的所有扩展程序!否则Babel将无法识别这些扩展定义的标签,并将忽略包含它们的Jinja2模板。

Babel提供与 makemessages 类似的功能,可以代替它,一般来说,不依赖于 gettext。有关更多信息,请阅读其有关 working with message catalogs 的文档。

没有gettext?

如果您没有安装 gettext 实用程序,makemessages 将创建空文件。如果是这样,请安装 gettext 实用程序,或只是复制英文消息文件(locale/en/LC_MESSAGES/django.po)(如果可用),并将其用作起点;它只是一个空的翻译文件。

在Windows上工作?

如果您使用Windows并需要安装GNU gettext实用程序,以便 makemessages 正常工作,请参阅 gettext 在Windows上 以获取更多信息。

.po 文件的格式很简单。每个 .po 文件包含一小部分元数据,例如翻译维护者的联系信息,但文件的大部分是 消息 的列表 - 翻译字符串和特定语言的实际翻译文本之间的简单映射。

例如,如果您的Django应用程序包含文本 "Welcome to my site." 的翻译字符串,就像这样:

_("Welcome to my site.")

...然后 django-admin makemessages 将创建一个 .po 文件,其中包含以下代码段 - 一条消息:

#: path/to/python/module.py:23
msgid "Welcome to my site."
msgstr ""

快速解释:

  • msgid 是翻译字符串,它出现在源中。不要改变它。

  • msgstr 是您放置语言特定翻译的地方。它开始是空的,所以你有责任改变它。确保您在翻译周围保留引号。

  • 为了方便起见,每个消息包括以 # 为前缀并且位于 msgid 行之上的注释行的形式,从中收集翻译字符串的文件名和行号。

长消息是一种特殊情况。在那里,紧接在 msgstr (或 msgid)之后的第一个字符串是空字符串。然后内容本身将被写在下几行作为每行一个字符串。这些字符串是直接连接的。不要忘记字符串中的尾随空格;否则,它们将被粘在一起,没有空格!

注意你的字符集

由于 gettext 工具在内部工作,因为我们想允许Django核心和应用程序中的非ASCII源字符串,必须 使用UTF-8作为PO文件的编码(创建PO文件时的默认值)。这意味着每个人都将使用相同的编码,这在Django处理PO文件时很重要。

要对所有源代码和模板重新检查新的翻译字符串并更新所有 所有 语言的消息文件,请运行此操作:

django-admin makemessages -a

编译消息文件

在创建消息文件后 - 每次对其进行更改时,您都需要将其编译为更有效的表单,以供 gettext 使用。使用 django-admin compilemessages 实用程序执行此操作。

此工具运行所有可用的 .po 文件,并创建 .mo 文件,这是优化为 gettext 使用的二进制文件。在运行 django-admin makemessages 的同一目录中,像这样运行 django-admin compilemessages:

django-admin compilemessages

而已。您的翻译已准备就绪。

Changed in Django 1.9:

compilemessages 现在匹配 makemessages 的操作,扫描项目树为 .po 文件编译。

在Windows上工作?

如果你正在使用Windows并需要安装GNU gettext实用程序,所以 django-admin compilemessages 工作参见 gettext 在Windows上 了解更多信息。

.po文件:编码和BOM使用。

Django只支持以UTF-8编码并且没有任何BOM(字节顺序标记)的 .po 文件,因此,如果您的文本编辑器在默认情况下将这样的标记添加到文件的开头,那么您将需要重新配置它。

疑难解答:ugettext() 在具有百分号的字符串中错误地检测到 python-format

在某些情况下,例如带有百分号后面跟着空格和 字符串转换类型 (例如 _("10% interest"))的字符串,ugettext() 不正确地用 python-format 标记字符串。

如果您尝试编译具有错误标记字符串的消息文件,您将收到一条错误消息,如 number of format specifications in 'msgid' and 'msgstr' does not match'msgstr' is not a valid Python format string, unlike 'msgid'

要解决此问题,您可以通过添加第二个百分号来转义百分号:

from django.utils.translation import ugettext as _
output = _("10%% interest)

或者,您可以使用 no-python-format,以便所有百分号被视为文字:

# xgettext:no-python-format
output = _("10% interest)

从JavaScript源代码创建消息文件

您可以使用 django-admin makemessages 工具以与其他Django消息文件相同的方式创建和更新消息文件。唯一的区别是,您需要通过提供一个 -d djangojs 参数来明确指定在gettext条件下称为一个域(在这种情况下为 djangojs 域),如下所示:

django-admin makemessages -d djangojs -l de

这将创建或更新用于德语的JavaScript的消息文件。更新消息文件后,只需运行 django-admin compilemessages,就像使用正常的Django消息文件一样。

gettext 在Windows上

这只需要那些想要提取消息ID或编译消息文件(.po)的人。翻译工作本身只涉及编辑此类型的现有文件,但如果要创建自己的消息文件,或者要测试或编译已更改的消息文件,请下载 一个预编译的二进制安装程序

您也可以使用您在其他地方获得的 gettext 二进制文件,只要 xgettext --version 命令正常工作。如果在Windows命令提示符下输入的命令 xgettext --version 导致弹出窗口说“xgettext.exe生成错误并且将被Windows关闭”,请不要尝试使用带有 gettext 程序包的Django转换实用程序。

自定义 makemessages 命令

如果要将其他参数传递给 xgettext,则需要创建自定义 makemessages 命令并覆盖其 xgettext_options 属性:

from django.core.management.commands import makemessages

class Command(makemessages.Command):
    xgettext_options = makemessages.Command.xgettext_options + ['--keyword=mytrans']

如果您需要更多的灵活性,您还可以向自定义 makemessages 命令中添加一个新参数:

from django.core.management.commands import makemessages

class Command(makemessages.Command):

    def add_arguments(self, parser):
        super(Command, self).add_arguments(parser)
        parser.add_argument(
            '--extra-keyword',
            dest='xgettext_keywords',
            action='append',
        )

    def handle(self, *args, **options):
        xgettext_keywords = options.pop('xgettext_keywords')
        if xgettext_keywords:
            self.xgettext_options = (
                makemessages.Command.xgettext_options[:] +
                ['--keyword=%s' % kwd for kwd in xgettext_keywords]
            )
        super(Command, self).handle(*args, **options)

set_language 重定向视图

set_language(request)[源代码]

为了方便起见,Django提供了一个视图 django.views.i18n.set_language(),它设置用户的语言首选项并重定向到指定的URL,或者默认情况下返回到上一页。

通过将以下行添加到URLconf来激活此视图:

url(r'^i18n/', include('django.conf.urls.i18n')),

(请注意,此示例使视图在 /i18n/setlang/ 处可用。)

警告

确保您不在 i18n_patterns() 中包括上述URL - 它需要与语言无关,以正常工作。

视图期望通过 POST 方法调用,在请求中设置 language 参数。如果启用了会话支持,则视图会在用户的会话中保存语言选项。否则,它会将语言选项保存在默认名为 django_language 的Cookie中。 (名称可以通过 LANGUAGE_COOKIE_NAME 设置更改。)

设置语言选择后,Django在 POSTGET 数据中查找 next 参数。如果找到并且Django认为它是一个安全的URL(即它不指向不同的主机并使用安全方案),将执行到该URL的重定向。否则,Django可能会退回到将用户重定向到来自 Referer 头的URL,或者如果没有设置,则根据请求的性质将它重定向到 /

  • 对于AJAX请求,只有在设置了 next 参数时,才会执行回退。否则将返回204状态代码(无内容)。

  • 对于非AJAX请求,将始终执行回退。

Changed in Django 1.10:

当未指定重定向时,返回204状态代码的AJAX请求是新的。

这里是示例HTML模板代码:

{% load i18n %}

<form action="{% url 'set_language' %}" method="post">{% csrf_token %}
    <input name="next" type="hidden" value="{{ redirect_to }}" />
    <select name="language">
        {% get_current_language as LANGUAGE_CODE %}
        {% get_available_languages as LANGUAGES %}
        {% get_language_info_list for LANGUAGES as languages %}
        {% for language in languages %}
            <option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected="selected"{% endif %}>
                {{ language.name_local }} ({{ language.code }})
            </option>
        {% endfor %}
    </select>
    <input type="submit" value="Go" />
</form>

在此示例中,Django在 redirect_to 上下文变量中查找用户将被重定向到的页面的URL。

显式设置活动语言

您可能需要显式设置当前会话的活动语言。例如,可能从另一个系统检索用户的语言偏好。您已经被介绍给 django.utils.translation.activate()。这只适用于当前线程。要为整个会话持久化语言,还要修改会话中的 LANGUAGE_SESSION_KEY:

from django.utils import translation
user_language = 'fr'
translation.activate(user_language)
request.session[translation.LANGUAGE_SESSION_KEY] = user_language

您通常希望同时使用两者:django.utils.translation.activate() 将更改此线程的语言,并修改会话使此首选项在未来的请求中保持。

如果您不使用会话,该语言将保留在cookie中,其名称在 LANGUAGE_COOKIE_NAME 中配置。例如:

from django.utils import translation
from django import http
from django.conf import settings
user_language = 'fr'
translation.activate(user_language)
response = http.HttpResponse(...)
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, user_language)

使用视图和模板外部的翻译

虽然Django提供了一组丰富的i18n工具来用于视图和模板,但它并不限制对Django特定的代码的使用。 Django翻译机制可以用于将任意文本翻译成Django支持的任何语言(当然,只要存在适当的翻译目录)。您可以加载翻译目录,将其激活并将文本翻译为您选择的语言,但记住切换回原始语言,因为激活翻译目录是基于每个线程完成的,这样的更改将影响在同一个线程中运行的代码。

例如:

from django.utils import translation

def welcome_translated(language):
    cur_language = translation.get_language()
    try:
        translation.activate(language)
        text = translation.ugettext('welcome')
    finally:
        translation.activate(cur_language)
    return text

使用值’de’调用此函数将给您 "Willkommen",而不管 LANGUAGE_CODE 和中间件设置的语言。

特别感兴趣的函数是返回当前线程中使用的语言的 django.utils.translation.get_language(),激活当前线程的翻译目录的 django.utils.translation.activate(),以及检查Django是否支持给定语言的 django.utils.translation.check_for_language()

为了帮助编写更简洁的代码,还有一个上下文管理器 django.utils.translation.override(),用于在输入时存储当前语言,并在退出时恢复它。有了它,上面的例子就变成了:

from django.utils import translation

def welcome_translated(language):
    with translation.override(language):
        return translation.ugettext('welcome')

实施说明

Django专业翻译

Django的翻译机制使用Python附带的标准 gettext 模块。如果你知道 gettext,你可能会注意到这些专业Django做翻译的方式:

  • 字符串域是 djangodjangojs。此字符串域用于区分将其数据存储在公共消息文件库(通常为 /usr/share/locale/)中的不同程序。 django 域用于Python和模板翻译字符串,并加载到全局翻译目录中。 djangojs 域仅用于JavaScript翻译目录,以确保这些目录尽可能小。

  • Django不单独使用 xgettext。它使用围绕 xgettextmsgfmt 的Python包装器。这主要是为了方便。

Django如何发现语言偏好

准备好翻译后,或者,如果您只想使用Django附带的翻译,只需为您的应用激活翻译即可。

在幕后,Django有一个非常灵活的模型,决定应该使用哪种语言 - 安装范围,特定用户,或两者。

要设置安装范围的语言首选项,请设置 LANGUAGE_CODE。 Django使用这种语言作为默认翻译 - 如果没有找到更好的匹配翻译通过场所中间件使用的方法之一的最终尝试(见下文)。

如果所有你想要的是用你的母语运行Django所有你需要做的是设置 LANGUAGE_CODE,并确保相应的 消息文件 和它们的编译版本(.mo)存在。

如果你想让每个用户指定他们喜欢的语言,那么你还需要使用 LocaleMiddlewareLocaleMiddleware 基于请求中的数据启用语言选择。它为每个用户定制内容。

要使用 LocaleMiddleware,请将 'django.middleware.locale.LocaleMiddleware' 添加到您的 MIDDLEWARE 设置。由于中间件顺序很重要,请遵循以下准则:

  • 确保它是安装的第一个中间件之一。

  • 它应该在 SessionMiddleware 之后,因为 LocaleMiddleware 使用会话数据。它应该在 CommonMiddleware 之前,因为 CommonMiddleware 需要一个激活的语言,以解决请求的URL。

  • 如果你使用 CacheMiddleware,放 LocaleMiddleware 后。

例如,您的 MIDDLEWARE 可能如下所示:

MIDDLEWARE = [
   'django.contrib.sessions.middleware.SessionMiddleware',
   'django.middleware.locale.LocaleMiddleware',
   'django.middleware.common.CommonMiddleware',
]

(有关中间件的更多信息,请参阅 中间件文档。)

LocaleMiddleware 尝试通过以下算法确定用户的语言首选项:

  • 首先,它在请求的URL中查找语言前缀。这仅在您在根URLconf中使用 i18n_patterns 函数时才会执行。有关语言前缀和如何使URL模式国际化的更多信息,请参阅 国际化:在URL模式中

  • 否则,它在当前用户的会话中查找 LANGUAGE_SESSION_KEY 密钥。

  • 如果没有,它寻找一个cookie。

    使用的cookie的名称由 LANGUAGE_COOKIE_NAME 设置设置。 (默认名称是 django_language。)

  • 如果没有,它查看 Accept-Language HTTP头。此标题由您的浏览器发送,并按优先级顺序告诉服务器您喜欢哪种语言。 Django尝试标题中的每种语言,直到找到一个可用的翻译。

  • 如果没有,它使用全局 LANGUAGE_CODE 设置。

笔记:

  • 在这些位置中,语言首选项应为标准 language format 中的字符串。例如,巴西葡萄牙语是 pt-br

  • 如果基本语言可用,但指定的子语言不是,Django使用基本语言。例如,如果用户指定 de-at (奥地利德语),但Django只有 de 可用,Django使用 de

  • 只能选择 LANGUAGES 设置中列出的语言。如果要将语言选择限制为所提供语言的一个子集(因为您的应用程序不提供所有这些语言),请将 LANGUAGES 设置为语言列表。例如:

    LANGUAGES = [
      ('de', _('German')),
      ('en', _('English')),
    ]
    

    此示例将可用于自动选择的语言限制为德语和英语(以及任何子语言,如de-ch或en-us)。

  • 如果定义自定义 LANGUAGES 设置,如上一个项目符号中所述,您可以将语言名称标记为翻译字符串 - 但是使用 ugettext_lazy() 而不是 ugettext() 来避免循环导入。

    这里有一个示例设置文件:

    from django.utils.translation import ugettext_lazy as _
    
    LANGUAGES = [
        ('de', _('German')),
        ('en', _('English')),
    ]
    

一旦 LocaleMiddleware 确定用户的偏好,它使得该偏好对于每个 HttpRequest 可用作 request.LANGUAGE_CODE。随意在您的视图代码中读取此值。这里有一个简单的例子:

from django.http import HttpResponse

def hello_world(request, count):
    if request.LANGUAGE_CODE == 'de-at':
        return HttpResponse("You prefer to read Austrian German.")
    else:
        return HttpResponse("You prefer to read another language.")

注意,使用静态(无中间件)翻译,语言是在 settings.LANGUAGE_CODE,而在动态(中间件)翻译,它在 request.LANGUAGE_CODE

Django如何发现翻译

在运行时,Django构建了一个内存中的文本 - 翻译目录。为了实现这一点,它通过遵循该算法关于其检查不同的文件路径以加载编译的 消息文件.mo)的顺序和相同文字的多个翻译的优先级来寻找翻译:

  1. LOCALE_PATHS 中列出的目录具有最高优先级,首先出现的优先级高于稍后出现的优先级。

  2. 然后,它查找并使用如果它在 INSTALLED_APPS 中列出的每个已安装应用程序中存在 locale 目录。首先出现的优先级高于稍后出现的优先级。

  3. 最后,使用Django提供的 django/conf/locale 中的基本翻译作为后备。

参见

JavaScript资源中包含的文字的翻译是根据类似但不完全相同的算法查找的。有关详细信息,请参阅 javascript_catalog查看文档

在所有情况下,包含翻译的目录的名称应使用 locale name 表示法命名。例如。 dept_BRes_AR

这样,您可以编写包含自己的翻译的应用程序,并且可以覆盖项目中的基本翻译。或者,您可以从几个应用程序构建一个大项目,并将所有翻译成一个大的通用消息文件,特定于您正在撰写的项目。这是你的选择。

所有消息文件存储库的结构都是相同的。他们是:

  • 将在设置文件中的 LOCALE_PATHS 中列出的所有路径搜索 <language>/LC_MESSAGES/django.(po|mo)

  • $APPPATH/locale/<language>/LC_MESSAGES/django.(po|mo)

  • $PYTHONPATH/django/conf/locale/<language>/LC_MESSAGES/django.(po|mo)

要创建消息文件,请使用 django-admin makemessages 工具。您使用 django-admin compilemessages 产生 gettext 使用的二进制 .mo 文件。

您还可以运行 django-admin compilemessages --settings=path.to.settings,使编译器处理 LOCALE_PATHS 设置中的所有目录。

使用非英语基本语言

Django通常假设可翻译项目中的原始字符串是用英语书写的。您可以选择其他语言,但您必须注意某些限制:

  • gettext 只提供原始邮件的两个复数形式,因此如果基本语言的复数规则与英语不同,则还需要提供基本语言的翻译以包括所有复数形式。

  • 当激活英语变体并缺少英语字符串时,后备语言不会是项目的 LANGUAGE_CODE,而是原始字符串。例如,访问以西班牙语作为默认语言的网站的英语用户和以俄语书写的原始字符串将回退到俄语,而不是西班牙语。