Spec-Zone.ru › Django 3.1

Перевод

Обзор

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

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

После этого Django позаботится о переводе веб-приложений на лету на каждом доступном языке в соответствии с предпочтениями языка пользователей.

Хуки интернационализации Django включены по умолчанию, и это означает, что в некоторых местах фреймворка есть определённая издержки, связанная с i18n. Если вы не используете интернационализацию, вам следует потратить две секунды на установку USE_I18N = False в вашем файле настроек. Тогда Django выполнит некоторые оптимизации, чтобы не загружать механизм интернационализации.

Примечание

Также существует независимый, но связанный, параметр USE_L10N, который контролирует, должен ли 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, Django поддерживает только синтаксисы, поддерживаемые gettext. В частности, 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 ""

Это также работает в шаблонах. Смотрите Комментарии для переводчиков в шаблонах для получения дополнительной информации.

Пометка строк как бездействующих

Используйте функцию 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(), если строка требует множественного числа. Обе принимают строку контекста в качестве первого параметра.

END_OF_DOCUMENT_MARKER

В полученном .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')

Методы модели short_description значения атрибутов

Для методов моделей вы можете предоставить переводы Django и сайту администрирования с помощью атрибута short_description:

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

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

    def is_mouse(self):
        return self.kind.type == MOUSE_TYPE
    is_mouse.short_description = _('Is it a mouse?')

Работа с ленивыми объектами перевода

Результат вызова 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})

Если строка содержит ровно один безымянный placeholder, вы можете выполнить интерполяцию непосредственно с аргументом 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 для отложенных переводов

В любом другом случае, когда вы хотите отложить перевод, но должны передать переводимую строку в качестве аргумента другой функции, вы можете обернуть эту функцию в вызов 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()

Функция 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 %}. Если ваши переводы требуют строк с переменными (placeholders), используйте {% 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" %}
Изменено в Django 3.1:

Тег trans был переименован в translate. Тег trans всё ещё поддерживается как алиас для обратной совместимости.

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

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

{% 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 не была указана.

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

Тег blocktrans был переименован в blocktranslate. Тег blocktrans всё ещё поддерживается как псевдоним для обратной совместимости.

Переменные строки, переданные тегам и фильтрам

Вы можете переводить строковые литералы, передаваемые в качестве аргументов тегам и фильтрам, используя знакомый синтаксис _():

{% 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 Array, значения элементов которого последовательно интерполируются в соответствующие 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='js18n-%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 на другом языке используйте тег шаблона 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, используйте Extract Messages из 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) — это пустая строка. Затем содержимое будет записано в следующие несколько строк как одна строка на строку. Эти строки будут непосредственно конкатенированы. Не забывайте о trailing spaces внутри строк; в противном случае они будут склеены без пробелов!

Обратите внимание на кодировку

Из-за того, как работают инструменты 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 (Нет содержимого).
Изменено в Django 3.1:

В более старых версиях различие для возврата основано на том, установлен ли заголовок X-Requested-With в значение XMLHttpRequest. Это устанавливается методом jQuery ajax().

Вот пример HTML-шаблона:

{% load i18n %}

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

В этом примере Django ищет 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 3.0:

В более старых версиях вы могли задать язык в текущей сессии.

Использование переводов вне представлений и шаблонов

Хотя Django предоставляет богатый набор инструментов для работы с локализацией в представлениях и шаблонах, он не ограничивает их использование кодом, специфичным для 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 и языка, установленного middleware.

Функции, представляющие особый интерес, это 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 использует этот язык в качестве языка по умолчанию для перевода — в качестве последней попытки, если не будет найдено более подходящего перевода по одному из методов, используемых модулем локализации (см. ниже).

Если вам нужно запустить 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.)

  • В случае неудачи проверяет заголовок Accept-Language HTTP. Этот заголовок отправляется вашим браузером и указывает серверу предпочтительный(ые) язык(и) в порядке приоритета. 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, которые используются gettext, используется django-admin compilemessages.

Вы также можете запустить 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/3.1/topics/i18n/translation/

Spec-Zone.ru

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