翻译

概述

为了使django项目具有可翻译性,必须向Python代码和模板添加最少数量的钩子。这些钩子叫做 translation strings . 他们告诉Django:“如果可以使用该语言翻译该文本,则应将该文本翻译为最终用户的语言。”标记可翻译字符串是您的责任;系统只能翻译它知道的字符串。

然后Django提供了将翻译字符串提取为 message file . 此文件是翻译人员提供目标语言中的翻译字符串等价物的方便方法。一旦翻译人员填写了消息文件,就必须对其进行编译。这个过程依赖于gnu gettext工具集。

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

Django的国际化钩子在默认情况下是打开的,这意味着在框架的某些地方有一些与i18n相关的开销。如果您不使用国际化,您应该用两秒钟来设置 USE_I18N = False 在设置文件中。然后,Django将进行一些优化,以避免加载国际化机器。

备注

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

国际化:在python代码中

标准翻译

使用函数指定翻译字符串 gettext() . 按照惯例,将其作为一个较短的别名导入, _ ,保存键入内容。

备注

python的标准库 gettext 模块安装 _() 作为全局命名空间的别名 gettext() . 在Django,我们选择不遵循这种做法,原因有两个:

  1. 有时,你应该使用 gettext_lazy() 作为特定文件的默认翻译方法。没有 _() 在全局名称空间中,开发人员必须考虑哪一个是最合适的翻译函数。

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

哪些函数可以别名为 _

因为怎样 xgettext (由使用) makemessages )有效,只有接受单个字符串参数的函数才能作为 _

在这个例子中,文本 "Welcome to my site." 标记为翻译字符串::

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


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

您可以在不使用别名的情况下编写此代码。此示例与上一个示例相同:

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


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的标准名为string interpolation语法指定的占位符。例子::

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 )当您有多个参数时。如果使用位置插值,翻译将无法重新排序占位符文本。

因为字符串提取是由 xgettext 命令,仅支持语法 gettext 由Django支持。尤其是Python f-strings 尚不支持 xgettext ,和JavaScript模板字符串需要 gettext 0.21+。

翻译意见

如果要向翻译人员提供有关可翻译字符串的提示,可以添加前缀为的注释。 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 ""

这也适用于模板。见 模板中翻译人员的注释 了解更多详细信息。

将字符串标记为no op

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

如果有常量字符串应存储在源语言中,因为它们在系统或用户(如数据库中的字符串)上交换,但应在最后可能的时间点(如向用户显示字符串时)进行转换,则使用此选项。

多元化

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

ngettext() 接受三个参数:单数转换字符串、复数转换字符串和对象数。

当您需要将Django应用程序本地化为语言时,此函数非常有用,因为这些语言的数量和复杂性 plural forms 大于英语中使用的两种形式(“object”表示单数形式,“objects”表示所有情况,其中 count 与一个不同,无论其值如何。)

例如::

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


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'

上下文标记

有时单词有几种含义,例如 "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 ""

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

懒惰的翻译

django.utils.translation (很容易被 lazy 在它们的名称中加上后缀)以惰性地转换字符串——当访问值时,而不是在调用它们时。

这些函数存储对字符串的惰性引用,而不是实际的转换。当字符串用于字符串上下文(如模板呈现)时,转换本身将完成。

当对这些函数的调用位于模块加载时执行的代码路径中时,这一点非常重要。

这是在定义模型、窗体和模型窗体时很容易发生的事情,因为Django实现了这些,使得它们的字段实际上是类级属性。因此,请确保在以下情况下使用惰性翻译:

模型字段和关系 verbose_namehelp_text 期权价值

例如,要翻译 name 字段在以下模型中,执行以下操作:

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"))

您可以标记的名称 ForeignKeyManyToManyFieldOneToOneField 可翻译关系 verbose_name 选项:

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 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")

模型法 description 参数设置为 @display 装饰师

对于模型方法,您可以提供到Django和管理站点的翻译 description 参数设置为 display() 装饰师::

from django.contrib import admin
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"),
    )

    @admin.display(description=_("Is it a mouse?"))
    def is_mouse(self):
        return self.kind.type == MOUSE_TYPE

使用懒惰的翻译对象

结果是一个 gettext_lazy() 只要使用字符串(A),就可以使用调用 str 对象)在其他Django代码中,但它可能不能与任意的Python代码一起工作。例如,以下代码将不起作用,因为 requests 库不能处理 gettext_lazy 对象::

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

你可以通过铸造来避免这些问题。 gettext_lazy() 对象到文本字符串,然后将其传递给非Django代码:

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

如果你不喜欢长的 gettext_lazy name,您可以将其别名为 _ (下划线),如:

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() 在模型和实用程序函数中标记字符串是一种常见的操作。当您在代码的其他地方处理这些对象时,应该确保不会意外地将它们转换为字符串,因为它们应该尽可能晚地进行转换(以便正确的区域设置生效)。这就需要使用下面描述的助手函数。

懒惰的翻译和复数

对复数字符串使用惰性翻译时 (n[p]gettext_lazy )你一般不知道 number 字符串定义时的参数。因此,您有权传递一个键名而不是一个整数作为 number 争论。然后 number 将在字符串插入期间在字典的该键下查找。例如:

from django import forms
from django.core.exceptions import ValidationError
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 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 ValidationError(self.error_message % number)

格式化字符串: format_lazy()

Python 的 str.format()format_string 或者任何关于 str.format() 包含惰性转换对象。相反,您可以使用 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)

在这种情况下,在 result 只有当 result 它本身用在字符串中(通常在模板呈现时)。

延迟翻译中lazy的其他用法

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

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

这个 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 属性仅对双向语言为真。

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

国际化:在模板代码中

翻译中 Django templates 使用两个模板标记和与Python代码稍有不同的语法。要让模板访问这些标签,请将 {{% load i18n %}} 朝向模板顶部。与所有模板标记一样,此标记需要加载到所有使用翻译的模板中,甚至那些从已加载 i18n 标签。

警告

转换后的字符串在模板中呈现时不会转义。这允许您在翻译中包含HTML,例如强调,但可能存在危险的字符(例如 " )也将保持不变。

translate 模板标签

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

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

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

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

在内部,内联转换使用 gettext() 打电话。

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

不能在字符串中混合模板变量 {{% translate %}} . 如果翻译需要带有变量(占位符)的字符串,请使用 {{% blocktranslate %}} 相反。

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

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

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

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

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

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

{{% translate %}} 也支持 contextual markers 使用 context 关键字:

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

blocktranslate 模板标签

与之相反, translate 标签,即 blocktranslate Tag允许您通过使用占位符标记由直译和可变内容组成的复杂句子进行翻译:

{% blocktranslate %}This string will have {{ value }} inside.{% endblocktranslate %}

要转换模板表达式--比方说,访问对象属性或使用模板过滤器--您需要将表达式绑定到局部变量,以便在转换块中使用。例如:

{% blocktranslate with amount=article.price %}
That will cost $ {{ amount }}.
{% endblocktranslate %}

{% blocktranslate with myvar=value|filter %}
This will have {{ myvar }} inside.
{% endblocktranslate %}

您可以在一个 blocktranslate 标签:

{% blocktranslate with book_t=book|title author_t=author|title %}
This is {{ book_t }} by {{ author_t }}
{% endblocktranslate %}

备注

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

其他块标记(例如 {% for %}{% if %} )是不允许在 blocktranslate 标签。

如果解析其中一个块参数失败, blocktranslate 将返回默认语言,方法是使用 deactivate_all() 功能。

这个标签还提供了复数形式。使用它:

  • 用名称指定并绑定计数器值 count . 该值将是用于选择正确复数形式的值。

  • 同时指定单数和复数形式,用 {{% plural %}} 标签内 {{% blocktranslate %}}{{% endblocktranslate %}} 标签。

举个例子:

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

一个更复杂的例子:

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

当您使用复数功能并将值绑定到除计数器值之外的局部变量时,请记住 blocktranslate 构造在内部转换为 ngettext 打电话。这意味着相同 notes regarding ngettext variables 申请。

反向URL查找不能在 blocktranslate 并应预先检索(和存储):

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

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

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

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

{{% blocktranslate %}} 也支持 contextual markers 使用 context 关键字:

{% blocktranslate with name=user.username context "greeting" %}Hi {{ name }}{% endblocktranslate %}

另一项功能 {% blocktranslate %} 支持的是 trimmed 选择。此选项将从内容的开头和结尾删除换行符 {% blocktranslate %} 标记,替换行首和行尾的任何空格,并使用空格字符将所有行合并为一行。这对于缩进 {% blocktranslate %} 标记中的相应条目中没有缩进字符的 .po 文件,这使得翻译过程变得更容易。

例如,以下内容 {% blocktranslate %} 标签:

{% blocktranslate trimmed %}
  First sentence.
  Second paragraph.
{% endblocktranslate %}

将导致条目 "First sentence. Second paragraph.".po 文件,与 "\n  First sentence.\n  Second paragraph.\n" ,如果 trimmed 尚未指定选项。

传递给标记和筛选器的字符串文本

您可以使用熟悉的 _() 语法:

{% 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 %}
{% translate "View" %}

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

或与 {{##}} one-line comment constructs

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

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

备注

为了完整性,这些是结果的相应片段 .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>{% translate "Welcome to our page" %}</p>

{% language 'en' %}
    {% get_current_language as LANGUAGE_CODE %}
    <!-- Current language: {{ LANGUAGE_CODE }} -->
    <p>{% translate "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 它是从左到右的语言,如英语、法语、德语等。

i18n 上下文处理器

如果启用 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 }}

get_language_info_list

您也可以使用 {{% get_language_info_list %}} 用于检索语言列表(如中指定的活动语言)信息的模板标记 LANGUAGES )见 the section about the set_language redirect view 有关如何显示语言选择器的示例,请使用 {{% get_language_info_list %}} .

除了 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 }}}} (德意志)

  • {{{{ LANGUAGE_CODE|language_bidi }}}} (假)

  • {{{{ LANGUAGE_CODE|language_name_translated }}}} (“n_mecky”,当活动语言为捷克语时)

国际化:在javascript代码中

向javascript添加翻译会带来一些问题:

  • javascript代码无法访问 gettext 实施。

  • javascript代码无权访问 .po.mo 文件;它们需要由服务器传递。

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

Django为这些问题提供了一个集成的解决方案:它将翻译转换为javascript,这样您就可以调用 gettext 等等,在javascript中。

这些问题的主要解决办法是 JavaScriptCatalog 视图,它生成一个JavaScript代码库,其中包含模拟 gettext 接口,加上一个翻译字符串数组。

这个 JavaScriptCatalog 看法

class JavaScriptCatalog[源代码]

一个视图,它生成一个带有模拟 gettext 接口,加上一个翻译字符串数组。

Attributes

domain

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

packages

列表 application names 在已安装的应用程序中。这些应用程序应该包含 locale 目录。所有这些目录加上 LOCALE_PATHS (始终包括在内)合并到一个目录中。默认为 None 这意味着所有可用的翻译 INSTALLED_APPS 在javascript输出中提供。

带默认值的示例 ::

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",
    ),
]

如果您的根urlconf使用 i18n_patterns()JavaScriptCatalog 也必须用 i18n_patterns() 以便正确生成目录。

实例与 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 src="{% url 'javascript-catalog' %}"></script>

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

  • gettext

  • ngettext

  • interpolate

  • get_format

  • gettext_noop

  • pgettext

  • npgettext

  • pluralidx

gettext

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

document.write(gettext("this is to be translated"))

ngettext

这个 ngettext 函数提供了一个复数单词和短语的接口:

const objectCount = 1 // or 0, or 2, or 3, ...
const string = ngettext(
    'literal for the singular case',
    'literal for the plural case',
    objectCount
);

interpolate

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

  • 位置内插: obj 包含一个JavaScript数组对象,然后将其元素值顺序内插到其对应的 fmt 占位符的显示顺序与它们的显示顺序相同。例如:

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

    const data = {
      count: 10,
      total: 50
    };
    
    const formats = ngettext(
        'Total: %(total)s, there is %(count)s object',
        'there are %(count)s of a total of %(total)s objects',
        data.count
    );
    const string = interpolate(formats, data, 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() ),提供了一个 pluralized 上下文翻译的单词:

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

在最简单的情况下,如果不需要自定义复数,则返回 false 整数 1true 所有其他数字。

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

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

这个 JSONCatalog 看法

class JSONCatalog[源代码]

为了使用另一个客户端库来处理翻译,您可能需要利用 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="jsi18n-%s" % get_version())(
            JavaScriptCatalog.as_view()
        ),
        name="javascript-catalog",
    ),
]

客户端缓存将节省带宽并使站点加载更快。如果你使用电子标签 (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目录,并将其作为静态文件使用。这种激进的技术是在 django-statici18n.

国际化:在URL模式中

Django提供了两种将URL模式国际化的机制:

警告

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

URL模式中的语言前缀

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

此函数可以在根urlconf中使用,django将自动将当前的活动语言代码前置到在其中定义的所有url模式 i18n_patterns() .

设置 prefix_default_languageFalse 从默认语言中删除前缀 (LANGUAGE_CODE )这在向现有网站添加翻译以便当前URL不会更改时非常有用。

URL模式示例:

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

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")),
)

定义这些URL模式后,Django将自动将语言前缀添加到由 i18n_patterns 功能。示例:

>>> 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/'

警告

i18n_patterns() 只允许在根urlconf中使用。在包含的urlconf中使用它将引发 ImproperlyConfigured 例外。

警告

确保没有可能与自动添加的语言前缀冲突的无前缀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/'

警告

在大多数情况下,最好只在语言代码前缀的模式块(使用 i18n_patterns() )以避免不小心翻译的URL与未翻译的URL模式发生冲突的可能性。

在模板中反转

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

{% load i18n %}

{% get_available_languages as languages %}

{% translate "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

…在哪里 delocale 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.py 文件扩展名。如果要覆盖该缺省值,请使用 --extension-e 用于指定要检查的文件扩展名的选项:

django-admin makemessages -l de -e txt

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

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

警告

什么时候? creating message files from JavaScript source code 你需要用这个特别的 djangojs 域, not -e js .

使用jinja2模板?

makemessages 不理解jinja2模板的语法。要从包含jinja2模板的项目中提取字符串,请使用 Message ExtractingBabel 相反。

下面是一个例子 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 安装的实用程序, makemessages 将创建空文件。如果是这样,请安装 gettext 实用程序或复制英文消息文件 (locale/en/LC_MESSAGES/django.po )如果可用,并将其用作起始点,这是一个空的翻译文件。

在Windows上工作?

如果您使用的是Windows并且需要安装GNU GetText实用程序,那么 makemessages 作品,见 gettext 在Windows上 更多信息。

.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源代码字符串,所以您 must 使用UTF-8作为您的 .po 文件(默认设置为 .po 创建文件)。这意味着每个人都将使用相同的编码,这在Django处理 .po 档案。

模糊条目

makemessages 有时会生成标记为模糊的翻译条目,例如,当从以前翻译的字符串中推断翻译时。默认情况下,模糊条目是 not 处理人 compilemessages .

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

django-admin makemessages -a

正在编译消息文件

在创建了消息文件之后——每次对其进行更改——您需要将其编译成一个更有效的形式,以供使用。 gettext . 用这个做 django-admin compilemessages 实用工具。

此工具适用于所有可用的 .po 文件和创建 .mo 文件,这些文件是为使用而优化的二进制文件 gettext 。在运行时所在的同一目录中 django-admin makemessages ,奔跑 django-admin compilemessages 如下所示:

django-admin compilemessages

就是这样。您的翻译可以使用了。

在Windows上工作?

如果您使用的是Windows并且需要安装GNU GetText实用程序,那么 django-admin compilemessages 作品见 gettext 在Windows上 更多信息。

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

仅Django支持 .po 文件以UTF-8编码,没有任何BOM(字节顺序标记),因此如果文本编辑器默认情况下在文件开头添加此类标记,则需要重新配置它。

故障排除: gettext() 检测不正确 python-format 在带有百分号的字符串中

在某些情况下,例如带有百分号、空格和 string conversion type (例如) _("10% interest")gettext() 错误地将字符串标记为 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 gettext as _

output = _("10%% interest")

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

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

从javascript源代码创建消息文件

创建和更新消息文件的方式与其他Django消息文件相同--使用 django-admin makemessages 工具。唯一的区别是您需要显式指定在gettext术语中称为域的内容,在本例中为 djangojs 域,通过提供一个 -d djangojs 参数,如下所示:

django-admin makemessages -d djangojs -l de

这将为德语版的JavaScript创建或更新消息文件。更新消息文件后,运行 django-admin compilemessages 和处理普通的django消息文件一样。

gettext 在Windows上

这仅适用于希望提取消息ID或编译消息文件的人。 (.po ). 翻译工作本身涉及到编辑这种类型的现有文件,但是如果您想创建自己的消息文件,或者想测试或编译一个已更改的消息文件,请下载 a precompiled binary installer .

您也可以使用 gettext 您在其他地方获得的二进制文件,只要 xgettext --version 命令工作正常。请勿尝试将Django翻译实用程序与 gettext 如果命令为 xgettext --version 在Windows命令提示符下输入会导致弹出窗口,显示“``xgetext.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)

其他

这个 set_language 重定向视图

set_language(request)[源代码]

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

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

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

(请注意,此示例使视图在 /i18n/setlang/

警告

确保您不在 i18n_patterns() -它需要独立于语言才能正常工作。

该视图应通过调用 POST 方法,用 language 请求中设置的参数。如果启用了会话支持,则视图将保存用户会话中的语言选择。它还将语言选择保存在一个名为 django_language 默认情况下。(可以通过 LANGUAGE_COOKIE_NAME 设置)

在设置语言选项后,Django查找 next 中的参数 POSTGET 数据。如果找到了该URL,并且Django认为它是一个安全的URL(即,它不指向其他主机并使用安全方案),则将执行对该URL的重定向。否则,django可能会返回到将用户重定向到 Referer 头,如果未设置,则为 / ,取决于请求的性质:

  • 如果请求接受HTML内容(基于 Accept HTTP头),将始终执行回退。

  • 如果请求不接受HTML,则只有在 next 参数已设置。否则将返回204状态代码(无内容)。

以下是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 上下文变量。

显式设置活动语言

您可能需要为当前会话显式设置活动语言。例如,可以从另一个系统检索用户的语言首选项。你已经被介绍给 django.utils.translation.activate() . 仅适用于当前线程。要在cookie中保留整个会话的语言,请设置 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.utils.translation.activate() 更改此线程的语言,设置cookie使此首选项在将来的请求中保持不变。

在视图和模板之外使用翻译

虽然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

用值调用此函数 'de' 会给你 "Willkommen" 不管 LANGUAGE_CODE 以及中间件设置的语言。

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

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

from django.utils import translation


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

实施说明

Django 翻译说明

Django 的翻译机器使用标准 gettext python附带的模块。如果你知道 gettext 你可以用Django翻译的方式注意到这些专业:

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

  • Django不使用 xgettext 独自一人。它在周围使用python包装 xgettextmsgfmt . 这主要是为了方便。

Django如何发现语言偏好

一旦你准备好你的翻译——或者,如果你想使用Django附带的翻译——你需要激活你的应用程序的翻译。

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

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

如果你只想用你的母语运行django,你所需要做的就是设置 LANGUAGE_CODE 并确保相应的 message files 以及他们的编译版本 (.mo )存在。

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

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

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

  • 它应该在后面 SessionMiddleware ,因为 LocaleMiddleware 使用会话数据。它应该在 CommonMiddleware 因为 CommonMiddleware 需要激活的语言才能解析请求的URL。

  • 如果你使用 CacheMiddlewareLocaleMiddleware 之后。

例如,您的 MIDDLEWARE 可能看起来像这样:

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

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

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

  • 首先,它在请求的URL中查找语言前缀。仅当使用 i18n_patterns 在根urlconf中的函数。见 国际化:在URL模式中 有关语言前缀和如何国际化URL模式的详细信息。

  • 如果失败了,它会寻找一个饼干。

    所用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-chen-us

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

    以下是示例设置文件:

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

一次 LocaleMiddleware 确定用户的首选项,它使此首选项可用为 request.LANGUAGE_CODE 对于每一个 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. 然后,如果它存在一个 locale 中列出的每个已安装应用程序的目录 INSTALLED_APPS . 先出现的优先级高于后出现的优先级。

  3. 最后,Django提供的基本翻译在 django/conf/locale 被用作后备手段。

参见

javascript资产中包含的文本翻译是按照类似但不相同的算法查找的。见 JavaScriptCatalog 了解更多详细信息。

你也可以把 custom format filesLOCALE_PATHS 如果还设置了目录 FORMAT_MODULE_PATH .

在所有情况下,包含翻译的目录的名称都应使用 locale name 表示法。例如。 dept_BRes_AR 等。用于区域语言变体的未翻译字符串使用通用语言的翻译。例如,未翻译 pt_BR 字符串使用 pt 翻译。

通过这种方式,您可以编写包含其自身翻译的应用程序,并且可以在项目中重写基本翻译。或者,你可以用几个应用程序构建一个大项目,然后把所有的翻译都放到一个大的通用消息文件中,这个文件是针对你正在撰写的项目的。选择权在你。

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

  • 中列出的所有路径 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 生成二进制文件 .mo 由使用的文件 gettext .

你也可以运行 django-admin compilemessages --settings=path.to.settings 使编译器处理 LOCALE_PATHS 设置。

使用非英语基础语言

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

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

  • 当激活英语变体并且缺少英语字符串时,回退语言将不是 LANGUAGE_CODE 项目,但原始字符串。例如,一个英语用户使用 LANGUAGE_CODE 设置为西班牙语,用俄语编写的原始字符串将看到俄语文本而不是西班牙语。