Spec-Zone.ru › Django 2.1

Перевод

Обзор

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

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

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

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

Примечание

Также существует независимая, но связанная с ней, настройка USE_L10N, которая управляет, должен ли Django реализовывать локализацию форматов. Подробнее см. Локализация форматов.

Примечание

Убедитесь, что вы активировали перевод для своего проекта (самый быстрый способ — проверить, содержит ли MIDDLEWARE django.middleware.locale.LocaleMiddleware). Если вы еще этого не сделали, см. Как Django определяет предпочтение языка.

Интернационализация: в коде Python

Стандартный перевод

Укажите строку перевода, используя функцию gettext(). Принято импортировать это как более короткое псевдоним, _, чтобы сэкономить набирание.

Примечание

Префикс u для функций gettext изначально использовался для различения использования между строками unicode и байтовыми строками в Python 2. Для кода, поддерживающего только Python 3, они могут использоваться взаимозаменяемо. В будущих выпусках Django может произойти устаревание префиксных функций.

Примечание

Стандартный модуль 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-строки и JavaScript шаблонные строки пока не поддерживаются xgettext.

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

Если вы хотите дать переводчикам подсказки о переводимой строке, вы можете добавить комментарий с префиксом 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'

Примечание

Форма множественного числа и файлы po

Django не поддерживает пользовательские уравнения для множественного числа в файлах po. Поскольку все каталоги переводов объединяются, рассматривается только форма множественного числа для основного файла po Django (в django/conf/locale/<lang_code>/LC_MESSAGES/django.po). Формы множественного числа во всех остальных файлах po игнорируются. Поэтому вы не должны использовать различные уравнения для множественного числа в ваших файлах po проекта или приложения.

Маркеры контекста

Иногда слова имеют несколько значений, например, "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, а не полагаться на поведение по умолчанию Django, которое определяет verbose_name, исходя из имени класса модели:

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

Ленивые переводы и множественное число

При использовании ленивого перевода для строки множественного числа ([u]n[p]gettext_lazy) вы, как правило, не знаете значение параметра number на момент определения строки. Поэтому вам разрешено передавать имя ключа вместо целого числа в качестве параметра number. Затем number будет извлечен из словаря по этому ключу во время интерполяции строки. Вот пример:

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

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

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

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

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

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

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

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

Другие применения ленивого перевода при отложенных переводах

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

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.

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

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

{% 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 [source]

Представление, генерирующее библиотеку 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 предоставляет интерфейс для склонения слов и фраз:

var object_count = 1 // or 0, or 2, or 3, ...
s = ngettext('literal for the singular case',
        'literal for the plural case', object_count);

interpolate

Функция interpolate поддерживает динамическое заполнение строки формата. Синтаксис интерполяции заимствован из Python, поэтому функция interpolate поддерживает как позиционную, так и именованную интерполяцию:

  • Позиционная интерполяция: obj содержит объект JavaScript Array, значения элементов которого затем последовательно интерполируются в соответствующие fmt заполнительные места в том же порядке, в котором они появляются. Например:

    fmts = ngettext('There is %s object. Remaining: %s',
            'There are %s objects. Remaining: %s', 11);
    s = interpolate(fmts, [11, 20]);
    // s is 'There are 11 objects. Remaining: 20'
    
  • Именованная интерполяция: Этот режим выбирается путём передачи необязательного булевого параметра named как true. obj содержит объект JavaScript или ассоциативный массив. Например:

    d = {
        count: 10,
        total: 50
    };
    
    fmts = ngettext('Total: %(total)s, there is %(count)s object',
    'there are %(count)s of a total of %(total)s objects', d.count);
    s = interpolate(fmts, d, true);
    

Однако не стоит злоупотреблять строковой интерполяцией: это всё ещё JavaScript, поэтому код должен выполнять многократные подстановки с помощью регулярных выражений. Это не так быстро, как интерполяция строк в Python, поэтому используйте её только в тех случаях, когда это действительно необходимо (например, в сочетании с ngettext для правильного склонения).

get_format

Функция get_format имеет доступ к настройкам форматирования i18n и может получить строку формата для заданного имени настройки:

document.write(get_format('DATE_FORMAT'));
// 'N j, Y'

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

  • 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 [source]

Для использования другой клиентской библиотеки для обработки переводов вы можете воспользоваться представлением 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) [source]

Эта функция может быть использована в корневом 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 прост. Каждый файл .po содержит небольшую часть метаданных, таких как контактная информация переводчика, но большая часть файла представляет собой список сообщений — простых сопоставлений между строками перевода и фактического переведённого текста для конкретного языка.

Например, если ваше приложение Django содержало строку перевода для текста "Welcome to my site.", как это:

_("Welcome to my site.")

…тогда django-admin makemessages создаст файл .po содержащий следующий фрагмент кода – сообщение:

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

Краткое объяснение:

  • msgid — это строка перевода, которая отображается в исходном коде. Не изменяйте её.
  • msgstr — это место, куда вы помещаете язык-специфический перевод. Вначале оно пустое, поэтому вы должны его изменить. Убедитесь, что вы сохраняете кавычки вокруг своего перевода.
  • Для удобства каждое сообщение включает, в виде комментария, начинающегося с #, и расположенного над строкой msgid, имя файла и номер строки, из которого была взята строка перевода.

Длинные сообщения — это особый случай. Там первая строка после msgstr (или msgid) — пустая строка. Затем содержимое будет записано в следующие несколько строк как одна строка на строку. Эти строки будут напрямую конкатенированы. Не забывайте о пробелах в конце строк; в противном случае они будут склеены без пробелов!

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

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

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

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) [source]

Для удобства 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 2.1:

В более ранних версиях cookie устанавливается только если поддержка сессий не включена.

После установки выбора языка 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(). Она применяется только к текущей нити. Чтобы сохранить язык для всей сессии, измените также LANGUAGE_SESSION_KEY в сессии:

from django.utils import translation
user_language = 'fr'
translation.activate(user_language)
request.session[translation.LANGUAGE_SESSION_KEY] = user_language

Обычно вам нужно использовать оба: django.utils.translation.activate() изменит язык для текущей нити, а изменение сессии сохранит это предпочтение в будущих запросах.

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

Например:

from django.utils import translation

def welcome_translated(language):
    cur_language = translation.get_language()
    try:
        translation.activate(language)
        text = translation.gettext('welcome')
    finally:
        translation.activate(cur_language)
    return text

Вызов этой функции со значением 'de' вернёт "Willkommen", независимо от LANGUAGE_CODE и языка, установленного средством обработки.

Функции, представляющие особый интерес, это django.utils.translation.get_language(), которая возвращает язык, используемый в текущем потоке, django.utils.translation.activate(), которая активирует каталог переводов для текущего потока, и django.utils.translation.check_for_language(), которая проверяет, поддерживается ли данный язык Django.

Для написания более лаконичного кода также есть менеджер контекста django.utils.translation.override(), который сохраняет текущий язык при входе и восстанавливает его при выходе. С помощью него пример выше становится:

from django.utils import translation

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

Файл языка

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

  • LANGUAGE_COOKIE_NAME
  • LANGUAGE_COOKIE_AGE
  • LANGUAGE_COOKIE_DOMAIN
  • LANGUAGE_COOKIE_PATH

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

Особенности локализации Django

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

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

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

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

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

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

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

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

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

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

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

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

(Подробнее о middleware см. документацию по middleware.)

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

  • Сначала он ищет префикс языка в запрошенном URL. Это выполняется только тогда, когда вы используете функцию i18n_patterns в вашем файле конфигурации URL. См. Международные переводы: в шаблонах URL для получения дополнительной информации о префиксе языка и о том, как выполнить международные переводы в шаблонах URL.
  • В противном случае он ищет ключ LANGUAGE_SESSION_KEY в текущем сеансе пользователя.
  • В противном случае он ищет 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.

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

Добавлена поддержка перехода на общий язык, как описано выше.

Таким образом, вы можете создавать приложения, которые включают собственные переводы, и переопределять базовые переводы в вашем проекте. Или вы можете просто создать большой проект из нескольких приложений и поместить все переводы в один большой общий файл сообщений, специфичный для проекта, который вы составляете. Выбор за вами.

Все хранилища файлов сообщений структурированы одинаково. Они:

  • Все пути, указанные в 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/2.1/topics/i18n/translation/

Spec-Zone.ru

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