Spec-Zone.ru › Django 4.2

Перевод

Обзор

Для того, чтобы сделать проект Django переводимым, необходимо добавить минимальное количество хуков в ваш Python-код и шаблоны. Эти хуки называются строками для перевода. Они сообщают Django: «Этот текст должен быть переведен на язык конечного пользователя, если для этого текста доступен перевод на этом языке». Вы несете ответственность за пометку переводимых строк; система может переводить только те строки, о которых она знает.

Затем Django предоставляет утилиты для извлечения строк перевода в файл сообщений. Этот файл является удобным способом для переводчиков предоставить эквивалент строк перевода на целевой язык. После того, как переводчики заполнили файл сообщений, его необходимо скомпилировать. Этот процесс опирается на инструмент 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 и тестах doctest. Установка глобальной функции _() вызывает конфликт. Явное импортирование gettext() как _() избегает этой проблемы.

Какие функции могут быть переименованы как _?

Из-за того, как работает xgettext (используется makemessages), только функции, принимающие один строковый аргумент, могут быть импортированы как _:

  • gettext()
  • gettext_lazy()

В этом примере текст "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. Пример:

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-строки ещё не поддерживаются 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 было локализуемым для языков, где количество и сложность форм множественного числа больше, чем две формы, используемые в английском языке («объект» для единственного числа и «объекты» для всех случаев, когда 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(), убедитесь, что вы используете одно имя для каждой извлечённой переменной, включённой в литерал. В приведённых выше примерах обратите внимание, как мы использовали переменную Python name в обеих строках перевода. Этот пример, помимо того, что он неверен на некоторых языках, как указано выше, потерпит неудачу:

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

Контекстные маркеры также поддерживаются тегами шаблонов translate и blocktranslate.

Ленивые переводы

Используйте ленивые версии функций перевода в django.utils.translation (легко узнаваемые по суффиксу lazy в их именах), чтобы лениво переводить строки — когда значение используется, а не когда они вызываются.

Эти функции хранят леничную ссылку на строку — а не фактический перевод. Сам перевод будет выполнен, когда строка будет использована в контексте строки, например, при рендеринге шаблона.

Это важно, когда вызовы этих функций находятся в кодовых путях, которые выполняются во время загрузки модуля.

Это может легко произойти при определении моделей, форм и форм моделей, потому что Django реализует их таким образом, что их поля фактически являются атрибутами класса. По этой причине убедитесь, что используете ленивые переводы в следующих случаях:

Поля и связи моделей verbose_name и help_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"))

Вы можете помечать имена связей ForeignKey, ManyToManyField или OneToOneField как переводимые, используя их параметры verbose_name:

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

Так же, как и в verbose_name, вы должны указать текстовое значение verbose_name в нижнем регистре, так как Django автоматически приведёт его к верхнему регистру, когда это необходимо.

Значения verbose name моделей

Рекомендуется всегда указывать явные значения verbose_name и verbose_name_plural, а не полагаться на фолловерную англоцентричную и несколько наивную процедуру определения verbose name, которую 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() может быть использован там, где вы бы использовали строку (объект 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, вы можете сократить его до _ (нижнее подчеркивание), как показано ниже:

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

Атрибуты name, name_local, и name_translated словаря содержат название языка на английском языке, на самом языке и на вашем текущем активном языке соответственно. Атрибут bidi равен True только для двунаправленных языков.

Источник информации о языке — модуль django.conf.locale . Аналогичный доступ к этой информации доступен и для кода шаблонов. Смотрите ниже.

Международная поддержка: в коде шаблонов

Переводы в шаблонах Django используют две теги шаблонов и несколько отличающуюся синтаксис, чем в 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 %} также поддерживает контекстуальные маркеры с помощью ключевого слова context:

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

blocktranslate тег шаблона

В отличие от тега translate, тег blocktranslate позволяет помечать сложные предложения, состоящие из литералов и переменного содержимого для перевода, используя заполнитель:

{% 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. Это означает, что применяются те же примечания, касающиеся переменных ngettext.

Обратные запросы 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 }}">

На практике вы будете использовать это для получения строки, которую можно использовать в нескольких местах шаблона или использовать вывод как аргумент для других тегов или фильтров шаблона.

Изменено в Django 4.2:

В более ранних версиях экземпляры asvar не помечались как безопасные для вывода (HTML).

{% blocktranslate %} также поддерживает контекстные маркеры с использованием ключевого слова 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, эти заметки для переводчиков можно указать с помощью комментариев, используя тег comment:

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

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

или с помощью конструкции однострочных комментариев {# … #} конструкций:

{# 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 %} возвращает список кортежей, в которых первый элемент — код языка, а второй — название языка (переведённое на текущий активный язык).

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 будет иметь доступ к LANGUAGES, LANGUAGE_CODE, и LANGUAGE_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). См. раздел о представлении перенаправления set_language для примера отображения селектора языка с использованием {% 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 }} («Deutsch»)
  • {{ LANGUAGE_CODE|language_bidi }} (False)
  • {{ 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

Представление, которое генерирует библиотеку кода JavaScript с функциями, имитирующими интерфейс gettext плюс массив строк переводов.

Атрибуты

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

Если ваша конфигурация URL-адресов корня использует 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 для определения URL-адреса представления каталога JavaScript. После загрузки каталога ваш 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 как true. obj содержит 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'

Она имеет доступ к следующим параметрам:

  • DATE_FORMAT
  • DATE_INPUT_FORMATS
  • DATETIME_FORMAT
  • DATETIME_INPUT_FORMATS
  • DECIMAL_SEPARATOR
  • FIRST_DAY_OF_WEEK
  • MONTH_DAY_FORMAT
  • NUMBER_GROUPING
  • SHORT_DATE_FORMAT
  • SHORT_DATETIME_FORMAT
  • THOUSAND_SEPARATOR
  • TIME_FORMAT
  • TIME_INPUT_FORMATS
  • YEAR_MONTH_FORMAT

Это полезно для поддержания согласованности форматирования с значениями, отображаемыми в 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

В простейшем случае, если склонение не требуется, она возвращает false для целого числа 1 и true для всех других чисел.

Однако, склонение не всегда так просто в разных языках. Если язык не поддерживает склонение, возвращается пустое значение.

Кроме того, если существуют сложные правила склонения, представление каталога будет отображать условное выражение. Это выражение будет оцениваться как значение true (следует использовать множественное число) или false (не следует использовать множественное число).

Представление JSONCatalog

class JSONCatalog

Для использования другой библиотеки на стороне клиента для обработки переводов, вы можете воспользоваться представлением JSONCatalog. Оно аналогично представлению JavaScriptCatalog, но возвращает ответ в формате JSON.

См. документацию для JavaScriptCatalog для получения информации о возможных значениях и использовании атрибутов domain и packages.

Формат ответа следующий:

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

Заметка о производительности

Различные представления JavaScript/JSON i18n генерируют каталог из файлов .mo при каждом запросе. Поскольку его выходные данные постоянны, по крайней мере для данной версии сайта, это хороший кандидат для кэширования.

Кэширование на стороне сервера уменьшит нагрузку на процессор. Его легко реализовать с помощью декоратора 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",
    ),
]

Кэширование на стороне клиента сэкономит пропускную способность и ускорит загрузку вашего сайта. Если вы используете ETags (ConditionalGetMiddleware), вы уже покрыты. В противном случае, вы можете применить условные декораторы. В следующем примере кэш обновляется всякий раз, когда вы перезапускаете сервер приложения:

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-адресов:

  • Добавление префикса языка в начало шаблона URL-адресов, чтобы LocaleMiddleware мог определить язык для активации из запрошенного URL-адреса.
  • Обеспечение возможности перевода самих шаблонов URL-адресов с помощью функции django.utils.translation.gettext_lazy().

Предупреждение

Использование любого из этих функций требует установки активного языка для каждого запроса; другими словами, вам нужен django.middleware.locale.LocaleMiddleware в вашем параметре MIDDLEWARE.

Префикс языка в шаблонах URL-адресов

i18n_patterns(*urls, prefix_default_language=True)

Эта функция может быть использована в основном URLconf, и Django автоматически добавит код текущего активного языка как префикс ко всем шаблонам URL-адресов, определённых внутри i18n_patterns().

Установка prefix_default_language в False удаляет префикс из языка по умолчанию (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 автоматически добавит префикс языка к шаблонам URL-адресов, которые были добавлены функцией 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=False и LANGUAGE_CODE='en', URL-адреса будут:

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

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

Предупреждение

i18n_patterns() разрешён только в главном 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 вызовет конфликт с непереведённым шаблоном 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 ожидает код языка в качестве единственного аргумента.

Локализация: как создать файлы языка

После того, как литералы строк приложения были помечены для последующего перевода, необходимо написать (или получить) сам перевод. Вот как это работает.

Файлы сообщений

Первым шагом является создание файла сообщений файла сообщений для нового языка. Файл сообщений — это текстовый файл, представляющий один язык, содержащий все доступные строки перевода и то, как они должны быть представлены на данном языке. Файлы сообщений имеют расширение .po.

Django поставляется с инструментом django-admin makemessages, который автоматизирует создание и обновление этих файлов.

Утилиты gettext

Команда makemessages (и команда compilemessages, обсуждаемая позже) используют команды из набора инструментов GNU gettext: xgettext, msgfmt, msgmerge и msguniq.

Минимальная поддерживаемая версия утилит gettext — 0.15.

Для создания или обновления файла сообщений выполните эту команду:

django-admin makemessages -l de

…где de — это имя локали для файла сообщений, который вы хотите создать. Например, 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

Предупреждение

При создании файлов сообщений из исходного кода JavaScript вам необходимо использовать специальное доменное имя djangojs, а не -e js.

Используете шаблоны Jinja2?

makemessages не понимает синтаксис шаблонов Jinja2. Для извлечения строк из проекта, содержащего шаблоны Jinja2, используйте Извлечение сообщений из Babel вместо этого.

Вот пример babel.cfg файла конфигурации:

# 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 в 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, и потому, что мы хотим разрешить строки исходного кода, отличные от ASCII, в ядре Django и ваших приложениях, вы **обязательно** должны использовать кодировку UTF-8 для ваших файлов .po (по умолчанию, когда создаются файлы .po). Это означает, что все будут использовать одну и ту же кодировку, что важно, когда Django обрабатывает файлы .po.

Размытые записи

Иногда makemessages генерирует записи перевода, помеченные как размытые, например, когда переводы выводятся из ранее переведённых строк. По умолчанию размытые записи не обрабатываются compilemessages.

Чтобы повторно проверить весь исходный код и шаблоны на новые строки перевода и обновить все файлы сообщений для **всех** языков, выполните следующую команду:

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 (Byte Order Mark), поэтому, если ваш текстовый редактор добавляет такие маркеры в начало файлов по умолчанию, вам нужно будет его перенастроить.

Отладка: gettext() неправильно определяет python-format в строках с символами процента

В некоторых случаях, таких как строки с знаком процента, за которым следует пробел и тип преобразования строки тип преобразования строки (например, _("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

Это необходимо только тем, кто хочет извлечь идентификаторы сообщений или скомпилировать файлы сообщений (.po). Работа над переводом сама по себе включает редактирование существующих файлов такого типа, но если вы хотите создать свои собственные файлы сообщений или хотите проверить или скомпилировать изменённый файл сообщений, скачайте установщик предварительно скомпилированного двоичного файла.

Вы также можете использовать gettext двоичные файлы, полученные из других источников, при условии, что команда xgettext --version работает должным образом. Не пытайтесь использовать утилиты перевода Django с пакетом gettext, если команда xgettext --version в командной строке 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)

Разное

Представление перенаправления языка

set_language(request)

Для удобства Django поставляется представление, django.views.i18n.set_language(), которое устанавливает предпочтения языка пользователя и перенаправляет его на заданный URL или, по умолчанию, на предыдущую страницу.

Активируйте это представление, добавив следующую строку в ваш URLconf:

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

(Обратите внимание, что в этом примере представление доступно по адресу /i18n/setlang/.)

Предупреждение

Убедитесь, что вы не включаете вышеуказанный URL в i18n_patterns() — он сам должен быть независимым от языка, чтобы работать правильно.

Представление ожидает вызова через метод POST, с параметром language, установленным в request. Если включена поддержка сеансов, представление сохраняет выбор языка в сеансе пользователя. Оно также сохраняет выбор языка в cookie с именем django_language по умолчанию. (Имя можно изменить с помощью параметра LANGUAGE_COOKIE_NAME.)

После установки выбора языка Django ищет параметр next в данных POST или GET. Если он найден и Django считает его безопасным URL (т.е. он не указывает на другой хост и использует безопасную схему), будет выполнено перенаправление на этот URL. В противном случае Django может перенаправить пользователя на URL из заголовка Referer или, если он не установлен, на /, в зависимости от характера запроса:

  • Если запрос принимает HTML-контент (на основе его заголовка HTTP Accept), резервное перенаправление всегда будет выполнено.
  • Если запрос не принимает 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 ищет URL страницы, на которую будет перенаправлен пользователь, в переменной контекста redirect_to.

Явное задание активного языка

Возможно, вам потребуется явно задать активный язык для текущего сеанса. Например, предположим, что предпочтение языка пользователя извлекается из другой системы. Вы уже познакомились с django.utils.translation.activate(). Это применяется только к текущей потоковой нити. Чтобы сохранить язык на весь сеанс в cookie, установите cookie LANGUAGE_COOKIE_NAME в ответе:

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

Cookie языка

Ряд параметров может быть использован для настройки параметров cookie языка:

  • LANGUAGE_COOKIE_NAME
  • LANGUAGE_COOKIE_AGE
  • LANGUAGE_COOKIE_DOMAIN
  • LANGUAGE_COOKIE_HTTPONLY
  • LANGUAGE_COOKIE_PATH
  • LANGUAGE_COOKIE_SAMESITE
  • LANGUAGE_COOKIE_SECURE

Примечания по реализации

Особенности перевода Django

Механизм перевода Django использует стандартный модуль gettext из Python. Если вы знаете gettext, вы можете заметить эти особенности в том, как Django выполняет перевод:

  • Домен строки — django или djangojs. Этот домен используется для различения разных программ, хранящих свои данные в общей библиотеке файлов сообщений (обычно /usr/share/locale/). Домен django используется для строк перевода Python и шаблонов и загружается в глобальные каталоги перевода. Домен djangojs используется только для каталогов перевода JavaScript, чтобы сделать их максимально компактными.
  • Django не использует xgettext в одиночку. Он использует обертки Python вокруг xgettext и msgfmt. Это в основном для удобства.

Как Django определяет предпочтения языка

После подготовки ваших переводов — или если вы хотите использовать переводы, которые поставляются с Django — вам нужно активировать перевод для вашего приложения.

Внутри Django используется очень гибкая модель для определения используемого языка — на уровне всей установки, для конкретного пользователя или для обоих.

Для установки предпочтения языка на уровне всей установки, установите LANGUAGE_CODE. Django использует этот язык как язык по умолчанию для перевода — как последний вариант, если не найдено более подходящего перевода одним из методов, используемых средством locale (см. ниже).

Если вам нужно только запустить Django с вашим родным языком, вам нужно установить LANGUAGE_CODE и убедиться, что соответствующие файлы сообщений и их скомпилированные версии (.mo) существуют.

Если вы хотите, чтобы каждый пользователь мог указать предпочитаемый язык, вам также необходимо использовать LocaleMiddleware. LocaleMiddleware позволяет выбирать язык на основе данных из запроса. Это настраивает контент для каждого пользователя.

Чтобы использовать LocaleMiddleware, добавьте 'django.middleware.locale.LocaleMiddleware' в настройку MIDDLEWARE. Поскольку порядок расположения middleware важен, следуйте этим рекомендациям:

  • Убедитесь, что это один из первых установленных middleware.
  • Он должен следовать за SessionMiddleware, потому что LocaleMiddleware использует данные сессии. И он должен предшествовать CommonMiddleware, потому что CommonMiddleware нуждается в активированном языке для разрешения запрошенного URL.
  • Если вы используете CacheMiddleware, поместите LocaleMiddleware после него.

Например, ваша настройка MIDDLEWARE может выглядеть так:

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

(Дополнительные сведения о middleware см. в документации по middleware.)

LocaleMiddleware пытается определить предпочтения языка пользователя, следуя этому алгоритму:

  • Сначала ищет префикс языка в запрошенном URL. Это выполняется только при использовании функции i18n_patterns в вашем корневом URLconf. Более подробную информацию о префиксе языка и о том, как локализовать URL-патерны, см. в разделе Локализация: в URL-шаблонах.
  • Если не найдено, то ищет cookie.

    Имя используемого cookie устанавливается настройкой LANGUAGE_COOKIE_NAME. (По умолчанию — django_language.)

  • Если и это не найдено, то ищет HTTP-заголовок Accept-Language. Этот заголовок отправляется вашим браузером и сообщает серверу, какие языки вы предпочитаете в порядке приоритета. Django пробует каждый язык в заголовке, пока не найдет язык с доступными переводами.
  • Если и это не найдено, использует глобальную настройку LANGUAGE_CODE.

Примечания:

  • В каждом из этих случаев ожидается, что предпочтения языка будут в стандартном формате языка в виде строки. Например, бразильский португальский язык — pt-br.
  • Если базовый язык доступен, но указанный подязык нет, Django использует базовый язык. Например, если пользователь указывает de-at (австрийский немецкий), но Django доступен только de, Django использует de.
  • Выбирать можно только языки, перечисленные в настройке LANGUAGES. Если вы хотите ограничить выбор языка подмножеством предоставленных языков (потому что ваше приложение не предоставляет все эти языки), установите LANGUAGES в список языков. Например:

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

    В этом примере доступными для автоматического выбора языками являются немецкий и английский (и любые подязыки, такие как de-ch или en-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.")

Обратите внимание, что при статическом (без middleware) переводе язык находится в settings.LANGUAGE_CODE, а при динамическом (с middleware) — в request.LANGUAGE_CODE.

Как Django обнаруживает переводы

Во время выполнения Django создает объединенный каталог переводимых литералов в оперативной памяти. Для этого он ищет переводы, следуя этому алгоритму относительно порядка, в котором он анализирует различные пути к файлам для загрузки скомпилированных файлов сообщений (.mo) и приоритетности нескольких переводов для одного и того же литерала:

  1. Директории, указанные в LOCALE_PATHS, имеют наивысший приоритет, причём те, которые указаны первыми, имеют более высокий приоритет, чем те, которые указаны позже.
  2. Затем он ищет и использует, если существует, директорию locale в каждой из установленных приложений, указанных в INSTALLED_APPS. Те, которые указаны первыми, имеют более высокий приоритет, чем те, которые указаны позже.
  3. Наконец, в качестве последнего варианта используется базовый перевод Django из django/conf/locale.

См. также

Переводы литералов, включённых в JavaScript-активы, ищутся по аналогичному, но не идентичному алгоритму. Подробнее см. JavaScriptCatalog.

Вы также можете поместить файлы пользовательского формата в директории LOCALE_PATHS, если также установить FORMAT_MODULE_PATH.

Во всех случаях имя директории, содержащей перевод, ожидается, что оно будет названо с использованием обозначения имени локали. Например, de, pt_BR, es_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. А для создания бинарных файлов .mo используйте django-admin compilemessages, которые используются gettext.

Вы также можете запустить django-admin compilemessages --settings=path.to.settings, чтобы заставить компилятор обработать все директории в настройке LOCALE_PATHS.

Использование неанглийского базового языка

Django предполагает, что исходные строки в переводимом проекте написаны на английском языке. Вы можете выбрать другой язык, но вы должны быть осведомлены об определённых ограничениях:

  • gettext предоставляет только две формы множественного числа для исходных сообщений, поэтому вам также потребуется предоставить перевод для базового языка, чтобы включить все формы множественного числа, если правила множественного числа для базового языка отличаются от английского.
  • Когда активирована английская версия и английские строки отсутствуют, язык по умолчанию не будет LANGUAGE_CODE проекта, а исходные строки. Например, английский пользователь, посещающий сайт с LANGUAGE_CODE, установленным на испанский, и исходными строками, написанными на русском языке, увидит русский текст, а не испанский.

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/4.2/topics/i18n/translation/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API