Spec-Zone.ru › Django 1.8

Перевод

Обзор

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

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

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

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

Примечание

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

Примечание

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

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

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

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

Примечание

Стандартная библиотека Python gettext устанавливает _() в глобальное пространство имен как псевдоним для gettext(). В Django мы решили не следовать этой практике по нескольким причинам:

  1. Для поддержки набора символов с международными знаками (Unicode) ugettext() более полезно, чем gettext(). Иногда следует использовать ugettext_lazy() в качестве метода перевода по умолчанию для конкретного файла. Без _() в глобальном пространстве имен разработчик должен думать о том, какая функция перевода наиболее подходит.
  2. Знак подчеркивания (_) используется для представления «предыдущего результата» в интерактивной оболочке Python и тестах doctest. Установка глобальной функции _() вызывает конфликты. Явное импортирование ugettext() как _() избегает этой проблемы.

В этом примере текст "Welcome to my site." помечен как строка перевода:

from django.utils.translation import ugettext as _
from django.http import HttpResponse

def my_view(request):
    output = _("Welcome to my site.")
    return HttpResponse(output)

Очевидно, вы можете написать это без использования алиаса. Этот пример идентичен предыдущему:

from django.utils.translation import ugettext
from django.http import HttpResponse

def my_view(request):
    output = ugettext("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 позже.)

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

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

Если вы хотите дать переводчикам подсказки о переводимой строке, вы можете добавить комментарий с префиксом Translators в строке перед строкой, например:

def my_view(request):
    # Translators: This message appears on the home page only
    output = ugettext("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.ugettext_noop(), чтобы отметить строку как строку перевода, не переводя её. Строка позже переводится из переменной.

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

Множественное число

Используйте функцию django.utils.translation.ungettext() для указания сообщений множественного числа.

ungettext принимает три аргумента: строку перевода единственного числа, строку перевода множественного числа и количество объектов.

Эта функция полезна, когда вам необходимо, чтобы ваше приложение Django было локализовано на языках, где количество и сложность форм множественного числа больше, чем две формы, используемые в английском языке («объект» для единственного числа и «объекты» для всех случаев, где count отличается от одного, независимо от его значения).

Например:

from django.utils.translation import ungettext
from django.http import HttpResponse

def hello_world(request, count):
    page = ungettext(
        'there is %(count)d object',
        'there are %(count)d objects',
    count) % {
        'count': count,
    }
    return HttpResponse(page)

В этом примере количество объектов передается языкам перевода как переменная count.

Обратите внимание, что множественное число сложно и работает по-разному на каждом языке. Сравнение count с 1 не всегда является правилом. Этот код выглядит сложным, но даст неправильные результаты для некоторых языков:

from django.utils.translation import ungettext
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 = ungettext(
    'There is %(count)d %(name)s available.',
    'There are %(count)d %(name)s available.',
    count
) % {
    'count': count,
    'name': name
}

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

text = ungettext(
    'There is %(count)d %(name)s object available.',
    'There are %(count)d %(name)s objects available.',
    count
) % {
    'count': count,
    'name': Report._meta.verbose_name,
}

Примечание

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

text = ungettext(
    '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 ugettext_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, related_name='kinds',
                             verbose_name=_('kind'))

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

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

Рекомендуется всегда указывать явные опции verbose_name и verbose_name_plural, а не полагаться на подстановку по умолчанию, ориентированную на английский язык и несколько наивную процедуру определения verbose name, которую выполняет Django, просматривая имя класса модели:

from django.db import models
from django.utils.translation import ugettext_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 ugettext_lazy as _

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

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

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

Результат вызова ugettext_lazy() может быть использован там, где вы использовали бы строку unicode (объект с типом unicode) в Python. Если вы попытаетесь использовать его там, где ожидается строка bytestring (объект str ), то всё не будет работать как ожидается, поскольку объект ugettext_lazy() не знает, как преобразовать себя в строку bytestring. Вы также не можете использовать строку unicode внутри строки bytestring, поэтому это согласуется с обычным поведением Python. Например:

# This is fine: putting a unicode proxy into a unicode string.
"Hello %s" % ugettext_lazy("people")

# This will not work, since you cannot insert a unicode object
# into a bytestring (nor can you insert our unicode proxy there)
b"Hello %s" % ugettext_lazy("people")

Если вы видите вывод, похожий на "hello <django.utils.functional...>", вы пытались вставить результат ugettext_lazy() в строку bytestring. Это ошибка в вашем коде.

Если вам не нравится длинное имя ugettext_lazy, вы можете просто использовать псевдоним _ (подчёркивание), как показано ниже:

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

class MyThing(models.Model):
    name = models.CharField(help_text=_('This is the help text'))

Использование ugettext_lazy() и ungettext_lazy() для маркировки строк в моделях и вспомогательных функциях – распространённая операция. Когда вы работаете с этими объектами в другом месте своего кода, убедитесь, что случайно не преобразуете их в строки, потому что они должны преобразовываться как можно позже (чтобы был активен нужный язык). Это требует использования вспомогательной функции, описанной ниже.

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

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

from django import forms
from django.utils.translation import ungettext_lazy

class MyForm(forms.Form):
    error_message = ungettext_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 = ungettext_lazy("You provided %d argument",
        "You provided %d arguments")

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

Объединение строк: string_concat()

Стандартные операции объединения строк Python (''.join([...])) не будут работать со списками, содержащими леничные объекты перевода. Вместо этого можно использовать django.utils.translation.string_concat(), которая создаёт леничный объект, конкатенирующий содержимое и преобразующий его в строки только тогда, когда результат включается в строку. Например:

from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy
...
name = ugettext_lazy('John Lennon')
instrument = ugettext_lazy('guitar')
result = string_concat(name, ': ', instrument)

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

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

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

from django.utils import six  # Python 3 compatibility
from django.utils.functional import lazy
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _

mark_safe_lazy = lazy(mark_safe, six.text_type)

И затем позже:

lazy_string = mark_safe_lazy(_("<p>My <strong>string!</strong></p>"))

Локализованные имена языков

get_language_info() [source]

Функция get_language_info() предоставляет подробную информацию о языках:

>>> from django.utils.translation import get_language_info
>>> li = get_language_info('de')
>>> print(li['name'], li['name_local'], li['bidi'])
German Deutsch False

Атрибуты name и name_local словаря содержат название языка на английском языке и на самом языке соответственно. Атрибут 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>

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

Если в тег передаётся переменная шаблона (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 внутренне преобразуется в вызов ungettext. Это означает, что те же примечания, касающиеся переменных ungettext применяются.

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

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

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

Была добавлена опция 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, как определено выше.

Процессор контекста i18n по умолчанию не включен для новых проектов.

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

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)

Международная локализация в коде JavaScript

Добавление переводов в код JavaScript создаёт некоторые проблемы:

  • Код JavaScript не имеет доступа к реализации gettext.
  • Код JavaScript не имеет доступа к файлам .po или .mo; они должны быть доставлены сервером.
  • Каталоги переводов для JavaScript должны быть как можно меньше.

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

Представление каталога JavaScript

javascript_catalog(request, domain='djangojs', packages=None) [source]

Основное решение этих проблем — представление django.views.i18n.javascript_catalog(), которое отправляет библиотеку кода JavaScript с функциями, имитирующими интерфейс gettext, плюс массив строковых переводов. Эти строковые переводы берутся из приложений или ядра Django в соответствии с указанным вами значением в info_dict или в URL. Пути, указанные в LOCALE_PATHS, также включаются.

Вы подключаете его так:

from django.views.i18n import javascript_catalog

js_info_dict = {
    'packages': ('your.app.package',),
}

urlpatterns = [
    url(r'^jsi18n/$', javascript_catalog, js_info_dict, name='javascript-catalog'),
]

Каждая строка в packages должна быть в синтаксисе Python с точкой (тот же формат, что и строки в INSTALLED_APPS), и должна ссылаться на пакет, содержащий каталог locale . Если вы указываете несколько пакетов, все эти каталоги объединяются в один каталог. Это полезно, если у вас JavaScript, использующий строки из разных приложений.

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

По умолчанию представление использует домен gettext djangojs. Это можно изменить, изменив аргумент domain.

Вы можете сделать представление динамическим, поместив пакеты в шаблон URL:

urlpatterns = [
    url(r'^jsi18n/(?P<packages>\S+?)/$', javascript_catalog, name='javascript-catalog'),
]

С этим вы указываете пакеты в виде списка имён пакетов, разделённых знаком «+», в URL. Это особенно полезно, если ваши страницы используют код из разных приложений, и это часто меняется, и вам не нужно подключать один большой файл каталога. В целях безопасности эти значения могут быть только django.conf или любым пакетом из настроек INSTALLED_APPS.

Переводы JavaScript, находящиеся в путях, указанных в настройке LOCALE_PATHS, также всегда включаются. Для сохранения согласованности с алгоритмом поиска переводов, используемым для Python и шаблонов, каталоги, перечисленные в LOCALE_PATHS, имеют наивысший приоритет, а каталоги, указанные первыми, имеют больший приоритет, чем каталоги, указанные позже.

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

Примечание по производительности

Представление javascript_catalog() генерирует каталог из файлов .mo при каждом запросе. Поскольку его вывод является постоянным (по крайней мере, для данной версии сайта), это хорошая кандидатура для кэширования.

Кэширование на стороне сервера уменьшит нагрузку на процессор. Его легко реализовать с помощью декоратора cache_page(). Чтобы инициировать обновление кэша при изменении переводов, используйте префикс ключа, зависящий от версии, как показано в примере ниже, или определите представление по адресу URL, зависящему от версии.

from django.views.decorators.cache import cache_page
from django.views.i18n import javascript_catalog

# The value returned by get_version() must change when translations change.
@cache_page(86400, key_prefix='js18n-%s' % get_version())
def cached_javascript_catalog(request, domain='djangojs', packages=None):
    return javascript_catalog(request, domain, packages)

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

from django.utils import timezone
from django.views.decorators.http import last_modified
from django.views.i18n import javascript_catalog

last_modified_date = timezone.now()

@last_modified(lambda req, **kw: last_modified_date)
def cached_javascript_catalog(request, domain='djangojs', packages=None):
    return javascript_catalog(request, domain, packages)

Вы также можете предварительно сгенерировать каталог JavaScript в ходе процедуры развертывания и предоставить его как статический файл. Этот радикальный метод реализован в django-statici18n.

Международные настройки URL-адресов

Django предоставляет два механизма для международных настроек URL-адресов:

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

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

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

Префикс языка в URL-адресах

i18n_patterns(prefix, pattern_description, ...) [source]

Устарело начиная с версии 1.8: Аргумент prefix функции i18n_patterns() устарел и не будет поддерживаться в Django 1.10. Вместо этого просто передайте список экземпляров django.conf.urls.url().

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

from django.conf.urls import include, url
from django.conf.urls.i18n import i18n_patterns

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

urlpatterns = [
    url(r'^sitemap\.xml$', sitemap, name='sitemap-xml'),
]

news_patterns = [
    url(r'^$', news_views.index, name='index'),
    url(r'^category/(?P<slug>[\w-]+)/$', news_views.category, name='category'),
    url(r'^(?P<slug>[\w-]+)/$', news_views.details, name='detail'),
]

urlpatterns += i18n_patterns(
    url(r'^about/$', about_views.main, name='about'),
    url(r'^news/', include(news_patterns, namespace='news')),
)

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

>>> from django.core.urlresolvers 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/'

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

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

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

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

Перевод URL-адресов

URL-адреса также можно сделать переводимыми с помощью функции ugettext_lazy(). Пример:

from django.conf.urls import include, url
from django.conf.urls.i18n import i18n_patterns
from django.utils.translation import ugettext_lazy as _

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

urlpatterns = [
    url(r'^sitemap\.xml$', sitemap, name='sitemap-xml'),
]

news_patterns = [
    url(r'^$', news_views.index, name='index'),
    url(_(r'^category/(?P<slug>[\w-]+)/$'), news_views.category, name='category'),
    url(r'^(?P<slug>[\w-]+)/$', news_views.details, name='detail'),
]

urlpatterns += i18n_patterns(
    url(_(r'^about/$'), about_views.main, name='about'),
    url(_(r'^news/'), include(news_patterns, namespace='news')),
)

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

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

Создание файлов сообщений из исходного кода 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:

  • Загрузите следующие zip-архивы с серверов GNOME https://download.gnome.org/binaries/win32/dependencies/

    • gettext-runtime-X.zip
    • gettext-tools-X.zip

    X — это номер версии, нам требуется версия 0.15 или выше.

  • Извлеките содержимое каталогов bin\ в обоих файлах в одну папку на вашей системе (т.е. C:\Program Files\gettext-utils)
  • Обновите системную переменную PATH:

    • Control Panel > System > Advanced > Environment Variables.
    • В списке System variables, нажмите Path, затем Edit.
    • Добавьте ;C:\Program Files\gettext-utils\bin в конец поля Variable value.

Вы также можете использовать бинарные файлы 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(Command, self).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(Command, self).handle(*args, **options)

Разное

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

set_language(request) [source]

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

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

url(r'^i18n/', include('django.conf.urls.i18n')),

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

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

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

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

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

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

Вот пример кода шаблона 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="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.utils import translation
from django import http
from django.conf import settings
user_language = 'fr'
translation.activate(user_language)
response = http.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.ugettext('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.ugettext('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 использует этот язык по умолчанию для перевода — как последний вариант, если не удаётся найти более подходящий перевод одним из методов, используемых middleware для обработки локали (см. ниже).

END_OF_DOCUMENT_MARKER

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

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

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

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

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

MIDDLEWARE_CLASSES = (
   'django.contrib.sessions.middleware.SessionMiddleware',
   'django.middleware.locale.LocaleMiddleware',
   'django.middleware.common.CommonMiddleware',
)

(Дополнительную информацию о миддлверах см. в документации по миддлверам.)

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

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

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

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

    from django.utils.translation import ugettext_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.")

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

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

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

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

См. также

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

Во всех случаях ожидается, что имя директории, содержащей перевод, будет использовать обозначение названия области. Например, 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 Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.8/topics/i18n/translation/

Spec-Zone.ru

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