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