Перевод
Обзор
Для того, чтобы сделать проект Django переводимым, вам нужно добавить минимальное количество крючков в свой Python-код и шаблоны. Эти крючки называются строками перевода. Они сообщают Django: «Этот текст должен быть переведен на язык конечного пользователя, если перевод этого текста доступен на этом языке». Вам нужно отметить переводимые строки; система может переводить только известные ей строки.
Затем Django предоставляет утилиты для извлечения строк перевода в файл сообщений. Этот файл является удобным способом для переводчиков предоставить эквивалент строк перевода на целевой язык. После того, как переводчики заполнят файл сообщений, его необходимо скомпилировать. Этот процесс зависит от набора инструментов GNU gettext.
После этого Django позаботится о переводе веб-приложений на лету на каждом доступном языке в соответствии с предпочтениями пользователей.
Крючки международной локализации Django включены по умолчанию, а это означает, что в некоторых местах фреймворка присутствует небольшая нагрузка, связанная с международной локализацией. Если вы не используете международную локализацию, вам нужно потратить две секунды на установку 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, а не полагаться на унаследованное, ориентированное на английский язык и несколько наивное определение 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 (объект 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)
Объединение строк: 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, как определено выше.
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 }}(Ложь) -
{{ LANGUAGE_CODE|language_name_translated }}(«německy», когда активный язык — чешский)
Был добавлен фильтр language_name_translated.
Международная локализация: в коде 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
-
javascript_catalog(request, domain='djangojs', packages=None)[source]
Устарело начиная с версии 1.10: javascript_catalog() устарело и будет удалено в Django 2.0. Рекомендуется использовать JavaScriptCatalog.
Основным решением этих проблем является просмотр 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заполнительные места в том же порядке, в котором они появляются. Например: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 ), и строки переводов. Строки переводов берутся из приложений или собственных переводов 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 (USE_ETAGS = True), вы уже покрыты. В противном случае, вы можете применить условные декораторы. В следующем примере кэш инвалидируется всякий раз, когда вы перезапускаете сервер приложения:
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 на другом языке используйте тег шаблона 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) — пустая строка. Затем содержимое будет записано в следующих нескольких строках как по одной строке на каждое значение. Эти строки будут соединены непосредственно. Не забывайте о пробелах в строках; в противном случае они будут объединены без пробелов!
Учитывайте кодировку
Из-за того, как утилиты 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
Всё готово. Ваши переводы готовы к использованию.
compilemessages теперь соответствует работе makemessages, сканируя проектную древовидную структуру на предмет файлов .po для компиляции.
Работа на 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
-
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="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 языка
Примечания по реализации
Особенности перевода Django
Механизм перевода Django использует стандартный модуль gettext, который поставляется с Python. Если вы знаете gettext, вы можете заметить эти особенности в способе, которым Django выполняет перевод:
- Домен строки —
djangoилиdjangojs. Этот строковый домен используется для различения различных программ, хранящих свои данные в общей библиотеке файлов сообщений (обычно/usr/share/locale/). Доменdjangoиспользуется для строк переводов Python и шаблонов и загружается в глобальные каталоги переводов. Доменdjangojsиспользуется только для каталогов переводов JavaScript, чтобы гарантировать, что они будут максимально компактными. - Django не использует
xgettextв одиночку. Он использует оболочки Python вокругxgettextиmsgfmt. Это в основном сделано для удобства.
Как Django определяет предпочтения языка
После подготовки переводов (или если вы просто хотите использовать переводы, которые поставляются с Django) вам нужно только активировать перевод для вашего приложения.
На заднем плане Django имеет очень гибкую модель определения языка, который должен использоваться: для всего приложения, для конкретного пользователя или для обоих.
Чтобы установить глобальное предпочтение языка для всего приложения, установите LANGUAGE_CODE. Django использует этот язык в качестве языка по умолчанию для перевода — последнюю попытку, если не будет найдено более подходящего перевода с помощью одного из методов, используемых средством локализации (см. ниже).
Если вам нужно просто запустить Django на родном языке, вам нужно установить LANGUAGE_CODE и убедиться, что соответствующие файлы сообщений и их скомпилированные версии (.mo) существуют.
Если вы хотите, чтобы каждый пользователь мог указать предпочтительный язык, то вам также нужно использовать LocaleMiddleware. LocaleMiddleware позволяет выбирать язык на основе данных из запроса. Это позволяет персонализировать содержимое для каждого пользователя.
Чтобы использовать LocaleMiddleware, добавьте 'django.middleware.locale.LocaleMiddleware' в настройку MIDDLEWARE. Поскольку порядок промежуточного ПО важен, следуйте этим рекомендациям:
- Убедитесь, что это один из первых установленных модулей.
- Он должен следовать за
SessionMiddleware, потому чтоLocaleMiddlewareиспользует данные сеанса. И он должен предшествоватьCommonMiddleware, потому чтоCommonMiddlewareтребует активированного языка для разрешения запрошенного URL. - Если вы используете
CacheMiddleware, поместитеLocaleMiddlewareпосле него.
Например, ваша настройка MIDDLEWARE может выглядеть так:
MIDDLEWARE = [ 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.locale.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', ]
(Для получения дополнительной информации о промежуточном ПО см. документацию по промежуточному ПО.)
LocaleMiddleware пытается определить предпочтения языка пользователя, следуя этому алгоритму:
- Во-первых, он ищет префикс языка в запрошенном URL. Это выполняется только при использовании функции
i18n_patternsв вашей корневой URL-конфигурации. См. Международная локализация: в шаблонах URL для получения дополнительной информации о префиксе языка и о том, как локализовать URL-шаблоны. - В противном случае он ищет ключ
LANGUAGE_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, используемых gettext, используется django-admin compilemessages.
Вы также можете запустить 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.10/topics/i18n/translation/