Перевод
Обзор
Для того, чтобы сделать проект 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 мы решили не следовать этой практике по нескольким причинам:
- Иногда вам следует использовать
gettext_lazy()в качестве метода перевода по умолчанию для определённого файла. Без_()в глобальном пространстве имён разработчик должен подумать о том, какая функция перевода является наиболее подходящей. - Символ нижнего подчёркивания (
_) используется для представления «предыдущего результата» в интерактивной оболочке Python и тестах doctest. Установка глобальной функции_()вызывает конфликт. Явное импортированиеgettext()как_()избегает этой проблемы.
Какие функции можно использовать в качестве псевдонимов для _?
Из-за того, как работает xgettext (используемая makemessages), импортировать функции в качестве _ можно только для функций, которые принимают один строковый аргумент:
В этом примере текст "Welcome to my site." отмечен как строка перевода:
from django.http import HttpResponse
from django.utils.translation import gettext as _
def my_view(request):
output = _("Welcome to my site.")
return HttpResponse(output)
Конечно, вы можете написать это без использования псевдонима. Этот пример идентичен предыдущему:
from django.http import HttpResponse
from django.utils.translation import gettext
def my_view(request):
output = gettext("Welcome to my site.")
return HttpResponse(output)
Перевод работает с вычисляемыми значениями. Этот пример идентичен двум предыдущим:
def my_view(request):
words = ['Welcome', 'to', 'my', 'site.']
output = _(' '.join(words))
return HttpResponse(output)
Перевод работает с переменными. Ещё один идентичный пример:
def my_view(request):
sentence = 'Welcome to my site.'
output = _(sentence)
return HttpResponse(output)
(Особенность использования переменных или вычисляемых значений, как в двух предыдущих примерах, заключается в том, что утилита обнаружения строк перевода Django, django-admin makemessages, не сможет найти эти строки. Подробнее о makemessages позже.)
Строки, которые вы передаёте _() или gettext() могут содержать заполнитель, указанный с помощью стандартного синтаксиса интерполяции именованных строк Python. Пример:
def my_view(request, m, d):
output = _('Today is %(month)s %(day)s.') % {'month': m, 'day': d}
return HttpResponse(output)
Этот метод позволяет переводу на языках с разной спецификой переупорядочивать текст с заполнитель. Например, английский перевод может быть "Today is November 26.", а испанский перевод может быть "Hoy es 26 de Noviembre." — с заменой местами заполнителей месяца и дня.
По этой причине вы должны использовать интерполяцию именованных строк (например, %(day)s) вместо позиционной интерполяции (например, %s или %d), когда у вас более одного параметра. Если вы использовали позиционную интерполяцию, переводы не смогут переупорядочивать текст заполнитель.
Поскольку извлечение строк выполняется командой xgettext, Django поддерживает только синтаксисы, поддерживаемые gettext. Python f-строки и строки шаблонов JavaScript пока не поддерживаются xgettext.
Комментарии для переводчиков
Если вы хотите дать переводчикам подсказки о переводимой строке, вы можете добавить комментарий с префиксом Translators в строке перед строкой, например:
def my_view(request):
# Translators: This message appears on the home page only
output = gettext("Welcome to my site.")
Комментарий затем отобразится в полученном .po файле, связанном с переводимым элементом, расположенным ниже, и должен быть также отображён большинством инструментов для перевода.
Примечание
Для полноты, вот фрагмент полученного .po файла:
#. Translators: This message appears on the home page only # path/to/python/file.py:123 msgid "Welcome to my site." msgstr ""
Это также работает в шаблонах. См. Комментарии для переводчиков в шаблонах для получения дополнительной информации.
Отметка строк как no-op
Используйте функцию django.utils.translation.gettext_noop(), чтобы отметить строку как строку перевода без перевода. Строка позже переводится из переменной.
Используйте этот метод, если у вас есть постоянные строки, которые должны храниться на языке источника, поскольку они обмениваются через системы или пользователей — такие как строки в базе данных — но должны быть переведены в последнюю возможную точку времени, например, когда строка представляется пользователю.
Множественное число
Используйте функцию django.utils.translation.ngettext() для указания сообщений с множественным числом.
ngettext() принимает три аргумента: строку перевода единственного числа, строку перевода множественного числа и количество объектов.
Эта функция полезна, когда вам нужно, чтобы ваше приложение Django было локализовано на языках, где количество и сложность форм множественного числа больше, чем две формы, используемые в английском языке («объект» для единственного числа и «объекты» для всех случаев, где count отличается от единицы, независимо от его значения.)
Например:
from django.http import HttpResponse
from django.utils.translation import ngettext
def hello_world(request, count):
page = ngettext(
'there is %(count)d object',
'there are %(count)d objects',
count) % {
'count': count,
}
return HttpResponse(page)
В этом примере количество объектов передаётся переводческим языкам как переменная count.
Обратите внимание, что множественное число сложно и работает по-разному на каждом языке. Сравнение count с 1 не всегда является правильным правилом. Этот код выглядит сложным, но даст неверные результаты для некоторых языков:
from django.utils.translation import ngettext
from myapp.models import Report
count = Report.objects.count()
if count == 1:
name = Report._meta.verbose_name
else:
name = Report._meta.verbose_name_plural
text = ngettext(
'There is %(count)d %(name)s available.',
'There are %(count)d %(name)s available.',
count
) % {
'count': count,
'name': name
}
Не пытайтесь реализовать свою собственную логику единственного или множественного числа; она не будет корректной. В таком случае, подумайте о чём-то подобном:
text = ngettext(
'There is %(count)d %(name)s object available.',
'There are %(count)d %(name)s objects available.',
count
) % {
'count': count,
'name': Report._meta.verbose_name,
}
Примечание
При использовании ngettext(), убедитесь, что вы используете одно имя для каждой переменной, включённой в литерал. В примерах выше обратите внимание на то, как мы использовали Python-переменную name в обеих строках перевода. Этот пример, помимо того, что является некорректным для некоторых языков, как отмечено выше, не сработает:
text = ngettext(
'There is %(count)d %(name)s available.',
'There are %(count)d %(plural_name)s available.',
count
) % {
'count': Report.objects.count(),
'name': Report._meta.verbose_name,
'plural_name': Report._meta.verbose_name_plural
}
При выполнении django-admin
compilemessages вы получите ошибку:
a format specification for argument 'name', as in 'msgstr[0]', doesn't exist in 'msgid'
Контекстные маркеры
Иногда слова имеют несколько значений, например, "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 names модели
Рекомендуется всегда указывать явные verbose_name и verbose_name_plural параметры, а не полагаться на унаследованное от англоязычных настроек и несколько наивное определение verbose name, которое Django выполняет, анализируя имя класса модели:
from django.db import models
from django.utils.translation import gettext_lazy as _
class MyThing(models.Model):
name = models.CharField(_('name'), help_text=_('This is the help text'))
class Meta:
verbose_name = _('my thing')
verbose_name_plural = _('my things')
Методы модели short_description значения атрибутов
Для методов моделей вы можете предоставить переводы Django и сайту администрирования с помощью атрибута short_description:
from django.db import models
from django.utils.translation import gettext_lazy as _
class MyThing(models.Model):
kind = models.ForeignKey(
ThingKind,
on_delete=models.CASCADE,
related_name='kinds',
verbose_name=_('kind'),
)
def is_mouse(self):
return self.kind.type == MOUSE_TYPE
is_mouse.short_description = _('Is it a mouse?')
Работа с объектами отложенного перевода
Результат вызова gettext_lazy() может быть использован там, где вы бы использовали строку (объект str) в другом коде Django, но он может не работать с произвольным Python-кодом. Например, следующее не сработает, потому что библиотека requests не обрабатывает gettext_lazy объекты:
body = gettext_lazy("I \u2764 Django") # (unicode :heart:)
requests.post('https://example.com/send', data={'body': body})
Вы можете избежать таких проблем, преобразовав gettext_lazy() объекты в текстовые строки перед передачей их в код, не относящийся к Django:
requests.post('https://example.com/send', data={'body': str(body)})
Если вам не нравится длинное имя gettext_lazy , вы можете просто переименовать его в _ (нижнее подчеркивание), как показано ниже:
from django.db import models
from django.utils.translation import gettext_lazy as _
class MyThing(models.Model):
name = models.CharField(help_text=_('This is the help text'))
Использование gettext_lazy() и ngettext_lazy() для маркировки строк в моделях и вспомогательных функциях — распространённая операция. Когда вы работаете с этими объектами в другом месте в вашем коде, вы должны убедиться, что случайно не преобразуете их в строки, потому что они должны преобразовываться как можно позже (так что в силе будет правильный регион). Это требует использования вспомогательной функции, описанной ниже.
Отложенные переводы и множественное число
При использовании отложенного перевода для множественной строки ([u]n[p]gettext_lazy), вы обычно не знаете аргумент number в момент определения строки. Поэтому вам разрешается передать имя ключа вместо целого числа как аргумент number. Затем number будет извлечён из словаря по этому ключу во время интерполяции строки. Вот пример:
from django import forms
from django.utils.translation import ngettext_lazy
class MyForm(forms.Form):
error_message = ngettext_lazy("You only provided %(num)d argument",
"You only provided %(num)d arguments", 'num')
def clean(self):
# ...
if error:
raise forms.ValidationError(self.error_message % {'num': number})
Если строка содержит ровно один безымянный заполнитель, вы можете непосредственно интерполировать с аргументом number:
class MyForm(forms.Form):
error_message = ngettext_lazy(
"You provided %d argument",
"You provided %d arguments",
)
def clean(self):
# ...
if error:
raise forms.ValidationError(self.error_message % number)
Форматирование строк: format_lazy()
Метод Python str.format() не будет работать, когда либо format_string , либо любой из аргументов метода str.format() содержит объекты отложенного перевода. Вместо этого вы можете использовать django.utils.text.format_lazy(), который создаёт отложенный объект, выполняющий метод str.format() только тогда, когда результат включается в строку. Например:
from django.utils.text import format_lazy
from django.utils.translation import gettext_lazy
...
name = gettext_lazy('John Lennon')
instrument = gettext_lazy('guitar')
result = format_lazy('{name}: {instrument}', name=name, instrument=instrument)
В этом случае отложенные переводы в result будут преобразованы в строки только тогда, когда result сам используется в строке (обычно во время рендеринга шаблона).
Другие случаи использования lazy при отложенных переводах
В любом другом случае, где вы хотите отложить перевод, но должны передать переводимую строку в качестве аргумента другой функции, вы можете обернуть эту функцию в вызов lazy самостоятельно. Например:
from django.utils.functional import lazy from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ mark_safe_lazy = lazy(mark_safe, str)
А затем позже:
lazy_string = mark_safe_lazy(_("<p>My <strong>string!</strong></p>"))
Локализованные имена языков
-
get_language_info()[source]
Функция get_language_info() предоставляет подробную информацию о языках:
>>> from django.utils.translation import activate, get_language_info
>>> activate('fr')
>>> li = get_language_info('de')
>>> print(li['name'], li['name_local'], li['name_translated'], li['bidi'])
German Deutsch Allemand False
Атрибуты name, name_local, и name_translated словаря содержат имя языка на английском языке, на языке самом по себе и на вашем текущем активном языке соответственно. Атрибут bidi равен True только для двунаправленных языков.
Источник информации о языке — модуль django.conf.locale. Аналогичный доступ к этой информации доступен для кода шаблонов. Смотрите ниже.
Международная поддержка: в коде шаблонов
Перевод в шаблонах Django использует два тега шаблона и немного другую синтаксическую конструкцию, чем в Python-коде. Чтобы предоставить вашему шаблону доступ к этим тегам, поместите {% load i18n %} в начале вашего шаблона. Как и все теги шаблона, этот тег необходимо загрузить во все шаблоны, которые используют переводы, даже в тех шаблонах, которые наследуются от других шаблонов, которые уже загрузили тег i18n.
Предупреждение
Переведённые строки не будут экранироваться при рендеринге в шаблоне. Это позволяет включать HTML в переводы, например, для выделения, но потенциально опасные символы (например, ") также будут отображаться без изменений.
trans тег шаблона
Тег шаблона {% trans %} переводит либо постоянную строку (в одинарных или двойных кавычках), либо переменное содержимое:
<title>{% trans "This is the title." %}</title>
<title>{% trans myvar %}</title>
Если опция noop присутствует, поиск переменной всё ещё происходит, но перевод пропускается. Это полезно, когда вы «заглушаете» содержимое, которое будет переведено в будущем:
<title>{% trans "myvar" noop %}</title>
Внутренне, встроенные переводы используют вызов gettext().
В случае, если переменная шаблона (myvar выше) передаётся тегу, тег сначала разрешит такую переменную до строки во время выполнения, а затем найдёт эту строку в каталогах сообщений.
Невозможно смешивать переменную шаблона внутри строки в {% trans
%}. Если ваши переводы требуют строк с переменными (заполнителями), используйте {% blocktrans %} вместо этого.
Если вы хотите получить переведенную строку без ее отображения, вы можете использовать следующий синтаксис:
{% trans "This is the title" as the_title %}
<title>{{ the_title }}</title>
<meta name="description" content="{{ the_title }}">
На практике вы будете использовать это для получения строки, которую можно использовать в нескольких местах шаблона или для использования вывода в качестве аргумента для других тегов или фильтров шаблона:
{% trans "starting point" as start %}
{% trans "end point" as end %}
{% trans "La Grande Boucle" as race %}
<h1>
<a href="/" title="{% blocktrans %}Back to '{{ race }}' homepage{% endblocktrans %}">{{ race }}</a>
</h1>
<p>
{% for stage in tour_stages %}
{% cycle start end %}: {{ stage }}{% if forloop.counter|divisibleby:2 %}<br>{% else %}, {% endif %}
{% endfor %}
</p>
{% trans %} также поддерживает контекстные маркеры с использованием ключевого слова context:
{% trans "May" context "month name" %}
blocktrans тег
В отличие от тега trans, тег blocktrans позволяет помечать сложные предложения, состоящие из литералов и содержимого переменных, для перевода, используя плейсхолдеры:
{% blocktrans %}This string will have {{ value }} inside.{% endblocktrans %}
Для перевода выражения шаблона — например, доступа к атрибутам объекта или использования фильтров шаблонов — необходимо привязать выражение к локальной переменной для использования в блоке перевода. Примеры:
{% blocktrans with amount=article.price %}
That will cost $ {{ amount }}.
{% endblocktrans %}
{% blocktrans with myvar=value|filter %}
This will have {{ myvar }} inside.
{% endblocktrans %}
Вы можете использовать несколько выражений внутри одного тега blocktrans:
{% blocktrans with book_t=book|title author_t=author|title %}
This is {{ book_t }} by {{ author_t }}
{% endblocktrans %}
Примечание
Предыдущий более подробный формат по-прежнему поддерживается: {% blocktrans with book|title as book_t and author|title as author_t %}
Другие теги блоков (например, {% for %} или {% if %}) не разрешены внутри тега blocktrans.
Если разрешение одного из аргументов блока завершится ошибкой, blocktrans вернется к языку по умолчанию, временно отключив текущий активный язык с помощью функции deactivate_all().
Этот тег также обеспечивает возможность множественного числа. Для его использования:
- Укажите и привяжите значение счетчика с именем
count. Это значение будет использоваться для выбора правильной формы множественного числа. - Укажите как форму единственного, так и множественного числа, разделяя их тегом
{% plural %}внутри тегов{% blocktrans %}и{% endblocktrans %}.
Пример:
{% blocktrans count counter=list|length %}
There is only one {{ name }} object.
{% plural %}
There are {{ counter }} {{ name }} objects.
{% endblocktrans %}
Более сложный пример:
{% blocktrans with amount=article.price count years=i.length %}
That will cost $ {{ amount }} per year.
{% plural %}
That will cost $ {{ amount }} per {{ years }} years.
{% endblocktrans %}
Когда вы используете функцию множественного числа и привязываете значения к локальным переменным в дополнение к значению счетчика, помните, что конструкция blocktrans внутренне преобразуется в вызов ngettext. Это означает, что применяются те же примечания относительно переменных ngettext.
Обратный поиск URL не может быть выполнен внутри тегов blocktrans и должен быть получен (и сохранен) предварительно:
{% url 'path.to.view' arg arg2 as the_url %}
{% blocktrans %}
This is a URL: {{ the_url }}
{% endblocktrans %}
Если вы хотите получить переведенную строку без ее отображения, вы можете использовать следующий синтаксис:
{% blocktrans asvar the_title %}The title is {{ title }}.{% endblocktrans %}
<title>{{ the_title }}</title>
<meta name="description" content="{{ the_title }}">
На практике вы будете использовать это для получения строки, которую можно использовать в нескольких местах шаблона или для использования вывода в качестве аргумента для других тегов или фильтров шаблона.
{% blocktrans %} также поддерживает контекстные маркеры с использованием ключевого слова context:
{% blocktrans with name=user.username context "greeting" %}Hi {{ name }}{% endblocktrans %}
Другая функция, которую {% blocktrans %} поддерживает, — это опция trimmed. Эта опция удалит символы новой строки из начала и конца содержимого тега {% blocktrans %}, заменит все пробелы в начале и конце строки и объединит все строки в одну, используя пробел в качестве разделителя. Это очень полезно для отступа содержимого тега {%
blocktrans %} без появления символов отступа в соответствующей записи в файле PO, что упрощает процесс перевода.
Например, следующий тег {% blocktrans %}:
{% blocktrans trimmed %}
First sentence.
Second paragraph.
{% endblocktrans %}
приведет к записи "First sentence. Second paragraph." в файле PO по сравнению с "\n First sentence.\n Second sentence.\n", если опция trimmed не была указана.
Переданные строковые литералы в теги и фильтры
{% some_tag _("Page not found") value|yesno:_("yes,no") %}
В этом случае и тег, и фильтр увидят переведенную строку, поэтому они не должны быть осведомлены о переводах.
Примечание
В этом примере инфраструктура перевода получит строку "yes,no", а не отдельные строки "yes" и "no". Переведенная строка должна содержать запятую, чтобы код парсинга фильтра знал, как разделить аргументы. Например, немецкий переводчик может перевести строку "yes,no" как "ja,nein" (сохраняя запятую).
Комментарии для переводчиков в шаблонах
Как и в коде Python, эти примечания для переводчиков могут быть указаны с помощью комментариев, либо с помощью тега comment:
{% comment %}Translators: View verb{% endcomment %}
{% trans "View" %}
{% comment %}Translators: Short intro blurb{% endcomment %}
<p>{% blocktrans %}A multiline translatable
literal.{% endblocktrans %}</p>
или с помощью тегов {# … #} конструций однострочных комментариев:
{# Translators: Label of a button that triggers search #}
<button type="submit">{% trans "Go" %}</button>
{# Translators: This is a text of the base template #}
{% blocktrans %}Ambiguous translatable block of text{% endblocktrans %}
Примечание
Для полноты картины, вот соответствующие фрагменты результирующего .po файла:
#. Translators: View verb # path/to/template/file.html:10 msgid "View" msgstr "" #. Translators: Short intro blurb # path/to/template/file.html:13 msgid "" "A multiline translatable" "literal." msgstr "" # ... #. Translators: Label of a button that triggers search # path/to/template/file.html:100 msgid "Go" msgstr "" #. Translators: This is a text of the base template # path/to/template/file.html:103 msgid "Ambiguous translatable block of text" msgstr ""
Переключение языка в шаблонах
Если вы хотите выбрать язык в шаблоне, вы можете использовать тег language:
{% load i18n %}
{% get_current_language as LANGUAGE_CODE %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
<p>{% trans "Welcome to our page" %}</p>
{% language 'en' %}
{% get_current_language as LANGUAGE_CODE %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
<p>{% trans "Welcome to our page" %}</p>
{% endlanguage %}
Хотя первое вхождение «Добро пожаловать на нашу страницу» использует текущий язык, второе всегда будет на английском языке.
Другие теги
get_available_languages
{% get_available_languages as LANGUAGES %} возвращает список кортежей, в котором первый элемент — код языка, а второй — имя языка (переведенное на текущий активный язык).
get_current_language
{% get_current_language as LANGUAGE_CODE %} возвращает предпочтительный язык текущего пользователя в виде строки. Пример: en-us. См. Как Django определяет предпочтительный язык.
get_current_language_bidi
{% get_current_language_bidi as LANGUAGE_BIDI %} возвращает направление текущего языка. Если True, это язык справа налево, например, иврит, арабский. Если False, это язык слева направо, например, английский, французский, немецкий и т. д.
i18n обработчик контекста
Если вы включите обработчик контекста django.template.context_processors.i18n, то каждый RequestContext будет иметь доступ к LANGUAGES, LANGUAGE_CODE, и LANGUAGE_BIDI как определено выше.
get_language_info
Вы также можете получить информацию о любом из доступных языков с помощью предоставленных тегов и фильтров шаблонов. Для получения информации об одном языке используйте тег {% get_language_info %}:
{% get_language_info for LANGUAGE_CODE as lang %}
{% get_language_info for "pl" as lang %}
Затем вы можете получить доступ к информации:
Language code: {{ lang.code }}<br>
Name of language: {{ lang.name_local }}<br>
Name in English: {{ lang.name }}<br>
Bi-directional: {{ lang.bidi }}
Name in the active language: {{ lang.name_translated }}
get_language_info_list
Вы также можете использовать тег {% get_language_info_list %} шаблона для получения информации для списка языков (например, активных языков, как указано в LANGUAGES). См. раздел о представлении перенаправления set_language для примера отображения селектора языка с использованием {% get_language_info_list %}.
В дополнение к списку кортежей в стиле LANGUAGES, {% get_language_info_list %} поддерживает простые списки кодов языков. Если вы сделаете это в своем представлении:
context = {'available_languages': ['en', 'es', 'fr']}
return render(request, 'mytemplate.html', context)
вы можете перебирать эти языки в шаблоне:
{% get_language_info_list for available_languages as langs %}
{% for lang in langs %} ... {% endfor %}
Фильтры шаблонов
Для удобства также доступны простые фильтры:
-
{{ LANGUAGE_CODE|language_name }}(«Немецкий») -
{{ LANGUAGE_CODE|language_name_local }}(«Deutsch») -
{{ LANGUAGE_CODE|language_bidi }}(False) -
{{ LANGUAGE_CODE|language_name_translated }}(«německy», когда активный язык чешский)
Международные языки в коде JavaScript
Добавление переводов в JavaScript создает некоторые проблемы:
- Код JavaScript не имеет доступа к реализации
gettext. - Код JavaScript не имеет доступа к файлам
.poили.mo; их необходимо доставить с сервера. - Справочники перевода для JavaScript должны быть максимально компактными.
Django предоставляет интегрированное решение для этих проблем: он передает переводы в JavaScript, так что вы можете вызывать gettext, и т. д., изнутри JavaScript.
Основное решение этих проблем — представление JavaScriptCatalog, которое генерирует библиотеку кода JavaScript с функциями, имитирующими интерфейс gettext, плюс массив строковых переводов.
Представление JavaScriptCatalog
-
class JavaScriptCatalog[source] -
Просмотр, который генерирует библиотеку кода JavaScript с функциями, имитирующими интерфейс
gettext, плюс массив строк перевода.Атрибуты
-
domain -
Домен перевода, содержащий строки, которые нужно добавить в вывод просмотра. По умолчанию
'djangojs'.
-
packages -
Список
application namesсреди установленных приложений. Эти приложения должны содержать каталогlocale. Все эти каталоги плюс все каталоги, найденные вLOCALE_PATHS(которые всегда включаются), объединяются в один каталог. По умолчаниюNone, что означает, что все доступные переводы из всехINSTALLED_APPSпредоставляются в выводе JavaScript.
Пример со значениями по умолчанию:
from django.views.i18n import JavaScriptCatalog urlpatterns = [ path('jsi18n/', JavaScriptCatalog.as_view(), name='javascript-catalog'), ]Пример с настраиваемыми пакетами:
urlpatterns = [ path('jsi18n/myapp/', JavaScriptCatalog.as_view(packages=['your.app.label']), name='javascript-catalog'), ]Если ваш корневой URL-шаблон использует
i18n_patterns(),JavaScriptCatalogтакже должен быть заключен вi18n_patterns()для правильной генерации каталога.Пример с
i18n_patterns():from django.conf.urls.i18n import i18n_patterns urlpatterns = i18n_patterns( path('jsi18n/', JavaScriptCatalog.as_view(), name='javascript-catalog'), ) -
Приоритет переводов таков, что пакеты, указанные позже в аргументе packages, имеют больший приоритет, чем те, которые указаны в начале. Это важно в случае конфликтующих переводов одного и того же значения.
Если вы используете более одного просмотра JavaScriptCatalog на сайте, и некоторые из них определяют одни и те же строки, приоритет имеют строки в каталоге, который был загружен последним.
Использование каталога переводов JavaScript
Для использования каталога просто включите динамически сгенерированный скрипт следующим образом:
<script type="text/javascript" src="{% url 'javascript-catalog' %}"></script>
Это использует обратный поиск URL для определения URL-адреса просмотра каталога JavaScript. После загрузки каталога ваш JavaScript-код может использовать следующие методы:
gettextngettextinterpolateget_formatgettext_nooppgettextnpgettextpluralidx
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_FORMATDATE_INPUT_FORMATSDATETIME_FORMATDATETIME_INPUT_FORMATSDECIMAL_SEPARATORFIRST_DAY_OF_WEEKMONTH_DAY_FORMATNUMBER_GROUPINGSHORT_DATE_FORMATSHORT_DATETIME_FORMATTHOUSAND_SEPARATORTIME_FORMATTIME_INPUT_FORMATSYEAR_MONTH_FORMAT
Это полезно для поддержания согласованности форматирования с значениями, отрисованными на Python.
gettext_noop
Это эмулирует функцию gettext но ничего не делает, возвращая то, что передается ей:
document.write(gettext_noop('this will not be translated'));
Это полезно для заглушки частей кода, которые потребуют перевода в будущем.
pgettext
Функция pgettext ведет себя как ее аналог на Python (pgettext()), предоставляя контекстно переведенное слово:
document.write(pgettext('month name', 'May'));
npgettext
Функция npgettext также ведет себя как ее аналог на Python (npgettext()), предоставляя **склоненное** контекстно переведенное слово:
document.write(npgettext('group', 'party', 1));
// party
document.write(npgettext('group', 'party', 2));
// parties
pluralidx
Функция pluralidx работает аналогично фильтру шаблонов pluralize, определяя, следует ли использовать множественное число слова для данного count или нет:
document.write(pluralidx(0)); // true document.write(pluralidx(1)); // false document.write(pluralidx(2)); // true
В простом случае, если настройка склонения не нужна, она возвращает false для целого числа 1 и true для всех других чисел.
Однако склонение в разных языках не такое простое. Если язык не поддерживает склонение, возвращается пустое значение.
Кроме того, если существуют сложные правила склонения, просмотр каталога отобразит условное выражение. Оно будет оцениваться либо как true (следует использовать множественное число), либо как false (множественное число не следует использовать).
Просмотр JSONCatalog
-
class JSONCatalog[source] -
Для использования другой клиентской библиотеки для обработки переводов, вы можете воспользоваться просмотром
JSONCatalog. Он похож наJavaScriptCatalog, но возвращает ответ в формате JSON.См. документацию по
JavaScriptCatalog, чтобы узнать о возможных значениях и использовании атрибутовdomainиpackages.Формат ответа следующий:
{ "catalog": { # Translations catalog }, "formats": { # Language formats for date, time, etc. }, "plural": "..." # Expression for plural forms, or null. }
Примечание по производительности
Различные JavaScript/JSON-просмотры i18n генерируют каталог из файлов .mo на каждом запросе. Поскольку вывод является постоянным, по крайней мере, для данной версии сайта, это хороший кандидат для кэширования.
Кэширование на стороне сервера уменьшит нагрузку на ЦП. Его легко реализовать с помощью декоратора cache_page(). Для запуска обновления кэша при изменении ваших переводов укажите префикс ключа, зависящий от версии, как показано в примере ниже, или сопоставьте просмотр с URL-адресом, зависящим от версии:
from django.views.decorators.cache import cache_page
from django.views.i18n import JavaScriptCatalog
# The value returned by get_version() must change when translations change.
urlpatterns = [
path('jsi18n/',
cache_page(86400, key_prefix='js18n-%s' % get_version())(JavaScriptCatalog.as_view()),
name='javascript-catalog'),
]
Кэширование на стороне клиента сэкономит пропускную способность и ускорит загрузку вашего сайта. Если вы используете ETags (ConditionalGetMiddleware), вы уже покрыты. В противном случае вы можете применить условные декораторы. В следующем примере кэш обновляется при каждом запуске сервера приложения:
from django.utils import timezone
from django.views.decorators.http import last_modified
from django.views.i18n import JavaScriptCatalog
last_modified_date = timezone.now()
urlpatterns = [
path('jsi18n/',
last_modified(lambda req, **kw: last_modified_date)(JavaScriptCatalog.as_view()),
name='javascript-catalog'),
]
Вы даже можете предварительно сгенерировать каталог JavaScript как часть вашей процедуры развертывания и предоставить его как статический файл. Этот радикальный метод реализован в django-statici18n.
Международные настройки: в шаблонах URL
Django предоставляет два механизма для локализации шаблонов URL:
- Добавление префикса языка в корень шаблонов URL, чтобы
LocaleMiddlewareмог определить язык для активации из запрошенного URL. - Делает шаблоны URL переводимыми с помощью функции
django.utils.translation.gettext_lazy().
Предупреждение
Использование любого из этих свойств требует установки активного языка для каждого запроса; другими словами, вам нужен django.middleware.locale.LocaleMiddleware в настройке MIDDLEWARE.
Префикс языка в шаблонах URL
-
i18n_patterns(*urls, prefix_default_language=True)[source]
Эта функция может использоваться в корневом файле URLconf, и Django автоматически добавит код активного языка к началу всех шаблонов URL, определённых внутри i18n_patterns().
Установка prefix_default_language на False удаляет префикс из языка по умолчанию (LANGUAGE_CODE). Это может быть полезно при добавлении переводов на существующий сайт, чтобы текущие URL не изменились.
Пример шаблонов URL:
from django.conf.urls.i18n import i18n_patterns
from django.urls import include, path
from about import views as about_views
from news import views as news_views
from sitemap.views import sitemap
urlpatterns = [
path('sitemap.xml', sitemap, name='sitemap-xml'),
]
news_patterns = ([
path('', news_views.index, name='index'),
path('category/<slug:slug>/', news_views.category, name='category'),
path('<slug:slug>/', news_views.details, name='detail'),
], 'news')
urlpatterns += i18n_patterns(
path('about/', about_views.main, name='about'),
path('news/', include(news_patterns, namespace='news')),
)
После определения этих шаблонов URL Django автоматически добавит префикс языка к шаблонам URL, которые были добавлены функцией i18n_patterns. Пример:
>>> from django.urls import reverse
>>> from django.utils.translation import activate
>>> activate('en')
>>> reverse('sitemap-xml')
'/sitemap.xml'
>>> reverse('news:index')
'/en/news/'
>>> activate('nl')
>>> reverse('news:detail', kwargs={'slug': 'news-slug'})
'/nl/news/news-slug/'
С prefix_default_language=False и LANGUAGE_CODE='en', URL будут:
>>> activate('en')
>>> reverse('news:index')
'/news/'
>>> activate('nl')
>>> reverse('news:index')
'/nl/news/'
Предупреждение
i18n_patterns() допускается только в корневом файле URLconf. Использование его внутри включённого файла URLconf вызовет исключение ImproperlyConfigured.
Предупреждение
Убедитесь, что у вас нет шаблонов URL без префикса, которые могут конфликтовать с автоматически добавленным префиксом языка.
Переводы шаблонов URL
Шаблоны URL также можно отметить как переводимые с помощью функции gettext_lazy(). Пример:
from django.conf.urls.i18n import i18n_patterns
from django.urls import include, path
from django.utils.translation import gettext_lazy as _
from about import views as about_views
from news import views as news_views
from sitemaps.views import sitemap
urlpatterns = [
path('sitemap.xml', sitemap, name='sitemap-xml'),
]
news_patterns = ([
path('', news_views.index, name='index'),
path(_('category/<slug:slug>/'), news_views.category, name='category'),
path('<slug:slug>/', news_views.details, name='detail'),
], 'news')
urlpatterns += i18n_patterns(
path(_('about/'), about_views.main, name='about'),
path(_('news/'), include(news_patterns, namespace='news')),
)
После создания переводов функция reverse() вернёт URL на активном языке. Пример:
>>> from django.urls import reverse
>>> from django.utils.translation import activate
>>> activate('en')
>>> reverse('news:category', kwargs={'slug': 'recent'})
'/en/news/category/recent/'
>>> activate('nl')
>>> reverse('news:category', kwargs={'slug': 'recent'})
'/nl/nieuws/categorie/recent/'
Предупреждение
В большинстве случаев лучше использовать переведённые URL только внутри блока шаблонов с префиксом кода языка (используя i18n_patterns()), чтобы избежать возможности того, что небрежно переведённый URL вызовет конфликт с непереведённым шаблоном URL.
Обращение в шаблонах
Если переведённые URL обращаются в шаблонах, они всегда используют текущий язык. Для ссылки на URL на другом языке используйте тег шаблона language. Он включает указанный язык в заключённом фрагменте шаблона:
{% load i18n %}
{% get_available_languages as languages %}
{% trans "View this category in:" %}
{% for lang_code, lang_name in languages %}
{% language lang_code %}
<a href="{% url 'category' slug=category.slug %}">{{ lang_name }}</a>
{% endlanguage %}
{% endfor %}
Тег language ожидает код языка как единственный аргумент.
Локализация: как создать файлы языка
После того как литералы строк приложения были помечены для последующего перевода, сами переводы необходимо записать (или получить). Вот как это работает.
Файлы сообщений
Первый шаг — создание файла сообщений для нового языка. Файл сообщений — это текстовый файл, представляющий один язык, содержащий все доступные строки перевода и то, как они должны быть представлены на данном языке. Файлы сообщений имеют расширение .po.
Django поставляется с инструментом django-admin makemessages, который автоматизирует создание и обслуживание этих файлов.
Утилиты Gettext
Команда makemessages (и compilemessages обсуждавшиеся позже) используют команды набора инструментов GNU gettext: xgettext, msgfmt, msgmerge и msguniq.
Минимальная поддерживаемая версия утилит gettext — 0.15.
Для создания или обновления файла сообщений выполните эту команду:
django-admin makemessages -l de
…где de — это имя локали для файла сообщений, который вы хотите создать. Например, pt_BR для бразильского португальского языка, de_AT для австрийского немецкого или id для индонезийского языка.
Сценарий должен быть запущен из одного из двух мест:
- Корневой каталог вашего проекта Django (тот, который содержит
manage.py). - Корневой каталог одного из ваших приложений Django.
Сценарий обрабатывает дерево исходных файлов вашего проекта или приложения и извлекает все строки, помеченные для перевода (см. Как Django обнаруживает переводы и убедитесь, что LOCALE_PATHS правильно сконфигурирован). Он создаёт (или обновляет) файл сообщений в каталоге locale/LANG/LC_MESSAGES. В примере de файл будет locale/de/LC_MESSAGES/django.po.
При запуске makemessages из корневого каталога вашего проекта извлечённые строки автоматически распределяются по соответствующим файлам сообщений. То есть строка, извлеченная из файла приложения, содержащего каталог locale , пойдёт в файл сообщений в этом каталоге. А строка, извлечённая из файла приложения без каталога locale , пойдёт либо в файл сообщений, указанный первым в LOCALE_PATHS, либо вызовет ошибку, если LOCALE_PATHS пустое.
По умолчанию django-admin makemessages проверяет каждый файл с расширением .html, .txt или .py. Если вы хотите изменить этот параметр по умолчанию, используйте опцию --extension или -e для указания расширений файлов, которые нужно проверить:
django-admin makemessages -l de -e txt
Разделяйте несколько расширений запятыми и/или используйте -e или --extension многократно:
django-admin makemessages -l de -e html,txt -e xml
Предупреждение
При создании файлов сообщений из JavaScript-кода необходимо использовать специальный домен djangojs, а не -e js.
Используете шаблоны Jinja2?
makemessages не понимает синтаксис шаблонов Jinja2. Для извлечения строк из проекта, содержащего шаблоны Jinja2, используйте Извлечение сообщений из Babel вместо этого.
Вот пример babel.cfg файла конфигурации:
# Extraction from Python source files [python: **.py] # Extraction from Jinja2 templates [jinja2: **.jinja] extensions = jinja2.ext.with_
Убедитесь, что вы указали все используемые расширения! В противном случае Babel не распознает теги, определённые этими расширениями, и полностью проигнорирует шаблоны Jinja2, содержащие их.
Babel предоставляет аналогичные функции makemessages, в целом может его заменить и не зависит от gettext. Для получения дополнительной информации ознакомьтесь с документацией по работе с каталогами сообщений.
Нет утилит gettext?
Если утилиты gettext не установлены, makemessages создаст пустые файлы. В этом случае установите утилиты gettext или просто скопируйте файл сообщений на английском языке (locale/en/LC_MESSAGES/django.po), если он доступен, и используйте его как отправную точку; это просто пустой файл перевода.
Работаете в Windows?
Если вы используете Windows и вам нужно установить утилиты GNU gettext, чтобы makemessages работало, см. gettext в Windows для получения дополнительной информации.
Формат файлов .po прост. Каждый файл .po содержит небольшую информацию о метаданных, например, контактные данные переводчика, но основная часть файла представляет собой список сообщений — простые сопоставления строк перевода и фактического переведенного текста для конкретного языка.
Например, если в вашем приложении Django есть строка перевода для текста "Welcome to my site.", как показано ниже:
_("Welcome to my site.")
…тогда django-admin makemessages создаст файл .po с приведенным ниже фрагментом — сообщением:
#: path/to/python/module.py:23 msgid "Welcome to my site." msgstr ""
Краткое объяснение:
-
msgid— это строка перевода, которая появляется в исходном коде. Не изменяйте её. -
msgstr— это место, куда вы помещаете перевод, специфичный для языка. Изначально оно пустое, поэтому вы должны его изменить. Убедитесь, что ваш перевод находится в кавычках. - Для удобства каждое сообщение включает в себя строку комментария, начинающуюся с
#, и расположенную над строкойmsgid, содержащую имя файла и номер строки, из которого была извлечена строка перевода.
Длинные сообщения — это особый случай. Там первая строка сразу после msgstr (или msgid) — пустая строка. Затем содержимое будет написано на следующих строках в виде одной строки на каждой. Эти строки непосредственно конкатенируются. Не забывайте о trailing spaces в строках; в противном случае они будут склеены без пробелов!
Учитывайте кодировку
Из-за того, как работают утилиты gettext и потому, что мы хотим разрешить использование строк, не являющихся ASCII, в ядре Django и ваших приложениях, вы обязательно должны использовать UTF-8 в качестве кодировки для ваших файлов PO (по умолчанию при создании файлов PO). Это означает, что все будут использовать одну и ту же кодировку, что важно при обработке файлов PO в Django.
Чтобы повторно проверить весь исходный код и шаблоны на наличие новых строк перевода и обновить все файлы сообщений для всех языков, выполните эту команду:
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.)
В более старых версиях cookie устанавливается только если поддержка сеансов не включена.
После установки выбора языка Django ищет параметр next в данных POST или GET. Если он найден и Django считает его безопасным URL-адресом (т.е. он не указывает на другой хост и использует безопасный протокол), будет выполнено перенаправление на этот URL-адрес. В противном случае Django может перенаправить пользователя на URL-адрес из заголовка Referer или, если он не задан, на /, в зависимости от характера запроса:
- Для AJAX-запросов резервное перенаправление будет выполнено только если параметр
nextбыл задан. В противном случае будет возвращён код состояния 204 (Без содержания). - Для запросов, не являющихся AJAX, резервное перенаправление всегда будет выполнено.
Вот пример кода шаблона HTML:
{% load i18n %}
<form action="{% url 'set_language' %}" method="post">{% csrf_token %}
<input name="next" type="hidden" value="{{ redirect_to }}">
<select name="language">
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %}
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected{% endif %}>
{{ language.name_local }} ({{ language.code }})
</option>
{% endfor %}
</select>
<input type="submit" value="Go">
</form>
В этом примере Django ищет URL-адрес страницы, на которую будет перенаправлен пользователь, в переменной контекста redirect_to.
Явное задание активного языка
Вы можете явно установить активный язык для текущего сеанса. Например, предположим, что предпочтение языка пользователя извлекается из другой системы. Вы уже знакомы с django.utils.translation.activate(). Это относится только к текущей нити. Чтобы сохранить язык для всего сеанса, также измените LANGUAGE_SESSION_KEY в сессии:
from django.utils import translation user_language = 'fr' translation.activate(user_language) request.session[translation.LANGUAGE_SESSION_KEY] = user_language
Обычно вы захотите использовать оба варианта: django.utils.translation.activate() изменит язык для этой нити, а изменение сессии сохранит это предпочтение в будущих запросах.
Если вы не используете сессии, язык будет сохраняться в cookie, имя которого настраивается в LANGUAGE_COOKIE_NAME. Например:
from django.conf import settings from django.http import HttpResponse from django.utils import translation user_language = 'fr' translation.activate(user_language) response = HttpResponse(...) response.set_cookie(settings.LANGUAGE_COOKIE_NAME, user_language)
Использование переводов вне представлений и шаблонов
Хотя Django предоставляет богатый набор инструментов 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 языка
Примечания по реализации
Особенности механизма перевода Django
Механизм перевода Django использует стандартный модуль gettext из Python. Если вы знакомы с gettext, вы, возможно, заметите эти особенности в способе перевода Django:
- Домен строки —
djangoилиdjangojs. Этот домен строки используется для дифференциации между различными программами, хранящими свои данные в одной общей библиотеке файлов сообщений (обычно/usr/share/locale/). Доменdjangoиспользуется для строк перевода Python и шаблонов и загружается в глобальные каталоги переводов. Доменdjangojsиспользуется только для каталогов переводов JavaScript, чтобы они были максимально компактными. - Django не использует
xgettextв чистом виде. Он использует обертки Python вокругxgettextиmsgfmt. Это в основном для удобства.
Как Django определяет предпочтения языка
После подготовки переводов (или если вы хотите использовать переводы, поставляемые с Django), вам просто нужно активировать перевод для своего приложения.
Внутри Django есть очень гибкая модель определения, какой язык следует использовать — для всей установки, для конкретного пользователя или и то, и другое.
Чтобы установить предпочтение языка для всей установки, задайте LANGUAGE_CODE. Django использует этот язык в качестве языка по умолчанию для перевода — в качестве последней попытки, если не удается найти подходящий перевод одним из методов, используемых средством локализации (см. ниже).
Если вы хотите использовать свой родной язык в Django, вам достаточно задать LANGUAGE_CODE и убедиться в существовании соответствующих файлов сообщений (файлов сообщений) и их скомпилированных версий (.mo).
Если вы хотите, чтобы каждый пользователь мог указать предпочитаемый язык, вам также необходимо использовать LocaleMiddleware. LocaleMiddleware позволяет выбирать язык на основе данных из запроса. Это персонализирует контент для каждого пользователя.
Для использования LocaleMiddleware, добавьте 'django.middleware.locale.LocaleMiddleware' в свою настройку MIDDLEWARE. Из-за важности порядка middleware придерживайтесь этих рекомендаций:
- Убедитесь, что это одно из первых установленных middleware.
- Он должен следовать за
SessionMiddleware, так какLocaleMiddlewareиспользует данные сессии. И он должен предшествоватьCommonMiddleware, так какCommonMiddlewareнуждается в активированном языке для разрешения запрошенного URL. - Если вы используете
CacheMiddleware, поместитеLocaleMiddlewareпосле него.
Например, ваша настройка MIDDLEWARE может выглядеть так:
MIDDLEWARE = [ 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.locale.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', ]
(Дополнительную информацию о middleware см. в документации по middleware.)
LocaleMiddleware пытается определить предпочтение языка пользователя, выполняя следующий алгоритм:
- Сначала ищет префикс языка в запрошенном URL. Это выполняется только тогда, когда вы используете функцию
i18n_patternsв вашем корневом URLconf. Дополнительную информацию о префиксе языка и о том, как интернационализировать URL-шаблоны, см. в разделе Интернационализация: в URL-шаблонах. - Если это не удалось, ищет ключ
LANGUAGE_SESSION_KEYв текущей сессии пользователя. -
Если это не удалось, ищет cookie.
Имя используемого cookie задается настройкой
LANGUAGE_COOKIE_NAME. (По умолчанию имя —django_language.) - Если и это не удалось, анализирует HTTP-заголовок
Accept-Language. Этот заголовок отправляется вашим браузером и сообщает серверу, какие языки вы предпочитаете в порядке приоритета. Django пробует каждый язык в заголовке, пока не найдет язык с доступными переводами. - Если и это не удалось, использует глобальную настройку
LANGUAGE_CODE.
Примечания:
- В каждом из этих случаев ожидается, что предпочтение языка будет представлено в стандартном формате кода языка в виде строки. Например, бразильский португальский —
pt-br. - Если базовый язык доступен, но указанный подязык недоступен, Django использует базовый язык. Например, если пользователь указывает
de-at(австрийский немецкий), но Django доступен толькоde, Django используетde. -
Доступны только языки, перечисленные в настройке
LANGUAGES. Если вы хотите ограничить выбор языка подмножеством предоставленных языков (поскольку ваше приложение не предоставляет все эти языки), установитеLANGUAGESв список языков. Например:LANGUAGES = [ ('de', _('German')), ('en', _('English')), ]В этом примере доступными для автоматического выбора языками являются немецкий и английский (и любые подязыки, такие как
de-chилиen-us). -
Если вы определяете пользовательскую настройку
LANGUAGES, как описано в предыдущем пункте, вы можете отметить имена языков как строки перевода — но используйтеgettext_lazy()вместоgettext(), чтобы избежать циклической зависимости.Вот пример файла настроек:
from django.utils.translation import gettext_lazy as _ LANGUAGES = [ ('de', _('German')), ('en', _('English')), ]
После того, как LocaleMiddleware определит предпочтение пользователя, оно будет доступно как request.LANGUAGE_CODE для каждого HttpRequest. Вы можете свободно использовать это значение в коде своего представления. Вот простой пример:
from django.http import HttpResponse
def hello_world(request, count):
if request.LANGUAGE_CODE == 'de-at':
return HttpResponse("You prefer to read Austrian German.")
else:
return HttpResponse("You prefer to read another language.")
Обратите внимание, что при статическом переводе (без middleware) язык находится в settings.LANGUAGE_CODE, а при динамическом переводе (с middleware) — в request.LANGUAGE_CODE.
Как Django обнаруживает переводы
При выполнении Django строит объединенный кэш переводов литералов в оперативной памяти. Для этого он ищет переводы, следуя этому алгоритму, определяющему порядок проверки различных путей к файлам скомпилированных файлами сообщений (.mo) и приоритет множественных переводов одного и того же литерала:
- Директории, перечисленные в
LOCALE_PATHS, имеют наивысший приоритет, причём те, которые указаны раньше, имеют больший приоритет, чем те, что указаны позже. - Затем, он ищет и, если найдёт, использует
localeдиректорию в каждой установленной приложении, перечисленной вINSTALLED_APPS. Причём те, что указаны раньше, имеют больший приоритет, чем те, что указаны позже. - Наконец, в качестве резервного варианта используется базовый перевод Django в
django/conf/locale.
См. также
Переводы литералов, включенных в JavaScript-активы, ищутся по схожему, но не идентичному алгоритму. Подробнее см. JavaScriptCatalog.
Вы также можете поместить файлы с пользовательским форматом в директории LOCALE_PATHS, если также зададите FORMAT_MODULE_PATH.
Во всех случаях ожидается, что имя директории, содержащей перевод, будет использовать обозначение имени локали. Например, de, pt_BR, es_AR, и т. д. Непереведённые строки для территориальных вариантов языка используют переводы общего языка. Например, непереведённые pt_BR строки используют переводы pt.
Добавлена поддержка падения к общему языку, как описано выше.
Таким образом, вы можете создавать приложения, включающие собственные переводы, и переопределять базовые переводы в своём проекте. Или же вы можете собрать большой проект из нескольких приложений и поместить все переводы в один большой общий файл сообщений, специфичный для создаваемого проекта. Выбор за вами.
Все хранилища файлов сообщений структурированы одинаково. Они:
- Все пути, указанные в
LOCALE_PATHSв вашем файле настроек, проверяются на наличие<language>/LC_MESSAGES/django.(po|mo) $APPPATH/locale/<language>/LC_MESSAGES/django.(po|mo)$PYTHONPATH/django/conf/locale/<language>/LC_MESSAGES/django.(po|mo)
Для создания файлов сообщений используется инструмент django-admin makemessages. А для создания двоичных файлов .mo, используемых gettext, применяется django-admin compilemessages.
Вы также можете запустить django-admin compilemessages
--settings=path.to.settings для обработки всех директорий в настройке LOCALE_PATHS.
Использование неанглийского базового языка
Django предполагает, что исходные строки в локализуемом проекте написаны на английском языке. Вы можете выбрать другой язык, но вам необходимо учитывать определённые ограничения:
-
gettextпредоставляет только две формы множественного числа для исходных сообщений, поэтому вам также потребуется предоставить перевод для базового языка, чтобы включить все формы множественного числа, если правила множественного числа для базового языка отличаются от английских. - Когда активирован английский вариант, и английские строки отсутствуют, языком резервного копирования не будет
LANGUAGE_CODEпроекта, а исходные строки. Например, английский пользователь, посещающий сайт сLANGUAGE_CODE, установленным на испанский, а исходные строки написаны на русском языке, увидит русский текст, а не испанский.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/2.2/topics/i18n/translation/