Перевод
Обзор
Для того, чтобы сделать проект 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, а JavaScript-строки шаблонов требуют 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, а не полагаться на англо-ориентированное и несколько наивное определение Django verbose name, определяемое по имени класса модели:
from django.db import models
from django.utils.translation import gettext_lazy as _
class MyThing(models.Model):
name = models.CharField(_("name"), help_text=_("This is the help text"))
class Meta:
verbose_name = _("my thing")
verbose_name_plural = _("my things")
Методы моделей 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.translation import gettext_lazy as _
def to_lower(string):
return string.lower()
to_lower_lazy = lazy(to_lower, str)
И затем позже:
lazy_string = to_lower_lazy(_("My STRING!"))
Локализованные названия языков
-
get_language_info(lang_code)[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 в переводы, например, для выделения, но потенциально опасные символы (например, ") также будут отображены без изменений.
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 }}">
На практике вы будете использовать это для получения строки, которую можно использовать в нескольких местах в шаблоне, или для использования вывода в качестве аргумента для других тегов или фильтров шаблона.
{% 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[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 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[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="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)[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 обращаются в шаблонах, они всегда используют текущий язык. Для ссылки на 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) — пустая строка. Затем само содержимое будет записано в следующих нескольких строках, по одной строке на каждое значение. Эти строки непосредственно конкатенируются. Не забывайте о пробелах в строках; в противном случае они будут склеиваться без пробелов!
Учитывайте кодировку
Из-за того, как утилиты 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)[source]
Для удобства Django поставляется с представлением django.views.i18n.set_language(), которое устанавливает предпочтительный язык пользователя и перенаправляет его на заданный URL или, по умолчанию, на предыдущую страницу.
Активируйте это представление, добавив следующую строку в свой URLconf:
path("i18n/", include("django.conf.urls.i18n")),
(Обратите внимание, что в этом примере представление доступно по адресу /i18n/setlang/.)
Предупреждение
Убедитесь, что вы не включаете указанный выше URL внутри i18n_patterns() - он сам должен быть независимым от языка, чтобы работать корректно.
Представление ожидает вызова через метод POST, с параметром language, заданным в request. Если поддержка сессии включена, представление сохраняет выбор языка в сессии пользователя. Оно также сохраняет выбор языка в cookie, имя которой по умолчанию django_language. (Имя можно изменить через настройку LANGUAGE_COOKIE_NAME.)
После установки выбора языка Django ищет параметр next в данных POST или GET. Если он найден и Django считает этот URL безопасным (т. е. он не указывает на другой хост и использует безопасную схему), будет выполнено перенаправление на этот URL. В противном случае Django может перейти к перенаправлению пользователя на URL из заголовка Referer или, если он не задан, на /, в зависимости от природы запроса:
- Если запрос принимает контент 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(). Это относится только к текущей потоковой нити. Чтобы сохранить язык для всей сессии в cookie, установите 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.utils.translation.activate() изменяет язык для данной потоковой нити, а установка cookie сохраняет это предпочтение в будущих запросах.
Использование переводов вне представлений и шаблонов
Хотя 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")
Файл языка
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в вашем корневом URLconf. Дополнительную информацию о префиксе языка и о том, как интернационализировать URL-образцы, см. в разделе Локализация: в шаблонах URL. -
Если это не удаётся, он ищет файл 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.")
Обратите внимание, что при статическом (без средств промежуточного ПО) переводе язык находится в 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.1/topics/i18n/translation/