Перевод
Обзор
Для того, чтобы сделать проект Django переводимым, необходимо добавить минимальное количество хуков в ваш код Python и шаблоны. Эти хуки называются строками перевода. Они сообщают Django: «Этот текст должен быть переведён на язык конечного пользователя, если перевод этого текста доступен на этом языке». Вы несёте ответственность за пометку переводимых строк; система может переводить только строки, о которых она знает.
Затем Django предоставляет утилиты для извлечения строк перевода в файл сообщений. Этот файл является удобным способом для переводчиков предоставить эквивалент строк перевода на целевой язык. После того, как переводчики заполнили файл сообщений, его необходимо скомпилировать. Этот процесс использует набор инструментов GNU gettext.
После этого Django позаботится о переводе веб-приложений на лету на каждом доступном языке в соответствии с предпочтениями языка пользователей.
В Django хуки для международной поддержки включены по умолчанию, что означает некоторую накладные расходы, связанные с i18n, в определенных местах фреймворка. Если вы не используете международную поддержку, вы должны потратить две секунды, чтобы установить USE_I18N = False в вашем файле настроек. Затем Django сделает некоторые оптимизации, чтобы не загружать механизм международной поддержки.
Примечание
Также существует независимая, но связанная настройка USE_L10N, которая управляет тем, должен ли Django реализовывать локализацию форматов. Подробнее см. Локализация форматов.
Примечание
Убедитесь, что вы активировали перевод для своего проекта (самый быстрый способ — проверить, включает ли MIDDLEWARE_CLASSES django.middleware.locale.LocaleMiddleware). Если нет, см. Как Django определяет предпочтения языка.
Международная поддержка: в коде Python
Стандартный перевод
Укажите строку перевода, используя функцию ugettext(). Принято импортировать это как более короткое псевдоним, _, чтобы сэкономить набирание.
Примечание
Стандартная библиотека Python gettext устанавливает _() в глобальное пространство имён как псевдоним для gettext(). В Django мы решили не следовать этой практике по нескольким причинам:
- Для поддержки наборов символов международного формата (Unicode)
ugettext()более полезно, чемgettext(). Иногда вы должны использоватьugettext_lazy()в качестве метода перевода по умолчанию для определённого файла. Без_()в глобальном пространстве имён разработчик должен подумать, какая функция перевода наиболее подходит. - Подчёркивание (
_) используется для обозначения «предыдущего результата» в интерактивной оболочке Python и тестах doctest. Установка глобальной функции_()вызывает конфликт. Явное импортированиеugettext()как_()решает эту проблему.
В этом примере текст "Welcome to my site." отмечен как строка перевода:
from django.utils.translation import ugettext as _
from django.http import HttpResponse
def my_view(request):
output = _("Welcome to my site.")
return HttpResponse(output)
Очевидно, вы можете написать это без использования псевдонима. Этот пример идентичен предыдущему:
from django.utils.translation import ugettext
from django.http import HttpResponse
def my_view(request):
output = ugettext("Welcome to my site.")
return HttpResponse(output)
Перевод работает с вычисленными значениями. Этот пример идентичен предыдущим двум:
def my_view(request):
words = ['Welcome', 'to', 'my', 'site.']
output = _(' '.join(words))
return HttpResponse(output)
Перевод работает с переменными. Ещё раз, вот идентичный пример:
def my_view(request):
sentence = 'Welcome to my site.'
output = _(sentence)
return HttpResponse(output)
(Особенность использования переменных или вычисленных значений, как в предыдущих двух примерах, заключается в том, что утилита Django для обнаружения строк перевода, django-admin makemessages, не сможет найти эти строки. Подробнее об makemessages позже.)
Строки, которые вы передаёте _() или ugettext(), могут принимать заменители, заданные с помощью стандартного синтаксиса подстановки имён строк Python. Пример:
def my_view(request, m, d):
output = _('Today is %(month)s %(day)s.') % {'month': m, 'day': d}
return HttpResponse(output)
Эта техника позволяет языковым переведенным текстам переупорядочивать текст заменителей. Например, английский перевод может быть "Today is November 26.", в то время как испанский перевод может быть "Hoy es 26 de Noviembre." – с заменителем месяца и дня, поменянными местами.
По этой причине вы должны использовать интерполяцию именованных строк (например, %(day)s) вместо позиционной интерполяции (например, %s или %d) всякий раз, когда у вас более одного параметра. Если бы вы использовали позиционную интерполяцию, переводы не смогли бы переупорядочить текст заменителей.
Комментарии для переводчиков
Если вы хотите дать переводчикам подсказки о переводимой строке, вы можете добавить комментарий с префиксом Translators в строке перед строкой, например:
def my_view(request):
# Translators: This message appears on the home page only
output = ugettext("Welcome to my site.")
Затем комментарий отобразится в результирующем .po файле, связанном с переводимым блоком ниже, и должен быть отображен большинством инструментов перевода.
Примечание
Для полноты, это соответствующий фрагмент результирующего .po файла:
#. Translators: This message appears on the home page only # path/to/python/file.py:123 msgid "Welcome to my site." msgstr ""
Это также работает в шаблонах. Подробнее см. Комментарии для переводчиков в шаблонах.
Отмечание строк как no-op
Используйте функцию django.utils.translation.ugettext_noop() для обозначения строки как строки перевода без её перевода. Позже строка будет переведена из переменной.
Используйте это, если у вас есть неизменные строки, которые должны храниться на языке источника, потому что они обмениваются между системами или пользователями (например, строки в базе данных), но должны быть переведены в последний возможный момент, например, когда строка представляется пользователю.
Множественное число
Используйте функцию django.utils.translation.ungettext() для задания сообщений множественного числа.
ungettext принимает три аргумента: строку перевода единственного числа, строку перевода множественного числа и количество объектов.
Эта функция полезна, когда вам нужно, чтобы ваше приложение Django было локализовано на языках, где количество и сложность форм множественного числа больше, чем две формы, используемые в английском языке («объект» для единственного числа и «объекты» для всех случаев, когда count отличается от единицы, независимо от его значения).
Например:
from django.utils.translation import ungettext
from django.http import HttpResponse
def hello_world(request, count):
page = ungettext(
'there is %(count)d object',
'there are %(count)d objects',
count) % {
'count': count,
}
return HttpResponse(page)
В этом примере количество объектов передаётся языкам перевода как переменная count.
Обратите внимание, что множественное число сложно и работает по-разному на каждом языке. Сравнение count с 1 не всегда является правильным правилом. Этот код выглядит сложным, но даст неверные результаты для некоторых языков:
from django.utils.translation import ungettext
from myapp.models import Report
count = Report.objects.count()
if count == 1:
name = Report._meta.verbose_name
else:
name = Report._meta.verbose_name_plural
text = ungettext(
'There is %(count)d %(name)s available.',
'There are %(count)d %(name)s available.',
count
) % {
'count': count,
'name': name
}
Не пытайтесь реализовать собственную логику единственного или множественного числа, это не будет правильно. В таком случае подумайте о чём-то вроде следующего:
text = ungettext(
'There is %(count)d %(name)s object available.',
'There are %(count)d %(name)s objects available.',
count
) % {
'count': count,
'name': Report._meta.verbose_name,
}
Примечание
При использовании ungettext(), убедитесь, что вы используете одно имя для каждой экстрагированной переменной, включённой в литерал. В приведённых выше примерах обратите внимание, как мы использовали переменную Python name в обеих строках перевода. Этот пример, помимо того, что является неверным на некоторых языках, как указано выше, не сработает:
text = ungettext(
'There is %(count)d %(name)s available.',
'There are %(count)d %(plural_name)s available.',
count
) % {
'count': Report.objects.count(),
'name': Report._meta.verbose_name,
'plural_name': Report._meta.verbose_name_plural
}
При запуске django-admin
compilemessages вы получите ошибку:
a format specification for argument 'name', as in 'msgstr[0]', doesn't exist in 'msgid'
Примечание
Форма множественного числа и файлы po
Django не поддерживает пользовательские уравнения для множественного числа в файлах po. Так как все каталоги переводов объединяются, учитывается только форма множественного числа для основного файла po Django (в django/conf/locale/<lang_code>/LC_MESSAGES/django.po). Формы множественного числа во всех других файлах po игнорируются. Следовательно, вы не должны использовать разные уравнения для множественного числа в файлах po вашего проекта или приложения.
Контекстные маркеры
Иногда слова имеют несколько значений, например, "May" на английском языке, которое относится к имени месяца и глаголу. Чтобы позволить переводчикам правильно переводить эти слова в разных контекстах, вы можете использовать функцию django.utils.translation.pgettext() или функцию django.utils.translation.npgettext(), если строка требует множественного числа. Оба принимают строку контекста как первую переменную.
В результирующем .po файле строка будет появляться столько раз, сколько существует различных контекстных маркеров для одной строки (контекст будет на msgctxt строке), что позволит переводчику предоставить другой перевод для каждого из них.
Например:
from django.utils.translation import pgettext
month = pgettext("month name", "May")
или:
from django.db import models
from django.utils.translation import pgettext_lazy
class MyThing(models.Model):
name = models.CharField(help_text=pgettext_lazy(
'help text for MyThing model', 'This is the help text'))
будут отображены в .po файле как:
msgctxt "month name" msgid "May" msgstr ""
Контекстные маркеры также поддерживаются тегами шаблона trans и blocktrans.
Отложенный перевод
Используйте ленивые версии функций перевода в django.utils.translation (легко распознаваемые по суффиксу lazy в их именах), чтобы переводить строки лениво – когда значение обращается к ним, а не когда они вызываются.
Эти функции хранят леничную ссылку на строку – а не фактический перевод. Сам перевод будет выполнен, когда строка будет использована в контексте строки, например, при рендеринге шаблона.
Это необходимо, когда вызовы этих функций находятся в пути кода, который выполняется во время загрузки модуля.
Это может легко произойти при определении моделей, форм и форм моделей, потому что Django реализует их таким образом, что их поля являются атрибутами уровня класса. По этой причине используйте ленивые переводы в следующих случаях:
Поля моделей и связи verbose_name и help_text значения опций
Например, чтобы перевести подсказку поля name в следующей модели, сделайте следующее:
from django.db import models
from django.utils.translation import ugettext_lazy as _
class MyThing(models.Model):
name = models.CharField(help_text=_('This is the help text'))
Вы можете пометить имена ForeignKey, ManyToManyField или OneToOneField связи как переводимые, используя их verbose_name опции:
class MyThing(models.Model):
kind = models.ForeignKey(
ThingKind,
on_delete=models.CASCADE,
related_name='kinds',
verbose_name=_('kind'),
)
Так же, как вы делаете в verbose_name, вы должны предоставить текстовое значение verbose name в нижнем регистре, так как Django автоматически приведёт его к верхнему регистру, когда это необходимо.
Значения verbose name моделей
Рекомендуется всегда предоставлять явные verbose_name и verbose_name_plural опции, а не полагаться на английский язык по умолчанию и несколько наивное определение verbose name, которое Django выполняет, глядя на имя класса модели:
from django.db import models
from django.utils.translation import 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 (объект с типом unicode) в Python. Если вы попытаетесь использовать его там, где ожидается байтовая строка (объект str), то всё не будет работать как ожидается, так как объект ugettext_lazy() не знает, как преобразовать себя в байтовую строку. Вы также не можете использовать строку unicode внутри байтовой строки, что соответствует обычному поведению Python. Например:
# This is fine: putting a unicode proxy into a unicode string.
"Hello %s" % ugettext_lazy("people")
# This will not work, since you cannot insert a unicode object
# into a bytestring (nor can you insert our unicode proxy there)
b"Hello %s" % ugettext_lazy("people")
Если вы когда-либо увидите вывод, похожий на "hello
<django.utils.functional...>", то вы попытались вставить результат ugettext_lazy() в байтовую строку. Это ошибка в вашем коде.
Если вам не нравится длинное имя ugettext_lazy, вы можете просто переименовать его как _ (подчёркивание), как показано ниже:
from django.db import models
from django.utils.translation import ugettext_lazy as _
class MyThing(models.Model):
name = models.CharField(help_text=_('This is the help text'))
Использование ugettext_lazy() и ungettext_lazy() для маркировки строк в моделях и служебных функциях – это распространённая операция. Когда вы работаете с этими объектами в другом месте в вашем коде, убедитесь, что вы случайно не преобразуете их в строки, потому что они должны быть преобразованы как можно позже (чтобы текущий язык был активен). Это требует использования описанной ниже вспомогательной функции.
Ленивые переводы и множественное число
При использовании ленивого перевода для строки множественного числа ([u]n[p]gettext_lazy) вы обычно не знаете аргумент number на момент определения строки. Поэтому вы можете передать имя ключа вместо целого числа в качестве аргумента number. Затем number будет найдено в словаре по этому ключу во время интерполяции строки. Вот пример:
from django import forms
from django.utils.translation import ungettext_lazy
class MyForm(forms.Form):
error_message = ungettext_lazy("You only provided %(num)d argument",
"You only provided %(num)d arguments", 'num')
def clean(self):
# ...
if error:
raise forms.ValidationError(self.error_message % {'num': number})
Если строка содержит ровно один неопределённый заполнитель, вы можете выполнить интерполяцию напрямую с помощью аргумента number:
class MyForm(forms.Form):
error_message = ungettext_lazy(
"You provided %d argument",
"You provided %d arguments",
)
def clean(self):
# ...
if error:
raise forms.ValidationError(self.error_message % number)
Объединение строк: string_concat()
Стандартные объединения строк Python (''.join([...])) не будут работать со списками, содержащими объекты ленивого перевода. Вместо этого вы можете использовать django.utils.translation.string_concat(), который создаёт ленивый объект, объединяющий содержимое и преобразующий его в строки только тогда, когда результат включён в строку. Например:
from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy
...
name = ugettext_lazy('John Lennon')
instrument = ugettext_lazy('guitar')
result = string_concat(name, ': ', instrument)
В этом случае ленивые переводы в result будут преобразованы в строки только тогда, когда сам result используется в строке (обычно во время рендеринга шаблона).
Другие случаи использования 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. Аналогичный доступ к этой информации доступен для кода шаблонов. См. ниже.
Атрибут 'name_translated' был добавлен.
Локализация: в коде шаблонов
Переводы в шаблонах 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 }}">
На практике вы будете использовать это, чтобы получить строку, которую можно использовать в нескольких местах в шаблоне, или чтобы использовать результат в качестве аргумента для других тегов или фильтров шаблона.
Добавлен синтаксис asvar.
{% 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 как определено выше.
Процессор контекста i18n по умолчанию не включён для новых проектов.
get_language_info
Вы также можете получить информацию о любом из доступных языков, используя предоставленные теги и фильтры шаблона. Чтобы получить информацию об одном языке, используйте тег {% get_language_info %}:
{% get_language_info for LANGUAGE_CODE as lang %}
{% get_language_info for "pl" as lang %}
Затем вы можете получить доступ к информации:
Language code: {{ lang.code }}<br />
Name of language: {{ lang.name_local }}<br />
Name in English: {{ lang.name }}<br />
Bi-directional: {{ lang.bidi }}
Name in the active language: {{ lang.name_translated }}
Атрибут 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», когда активный язык чешский)
Фильтр language_name_translated был добавлен.
Локализация в коде JavaScript
Добавление переводов в JavaScript ставит некоторые проблемы:
- Код JavaScript не имеет доступа к реализации
gettext. - Код JavaScript не имеет доступа к файлам
.poили.mo; их необходимо передать сервером. - Каталоги переводов для JavaScript должны быть максимально компактными.
Django предоставляет интегрированное решение для этих проблем: он передаёт переводы в JavaScript, так что вы можете вызывать gettext и т. д. изнутри JavaScript.
Представление каталога JavaScript
-
javascript_catalog(request, domain='djangojs', packages=None)[source]
Основное решение этих проблем — представление django.views.i18n.javascript_catalog(), которое отправляет JavaScript-библиотеку кода с функциями, имитирующими интерфейс gettext, плюс массив строк перевода. Эти строки перевода берутся из приложений или ядра Django в соответствии с тем, что вы указываете в info_dict или в URL. Пути, перечисленные в LOCALE_PATHS, также включаются.
Вы подключаете его так:
from django.views.i18n import javascript_catalog
js_info_dict = {
'packages': ('your.app.package',),
}
urlpatterns = [
url(r'^jsi18n/$', javascript_catalog, js_info_dict, name='javascript-catalog'),
]
Каждая строка в packages должна быть в синтаксисе имён пакетов Python (тот же формат, что и в строках в INSTALLED_APPS) и должна ссылаться на пакет, содержащий каталог locale. Если вы указываете несколько пакетов, все эти каталоги объединяются в один каталог. Это полезно, если у вас есть JavaScript, использующий строки из разных приложений.
Порядок приоритетов переводов таков, что пакеты, появляющиеся позже в аргументе packages имеют больший приоритет, чем те, которые появляются в начале, это важно в случае конфликтующих переводов для одной и той же строки.
По умолчанию представление использует домен 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 на сайте, и некоторые из них определяют одни и те же строки, строки в каталоге, который был загружен последним, имеют больший приоритет.
Перед Django 1.9 каталоги полностью перезаписывали друг друга, и вы могли использовать только один за раз.
Переводы 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местаholders в том же порядке, в котором они появляются. Например: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 (не следует склонять).
Представление каталога в формате JSON
-
json_catalog(request, domain='djangojs', packages=None)[source]
Чтобы использовать другую библиотеку для обработки переводов на стороне клиента, вы можете воспользоваться представлением json_catalog(). Оно похоже на javascript_catalog(), но возвращает ответ в формате JSON.
Объект JSON содержит параметры форматирования i18n (доступные для get_format), правило склонения (как часть GNU gettext plural выражения) и строки перевода. Строки перевода берутся из приложений или собственных переводов 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_catalog() генерирует каталог из файлов .mo при каждом запросе. Поскольку его вывод является постоянным (по крайней мере, для данной версии сайта), это хороший кандидат для кеширования.
Кэширование на стороне сервера снизит нагрузку на процессор. Его легко реализовать с помощью декоратора cache_page(). Чтобы инициировать обновление кеша при изменении переводов, укажите префикс ключа, зависящий от версии, как показано в примере ниже, или сопоставьте представление с URL-адресом, зависящим от версии:
from django.views.decorators.cache import cache_page
from django.views.i18n import javascript_catalog
# The value returned by get_version() must change when translations change.
@cache_page(86400, key_prefix='js18n-%s' % get_version())
def cached_javascript_catalog(request, domain='djangojs', packages=None):
return javascript_catalog(request, domain, packages)
Кэширование на стороне клиента сэкономит трафик и ускорит загрузку вашего сайта. Если вы используете ETags (USE_ETAGS = True), это уже реализовано. В противном случае вы можете применить условные декораторы. В следующем примере кэш обновляется при каждом перезапуске сервера приложения:
from django.utils import timezone
from django.views.decorators.http import last_modified
from django.views.i18n import javascript_catalog
last_modified_date = timezone.now()
@last_modified(lambda req, **kw: last_modified_date)
def cached_javascript_catalog(request, domain='djangojs', packages=None):
return javascript_catalog(request, domain, packages)
Вы даже можете предварительно сгенерировать каталог JavaScript как часть вашей процедуры развертывания и предоставить его как статический файл. Эта радикальная техника реализована в django-statici18n.
Международная локализация URL-адресов
Django предоставляет два механизма для локализации URL-адресов:
- Добавление префикса языка в корень URL-адресов, чтобы
LocaleMiddlewareмог определить язык для активации из запрошенного URL-адреса. - Осуществление перевода самих URL-адресов с помощью функции
django.utils.translation.ugettext_lazy().
Предупреждение
Использование этих функций требует, чтобы для каждого запроса был установлен активный язык; другими словами, вам необходимо иметь django.middleware.locale.LocaleMiddleware в настройке MIDDLEWARE_CLASSES.
Префикс языка в шаблонах URL
-
i18n_patterns(prefix, pattern_description, ...)[source]
Устаревшее начиная с версии 1.8: Аргумент prefix для функции i18n_patterns() устарел и не будет поддерживаться в Django 1.10. Просто передайте список экземпляров django.conf.urls.url() вместо него.
Эта функция может быть использована в вашем основном URL-конфигураторе, и Django автоматически добавит код текущего активного языка в начало всех URL-шаблонов, определённых в i18n_patterns(). Пример URL-шаблонов:
from django.conf.urls import include, url
from django.conf.urls.i18n import i18n_patterns
from about import views as about_views
from news import views as news_views
from sitemap.views import sitemap
urlpatterns = [
url(r'^sitemap\.xml$', sitemap, name='sitemap-xml'),
]
news_patterns = ([
url(r'^$', news_views.index, name='index'),
url(r'^category/(?P<slug>[\w-]+)/$', news_views.category, name='category'),
url(r'^(?P<slug>[\w-]+)/$', news_views.details, name='detail'),
], '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.core.urlresolvers import reverse
>>> from django.utils.translation import activate
>>> activate('en')
>>> reverse('sitemap-xml')
'/sitemap.xml'
>>> reverse('news:index')
'/en/news/'
>>> activate('nl')
>>> reverse('news:detail', kwargs={'slug': 'news-slug'})
'/nl/news/news-slug/'
Предупреждение
i18n_patterns() разрешена только в главном URL-конфигураторе. Использование её внутри включённого URL-конфигуратора вызовет исключение ImproperlyConfigured.
Предупреждение
Убедитесь, что у вас нет URL-шаблонов без префикса, которые могут конфликтовать с автоматически добавленным префиксом языка.
Перевод URL-шаблонов
URL-шаблоны также могут быть помечены как переводимые с помощью функции ugettext_lazy(). Пример:
from django.conf.urls import include, url
from django.conf.urls.i18n import i18n_patterns
from django.utils.translation import ugettext_lazy as _
from about import views as about_views
from news import views as news_views
from sitemaps.views import sitemap
urlpatterns = [
url(r'^sitemap\.xml$', sitemap, name='sitemap-xml'),
]
news_patterns = ([
url(r'^$', news_views.index, name='index'),
url(_(r'^category/(?P<slug>[\w-]+)/$'), news_views.category, name='category'),
url(r'^(?P<slug>[\w-]+)/$', news_views.details, name='detail'),
], 'news')
urlpatterns += i18n_patterns(
url(_(r'^about/$'), about_views.main, name='about'),
url(_(r'^news/'), include(news_patterns, namespace='news')),
)
После создания переводов функция reverse() вернёт URL на активном языке. Пример:
>>> from django.core.urlresolvers import reverse
>>> from django.utils.translation import activate
>>> activate('en')
>>> reverse('news:category', kwargs={'slug': 'recent'})
'/en/news/category/recent/'
>>> activate('nl')
>>> reverse('news:category', kwargs={'slug': 'recent'})
'/nl/nieuws/categorie/recent/'
Предупреждение
В большинстве случаев лучше использовать переведённые URL-адреса только в блоке шаблонов с префиксом кода языка (используя i18n_patterns()), чтобы избежать возможности того, что небрежно переведённый URL-адрес вызовет конфликт с непроведённым URL-шаблоном.
Обратное преобразование в шаблонах
Если локализованные URL-адреса преобразуются в шаблонах, они всегда используют текущий язык. Чтобы связать URL-адрес с другим языком, используйте тег шаблона language. Он включает указанный язык в заключённой секции шаблона:
{% load i18n %}
{% get_available_languages as languages %}
{% trans "View this category in:" %}
{% for lang_code, lang_name in languages %}
{% language lang_code %}
<a href="{% url 'category' slug=category.slug %}">{{ lang_name }}</a>
{% endlanguage %}
{% endfor %}
Тег language ожидает код языка в качестве единственного аргумента.
Локализация: как создать файлы языка
После того, как строковые литералы приложения были помечены для последующего перевода, сами переводы нужно написать (или получить). Вот как это работает.
Файлы сообщений
Первый шаг – создание файла сообщений для нового языка. Файл сообщений – это текстовый файл, представляющий один язык, который содержит все доступные строки перевода и то, как они должны быть представлены на данном языке. Файлы сообщений имеют расширение .po.
Django поставляется с инструментом django-admin makemessages, который автоматизирует создание и поддержку этих файлов.
Утилиты gettext
Команда makemessages (и compilemessages далее) используют команды из набора инструментов GNU gettext: xgettext, msgfmt, msgmerge и msguniq.
Минимальная поддерживаемая версия утилит gettext – 0.15.
Для создания или обновления файла сообщений выполните эту команду:
django-admin makemessages -l de
… где de – имя локали для файла сообщений, который вы хотите создать. Например, pt_BR для бразильского португальского, de_AT для австрийского немецкого или id для индонезийского.
Скрипт должен запускаться из одного из двух мест:
- Главный каталог вашего проекта Django (который содержит
manage.py). - Главный каталог одного из ваших приложений Django.
Скрипт выполняется над вашим деревом проекта или вашим деревом приложения и извлекает все строки, помеченные для перевода (см. Как Django обнаруживает переводы и убедитесь, что LOCALE_PATHS правильно настроен). Он создаёт (или обновляет) файл сообщений в каталоге locale/LANG/LC_MESSAGES. В примере de, файл будет locale/de/LC_MESSAGES/django.po.
При запуске makemessages из каталога корня проекта извлеченные строки автоматически распределяются по соответствующим файлам сообщений. То есть, строка, извлечённая из файла приложения, содержащего каталог locale , попадет в файл сообщений в этом каталоге. Строка, извлечённая из файла приложения без каталога locale , либо попадет в файл сообщений в каталоге, указанном первым в LOCALE_PATHS, либо сгенерирует ошибку, если LOCALE_PATHS пуст.
По умолчанию django-admin makemessages проверяет каждый файл, который имеет расширение .html или .txt. В случае, если вы хотите переопределить это значение по умолчанию, используйте параметр --extension или -e для указания расширений файлов для проверки:
django-admin makemessages -l de -e txt
Разделяйте несколько расширений запятыми и/или используйте -e или --extension несколько раз:
django-admin makemessages -l de -e html,txt -e xml
Предупреждение
При создании файлов сообщений из исходного кода JavaScript вам необходимо использовать специальный домен «djangojs», а не -e js.
Используете шаблоны Jinja2?
makemessages не понимает синтаксис шаблонов Jinja2. Для извлечения строк из проекта, содержащего шаблоны Jinja2, используйте Извлечение сообщений из Babel вместо этого.
Вот пример babel.cfg файла конфигурации:
# Extraction from Python source files [python: **.py] # Extraction from Jinja2 templates [jinja2: **.jinja] extensions = jinja2.ext.with_
Убедитесь, что вы указали все используемые расширения! В противном случае Babel не распознает теги, определённые этими расширениями, и полностью проигнорирует шаблоны Jinja2, содержащие их.
Babel предоставляет аналогичные возможности makemessages, может заменить его в целом и не зависит от gettext. Для получения дополнительной информации ознакомьтесь с документацией о работе с каталогами сообщений.
Нет утилит gettext?
Если у вас не установлены утилиты gettext , makemessages создаст пустые файлы. В этом случае установите утилиты gettext или просто скопируйте файл сообщений на английском языке (locale/en/LC_MESSAGES/django.po) , если он доступен, и используйте его в качестве отправной точки; это просто пустой файл перевода.
Работа на Windows?
Если вы используете Windows и вам нужно установить утилиты GNU gettext, чтобы makemessages работал, см. gettext на Windows для получения дополнительной информации.
Формат файлов .po прост. Каждый файл .po содержит небольшую часть метаданных, таких как контактная информация переводчика, но основная часть файла – это список сообщений – простые соответствия между строками перевода и фактическим переведённым текстом для конкретного языка.
Например, если ваше приложение Django содержало строку перевода для текста "Welcome to my site.", как показано ниже:
_("Welcome to my site.")
…тогда django-admin makemessages создаст файл .po , содержащий следующий фрагмент – сообщение:
#: path/to/python/module.py:23 msgid "Welcome to my site." msgstr ""
Небольшое пояснение:
-
msgidявляется строкой перевода, которая отображается в исходном коде. Не изменяйте её. -
msgstr— место, куда вы помещаете перевод, соответствующий языку. Поначалу оно пустое, поэтому вам нужно его изменить. Убедитесь, что вы сохранили кавычки вокруг вашего перевода. - Для удобства каждая строка сообщения включает, в виде комментария, начинающегося с
#, и расположенного над строкойmsgid, имя файла и номер строки, откуда была взята строка перевода.
Длинные сообщения — это особый случай. Там первая строка сразу после msgstr (или msgid) является пустой строкой. Затем содержимое будет написано на следующих нескольких строках как одна строка на строке. Эти строки будут непосредственно конкатенированы. Не забывайте о trailing пробелах внутри строк; в противном случае они будут склеены без пробелов!
Учтите кодировку
Из-за того, как работают инструменты gettext, и потому что мы хотим разрешить строки исходного кода, не являющиеся ASCII, в ядре Django и ваших приложениях, вы обязательно должны использовать UTF-8 в качестве кодировки для ваших файлов PO (это значение по умолчанию при создании файлов PO). Это означает, что все будут использовать одну и ту же кодировку, что важно при обработке файлов PO Django.
Для повторного анализа всего исходного кода и шаблонов на предмет новых строк перевода и обновления всех файлов сообщений для всех языков выполните следующую команду:
django-admin makemessages -a
Компиляция файлов сообщений
После создания файла сообщений — и каждый раз, когда вы вносите в него изменения — вам нужно будет скомпилировать его в более эффективный формат для использования gettext. Сделайте это с помощью утилиты django-admin compilemessages.
Этот инструмент проходит по всем доступным .po файлам и создаёт .mo файлы, которые являются двоичными файлами, оптимизированными для использования gettext. В той же директории, из которой вы запустили django-admin makemessages, запустите django-admin compilemessages следующим образом:
django-admin compilemessages
Всё. Ваши переводы готовы к использованию.
compilemessages теперь соответствует действию makemessages, сканируя дерево проекта в поисках .po файлов для компиляции.
Работаете в Windows?
Если вы используете Windows и вам нужно установить утилиты GNU gettext, чтобы django-admin compilemessages работала, ознакомьтесь с gettext в Windows для получения дополнительной информации.
.po файлы: кодировка и использование BOM.
Django поддерживает только .po файлы, закодированные в UTF-8 и без BOM (Byte Order Mark), поэтому если ваш текстовый редактор по умолчанию добавляет такие метки в начало файлов, вам потребуется его перенастроить.
Создание файлов сообщений из исходного кода JavaScript
Вы создаёте и обновляете файлы сообщений так же, как и другие файлы сообщений Django — с помощью инструмента django-admin makemessages. Единственное отличие состоит в том, что вам нужно явно указать то, что в терминологии gettext называется областью (domain) — в данном случае 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
-
set_language(request)[source]
Для удобства Django поставляется вид django.views.i18n.set_language(), который устанавливает языковые предпочтения пользователя и перенаправляет его на заданный URL или, по умолчанию, на предыдущую страницу.
Активируйте этот вид, добавив следующую строку в ваш URLconf:
url(r'^i18n/', include('django.conf.urls.i18n')),
(Обратите внимание, что в данном примере вид доступен по адресу /i18n/setlang/.)
Предупреждение
Убедитесь, что вы не включаете вышеуказанный URL внутри i18n_patterns() — он сам должен быть независимым от языка для правильной работы.
Вид ожидает вызова через метод POST, с параметром language установленным в request. Если поддержка сеансов включена, вид сохраняет выбор языка в сеансе пользователя. В противном случае он сохраняет выбор языка в cookie, по умолчанию с именем django_language. (Имя может быть изменено через настройку LANGUAGE_COOKIE_NAME.)
После установки выбора языка Django перенаправляет пользователя, следуя этому алгоритму:
- Django ищет параметр
nextв данныхPOST. - Если он отсутствует или пустой, Django пытается получить URL из заголовка
Referrer. - Если он пустой — например, если браузер пользователя подавляет этот заголовок — то пользователь будет перенаправлен на
/(корень сайта) в качестве резервного варианта.
Вот пример кода HTML-шаблона:
{% load i18n %}
<form action="{% url 'set_language' %}" method="post">{% csrf_token %}
<input name="next" type="hidden" value="{{ redirect_to }}" />
<select name="language">
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %}
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected="selected"{% endif %}>
{{ language.name_local }} ({{ language.code }})
</option>
{% endfor %}
</select>
<input type="submit" value="Go" />
</form>
В этом примере Django ищет URL страницы, на которую будет перенаправлен пользователь, в переменной контекста redirect_to.
Явное задание активного языка
Вам может потребоваться явно установить активный язык для текущего сеанса. Возможно, например, предпочтение языка пользователя извлекается из другой системы. Вы уже познакомились с django.utils.translation.activate(). Это применяется только к текущей нити. Чтобы сохранить язык для всего сеанса, также измените LANGUAGE_SESSION_KEY в сеансе:
from django.utils import translation user_language = 'fr' translation.activate(user_language) request.session[translation.LANGUAGE_SESSION_KEY] = user_language
Обычно вам нужно использовать оба: django.utils.translation.activate() изменит язык для этой нити, а изменение сеанса сохранит это предпочтение в будущих запросах.
Если вы не используете сеансы, язык будет сохраняться в cookie, имя которого настроено в LANGUAGE_COOKIE_NAME. Например:
from django.utils import translation from django import http from django.conf import settings user_language = 'fr' translation.activate(user_language) response = http.HttpResponse(...) response.set_cookie(settings.LANGUAGE_COOKIE_NAME, user_language)
Использование переводов за пределами представлений и шаблонов
Хотя Django предоставляет богатый набор инструментов 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 и языка, установленного middleware.
Функции, представляющие особый интерес, это django.utils.translation.get_language(), которая возвращает язык, используемый в текущей нити, django.utils.translation.activate(), которая активирует каталог перевода для текущей нити, и django.utils.translation.check_for_language(), которая проверяет, поддерживается ли данный язык Django.
Для написания более лаконичного кода также есть менеджер контекста django.utils.translation.override(), который сохраняет текущий язык при входе и восстанавливает его при выходе. С его помощью вышеприведенный пример становится:
from django.utils import translation
def welcome_translated(language):
with translation.override(language):
return translation.ugettext('welcome')
Файл cookie языка
Несколько настроек могут быть использованы для изменения параметров файла cookie языка:
Примечания по реализации
Особенности перевода 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_CLASSES. Поскольку порядок средневого программного обеспечения имеет значение, следует руководствоваться этими указаниями:
- Убедитесь, что это один из первых установленных средневых программных обеспечений.
- Он должен следовать за
SessionMiddleware, так какLocaleMiddlewareиспользует данные сеанса. И он должен предшествоватьCommonMiddlewareпотому чтоCommonMiddlewareнуждается в активированном языке для разрешения запрошенного URL. - Если вы используете
CacheMiddleware, поместитеLocaleMiddlewareпосле него.
Например, ваша настройка MIDDLEWARE_CLASSES может выглядеть так:
MIDDLEWARE_CLASSES = [ 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.locale.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', ]
(Дополнительную информацию о средневом программном обеспечении см. в документации по средневому программному обеспечению.)
LocaleMiddleware пытается определить предпочтение языка пользователя, следуя этому алгоритму:
- Сначала он ищет префикс языка в запрошенном URL. Это выполняется только в том случае, если вы используете функцию
i18n_patternsв вашем корневом URL-конфигурации. См. Международная локализация: в шаблонах URL для получения дополнительной информации о префиксе языка и о том, как выполнить международную локализация шаблонов URL. - В противном случае он ищет ключ
LANGUAGE_SESSION_KEYв текущем сеансе пользователя. -
В противном случае он ищет файл cookie.
Имя используемого файла cookie задается настройкой
LANGUAGE_COOKIE_NAME. (По умолчанию имя —django_language.) - В противном случае он смотрит на заголовок HTTP
Accept-Language. Этот заголовок отправляется вашим браузером и сообщает серверу, какой язык(и) вы предпочитаете, в порядке приоритета. Django пробует каждый язык в заголовке, пока не найдёт язык с доступными переводами. - В противном случае он использует глобальную настройку
LANGUAGE_CODE.
Примечания:
- В каждом из этих мест предпочтение языка ожидается в стандартном формате формата кода языка, как строка. Например, бразильское португальское —
pt-br. - Если базовый язык доступен, но указанный подязык отсутствует, Django использует базовый язык. Например, если пользователь указывает
de-at(австрийский немецкий), но у Django доступен толькоde, Django используетde. -
Доступны только языки, перечисленные в настройке
LANGUAGES. Если вы хотите ограничить выбор языка подмножеством предоставленных языков (потому что ваше приложение не предоставляет все языки), установитеLANGUAGESв список языков. Например:LANGUAGES = [ ('de', _('German')), ('en', _('English')), ]Этот пример ограничивает доступные для автоматического выбора языки немецким и английским (и любыми подязыками, такими как de-ch или en-us).
-
Если вы определите пользовательскую настройку
LANGUAGES, как описано в предыдущем пункте, вы можете отметить названия языков как строки перевода — но используйтеugettext_lazy()вместоugettext(), чтобы избежать циклического импорта.Вот пример файла настроек:
from django.utils.translation import ugettext_lazy as _ LANGUAGES = [ ('de', _('German')), ('en', _('English')), ]
После того, как LocaleMiddleware определит предпочтение пользователя, это предпочтение становится доступным как request.LANGUAGE_CODE для каждого HttpRequest. Вы можете прочитать это значение в вашем коде представления. Вот простой пример:
from django.http import HttpResponse
def hello_world(request, count):
if request.LANGUAGE_CODE == 'de-at':
return HttpResponse("You prefer to read Austrian German.")
else:
return HttpResponse("You prefer to read another language.")
Обратите внимание, что при статическом (без средневого программного обеспечения) переводе язык находится в settings.LANGUAGE_CODE, а при динамическом (с помощью средневого программного обеспечения) — в request.LANGUAGE_CODE.
Как Django определяет переводы
Во время выполнения Django создает объединенный кэш переводов в оперативной памяти. Для этого он ищет переводы, следуя этому алгоритму, касающемуся порядка, в котором он проверяет различные пути к файлам для загрузки скомпилированных файлов сообщений (.mo) и приоритета нескольких переводов для одного и того же слова:
- Директории, перечисленные в
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 Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.9/topics/i18n/translation/