Spec-Zone.ru › Django 2.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(). Принято импортировать её в качестве более короткого псевдонима, _, чтобы сэкономить набивание.

Примечание

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

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

При использовании ленивого перевода для строки множественного числа ([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()

Метод str.format() Python не будет работать, когда либо 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 %}

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

Если вы включите обработчик контекста 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, url

from about import views as about_views
from news import views as news_views
from sitemap.views import sitemap

urlpatterns = [
    path('sitemap.xml', sitemap, name='sitemap-xml'),
]

news_patterns = ([
    path('', news_views.index, name='index'),
    path('category/<slug:slug>/', news_views.category, name='category'),
    path('<slug:slug>/', news_views.details, name='detail'),
], 'news')

urlpatterns += i18n_patterns(
    path('about/', about_views.main, name='about'),
    path('news/', include(news_patterns, namespace='news')),
)

После определения этих шаблонов URL, Django автоматически добавит префикс языка к шаблонам URL, которые были добавлены функцией i18n_patterns. Пример:

>>> from django.urls import reverse
>>> from django.utils.translation import activate

>>> activate('en')
>>> reverse('sitemap-xml')
'/sitemap.xml'
>>> reverse('news:index')
'/en/news/'

>>> activate('nl')
>>> reverse('news:detail', kwargs={'slug': 'news-slug'})
'/nl/news/news-slug/'

При использовании prefix_default_language=False и LANGUAGE_CODE='en', URL будут такими:

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

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

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

i18n_patterns() разрешена только в корневом URLconf. Использование её внутри включаемого URLconf вызовет исключение ImproperlyConfigured.

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

Убедитесь, что у вас нет шаблонов URL без префикса, которые могут конфликтовать с автоматически добавленным префиксом языка.

Перевод шаблонов URL

Шаблоны URL также можно пометить как переводимые, используя функцию gettext_lazy(). Пример:

from django.conf.urls.i18n import i18n_patterns
from django.urls import include, path
from django.utils.translation import gettext_lazy as _

from about import views as about_views
from news import views as news_views
from sitemaps.views import sitemap

urlpatterns = [
    path('sitemap.xml', sitemap, name='sitemap-xml'),
]

news_patterns = ([
    path('', news_views.index, name='index'),
    path(_('category/<slug:slug>/'), news_views.category, name='category'),
    path('<slug:slug>/', news_views.details, name='detail'),
], 'news')

urlpatterns += i18n_patterns(
    path(_('about/'), about_views.main, name='about'),
    path(_('news/'), include(news_patterns, namespace='news')),
)

После создания переводов функция reverse() вернёт URL на активном языке. Пример:

>>> from django.urls import reverse
>>> from django.utils.translation import activate

>>> activate('en')
>>> reverse('news:category', kwargs={'slug': 'recent'})
'/en/news/category/recent/'

>>> activate('nl')
>>> reverse('news:category', kwargs={'slug': 'recent'})
'/nl/nieuws/categorie/recent/'

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

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

Обращение к URL в шаблонах

Если локализованные URL обращаются в шаблонах, они всегда используют текущий язык. Для ссылки на URL на другом языке используйте тег шаблона language. Он активирует указанный язык в заключённом разделе шаблона:

{% load i18n %}

{% get_available_languages as languages %}

{% 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) является пустой строкой. Затем содержимое будет записано в течение следующих нескольких строк как одна строка на строке. Эти строки непосредственно конкатенируются. Не забывайте о trailing пробелах в строках; в противном случае они будут склеены без пробелов!

Учтите кодировку

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

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

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

Примечания:

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

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

    Этот пример ограничивает языки, доступные для автоматического выбора, немецким и английским (и любыми подязыками, например, de-ch или en-us).

  • Если вы определили пользовательскую настройку LANGUAGES, как объяснено в предыдущем пункте, вы можете отметить имена языков как строки перевода — но используйте gettext_lazy() вместо gettext(), чтобы избежать циклической зависимости.

    Вот пример файла настроек:

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

После того как LocaleMiddleware определит предпочтение пользователя, это предпочтение становится доступным как request.LANGUAGE_CODE для каждого HttpRequest. Не стесняйтесь считывать это значение в вашем коде представления. Вот простой пример:

from django.http import HttpResponse

def hello_world(request, count):
    if request.LANGUAGE_CODE == 'de-at':
        return HttpResponse("You prefer to read Austrian German.")
    else:
        return HttpResponse("You prefer to read another language.")

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

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

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

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

См. также

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

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

В любом случае ожидается, что имя каталога, содержащего перевод, будет названо с использованием обозначения имени локали. Например, de, pt_BR, es_AR, и т.д.

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

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

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

Spec-Zone.ru

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