Spec-Zone.ru › Django 3.0

Перевод

Обзор

Для того, чтобы проект 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 ""

Это также работает в шаблонах. Подробнее см. Комментарии для переводчиков в шаблонах.

Пометка строк как 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 ""

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

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

Используйте ленивые версии функций перевода в 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.utils.translation import ngettext_lazy

class MyForm(forms.Form):
    error_message = ngettext_lazy("You only provided %(num)d argument",
        "You only provided %(num)d arguments", 'num')

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

Если строка содержит ровно один безымянный заполнитель, вы можете выполнить интерполяцию напрямую с аргументом number:

class MyForm(forms.Form):
    error_message = ngettext_lazy(
        "You provided %d argument",
        "You provided %d arguments",
    )

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

Форматирование строк: format_lazy()

Метод Python 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() [source]

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

trans тег шаблона

Тег шаблона {% trans %} переводит либо постоянную строку (в одинарных или двойных кавычках), либо переменное содержимое:

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

Если присутствует параметр noop, поиск переменной всё ещё выполняется, но перевод пропускается. Это полезно при «заглушке» содержимого, которое потребует перевода в будущем:

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

Внутри теги перевода используют вызов gettext().

В случае, если в тег передаётся переменная шаблона (myvar выше), тег сначала преобразует такую переменную в строку во время выполнения, а затем ищет эту строку в каталогах сообщений.

Смешивание переменной шаблона внутри строки в теге {% trans %} невозможно. Если ваши переводы требуют строк с переменными (заполнителями), используйте {% blocktrans %} вместо этого.

Если вам нужно получить переведённую строку, не отображая её, вы можете использовать следующий синтаксис:

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

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

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

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

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

{% trans %} также поддерживает контекстные маркеры с помощью ключевого слова context:

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

blocktrans тег шаблона

В отличие от тега trans, тег blocktrans позволяет помечать сложные предложения, состоящие из литералов и переменного содержимого, для перевода с использованием плейсхолдеров:

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

Для перевода выражения шаблона — например, доступа к атрибутам объекта или использования фильтров шаблона — необходимо привязать выражение к локальной переменной для использования в блоке перевода. Примеры:

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

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

Вы можете использовать несколько выражений внутри одного тега blocktrans:

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

Примечание

Предыдущий более подробный формат все еще поддерживается: {% blocktrans with book|title as book_t and author|title as author_t %}

Другие теги блоков (например, {% for %} или {% if %}) не разрешены внутри тега blocktrans.

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

Этот тег также предоставляет возможность для множественного числа. Для его использования:

  • Укажите и привяжите значение счетчика с именем count. Это значение будет использовано для выбора правильной формы множественного числа.
  • Укажите форму единственного и множественного числа, разделив их тегом {% plural %} внутри тегов {% blocktrans %} и {% endblocktrans %}.

Пример:

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

Более сложный пример:

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

При использовании функции множественного числа и привязки значений к локальным переменным помимо значения счетчика, имейте в виду, что конструкция blocktrans внутренне преобразуется в вызов ngettext. Это означает, что применяются те же примечания по переменным ngettext.

Обращение к обратным URL-адресам не может быть выполнено внутри тегов blocktrans и должно быть получено (и сохранено) заранее:

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

Если вам нужно получить переведенную строку без ее отображения, вы можете использовать следующий синтаксис:

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

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

{% blocktrans %} также поддерживает контекстные маркеры с помощью ключевого слова context:

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

Еще одна функция, которую поддерживает {% blocktrans %}, это параметр trimmed. Этот параметр удалит символы новой строки из начала и конца содержимого тега {% blocktrans %}, заменит любые пробелы в начале и конце строки и объединит все строки в одну, используя пробел в качестве разделителя. Это очень полезно для отступа содержимого тега {% blocktrans %} без включения символов отступа в соответствующую запись в файле PO, что упрощает процесс перевода.

Например, следующий тег {% blocktrans %}:

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

приведет к записи "First sentence. Second paragraph." в файле PO по сравнению с "\n  First sentence.\n  Second sentence.\n", если параметр trimmed не был указан.

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

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

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

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

Примечание

В этом примере инфраструктура перевода получит строку "yes,no", а не отдельные строки "yes" и "no". Переведенная строка должна содержать запятую, чтобы код разбора фильтра знал, как разделить аргументы. Например, немецкий переводчик мог бы перевести строку "yes,no" как "ja,nein" (сохраняя запятую).

Комментарии для переводчиков в шаблонах

Так же, как и в коде Python, эти заметки для переводчиков могут быть указаны с помощью комментариев, либо с помощью тега comment:

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

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

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

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

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

Примечание

Для полноты картины, вот соответствующие фрагменты результирующего .po файла:

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

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

# ...

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

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

Изменение языка в шаблонах

Если вы хотите выбрать язык в шаблоне, вы можете использовать тег шаблона language:

{% load i18n %}

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

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

Хотя первый случай «Добро пожаловать на нашу страницу» использует текущий язык, второй всегда будет на английском.

Другие теги

Эти теги также требуют {% load i18n %}.

get_available_languages

{% get_available_languages as LANGUAGES %} возвращает список кортежей, в котором первый элемент — код языка, а второй — имя языка (переведенное на текущий активный язык локали).

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

Если ваш корневой URLconf использует i18n_patterns(), то JavaScriptCatalog также необходимо обернуть в i18n_patterns() для правильной генерации каталога.

Пример с i18n_patterns():

from django.conf.urls.i18n import i18n_patterns

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

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

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

Использование каталога переводов JavaScript

Для использования каталога подключите динамически сгенерированный скрипт следующим образом:

<script type="text/javascript" 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 %}

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

Тег language ожидает код языка в качестве единственного аргумента.

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

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

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

Первый шаг - создать файл сообщений для нового языка. Файл сообщений — это текстовый файл, представляющий один язык, содержащий все доступные строки перевода и то, как они должны быть представлены на данном языке. Файлы сообщений имеют расширение .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) — пустая строка. Затем содержимое будет записано на следующих строках как одна строка на строке. Эти строки будут непосредственно конкатенированы. Не забывайте о trailing spaces в строках; в противном случае они будут склеены без пробелов!

Учитывайте кодировку

Из-за способа работы внутренних инструментов gettext и потому, что мы хотим разрешить строки исходного кода, отличные от ASCII, в ядре Django и ваших приложениях, вы **обязательно** должны использовать UTF-8 в качестве кодировки для файлов PO (по умолчанию при создании файлов PO). Это означает, что все будут использовать одну и ту же кодировку, что важно при обработке файлов PO Django.

Нечеткие записи

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 или, если он не установлен, на /, в зависимости от характера запроса:

  • Для AJAX-запросов перенаправление выполняется только в том случае, если параметр next был установлен. В противном случае возвращается код состояния 204 (Нет содержимого).
  • Для запросов, не являющихся AJAX, перенаправление всегда выполняется.

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

{% load i18n %}

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

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

Если вам нужно только запустить 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 в вашей конфигурации корневого URL. См. Международная локализация: в шаблонах 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. И используется django-admin compilemessages для создания двоичных .mo файлов, которые используются 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/3.0/topics/i18n/translation/

Spec-Zone.ru

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