Перевод
Обзор
Для того, чтобы сделать проект Django переводимым, необходимо добавить минимальное количество хуков в ваш Python-код и шаблоны. Эти хуки называются строками перевода. Они сообщают Django: «Этот текст должен быть переведён на язык конечного пользователя, если перевод этого текста доступен на этом языке». Вы несете ответственность за пометку переводимых строк; система может переводить только те строки, о которых ей известно.
Затем Django предоставляет утилиты для извлечения строк перевода в файл сообщений. Этот файл является удобным способом для переводчиков предоставить эквивалент строк перевода на целевой язык. После того, как переводчики заполнили файл сообщений, его необходимо скомпилировать. Этот процесс опирается на набор инструментов GNU gettext.
После этого Django позаботится о переводе веб-приложений на лету на каждом доступном языке в соответствии с предпочтениями языка пользователей.
В Django хуки интернационализации включены по умолчанию, а это означает, что в определённых местах фреймворка есть небольшая издержка, связанная с интернационализацией. Если вы не используете интернационализацию, вы должны потратить две секунды на установку USE_I18N = False в вашем файле настроек. Тогда Django произведёт некоторые оптимизации, чтобы не загружать механизм интернационализации.
Примечание
Убедитесь, что вы активировали перевод для своего проекта (самый быстрый способ — проверить, включает ли MIDDLEWARE django.middleware.locale.LocaleMiddleware). Если нет, см. Как Django определяет предпочтения языка.
Интернационализация: в коде Python
Стандартный перевод
Укажите строку перевода, используя функцию gettext(). Принято импортировать её как более короткое псевдоним, _, для экономии набора.
Примечание
Стандартная библиотека 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-строки пока не поддерживаются xgettext, а JS-строки шаблонов требуют gettext 0.21+.
Комментарии для переводчиков
Если вы хотите дать переводчикам подсказки относительно переводимой строки, вы можете добавить комментарий с префиксом Translators в строке, предшествующей строке, например:
def my_view(request):
# Translators: This message appears on the home page only
output = gettext("Welcome to my site.")
Комментарий затем появится в результирующем .po файле, связанном с переводимым конструктом, расположенным ниже, а также должен отображаться большинством инструментов для перевода.
Примечание
Для полноты картины, вот соответствующий фрагмент результирующего .po файла:
#. Translators: This message appears on the home page only # path/to/python/file.py:123 msgid "Welcome to my site." msgstr ""
Это также работает в шаблонах. См. Комментарии для переводчиков в шаблонах для получения дополнительной информации.
Пометка строк как no-op
Используйте функцию django.utils.translation.gettext_noop(), чтобы пометить строку как строку перевода без перевода её. Строка затем переводится из переменной.
Используйте это, если у вас есть неизменные строки, которые должны храниться на исходном языке, потому что они обмениваются между системами или пользователями — например, строки в базе данных — но должны быть переведены в последний возможный момент, например, когда строка отображается пользователю.
Множественное число
Используйте функцию django.utils.translation.ngettext() для указания сообщений множественного числа.
ngettext() принимает три аргумента: строку перевода единственного числа, строку перевода множественного числа и количество объектов.
Эта функция полезна, когда вам нужно, чтобы ваше приложение Django было локализуемым для языков, где количество и сложность форм множественного числа больше, чем две формы, используемые в английском языке («объект» для единственного числа и «объекты» для всех случаев, где count отличается от одного, независимо от его значения).
Например:
from django.http import HttpResponse
from django.utils.translation import ngettext
def hello_world(request, count):
page = ngettext(
"there is %(count)d object",
"there are %(count)d objects",
count,
) % {
"count": count,
}
return HttpResponse(page)
В этом примере количество объектов передаётся языкам перевода как переменная count.
Обратите внимание, что множественное число сложно и работает по-разному в каждом языке. Сравнение count с 1 не всегда является правилом. Этот код выглядит сложным, но даст неверные результаты для некоторых языков:
from django.utils.translation import ngettext
from myapp.models import Report
count = Report.objects.count()
if count == 1:
name = Report._meta.verbose_name
else:
name = Report._meta.verbose_name_plural
text = ngettext(
"There is %(count)d %(name)s available.",
"There are %(count)d %(name)s available.",
count,
) % {"count": count, "name": name}
Не пытайтесь реализовывать собственную логику единственного или множественного числа; она будет неверной. В таком случае рассмотрите что-то вроде следующего:
text = ngettext(
"There is %(count)d %(name)s object available.",
"There are %(count)d %(name)s objects available.",
count,
) % {
"count": count,
"name": Report._meta.verbose_name,
}
Примечание
При использовании ngettext(), убедитесь, что вы используете одно имя для каждой экстраполированной переменной, включённой в литерал. В приведённых примерах обратите внимание, как мы использовали Python-переменную name в обеих строках перевода. Этот пример, помимо того, что является неправильным на некоторых языках, как указано выше, провалится:
text = ngettext(
"There is %(count)d %(name)s available.",
"There are %(count)d %(plural_name)s available.",
count,
) % {
"count": Report.objects.count(),
"name": Report._meta.verbose_name,
"plural_name": Report._meta.verbose_name_plural,
}
При запуске django-admin
compilemessages вы получите ошибку:
a format specification for argument 'name', as in 'msgstr[0]', doesn't exist in 'msgid'
Контекстные маркеры
Иногда слова имеют несколько значений, например, "May" на английском языке, что относится к названию месяца и к глаголу. Чтобы помочь переводчикам правильно перевести эти слова в разных контекстах, вы можете использовать функцию django.utils.translation.pgettext() или функцию django.utils.translation.npgettext(), если строка требует множественного числа. Оба принимают строку контекста как первый аргумент.
В результирующем .po файле строка будет появляться столько раз, сколько существует разных контекстных маркеров для одной строки (контекст будет указан в msgctxt строке), что позволит переводчику предоставить разные переводы для каждого из них.
Например:
from django.utils.translation import pgettext
month = pgettext("month name", "May")
или:
from django.db import models
from django.utils.translation import pgettext_lazy
class MyThing(models.Model):
name = models.CharField(
help_text=pgettext_lazy("help text for MyThing model", "This is the help text")
)
будет отображаться в .po файле как:
msgctxt "month name" msgid "May" msgstr ""
Контекстуальные маркеры также поддерживаются тегами шаблонов translate и blocktranslate.
Ленивая трансляция
Используйте ленивые версии функций трансляции в django.utils.translation (легко узнаваемые по суффиксу lazy в их именах), чтобы транслировать строки лениво – когда значение используется, а не когда они вызываются.
Эти функции хранят леничную ссылку на строку – не фактический перевод. Сам перевод будет выполнен, когда строка будет использована в контексте строки, например, при рендеринге шаблона.
Это необходимо, когда вызовы этих функций находятся в путях кода, которые выполняются при загрузке модуля.
Это может легко произойти при определении моделей, форм и форм моделей, потому что Django реализует их таким образом, что их поля фактически являются атрибутами уровня класса. По этой причине обязательно используйте ленивые трансляции в следующих случаях:
Поля моделей и отношения verbose_name и help_text значения опций
Например, чтобы перевести текст справки поля name в следующей модели, выполните следующие действия:
from django.db import models
from django.utils.translation import gettext_lazy as _
class MyThing(models.Model):
name = models.CharField(help_text=_("This is the help text"))
Вы можете пометить имена ForeignKey, ManyToManyField или OneToOneField отношений как переводимые, используя их опции verbose_name:
class MyThing(models.Model):
kind = models.ForeignKey(
ThingKind,
on_delete=models.CASCADE,
related_name="kinds",
verbose_name=_("kind"),
)
Так же, как вы делаете в verbose_name, вы должны предоставить текстовое значение verbose_name в нижнем регистре для отношения, так как Django автоматически приведёт его к верхнему регистру, когда это необходимо.
Значения verbose_name модели
Рекомендуется всегда предоставлять явные опции verbose_name и verbose_name_plural, а не полагаться на возвратную англоцентрическую и несколько наивную интерпретацию verbose_name, которую Django выполняет, смотря на имя класса модели:
from django.db import models
from django.utils.translation import gettext_lazy as _
class MyThing(models.Model):
name = models.CharField(_("name"), help_text=_("This is the help text"))
class Meta:
verbose_name = _("my thing")
verbose_name_plural = _("my things")
Методы модели description аргумент к декоратору @display
Для методов моделей вы можете предоставить переводы Django и сайту администрирования с аргументом description к декоратору display():
from django.contrib import admin
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"),
)
@admin.display(description=_("Is it a mouse?"))
def is_mouse(self):
return self.kind.type == MOUSE_TYPE
Работа с объектами ленивой трансляции
Результат вызова gettext_lazy() может быть использован везде, где вы бы использовали строку (объект str) в другом коде Django, но он может не работать с произвольным кодом Python. Например, следующее не сработает, потому что библиотека requests не обрабатывает gettext_lazy объекты:
body = gettext_lazy("I \u2764 Django") # (Unicode :heart:)
requests.post("https://example.com/send", data={"body": body})
Вы можете избежать таких проблем, преобразовав gettext_lazy() объекты в текстовые строки перед передачей их в код, не относящийся к Django:
requests.post("https://example.com/send", data={"body": str(body)})
Если вам не нравится длинное имя gettext_lazy, вы можете использовать псевдоним _ (подчёркивание), как показано ниже:
from django.db import models
from django.utils.translation import gettext_lazy as _
class MyThing(models.Model):
name = models.CharField(help_text=_("This is the help text"))
Использование gettext_lazy() и ngettext_lazy() для маркировки строк в моделях и служебных функциях является распространённой операцией. Когда вы работаете с этими объектами в другом месте вашего кода, вы должны убедиться, что случайно не преобразуете их в строки, потому что они должны быть преобразованы как можно позже (чтобы был активен правильный язык). Это требует использования описанной далее вспомогательной функции.
Ленивые переводы и множественное число
При использовании ленивой трансляции для строки множественного числа (n[p]gettext_lazy) вы обычно не знаете аргумент number во время определения строки. Поэтому вы можете передать имя ключа вместо целого числа как аргумент number. Затем number будет искаться в словаре по этому ключу во время интерполяции строки. Вот пример:
from django import forms
from django.core.exceptions import ValidationError
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 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 ValidationError(self.error_message % number)
Форматирование строк: format_lazy()
Метод Python str.format() не будет работать, если format_string или какой-либо из аргументов для str.format() содержит объекты ленивой трансляции. Вместо этого вы можете использовать django.utils.text.format_lazy(), которая создаёт ленивый объект, выполняющий метод str.format() только при включении результата в строку. Например:
from django.utils.text import format_lazy
from django.utils.translation import gettext_lazy
...
name = gettext_lazy("John Lennon")
instrument = gettext_lazy("guitar")
result = format_lazy("{name}: {instrument}", name=name, instrument=instrument)
В этом случае ленивые переводы в result будут преобразованы в строки только тогда, когда сам result используется в строке (обычно при рендеринге шаблона).
Другие способы использования lazy для отложенных переводов
В любом другом случае, когда вам нужно отложить перевод, но вам нужно передать переводимую строку как аргумент другой функции, вы можете обернуть эту функцию в ленивый вызов самостоятельно. Например:
from django.utils.functional import lazy from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ mark_safe_lazy = lazy(mark_safe, str)
А затем позже:
lazy_string = mark_safe_lazy(_("<p>My <strong>string!</strong></p>"))
Локализованные названия языков
-
get_language_info(lang_code)
Функция 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 в переводы, например, для выделения, но потенциально опасные символы (например, ") также будут отображаться без изменений.
translate тег шаблона
Тег шаблона {% translate %} транслирует либо постоянную строку (в одинарных или двойных кавычках), либо содержимое переменной:
<title>{% translate "This is the title." %}</title>
<title>{% translate myvar %}</title>
Если опция noop присутствует, поиск переменной всё ещё происходит, но перевод пропускается. Это полезно при «заглушке» содержимого, которое потребует перевода в будущем:
<title>{% translate "myvar" noop %}</title>
Внутренне, встроенные переводы используют вызов gettext().
В случае, если переменная шаблона (myvar выше) передаётся в тег, тег сначала разрешит такую переменную в строку во время выполнения, а затем найдёт эту строку в каталогах сообщений.
Невозможно смешивать переменную шаблона внутри строки в {% translate %}. Если ваши переводы требуют строк с переменными (заполнителями), используйте {% blocktranslate %} вместо этого.
Если вы хотите получить переведённую строку без её отображения, вы можете использовать следующий синтаксис:
{% translate "This is the title" as the_title %}
<title>{{ the_title }}</title>
<meta name="description" content="{{ the_title }}">
На практике вы будете использовать это, чтобы получить строку, которую вы можете использовать в нескольких местах в шаблоне или чтобы вы могли использовать вывод как аргумент для других тегов или фильтров шаблонов:
{% translate "starting point" as start %}
{% translate "end point" as end %}
{% translate "La Grande Boucle" as race %}
<h1>
<a href="/" title="{% blocktranslate %}Back to '{{ race }}' homepage{% endblocktranslate %}">{{ race }}</a>
</h1>
<p>
{% for stage in tour_stages %}
{% cycle start end %}: {{ stage }}{% if forloop.counter|divisibleby:2 %}<br>{% else %}, {% endif %}
{% endfor %}
</p>
{% translate %} также поддерживает контекстуальные маркеры с использованием ключевого слова context:
{% translate "May" context "month name" %}
blocktranslate тег шаблона
В отличие от тега translate, тег blocktranslate позволяет пометить сложные предложения, состоящие из литералов и содержимого переменной для перевода, используя заполнитель:
{% blocktranslate %}This string will have {{ value }} inside.{% endblocktranslate %}
Для перевода выражения шаблона – например, доступа к атрибутам объекта или использования фильтров шаблонов – необходимо привязать выражение к локальной переменной для использования в блоке перевода. Примеры:
{% blocktranslate with amount=article.price %}
That will cost $ {{ amount }}.
{% endblocktranslate %}
{% blocktranslate with myvar=value|filter %}
This will have {{ myvar }} inside.
{% endblocktranslate %}
Вы можете использовать несколько выражений внутри одного тега blocktranslate:
{% blocktranslate with book_t=book|title author_t=author|title %}
This is {{ book_t }} by {{ author_t }}
{% endblocktranslate %}
Примечание
Предыдущий более подробный формат все еще поддерживается: {% blocktranslate with book|title as book_t and author|title as author_t %}
Другие теги блоков (например, {% for %} или {% if %}) не разрешены внутри тега blocktranslate.
Если разрешение одного из аргументов блока завершается ошибкой, blocktranslate будет переходить к языку по умолчанию, временно деактивируя текущий активный язык с помощью функции deactivate_all().
Этот тег также обеспечивает возможность множественного числа. Чтобы его использовать:
- Укажите и привяжите значение счетчика с именем
count. Это значение будет использоваться для выбора правильной формы множественного числа. - Укажите форму единственного и множественного числа, разделяя их тегом
{% plural %}внутри тегов{% blocktranslate %}и{% endblocktranslate %}.
Пример:
{% blocktranslate count counter=list|length %}
There is only one {{ name }} object.
{% plural %}
There are {{ counter }} {{ name }} objects.
{% endblocktranslate %}
Более сложный пример:
{% blocktranslate with amount=article.price count years=i.length %}
That will cost $ {{ amount }} per year.
{% plural %}
That will cost $ {{ amount }} per {{ years }} years.
{% endblocktranslate %}
Когда вы используете функцию множественного числа и привязываете значения к локальным переменным помимо значения счетчика, помните, что конструкция blocktranslate внутренне преобразуется в вызов ngettext. Это означает, что применяются те же примечания, касающиеся переменных ngettext.
Обратные URL-поиски не могут быть выполнены внутри тега blocktranslate и должны быть получены (и сохранены) предварительно:
{% url 'path.to.view' arg arg2 as the_url %}
{% blocktranslate %}
This is a URL: {{ the_url }}
{% endblocktranslate %}
Если вы хотите получить переведенную строку без ее отображения, вы можете использовать следующий синтаксис:
{% blocktranslate asvar the_title %}The title is {{ title }}.{% endblocktranslate %}
<title>{{ the_title }}</title>
<meta name="description" content="{{ the_title }}">
На практике вы будете использовать это, чтобы получить строку, которую вы можете использовать в нескольких местах в шаблоне, или чтобы использовать вывод в качестве аргумента для других тегов или фильтров шаблона.
В более старых версиях экземпляры asvar не отмечались как безопасные для вывода (HTML).
{% blocktranslate %} также поддерживает контекстные маркеры с использованием ключевого слова context:
{% blocktranslate with name=user.username context "greeting" %}Hi {{ name }}{% endblocktranslate %}
Другая функция, которую поддерживает {% blocktranslate %}, это опция trimmed . Эта опция удалит символы новой строки из начала и конца содержимого тега {% blocktranslate %}, заменит любые пробелы в начале и конце строки и объединит все строки в одну, используя пробел в качестве разделителя. Это очень полезно для отступа содержимого тега {% blocktranslate %} без переноса символов отступа в соответствующую запись в файле .po, что упрощает процесс перевода.
Например, следующий тег {% blocktranslate %}:
{% blocktranslate trimmed %}
First sentence.
Second paragraph.
{% endblocktranslate %}
приведет к записи "First sentence. Second paragraph." в файле .po, по сравнению с "\n First sentence.\n Second paragraph.\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 %}
{% translate "View" %}
{% comment %}Translators: Short intro blurb{% endcomment %}
<p>{% blocktranslate %}A multiline translatable
literal.{% endblocktranslate %}</p>
или с помощью тегов {# … #} конструирования однострочных комментариев:
{# Translators: Label of a button that triggers search #}
<button type="submit">{% translate "Go" %}</button>
{# Translators: This is a text of the base template #}
{% blocktranslate %}Ambiguous translatable block of text{% endblocktranslate %}
Примечание
Для полноты картины, вот соответствующие фрагменты результирующего файла .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>{% translate "Welcome to our page" %}</p>
{% language 'en' %}
{% get_current_language as LANGUAGE_CODE %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
<p>{% translate "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 }}(Ложь) -
{{ LANGUAGE_CODE|language_name_translated }}(«německy», когда активным языком является чешский)
Международная локализация: в коде JavaScript
Добавление переводов в код JavaScript создает некоторые проблемы:
- Код JavaScript не имеет доступа к реализации
gettext. - Код JavaScript не имеет доступа к файлам
.poили.mo; они должны быть переданы сервером. - Каталоги переводов для JavaScript должны быть максимально компактными.
Django предоставляет интегрированное решение для этих проблем: он передает переводы в JavaScript, так что вы можете вызывать gettext, и т. д., внутри JavaScript.
Основное решение этих проблем состоит в следующем представлении JavaScriptCatalog, которое генерирует библиотеку кода JavaScript с функциями, имитирующими интерфейс gettext, плюс массив строк переводов.
Представление JavaScriptCatalog
-
class JavaScriptCatalog -
Представление, которое генерирует библиотеку кода JavaScript с функциями, имитирующими интерфейс
gettext, плюс массив строк переводов.Атрибуты
-
domain -
Домен перевода, содержащий строки, которые нужно добавить в вывод представления. По умолчанию
'djangojs'.
-
packages -
Список
application namesсреди установленных приложений. Эти приложения должны содержать каталогlocale. Все эти каталоги плюс все каталоги, найденные вLOCALE_PATHS(которые всегда включаются), объединяются в один каталог. По умолчаниюNone, что означает, что все доступные переводы из всехINSTALLED_APPSпредоставляются в выводе JavaScript.
Пример со значениями по умолчанию:
from django.views.i18n import JavaScriptCatalog urlpatterns = [ path("jsi18n/", JavaScriptCatalog.as_view(), name="javascript-catalog"), ]Пример с настраиваемыми пакетами:
urlpatterns = [ path( "jsi18n/myapp/", JavaScriptCatalog.as_view(packages=["your.app.label"]), name="javascript-catalog", ), ]Если ваша корневая 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 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 предоставляет интерфейс для склонения слов и фраз:
const objectCount = 1 // or 0, or 2, or 3, ...
const string = ngettext(
'literal for the singular case',
'literal for the plural case',
objectCount
);
interpolate
Функция interpolate поддерживает динамическое заполнение строки формата. Синтаксис интерполяции позаимствован из Python, поэтому функция interpolate поддерживает как позиционную, так и именованную интерполяцию:
-
Позиционная интерполяция:
objсодержит объект JavaScript Array, значения элементов которого затем последовательно интерполируются в соответствующиеfmtзаполнительные места в том же порядке, в котором они появляются. Например:const formats = ngettext( 'There is %s object. Remaining: %s', 'There are %s objects. Remaining: %s', 11 ); const string = interpolate(formats, [11, 20]); // string is 'There are 11 objects. Remaining: 20'
-
Именованная интерполяция: Этот режим выбирается путем передачи необязательного булевого параметра
namedв качествеtrue.objсодержит объект JavaScript или ассоциативный массив. Например:const data = { count: 10, total: 50 }; const formats = ngettext( 'Total: %(total)s, there is %(count)s object', 'there are %(count)s of a total of %(total)s objects', data.count ); const string = interpolate(formats, data, true);
Однако не стоит злоупотреблять строковой интерполяцией: это всё-таки JavaScript, поэтому код должен выполнять многократные подстановки с помощью регулярных выражений. Это не так быстро, как интерполяция строк в Python, поэтому используйте её только в тех случаях, когда это действительно необходимо (например, в сочетании с ngettext для создания правильного склонения).
get_format
Функция get_format имеет доступ к настроенным настройкам форматирования i18n и может получить строку формата для данного имени настройки:
document.write(get_format('DATE_FORMAT'));
// 'N j, Y'
Она имеет доступ к следующим настройкам:
DATE_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 -
Для использования другой клиентской библиотеки для обработки переводов, вы можете воспользоваться представлением
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="jsi18n-%s" % get_version())(
JavaScriptCatalog.as_view()
),
name="javascript-catalog",
),
]
Кэширование на стороне клиента сэкономит пропускную способность и ускорит загрузку вашего сайта. Если вы используете ETags (ConditionalGetMiddleware), вы уже покрыты. В противном случае вы можете применить условные декораторы. В следующем примере кеш обновляется каждый раз при перезапуске сервера приложения:
from django.utils import timezone
from django.views.decorators.http import last_modified
from django.views.i18n import JavaScriptCatalog
last_modified_date = timezone.now()
urlpatterns = [
path(
"jsi18n/",
last_modified(lambda req, **kw: last_modified_date)(
JavaScriptCatalog.as_view()
),
name="javascript-catalog",
),
]
Вы даже можете предварительно сгенерировать каталог JavaScript как часть процесса развертывания и предоставить его как статический файл. Эта радикальная техника реализована в django-statici18n.
Международный язык в URL-паттернах
Django предоставляет два механизма для интернационализации шаблонов URL:
- Добавление префикса языка в корень шаблонов URL, чтобы
LocaleMiddlewareсмог определить язык для активации из запрошенного URL. - Делает шаблоны URL переводимыми с помощью функции
django.utils.translation.gettext_lazy().
Предупреждение
Использование любого из этих функций требует установки активного языка для каждого запроса; другими словами, вам нужно иметь django.middleware.locale.LocaleMiddleware в настройке MIDDLEWARE.
Префикс языка в шаблонах URL
-
i18n_patterns(*urls, prefix_default_language=True)
Эта функция может быть использована в корневом URLconf, и Django автоматически добавит код активного языка в префикс всех шаблонов URL, определенных внутри i18n_patterns().
Установка prefix_default_language на False удаляет префикс из языка по умолчанию (LANGUAGE_CODE). Это может быть полезно при добавлении переводов на существующий сайт, чтобы текущие URL не изменились.
Пример шаблонов URL:
from django.conf.urls.i18n import i18n_patterns
from django.urls import include, path
from about import views as about_views
from news import views as news_views
from sitemap.views import sitemap
urlpatterns = [
path("sitemap.xml", sitemap, name="sitemap-xml"),
]
news_patterns = (
[
path("", news_views.index, name="index"),
path("category/<slug:slug>/", news_views.category, name="category"),
path("<slug:slug>/", news_views.details, name="detail"),
],
"news",
)
urlpatterns += i18n_patterns(
path("about/", about_views.main, name="about"),
path("news/", include(news_patterns, namespace="news")),
)
После определения этих шаблонов URL, Django автоматически добавит префикс языка к шаблонам URL, которые были добавлены функцией i18n_patterns . Пример:
>>> from django.urls import reverse
>>> from django.utils.translation import activate
>>> activate("en")
>>> reverse("sitemap-xml")
'/sitemap.xml'
>>> reverse("news:index")
'/en/news/'
>>> activate("nl")
>>> reverse("news:detail", kwargs={"slug": "news-slug"})
'/nl/news/news-slug/'
При использовании prefix_default_language=False и LANGUAGE_CODE='en', URL будут следующими:
>>> activate("en")
>>> reverse("news:index")
'/news/'
>>> activate("nl")
>>> reverse("news:index")
'/nl/news/'
Предупреждение
i18n_patterns() может быть использован только в корневом URLconf. Использование его в включенном URLconf вызовет исключение ImproperlyConfigured.
Предупреждение
Убедитесь, что у вас нет шаблонов URL без префикса, которые могут конфликтовать с автоматически добавленным префиксом языка.
Перевод шаблонов URL
Шаблоны URL также можно помечать как переводимые, используя функцию gettext_lazy(). Пример:
from django.conf.urls.i18n import i18n_patterns
from django.urls import include, path
from django.utils.translation import gettext_lazy as _
from about import views as about_views
from news import views as news_views
from sitemaps.views import sitemap
urlpatterns = [
path("sitemap.xml", sitemap, name="sitemap-xml"),
]
news_patterns = (
[
path("", news_views.index, name="index"),
path(_("category/<slug:slug>/"), news_views.category, name="category"),
path("<slug:slug>/", news_views.details, name="detail"),
],
"news",
)
urlpatterns += i18n_patterns(
path(_("about/"), about_views.main, name="about"),
path(_("news/"), include(news_patterns, namespace="news")),
)
После создания переводов функция reverse() вернёт URL в активном языке. Пример:
>>> from django.urls import reverse
>>> from django.utils.translation import activate
>>> activate("en")
>>> reverse("news:category", kwargs={"slug": "recent"})
'/en/news/category/recent/'
>>> activate("nl")
>>> reverse("news:category", kwargs={"slug": "recent"})
'/nl/nieuws/categorie/recent/'
Предупреждение
В большинстве случаев лучше использовать переведённые URL только внутри блока шаблонов с префиксом кода языка (с использованием i18n_patterns()), чтобы избежать возможности того, что небрежно переведенный URL вызовет конфликт с непроверенным шаблоном URL.
Обратная ссылка в шаблонах
Если локализованные URL обращаются в шаблонах, они всегда используют текущий язык. Чтобы связаться с URL на другом языке, используйте тег шаблона language. Он активирует указанный язык в заключённом фрагменте шаблона:
{% load i18n %}
{% get_available_languages as languages %}
{% translate "View this category in:" %}
{% for lang_code, lang_name in languages %}
{% language lang_code %}
<a href="{% url 'category' slug=category.slug %}">{{ lang_name }}</a>
{% endlanguage %}
{% endfor %}
Тег language ожидает код языка в качестве единственного аргумента.
Локализация: как создать файлы языков
После того, как строковые литералы приложения были помечены для последующего перевода, сами переводы необходимо написать (или получить). Вот как это работает.
Файлы сообщений
Первый шаг — создание файла сообщений для нового языка. Файл сообщений — это обычный текстовый файл, представляющий один язык, который содержит все доступные строки перевода и то, как они должны быть представлены на данном языке. Файлы сообщений имеют расширение .po.
Django поставляется со средством django-admin makemessages, которое автоматизирует создание и обслуживание этих файлов.
Утилиты Gettext
Команда makemessages (и команда compilemessages ниже) используют команды из набора инструментов GNU gettext: xgettext, msgfmt, msgmerge и msguniq.
Минимальная поддерживаемая версия утилит gettext составляет 0.15.
Чтобы создать или обновить файл сообщений, выполните эту команду:
django-admin makemessages -l de
…где de — это имя локали для файла сообщений, который вы хотите создать. Например, pt_BR для бразильского португальского языка, de_AT для австрийского немецкого языка или id для индонезийского языка.
Скрипт должен быть запущен из одного из двух мест:
- Корневой каталог вашего проекта Django (тот, который содержит
manage.py). - Корневой каталог одного из ваших приложений Django.
Скрипт выполняется над деревом исходного кода проекта или дерева исходного кода приложения и извлекает все строки, помеченные для перевода (см. Как Django обнаруживает переводы и убедитесь, что LOCALE_PATHS настроен правильно). Он создаёт (или обновляет) файл сообщений в каталоге locale/LANG/LC_MESSAGES. В примере de файл будет locale/de/LC_MESSAGES/django.po.
Когда вы запускаете makemessages из корневой директории проекта, извлечённые строки автоматически распределяются по соответствующим файлам сообщений. То есть, строка, извлечённая из файла приложения, содержащего locale каталог, помещается в файл сообщений под этим каталогом. Строка, извлечённая из файла приложения без каталога locale, попадает либо в файл сообщений под каталогом, указанным первым в LOCALE_PATHS, либо приводит к ошибке, если LOCALE_PATHS пусто.
По умолчанию django-admin makemessages проверяет каждый файл, имеющий расширение .html, .txt или .py. Если вы хотите изменить это поведение, используйте опцию --extension или -e для указания расширений файлов для проверки:
django-admin makemessages -l de -e txt
Отдельные расширения разделяются запятыми и/или можно использовать -e или --extension несколько раз:
django-admin makemessages -l de -e html,txt -e xml
Предупреждение
При создании файлов сообщений из исходного кода JavaScript необходимо использовать специальный домен djangojs, а не -e js.
Использование шаблонов Jinja2?
makemessages не понимает синтаксис шаблонов Jinja2. Для извлечения строк из проекта, содержащего шаблоны Jinja2, используйте Извлечение сообщений из Babel вместо этого.
Вот пример babel.cfg конфигурационного файла:
# Extraction from Python source files [python: **.py] # Extraction from Jinja2 templates [jinja2: **.jinja] extensions = jinja2.ext.with_
Убедитесь, что вы перечислили все используемые вами расширения! В противном случае Babel не распознает теги, определённые этими расширениями, и полностью пропустит шаблоны Jinja2, содержащие их.
Babel предоставляет аналогичные функции makemessages, может заменить его в общем случае и не зависит от gettext. Для получения дополнительной информации ознакомьтесь с документацией о работе с каталогами сообщений.
Нет утилит gettext?
Если у вас не установлены утилиты gettext , makemessages создаст пустые файлы. В этом случае либо установите утилиты gettext , либо скопируйте файл сообщений на английском языке (locale/en/LC_MESSAGES/django.po ), если он доступен, и используйте его в качестве отправной точки, то есть пустой файл перевода.
Работа на Windows?
Если вы работаете на Windows и вам нужно установить утилиты GNU gettext, чтобы makemessages работали, см. gettext на Windows для получения дополнительной информации.
Каждый файл .po содержит небольшую метаданные, например, контактную информацию ответственного за перевод, но основная часть файла — это список сообщений — сопоставления между строками перевода и фактическим переведенным текстом для конкретного языка.
Например, если в вашем приложении Django содержалась строка перевода для текста "Welcome to my site.", вот так:
_("Welcome to my site.")
…тогда django-admin makemessages будет создан файл .po, содержащий следующий фрагмент — сообщение:
#: path/to/python/module.py:23 msgid "Welcome to my site." msgstr ""
Краткое объяснение:
-
msgid— это строка перевода, которая отображается в исходном файле. Не меняйте её. -
msgstr— это место, куда вы помещаете перевод, специфичный для языка. Вначале оно пустое, поэтому вам нужно его изменить. Убедитесь, что вы сохраняете кавычки вокруг вашего перевода. - Для удобства каждое сообщение включает в себя строку комментария, начинающуюся с
#и расположенную над строкойmsgid, с указанием файла и строки, откуда была взята строка перевода.
Длинные сообщения — это особый случай. Там первая строка сразу после msgstr (или msgid) — пустая строка. Затем содержимое будет написано в следующих нескольких строках, по одной строке на строку. Эти строки напрямую конкатенируются. Не забывайте о trailing-пробелах в строках; иначе они будут склеены без пробелов!
Учитывайте кодировку
Из-за того, как работают внутренние инструменты gettext, и потому, что мы хотим разрешить строки исходного кода, не являющиеся ASCII, в ядре Django и ваших приложениях, вы обязательно должны использовать UTF-8 в качестве кодировки для ваших файлов .po (по умолчанию при создании файлов .po). Это означает, что все будут использовать одну и ту же кодировку, что важно, когда Django обрабатывает файлы .po.
Размытые записи
makemessages иногда генерирует записи перевода, помеченные как размытые, например, когда переводы выводятся из ранее переведённых строк. По умолчанию размытые записи не обрабатываются compilemessages.
Чтобы повторно проверить весь исходный код и шаблоны на наличие новых строк перевода и обновить все файлы сообщений для всех языков, выполните это:
django-admin makemessages -a
Компиляция файлов сообщений
После создания файла сообщений — и каждый раз, когда вы вносите в него изменения — вам нужно будет скомпилировать его в более эффективный формат для использования gettext. Для этого используйте утилиту django-admin compilemessages.
Этот инструмент обрабатывает все доступные файлы .po и создаёт файлы .mo, которые являются двоичными файлами, оптимизированными для использования gettext. В той же директории, из которой вы запустили django-admin makemessages, запустите django-admin compilemessages так:
django-admin compilemessages
Всё готово. Ваши переводы готовы к использованию.
Работаете на Windows?
Если вы используете Windows и вам необходимо установить утилиты GNU gettext для работы django-admin compilemessages, см. gettext на Windows для получения дополнительной информации.
Файлы .po: Кодировка и использование BOM.
Django поддерживает только файлы .po в кодировке UTF-8 и без BOM (Byte Order Mark), поэтому, если ваш текстовый редактор добавляет такие метки в начало файлов по умолчанию, вам необходимо его настроить.
Отладка: gettext() неправильно обнаруживает python-format в строках с символами процента
В некоторых случаях, например, в строках с символом процента, за которым следует пробел и тип преобразования строки типа преобразования строки (например, _("10% interest")), gettext() неправильно маркирует строки с python-format.
Если вы попытаетесь скомпилировать файлы сообщений с неправильно помеченными строками, вы получите сообщение об ошибке, подобное number of format specifications in 'msgid' and
'msgstr' does not match или 'msgstr' is not a valid Python format string,
unlike 'msgid'.
Чтобы обойти эту проблему, вы можете экранировать символы процента, добавив второй символ процента:
from django.utils.translation import gettext as _
output = _("10%% interest")
Или вы можете использовать no-python-format, чтобы все символы процента обрабатывались как литералы:
# xgettext:no-python-format
output = _("10% interest")
Создание файлов сообщений из исходного кода JavaScript
Вы создаете и обновляете файлы сообщений так же, как и другие файлы сообщений Django — с помощью инструмента django-admin makemessages. Единственное отличие заключается в том, что вам нужно явно указать то, что в терминологии gettext известно как домен, в данном случае домен djangojs, указав параметр -d
djangojs, например так:
django-admin makemessages -d djangojs -l de
Это создаст или обновит файл сообщений для JavaScript для немецкого языка. После обновления файлов сообщений выполните django-admin compilemessages так же, как и с обычными файлами сообщений Django.
gettext на Windows
Это необходимо только тем, кто хочет извлечь идентификаторы сообщений или скомпилировать файлы сообщений (.po). Работа с переводом сама по себе подразумевает редактирование имеющихся файлов такого типа, но если вы хотите создать свои собственные файлы сообщений или хотите протестировать или скомпилировать изменённый файл сообщений, скачайте установщик предварительно скомпилированного двоичного файла.
Вы также можете использовать двоичные файлы gettext, полученные из других источников, при условии, что команда xgettext --version работает правильно. Не пытайтесь использовать утилиты перевода Django с пакетом gettext, если команда xgettext
--version при вводе в командной строке Windows вызывает всплывающее окно с сообщением «xgettext.exe вызвало ошибки и будет закрыто Windows».
Настройка команды makemessages
Если вы хотите передать дополнительные параметры в xgettext, вам нужно создать пользовательскую команду makemessages и переопределить её атрибут xgettext_options:
from django.core.management.commands import makemessages
class Command(makemessages.Command):
xgettext_options = makemessages.Command.xgettext_options + ["--keyword=mytrans"]
Если вам нужна большая гибкость, вы также можете добавить новый аргумент в свою пользовательскую команду makemessages:
from django.core.management.commands import makemessages
class Command(makemessages.Command):
def add_arguments(self, parser):
super().add_arguments(parser)
parser.add_argument(
"--extra-keyword",
dest="xgettext_keywords",
action="append",
)
def handle(self, *args, **options):
xgettext_keywords = options.pop("xgettext_keywords")
if xgettext_keywords:
self.xgettext_options = makemessages.Command.xgettext_options[:] + [
"--keyword=%s" % kwd for kwd in xgettext_keywords
]
super().handle(*args, **options)
Разное
Предварительная настройка редиректа языка
-
set_language(request)
Для удобства Django поставляется представление django.views.i18n.set_language(), которое устанавливает предпочтения языка пользователя и перенаправляет на указанный URL или, по умолчанию, на предыдущую страницу.
Активируйте это представление, добавив следующую строку в ваш файл URLconf:
path("i18n/", include("django.conf.urls.i18n")),
(Обратите внимание, что в этом примере представление доступно по адресу /i18n/setlang/.)
Предупреждение
Убедитесь, что вы не включаете указанный выше URL в i18n_patterns() — он должен быть независимым от языка, чтобы правильно работать.
Представление ожидает вызова через метод POST, с параметром language, заданным в запросе. Если поддержка сеансов включена, представление сохраняет выбор языка в сеансе пользователя. Также оно сохраняет выбор языка в cookie с именем django_language по умолчанию. (Имя можно изменить с помощью настройки LANGUAGE_COOKIE_NAME.)
После выбора языка Django ищет параметр next в данных POST или GET. Если он найден и Django считает URL безопасным (то есть он не указывает на другой хост и использует безопасный протокол), выполняется перенаправление на этот URL. В противном случае Django может перенаправить пользователя на URL из заголовка Referer или, если он не задан, на /, в зависимости от типа запроса:
- Если запрос принимает HTML-контент (на основе его заголовка HTTP
Accept), отказ всегда выполняется. - Если запрос не принимает HTML, отказ будет выполнен только если параметр
nextбыл задан. В противном случае будет возвращен код состояния 204 (Нет контента).
Вот пример кода шаблона 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_COOKIE_NAME в ответе:
from django.conf import settings from django.http import HttpResponse from django.utils import translation user_language = "fr" translation.activate(user_language) response = HttpResponse(...) response.set_cookie(settings.LANGUAGE_COOKIE_NAME, user_language)
Обычно требуется использовать оба метода: django.utils.translation.activate() изменяет язык для этой потоковой нити, а установка куки сохраняет это предпочтение для будущих запросов.
Использование переводов вне представлений и шаблонов
Хотя 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 и языка, установленного средством промежуточного ПО.
Функции, представляющие особый интерес, включают django.utils.translation.get_language(), которая возвращает язык, используемый в текущей потоковой нити, django.utils.translation.activate(), которая активирует каталог переводов для текущей потоковой нити, и django.utils.translation.check_for_language(), которая проверяет, поддерживает ли Django данный язык.
Для написания более компактного кода также имеется менеджер контекста django.utils.translation.override(), который сохраняет текущий язык при входе и восстанавливает его при выходе. С его помощью приведенный выше пример становится:
from django.utils import translation
def welcome_translated(language):
with translation.override(language):
return translation.gettext("welcome")
Куки языка
LANGUAGE_COOKIE_NAMELANGUAGE_COOKIE_AGELANGUAGE_COOKIE_DOMAINLANGUAGE_COOKIE_HTTPONLYLANGUAGE_COOKIE_PATHLANGUAGE_COOKIE_SAMESITELANGUAGE_COOKIE_SECURE
Примечания по реализации
Особенности перевода Django
Механизм перевода Django использует стандартный модуль gettext из Python. Если вы знакомы с gettext, вы можете заметить следующие особенности способа перевода Django:
- Домен строки —
djangoилиdjangojs. Этот домен используется для различения различных программ, хранящих свои данные в общей библиотеке файлов сообщений (обычно/usr/share/locale/). Доменdjangoиспользуется для строк перевода Python и шаблонов и загружается в глобальные каталоги переводов. Доменdjangojsиспользуется только для каталогов переводов JavaScript, чтобы обеспечить их минимальный размер. - Django не использует
xgettextв одиночку. Он использует обертки Python надxgettextиmsgfmt. Это в основном делается для удобства.
Как Django определяет предпочтение языка
После подготовки ваших переводов (или если вы хотите использовать переводы, поставляемые с Django), вам необходимо активировать перевод для вашего приложения.
Внутри Django существует гибкая модель определения используемого языка — для всего приложения, для конкретного пользователя или для обоих.
Чтобы установить предпочтение языка для всего приложения, задайте настройку LANGUAGE_CODE. Django использует этот язык как значение по умолчанию для перевода — окончательный вариант, если не найдено более подходящего перевода одним из методов, используемых средством промежуточного ПО (см. ниже).
Если вам нужно запустить Django с родным языком, вам нужно задать настройку LANGUAGE_CODE и убедиться, что соответствующие файлы сообщений (файлы сообщений) и их скомпилированные версии (.mo) существуют.
Если вы хотите позволить каждому пользователю указать предпочтительный язык, вам также необходимо использовать LocaleMiddleware. LocaleMiddleware позволяет выбирать язык на основе данных из запроса. Это позволяет настраивать контент для каждого пользователя.
Чтобы использовать LocaleMiddleware, добавьте 'django.middleware.locale.LocaleMiddleware' в настройку MIDDLEWARE. Поскольку порядок средств промежуточного ПО имеет значение, следуйте этим рекомендациям:
- Убедитесь, что он установлен одним из первых.
- Он должен быть после
SessionMiddleware, так какLocaleMiddlewareиспользует данные сессии. И он должен стоять передCommonMiddleware, так какCommonMiddlewareтребует активированного языка для разрешения запрошенного URL. - Если вы используете
CacheMiddleware, разместитеLocaleMiddlewareпосле него.
Например, ваша настройка MIDDLEWARE может выглядеть так:
MIDDLEWARE = [
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.locale.LocaleMiddleware",
"django.middleware.common.CommonMiddleware",
]
(Для получения дополнительной информации о средствах промежуточного ПО см. документацию по средствам промежуточного ПО.)
LocaleMiddleware пытается определить предпочтение языка пользователя, следуя этому алгоритму:
- Сначала ищет префикс языка в запрошенном URL. Это выполняется только при использовании функции
i18n_patternsв вашем конфигурационном файле URL-адресов. Для получения дополнительной информации о префиксе языка и о том, как сделать URL-адреса интернационализированными, см. Интернационализация: в шаблонах URL-адресов. -
Если это не помогает, ищет куки.
Имя используемого куки задано настройкой
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.")
Обратите внимание, что при статическом (без средств промежуточного ПО) переводе язык находится в settings.LANGUAGE_CODE, а при динамическом (со средствами промежуточного ПО) — в 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/5.0/topics/i18n/translation/