翻译¶ T0>

概述¶ T0>

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

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

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

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

注意

There is also an independent but related USE_L10N setting that controls if Django should implement format localization. 有关更多详细信息,请参阅Format localization

注意

Make sure you’ve activated translation for your project (the fastest way is to check if MIDDLEWARE includes django.middleware.locale.LocaleMiddleware). 如果还没有,请参阅How Django discovers language preference

国际化:在Python代码中

标准翻译

使用函数gettext()指定翻译字符串。 按照约定将其作为较短的别名_导入,以节省输入。

注意

gettext函数的u前缀最初是为了区分Python 2上unicode字符串和字节串之间的用法。 对于只支持Python 3的代码,它们可以互换使用。 在未来的Django发行版中可能会出现对前缀函数的弃用。

注意

Python的标准库gettext模块将_()安装到全局名称空间中,作为gettext()的别名。 在Django,我们选择不采取这种做法,原因有两个:

  1. 有时,您应该使用gettext_lazy()作为特定文件的默认翻译方法。 在全局命名空间中没有_(),开发人员必须考虑哪个是最合适的翻译功能。
  2. 在Python的交互式shell和doctest测试中,下划线字符(_)用于表示“以前的结果”。 安装全局_()功能会造成干扰。 显式地将gettext()导入为_()可以避免这个问题。

哪些函数可能被别名为_

由于xgettext(由makemessages使用)是如何工作的,只有带单个字符串参数的函数才能被导入为_

在这个例子中,文本“Welcome” to my site。“作为翻译字符串:

from django.utils.translation import gettext as _
from django.http import HttpResponse

def my_view(request):
    output = _("Welcome to my site.")
    return HttpResponse(output)

很明显,你可以编码而不使用别名。 这个例子和前面的例子是一样的:

from django.utils.translation import gettext
from django.http import HttpResponse

def my_view(request):
    output = gettext("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)。

您传递给_()gettext()的字符串可以占位符,用Python的标准命名字符串插值语法指定。 例:

def my_view(request, m, d):
    output = _('Today is %(month)s %(day)s.') % {'month': m, 'day': d}
    return HttpResponse(output)

这种技术可以让语言特定的翻译重新排列占位符文本。 For example, an English translation may be "Today is November 26.", while a Spanish translation may be "Hoy es 26 de Noviembre." – with the month and the day placeholders swapped.

因此,应该使用命名字符串插值(例如%(day)s)来代替位置插值(例如%s%d 如果您使用位置插值,翻译将无法重新排列占位符文本。

翻译员评论

如果您希望为翻译字符串提供翻译提示,则可以在字符串前面的行上添加以Translators关键字作为前缀的注释,例如:

def my_view(request):
    # Translators: This message appears on the home page only
    output = gettext("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 ""

这也适用于模板。 有关更多详细信息,请参阅Comments for translators in templates

将字符串标记为no-op

使用函数django.utils.translation.gettext_noop()将字符串标记为翻译字符串,而不进行翻译。 该字符串稍后从一个变量转换。

如果你的常量字符串应该以源语言存储,因为它们是通过系统或用户进行交换的 - 比如数据库中的字符串 - 但是应该在最后一个时间点进行翻译,比如字符串出现时给用户。

复数¶ T0>

使用函数django.utils.translation.ngettext()指定复数消息。

ngettext()有三个参数:单数转换字符串,复数转换字符串和对象数量。

当需要将Django应用程序本地化为复杂度复数形式的数量和复杂度大于英语使用的两种形式(“对象”为单数和“对象”)的语言时,对于count不同于一个的所有情况,无论其值如何)。

例如:

from django.utils.translation import ngettext
from django.http import HttpResponse

def hello_world(request, count):
    page = ngettext(
        'there is %(count)d object',
        'there are %(count)d objects',
    count) % {
        'count': count,
    }
    return HttpResponse(page)

在这个例子中,对象的数量作为count变量​​传递给翻译语言。

请注意,多元化是复杂的,并在每种语言工作不同。 比较count为1并不总是正确的规则。 这段代码看起来很复杂,但对于某些语言会产生不正确的结果:

from django.utils.translation import ngettext
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 = ngettext(
    'There is %(count)d %(name)s available.',
    'There are %(count)d %(name)s available.',
    count
) % {
    'count': count,
    'name': name
}

不要试图实现你自己的单数或复数逻辑,这将是不正确的。 在这种情况下,请考虑如下内容:

text = ngettext(
    'There is %(count)d %(name)s object available.',
    'There are %(count)d %(name)s objects available.',
    count
) % {
    'count': count,
    'name': Report._meta.verbose_name,
}

注意

当使用ngettext()时,确保对文字中包含的每个外插变量使用单个名称。 在上面的例子中,请注意我们如何在两个转换字符串中使用name Python变量。 这个例子除了在上面提到的某些语言中是不正确的,还会失败:

text = ngettext(
    '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主文件(位于django/conf/locale/<lang_code>/LC_MESSAGES/django.po)的复数形式。 所有其他po文件中的复数形式都被忽略。 因此,你不应该在你的项目或应用程序文件中使用不同的复数方程。

上下文标记

Sometimes words have several meanings, such as "May" in English, which refers to a month name and to a verb. 要使翻译人员能够在不同的上下文中正确翻译这些单词,可以使用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选项值

例如,要翻译以下模型中的名称字段的帮助文本,请执行以下操作:

from django.db import models
from django.utils.translation import gettext_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选项,而不是依赖以英语为中心的后备式,并且通过查看模型的类名:

from django.db import models
from django.utils.translation import gettext_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 gettext_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?')

使用懒惰的翻译对象

在其他Django代码中,只要使用字符串(str对象),就可以使用gettext_lazy()调用的结果,但可能无法使用任意Python代码。 例如,以下内容不起作用,因为请求库不处理gettext_lazy对象:

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

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

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

如果您不喜欢长的gettext_lazy名称,您可以将其作为_(下划线)来使用,如下所示:

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

class MyThing(models.Model):
    name = models.CharField(help_text=_('This is the help text'))

使用gettext_lazy()ngettext_lazy()标记模型和实用函数中的字符串是常见操作。 当您在代码中的其他地方使用这些对象时,应确保不会意外地将它们转换为字符串,因为它们应尽可能晚地进行转换(以便正确的区域设置生效)。 这需要使用下面描述的帮助函数。

懒惰的翻译和复数

对复数形式的字符串([u]n[p]gettext_lazy)使用惰性转换时,通常在字符串定义时不知道number参数。 因此,您被授权将密钥名称而不是整数作为number参数传递。 然后在字符串插值过程中,在该字典下的字典中查找number 这是一个例子:

from django import forms
from django.utils.translation import ngettext_lazy

class MyForm(forms.Form):
    error_message = ngettext_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 = ngettext_lazy(
        "You provided %d argument",
        "You provided %d arguments",
    )

    def clean(self):
        # ...
        if error:
            raise forms.ValidationError(self.error_message % number)

格式化字符串:format_lazy()

Python’s str.format() method will not work when either the format_string or any of the arguments to str.format() contains lazy translation objects. 相反,您可以使用django.utils.text.format_lazy(),它会创建一个惰性对象,只有当结果包含在str.format() 例如:

from django.utils.text import format_lazy
from django.utils.translation import gettext_lazy
...
name = gettext_lazy('John Lennon')
instrument = gettext_lazy('guitar')
result = format_lazy('{name}: {instrument}', name=name, instrument=instrument)

In this case, the lazy translations in result will only be converted to strings when result itself is used in a string (usually at template rendering time).

延迟翻译中的其他用法

对于任何其他您希望延迟翻译的情况,但必须将可翻译的字符串作为参数传递给另一个函数,则可以自己将此函数包装在惰性调用中。 例如:

from django.utils.functional import lazy
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _

mark_safe_lazy = lazy(mark_safe, str)

再后来:

lazy_string = mark_safe_lazy(_("<p>My <strong>string!</strong></p>"))

语言的本地化名称

get_language_info()[source]

The get_language_info() function provides detailed information about languages:

>>> 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模块。 类似的访问这些信息可用于模板代码。 见下文。

国际化:在模板代码

Translations in Django templates uses two template tags and a slightly different syntax than in Python code. 为了让您的模板可以访问这些标签,请将{% load i18n %} t0 >朝向模板的顶部。 与所有的模板标签一样,这个标签需要加载到所有使用翻译的模板中,甚至是那些从已经加载了i18n标签的其他模板扩展的模板。

trans模板标签

{% trans %}模板标记可以翻译一个常量字符串(用单引号或双引号括起来)或可变内容:

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

如果存在noop选项,变量查找仍然会发生,但是跳过了翻译。 当“剔除”将来需要翻译的内容时,这非常有用:

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

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

如果一个模板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 %} also supports contextual markers using the context keyword:

{% 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 %}

注意

The previous more verbose format is still supported: {% blocktrans with book|title as book_t and author|title as author_t %}

Other block tags (for example {% for %} or {% if %}) are not allowed inside a blocktrans tag.

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

这个标签也提供了多元化。 要使用它:

  • 用名称count指定并绑定一个计数器值。 这个值将是用来选择正确的复数形式。
  • Specify both the singular and plural forms separating them with the {% plural %} tag within the {% blocktrans %} and {% endblocktrans %} tags.

一个例子:

{% 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构造在内部转换为ngettext调用。 这意味着应用了与ngettext变量相同的notes regarding ngettext variables

反向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 }}">

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

{% blocktrans %} also supports contextual markers using the context keyword:

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

Another feature {% blocktrans %} supports is the trimmed option. 此选项将从{% blocktrans %}的内容的开头和结尾删除换行符标记,替换行的开头和结尾的空格,并将所有行合并为一个空格字符以分隔它们。 这对缩进{% blocktrans %}标签的内容非常有用,而不需要缩进字符结束在PO文件中的相应条目中,这使得翻译过程更容易。

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

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

将导致进入 “第一 句子。 第二 段。” in the PO file, compared to "\n  First sentence.\n  Second sentence.\n", if the trimmed option had not been specified.

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

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

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

在这种情况下,标签和过滤器都会看到翻译的字符串,所以他们不需要知道翻译。

注意

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

模板中翻译员的注释

就像使用Python code一样,可以使用comment标签使用注释来指定这些转换器的注释:

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

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

或用{# ... #} one-line comment constructs

{# 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 %}

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

其他标签

这些标签还需要一个{% 加载 i18n %}

get_available_languages

{% get_available_languages as LANGUAGES %} returns a list of tuples in which the first element is the language code and the second is the language name (translated into the currently active locale).

get_current_language

{% get_current_language 作为 LANGUAGE_CODE %}当前用户的首选语言为字符串。 例如:en-us 请参阅How Django discovers language preference

get_current_language_bidi

{% get_current_language_bidi as LANGUAGE_BIDI %} returns the current locale’s direction. 如果True,则它是从右向左的语言,例如希伯来语,阿拉伯语。 如果False它是从左到右的语言,例如英文,法文,德文等

If you enable the django.template.context_processors.i18n context processor then each RequestContext will have access to LANGUAGES, LANGUAGE_CODE, and LANGUAGE_BIDI as defined above.

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 }}

get_language_info_list

您也可以使用{% get_language_info_list %}模板标签来检索语言列表的信息在LANGUAGES中指定的活动语言)。 有关如何使用{% get_language_info_list %显示语言选择器的示例,请参阅the section about the set_language redirect view } T5> T2>。

In addition to LANGUAGES style list of tuples, {% get_language_info_list %} supports simple lists of language codes. 如果你在你看来这样做:

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 }}
  • {{ LANGUAGE_CODE | language_bidi }}
  • {{ LANGUAGE_CODE|language_name_translated }} (“německy”, when active language is Czech)

国际化:在JavaScript代码中

将翻译添加到JavaScript会带来一些问题:

  • JavaScript代码无法访问gettext实现。
  • JavaScript代码无法访问.po.mo文件;他们需要由服务器交付。
  • JavaScript的翻译目录应尽可能小。

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

The main solution to these problems is the following JavaScriptCatalog view, which generates a JavaScript code library with functions that mimic the gettext interface, plus an array of translation strings.

JavaScriptCatalog视图

JavaScriptCatalog[source]

生成一个带有模拟gettext接口的函数的JavaScript代码库和一个转换字符串数组的视图。

属性

域 T0> ¶ T1>

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

包 T0> ¶ T1>

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

使用默认值的示例

from django.views.i18n import JavaScriptCatalog

urlpatterns = [
    path('jsi18n/', JavaScriptCatalog.as_view(), name='javascript-catalog'),
]

自定义软件包示例

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

If your root URLconf uses i18n_patterns(), JavaScriptCatalog must also be wrapped by i18n_patterns() for the catalog to be correctly generated.

i18n_patterns()的示例:

from django.conf.urls.i18n import i18n_patterns

urlpatterns = i18n_patterns(
    path('jsi18n/', JavaScriptCatalog.as_view(), name='javascript-catalog'),
)

翻译的优先级是这样的:后来出现在packages参数中的包优先于出现在开头的那些。 对于相同的字面意义上的翻译,这是非常重要的。

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

使用JavaScript翻译目录

要使用目录,只需像下面这样引入动态生成的脚本:

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

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

  • gettext的
  • ngettext
  • GET_FORMAT
  • gettext_noop
  • pgettext
  • npgettext
  • pluralidx

gettext

在您的Python代码中,gettext函数的行为与标准的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函数支持位置和指定插值:

  • Positional interpolation: obj contains a JavaScript Array object whose elements values are then sequentially interpolated in their corresponding fmt placeholders in the same order they appear. 例如:

    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 contains a JavaScript object or associative array. 例如:

    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

The pluralidx function works in a similar way to the pluralize template filter, determining if a given count should use a plural form of a word or not:

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

In the simplest case, if no custom pluralization is needed, this returns false for the integer 1 and true for all other numbers.

但是,多元化在所有语言中都不是那么简单。 如果语言不支持多元化,则提供一个空值。

此外,如果在复数周围存在复杂的规则,则目录视图将呈现条件表达式。 这将评估为true(应该复数化)或false(应该不是复数化)值。

JSONCatalog视图

JSONCatalog[source]

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

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

响应格式如下:

{
    "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 = [
    path('jsi18n/',
         cache_page(86400, key_prefix='js18n-%s' % get_version())(JavaScriptCatalog.as_view()),
         name='javascript-catalog'),
]

客户端缓存将节省带宽,使您的网站加载速度更快。 如果您使用的是ETags(ConditionalGetMiddleware),那么您已经被覆盖了。 否则,您可以应用conditional decorators 在以下示例中,每当您重新启动应用程序服务器时,缓存都会失效:

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 = [
    path('jsi18n/',
         last_modified(lambda req, **kw: last_modified_date)(JavaScriptCatalog.as_view()),
         name='javascript-catalog'),
]

您甚至可以预先生成JavaScript目录作为部署过程的一部分,并将其作为静态文件提供。 This radical technique is implemented in django-statici18n.

国际化:以URL模式

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

警告

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

URL模式中的语言前缀

i18n_patterns(*urls, prefix_default_language=True)[source]

This function can be used in a root URLconf and Django will automatically prepend the current active language code to all URL patterns defined within i18n_patterns().

prefix_default_language设置为False会从默认语言(LANGUAGE_CODE)中删除前缀。 在向现有网站添加翻译时,这样做可能很有用,以便当前的网址不会更改。

示例网址格式:

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

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

urlpatterns = [
    path('sitemap.xml', sitemap, name='sitemap-xml'),
]

news_patterns = ([
    path('', news_views.index, name='index'),
    path('category/<slug:slug>/', news_views.category, name='category'),
    path('<slug:slug>/', news_views.details, name='detail'),
], 'news')

urlpatterns += i18n_patterns(
    path('about/', about_views.main, name='about'),
    path('news/', include(news_patterns, namespace='news')),
)

After defining these URL patterns, Django will automatically add the language prefix to the URL patterns that were added by the i18n_patterns function. 例:

>>> 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',网址将为:

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

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

警告

i18n_patterns() is only allowed in a root URLconf. 在包含的URLconf中使用它将引发ImproperlyConfigured异常。

警告

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

翻译URL模式

URL模式也可以使用gettext_lazy()函数标记为可翻译的。 例:

from django.conf.urls.i18n import i18n_patterns
from django.urls import include, path
from django.utils.translation import gettext_lazy as _

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

urlpatterns = [
    path('sitemap.xml', sitemap, name='sitemap-xml'),
]

news_patterns = ([
    path('', news_views.index, name='index'),
    path(_('category/<slug:slug>/'), news_views.category, name='category'),
    path('<slug:slug>/', news_views.details, name='detail'),
], 'news')

urlpatterns += i18n_patterns(
    path(_('about/'), about_views.main, name='about'),
    path(_('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/'

警告

在大多数情况下,最好只在带有语言代码前缀的模式块中使用翻译的URL(使用i18n_patterns()),以避免粗心翻译的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 comes with a tool, django-admin makemessages, that automates the creation and upkeep of these files.

Gettext实用程序

The makemessages command (and compilemessages discussed later) use commands from the GNU gettext toolset: xgettext, msgfmt, msgmerge and msguniq.

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

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

django-admin makemessages -l de

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

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

  • Django项目(包含manage.py)的根目录。
  • 其中一个Django应用程序的根目录。

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

当您从项目的根目录运行makemessages时,提取的字符串将自动分配到正确的消息文件。 That is, a string extracted from a file of an app containing a locale directory will go in a message file under that directory. A string extracted from a file of an app without any locale directory will either go in a message file under the directory listed first in LOCALE_PATHS or will generate an error if LOCALE_PATHS is empty.

By default django-admin makemessages examines every file that has the .html, .txt or .py file extension. 如果您要覆盖该默认值,请使用--extension-e选项指定要检查的文件扩展名:

django-admin makemessages -l de -e txt

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

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

警告

When creating message files from JavaScript source code you need to use the special ‘djangojs’ domain, not -e js.

使用Jinja2模板?

makemessages doesn’t understand the syntax of Jinja2 templates. To extract strings from a project containing Jinja2 templates, use Message Extracting from Babel instead.

Here’s an example babel.cfg configuration file:

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

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

确保你列出你正在使用的所有扩展名! 否则,Babel将无法识别这些扩展所定义的标签,并会完全忽略包含它们的Jinja2模板。

Babel提供了类似于makemessages的特性,可以替代它,并且不依赖于gettext 有关更多信息,请阅读有关使用消息目录的文档。

没有gettext?

如果你没有安装gettext工具,makemessages将会创建空文件。 如果是这样的话,可以安装gettext工具,或者复制英文信息文件(locale/en/LC_MESSAGES/django.po),点;这只是一个空的翻译文件。

在Windows上工作?

如果您使用的是Windows,并且需要安装GNU gettext实用程序,以使makemessages有效,请参阅gettext on Windows以获取更多信息。

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

例如,如果您的Django应用程序包含文本“Welcome 我的 网站的翻译字符串。 t4>,如下所示:

_("Welcome to my site.")

…then django-admin makemessages will have created a .po file containing the following snippet – a message:

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

快速解释一下:

  • msgid is the translation string, which appears in the source. 不要改变它。
  • msgstr is where you put the language-specific translation. 它开始是空的,所以你有责任改变它。 确保你保留在你的翻译报价。
  • 为方便起见,每条消息都以注释行的形式包含以#为前缀并位于msgid行之上的文件名和行号,收集。

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

注意你的字符集

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

要重新检查新翻译字符串的所有源代码和模板,并更新all所有语言的所有消息文件,请运行以下命令:

django-admin makemessages -a

编译消息文件

在创建消息文件之后(每次更改消息文件),都需要将其编译为更有效的形式,供gettext使用。 使用django-admin compilemessages工具执行此操作。

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

django-admin compilemessages

而已。 你的翻译准备好了。

在Windows上工作?

If you’re using Windows and need to install the GNU gettext utilities so django-admin compilemessages works see gettext on Windows for more information.

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

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

故障排除:gettext()在具有百分号的字符串中错误地检测到python-format

在某些情况下,如带有百分号后跟空格和string conversion type的字符串(例如_(“10% interest”)) gettext()错误地标记了python-format字符串。

If you try to compile message files with incorrectly flagged strings, you’ll get an error message like number of format specifications in 'msgid' and 'msgstr' does not match or 'msgstr' is not a valid Python format string, unlike 'msgid'.

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

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

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

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

从JavaScript源代码创建消息文件

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

django-admin makemessages -d djangojs -l de

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

Windows上的gettext

这只是需要提取消息ID或编译消息文件(.po)的人才需要的。 Translation work itself just involves editing existing files of this type, but if you want to create your own message files, or want to test or compile a changed message file, download a precompiled binary installer.

只要xgettext - version命令正常工作,您也可以使用您在别处获得的gettext二进制文件。 如果命令xgettext - 版本,则不要试图在gettext包中使用Django转换实用程序Windows命令提示符导致一个弹出窗口说“xgettext.exe已经生成错误,将被Windows关闭”。

自定义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().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().handle(*args, **options)

其它¶ T0>

set_language重定向视图

set_language(request)[source]

As a convenience, Django comes with a view, django.views.i18n.set_language(), that sets a user’s language preference and redirects to a given URL or, by default, back to the previous page.

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

path('i18n/', include('django.conf.urls.i18n')),

(请注意,这个例子使视图在/i18n/setlang/中可用。)

警告

Make sure that you don’t include the above URL within i18n_patterns() - it needs to be language-independent itself to work correctly.

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

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

  • 对于AJAX请求,只有在设置next参数时才会执行回退。 否则,将返回一个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{% 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()将更改此线程的语言,并修改会话使此偏好在以后的请求中保留。

如果您不使用会话,则语言将保存在名为LANGUAGE_COOKIE_NAME中配置的cookie中。 例如:

from django.conf import settings
from django.http import HttpResponse
from django.utils import translation
user_language = 'fr'
translation.activate(user_language)
response = 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.gettext('welcome')
    finally:
        translation.activate(cur_language)
    return text

Calling this function with the value ‘de’ will give you "Willkommen", regardless of LANGUAGE_CODE and language set by middleware.

Functions of particular interest are django.utils.translation.get_language() which returns the language used in the current thread, django.utils.translation.activate() which activates a translation catalog for the current thread, and django.utils.translation.check_for_language() which checks if the given language is supported by Django.

To help write more concise code, there is also a context manager django.utils.translation.override() that stores the current language on enter and restores it on exit. 有了它,上面的例子变成:

from django.utils import translation

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

实现说明

Django翻译的特点

Django的翻译机构使用Python自带的标准gettext模块。 如果您知道gettext,您可能会注意到Django翻译的这些特色:

  • The string domain is django or djangojs. 该字符串域用于区分将数据存储在公用消息文件库(通常为/usr/share/locale/)中的不同程序。 The django domain is used for Python and template translation strings and is loaded into the global translation catalogs. djangojs域仅用于JavaScript翻译目录,以确保这些目录尽可能小。
  • Django不单独使用xgettext 它使用围绕xgettextmsgfmt的Python包装。 这主要是为了方便。

Django如何发现语言偏好

准备好翻译之后 - 或者,如果您只是想使用Django附带的翻译版本,则只需激活您的应用的翻译。

在幕后,Django有一个非常灵活的模式来决定哪种语言应该被使用 - 安装范围,特定用户,还是两者兼而有之。

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

如果你只想用你的母语来运行Django,你只需要设置LANGUAGE_CODE并确保相应的message files及其编译版本(.mo

如果你想让每个用户指定他们喜欢的语言,那么你还需要使用LocaleMiddleware LocaleMiddleware enables language selection based on data from the request. 它为每个用户定制内容。

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

  • 确保它是第一个安装的中间件。
  • 它应该在SessionMiddleware之后,因为LocaleMiddleware利用会话数据。 And it should come before CommonMiddleware because CommonMiddleware needs an activated language in order to resolve the requested URL.
  • 如果使用CacheMiddleware,请在LocaleMiddleware之后。

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

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

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

LocaleMiddleware tries to determine the user’s language preference by following this algorithm:

  • 首先,它在请求的URL中查找语言前缀。 这只有在你的根URLconf里使用i18n_patterns功能时才会执行。 请参阅Internationalization: in URL patterns中了解有关语言前缀以及如何国际化网址格式的详情。

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

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

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

  • 如果失败了,它会查看Accept-Language HTTP标头。 这个头文件是由你的浏览器发送的,并按优先顺序告诉服务器你喜欢哪种语言。 Django在头文件中尝试每种语言,直到找到可用的翻译。

  • 如果失败了,它会使用全局的LANGUAGE_CODE设置。

Notes:

  • 在这些地方的每一个地方,语言首选项都应该是标准的language format,作为一个字符串。 For example, Brazilian Portuguese is pt-br.

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

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

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

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

  • If you define a custom LANGUAGES setting, as explained in the previous bullet, you can mark the language names as translation strings – but use gettext_lazy() instead of gettext() to avoid a circular import.

    这是一个示例设置文件:

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

Once LocaleMiddleware determines the user’s preference, it makes this preference available as request.LANGUAGE_CODE for each HttpRequest. 随意在你的视图代码中读取这个值。 这是一个简单的例子:

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构建了一个内存统一的文字翻译目录。 为了达到这个目的,它通过遵循这个算法来查找翻译,这个算法关于它检查不同文件路径的顺序来加载编译的message files.mo),多个翻译相同的文字:

  1. LOCALE_PATHS中列出的目录具有最高的优先顺序,首先出现的目录优先级高于稍后出现的目录。
  2. 然后,它查找并使用它在INSTALLED_APPS中列出的每个已安装应用程序中是否存在locale目录。 先出现的优先级高于后来出现的优先级。
  3. 最后,Django在django/conf/locale中提供的基本转换被用作后备。

也可以看看

包含在JavaScript资源中的文字的翻译按照类似但不相同的算法进行查找。 有关更多详细信息,请参阅JavaScriptCatalog

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

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

所有的消息文件库的结构都是一样的。 他们是:

  • 所有在LOCALE_PATHS中列出的路径都将被搜索到 / LC_MESSAGES / django的。(PO | MO)
  • $ APPPATH /区域/ / LC_MESSAGES / django的。(PO | MO)
  • $ PYTHONPATH / django的/ CONF /区域/ / LC_MESSAGES / django的。(PO | MO)

要创建消息文件,可以使用django-admin makemessages工具。 And you use django-admin compilemessages to produce the binary .mo files that are used by gettext.

你也可以运行django-admin compilemessages --settings=path.to.settings您的LOCALE_PATHS设置中的所有目录。

使用非英语基础语言

Django认为一个可翻译项目中的原始字符串是用英文书写的。 您可以选择其他语言,但是您必须了解某些限制:

  • gettext only provides two plural forms for the original messages, so you will also need to provide a translation for the base language to include all plural forms if the plural rules for the base language are different from English.
  • When an English variant is activated and English strings are missing, the fallback language will not be the LANGUAGE_CODE of the project, but the original strings. 例如,访问以西班牙语作为默认语言的网站的英语用户以及用俄语写成的原始字符串将回退到俄语,而不回到西班牙语。