Перевод

Обзор

Чтобы сделать проект Django переводимым, вам нужно добавить минимальное количество хуков в свой код и шаблоны Python. Эти перехватчики называются строками перевода . Они говорят Django: «Этот текст должен быть переведен на язык конечного пользователя, если перевод для этого текста доступен на этом языке». Вы несете ответственность за пометку переводимых строк; система может переводить только те строки, которые ей известны.

Затем Django предоставляет утилиты для извлечения строк перевода в файл сообщения . Этот файл - удобный способ для переводчиков предоставить эквивалент строк перевода на целевой язык. После того, как переводчики заполнили файл сообщения, его нужно скомпилировать. В этом процессе используется набор инструментов GNU gettext.

Как только это будет сделано, Django позаботится о переводе веб-приложений на лету на каждый доступный язык в соответствии с языковыми предпочтениями пользователей.

Перехватчики интернационализации Django включены по умолчанию, а это означает, что в определенных местах фреймворка есть небольшие накладные расходы, связанные с i18n. Если вы не используете интернационализацию, вам потребуется две секунды, чтобы задать их в файле настроек. Затем Django произведет некоторые оптимизации, чтобы не загружать механизм интернационализации.USE_I18N = False

Примечание

Существует также независимый, но связанный USE_L10Nпараметр, который определяет, должен ли Django реализовывать локализацию формата. Подробнее см. Локализация формата .

Примечание

Убедитесь, что вы активировали перевод для своего проекта (самый быстрый способ - проверить, MIDDLEWAREвключен ли перевод django.middleware.locale.LocaleMiddleware). Если вы еще этого не сделали, посмотрите, как Django обнаруживает языковые предпочтения .

Интернационализация: в коде Python

Стандартный перевод

Укажите строку перевода с помощью функции gettext(). Это соглашение импортировать это как более короткий псевдоним _, чтобы сэкономить на вводе.

Примечание

gettextМодуль стандартной библиотеки Python устанавливается _()в глобальное пространство имен как псевдоним для gettext(). В Django мы решили не следовать этой практике по нескольким причинам:

  1. Иногда вам следует использовать gettext_lazy() как метод перевода по умолчанию для определенного файла. Без _() глобального пространства имен разработчик должен подумать о том, какая функция перевода является наиболее подходящей.
  2. Символ подчеркивания ( _) используется для представления «предыдущего результата» в интерактивной оболочке Python и тестах doctest. Установка глобальной _()функции вызывает помехи. Явный импорт gettext()as _()позволяет избежать этой проблемы.

Какие функции могут иметь псевдонимы _?

Из-за того, как xgettext(используется makemessages) работает, только функции, которые принимают единственный строковый аргумент, могут быть импортированы как _:

В этом примере текст помечен как строка перевода:"Welcome to my site."

from django.http import HttpResponse
from django.utils.translation import gettext as _

def my_view(request):
    output = _("Welcome to my site.")
    return HttpResponse(output)

Вы можете закодировать это без использования псевдонима. Этот пример идентичен предыдущему:

from django.http import HttpResponse
from django.utils.translation import gettext

def my_view(request):
    output = gettext("Welcome to my site.")
    return HttpResponse(output)

Перевод работает с вычисленными значениями. Этот пример идентичен двум предыдущим:

def my_view(request):
    words = ['Welcome', 'to', 'my', 'site.']
    output = _(' '.join(words))
    return HttpResponse(output)

Перевод работает с переменными. Опять же, вот идентичный пример:

def my_view(request):
    sentence = 'Welcome to my site.'
    output = _(sentence)
    return HttpResponse(output)

(Предостережение при использовании переменных или вычисленных значений, как в предыдущих двух примерах, заключается в том, что утилита Django для обнаружения строк перевода , не сможет найти эти строки. Подробнее об этом позже.)django-admin makemessagesmakemessages

Строки, которые вы передаете _()или gettext()могут занимать заполнители, указанные с помощью стандартного синтаксиса интерполяции именованных строк Python. Пример:

def my_view(request, m, d):
    output = _('Today is %(month)s %(day)s.') % {'month': m, 'day': d}
    return HttpResponse(output)

Этот метод позволяет переводам для конкретных языков изменять порядок текста-заполнителя. Например, английский перевод может быть , а испанский - с заменой местозаполнителей месяца и дня."Today is November 26.""Hoy es 26 de noviembre."

По этой причине вы должны использовать интерполяцию именованных строк (например, %(day)s) вместо позиционной интерполяции (например, %sили %d) всякий раз, когда у вас есть более одного параметра. Если вы использовали позиционную интерполяцию, переводы не смогли бы изменить порядок текста заполнителя.

Поскольку извлечение строк выполняется xgettextкомандой, gettextDjango поддерживает только синтаксис , поддерживаемый. В частности, f-строки Python еще не поддерживаются xgettext, а строки шаблонов JavaScript требуют gettext0.21+.

Комментарии для переводчиков

Если вы хотите дать переводчику подсказку о переводимой строке, вы можете добавить комментарий с префиксом Translatorsключевого слова в строке, предшествующей строке, например:

def my_view(request):
    # Translators: This message appears on the home page only
    output = gettext("Welcome to my site.")

После этого комментарий появится в итоговом .poфайле, связанном с переводимой конструкцией, расположенной под ним, и также должен отображаться большинством инструментов перевода.

Примечание

Для полноты картины это соответствующий фрагмент получившегося .poфайла:

#. Translators: This message appears on the home page only
# path/to/python/file.py:123
msgid "Welcome to my site."
msgstr ""

Это также работает в шаблонах. Подробнее см. Комментарии для переводчиков в шаблонах .

Пометка строк как нерабочих

Используйте эту функцию, django.utils.translation.gettext_noop()чтобы пометить строку как строку перевода, не переводя ее. Позже строка переводится из переменной.

Используйте это, если у вас есть постоянные строки, которые должны храниться на исходном языке, потому что они обмениваются между системами или пользователями - например, строки в базе данных - но должны быть переведены в последний возможный момент времени, например, когда строка представлена пользователю.

Множественное число

Используйте функцию, django.utils.translation.ngettext()чтобы указать сообщения во множественном числе.

ngettext() принимает три аргумента: строку перевода в единственном числе, строку перевода во множественном числе и количество объектов.

Эта функция полезна, когда вам нужно, чтобы ваше приложение Django было локализовано на языки, где количество и сложность форм множественного числа больше, чем у двух форм, используемых в английском языке («объект» для единственного числа и «объекты» для всех случаев, когда countразные формы от одного, независимо от его стоимости.)

Например:

from django.http import HttpResponse
from django.utils.translation import ngettext

def hello_world(request, count):
    page = ngettext(
        'there is %(count)d object',
        'there are %(count)d objects',
        count,
    ) % {
        'count': count,
    }
    return HttpResponse(page)

В этом примере количество объектов передается языкам перевода как countпеременная.

Обратите внимание, что плюрализация сложна и работает по-разному на каждом языке. Сравнение countс 1 - не всегда правильное правило. Этот код выглядит сложным, но для некоторых языков будет давать неверные результаты:

from django.utils.translation import ngettext
from myapp.models import Report

count = Report.objects.count()
if count == 1:
    name = Report._meta.verbose_name
else:
    name = Report._meta.verbose_name_plural

text = ngettext(
    'There is %(count)d %(name)s available.',
    'There are %(count)d %(name)s available.',
    count,
) % {
    'count': count,
    'name': name
}

Не пытайтесь реализовать свою собственную логику единственного или множественного числа; это не будет правильно. В таком случае рассмотрите что-то вроде следующего:

text = ngettext(
    'There is %(count)d %(name)s object available.',
    'There are %(count)d %(name)s objects available.',
    count,
) % {
    'count': count,
    'name': Report._meta.verbose_name,
}

Примечание

При использовании ngettext()убедитесь, что вы используете одно имя для каждой экстраполированной переменной, включенной в литерал. В приведенных выше примерах обратите внимание, как мы использовали nameпеременную Python в обеих строках перевода. Этот пример, помимо того, что он неверен для некоторых языков, как указано выше, завершится ошибкой:

text = ngettext(
    'There is %(count)d %(name)s available.',
    'There are %(count)d %(plural_name)s available.',
    count,
) % {
    'count': Report.objects.count(),
    'name': Report._meta.verbose_name,
    'plural_name': Report._meta.verbose_name_plural,
}

Вы получите ошибку при запуске :django-admin compilemessages

a format specification for argument 'name', as in 'msgstr[0]', doesn't exist in 'msgid'

Контекстные маркеры

Иногда слова имеют несколько значений, например, "May"в английском языке это относится к названию месяца и к глаголу. Чтобы переводчики могли правильно переводить эти слова в разных контекстах, вы можете использовать django.utils.translation.pgettext()функцию или django.utils.translation.npgettext()функцию, если строка требует множественного числа. Оба принимают строку контекста в качестве первой переменной.

В итоговом .poфайле строка будет появляться столько раз, сколько существуют разные контекстные маркеры для одной и той же строки (контекст появится в msgctxtстроке), что позволяет переводчику давать разные переводы для каждой из них.

Например:

from django.utils.translation import pgettext

month = pgettext("month name", "May")

или же:

from django.db import models
from django.utils.translation import pgettext_lazy

class MyThing(models.Model):
    name = models.CharField(help_text=pgettext_lazy(
        'help text for MyThing model', 'This is the help text'))

появится в .poфайле как:

msgctxt "month name"
msgid "May"
msgstr ""

Контекстные маркеры также поддерживаются тегами шаблона translateи blocktranslate.

Ленивый перевод

Используйте ленивые версии функций перевода в django.utils.translation(легко распознаваемые по lazyсуффиксу в их именах) для ленивого перевода строк - когда осуществляется доступ к значению, а не когда они вызываются.

Эти функции хранят ленивую ссылку на строку, а не фактический перевод. Сам перевод будет выполнен, когда строка используется в строковом контексте, например, при рендеринге шаблона.

Это важно, когда вызовы этих функций расположены в путях кода, которые выполняются во время загрузки модуля.

Это то, что может легко произойти при определении моделей, форм и форм моделей, потому что Django реализует их так, что их поля фактически являются атрибутами уровня класса. По этой причине обязательно используйте отложенный перевод в следующих случаях:

Поля модели, отношения verbose_nameи help_textзначения опций

Например, чтобы перевести текст справки поля имени в следующей модели, выполните следующие действия:

from django.db import models
from django.utils.translation import gettext_lazy as _

class MyThing(models.Model):
    name = models.CharField(help_text=_('This is the help text'))

Вы можете отметить имена ForeignKey, ManyToManyFieldили OneToOneFieldотношения , как переводимые, используя свои verbose_nameвозможности:

class MyThing(models.Model):
    kind = models.ForeignKey(
        ThingKind,
        on_delete=models.CASCADE,
        related_name='kinds',
        verbose_name=_('kind'),
    )

Точно так же, как вы это сделали бы, verbose_nameвы должны предоставить подробный текст имени в нижнем регистре для отношения, так как Django автоматически будет указывать его, когда это необходимо.

Значения подробных имен модели

Рекомендуется всегда обеспечивать явные verbose_nameи verbose_name_pluralварианты , а не полагаться на запасной вариант на английском центрическую и несколько наивное определение многословных названий Джанго преформ, посмотрев на имя класса модели:

from django.db import models
from django.utils.translation import gettext_lazy as _

class MyThing(models.Model):
    name = models.CharField(_('name'), help_text=_('This is the help text'))

    class Meta:
        verbose_name = _('my thing')
        verbose_name_plural = _('my things')

descriptionАргумент методов модели @displayдекоратору

Для методов модели вы можете предоставить переводы в Django и на сайт администратора с descriptionаргументом display() декоратора:

from django.contrib import admin
from django.db import models
from django.utils.translation import gettext_lazy as _

class MyThing(models.Model):
    kind = models.ForeignKey(
        ThingKind,
        on_delete=models.CASCADE,
        related_name='kinds',
        verbose_name=_('kind'),
    )

    @admin.display(description=_('Is it a mouse?'))
    def is_mouse(self):
        return self.kind.type == MOUSE_TYPE

Работа с объектами отложенного перевода

Результат gettext_lazy()вызова можно использовать везде, где вы бы использовали строку ( strобъект) в другом коде Django, но он может не работать с произвольным кодом Python. Например, следующее не будет работать, потому что библиотека запросов не обрабатывает gettext_lazyобъекты:

body = gettext_lazy("I \u2764 Django")  # (Unicode :heart:)
requests.post('https://example.com/send', data={'body': body})

Вы можете избежать таких проблем, gettext_lazy()преобразовав объекты в текстовые строки перед их передачей в код, отличный от Django:

requests.post('https://example.com/send', data={'body': str(body)})

Если вам не нравится длинное gettext_lazyимя, вы можете использовать псевдоним _ (подчеркивание), например:

from django.db import models
from django.utils.translation import gettext_lazy as _

class MyThing(models.Model):
    name = models.CharField(help_text=_('This is the help text'))

Использование gettext_lazy()и ngettext_lazy()для обозначения строк в моделях и служебных функциях - обычная операция. Когда вы работаете с этими объектами в другом месте вашего кода, вы должны убедиться, что вы случайно не преобразовали их в строки, потому что они должны быть преобразованы как можно позже (чтобы действовал правильный языковой стандарт). Это требует использования вспомогательной функции, описанной ниже.

Ленивые переводы и множественное число

При использовании отложенного перевода для множественного числа string ( n[p]gettext_lazy) вы обычно не знаете numberаргумент во время определения строки. Следовательно, вам разрешено передавать имя ключа вместо целого числа в качестве numberаргумента. Затем numberбудет искать в словаре под этим ключом во время интерполяции строки. Вот пример:

from django import forms
from django.core.exceptions import ValidationError
from django.utils.translation import ngettext_lazy

class MyForm(forms.Form):
    error_message = ngettext_lazy("You only provided %(num)d argument",
        "You only provided %(num)d arguments", 'num')

    def clean(self):
        # ...
        if error:
            raise ValidationError(self.error_message % {'num': number})

Если строка содержит ровно один безымянный заполнитель, вы можете напрямую интерполировать numberаргумент:

class MyForm(forms.Form):
    error_message = ngettext_lazy(
        "You provided %d argument",
        "You provided %d arguments",
    )

    def clean(self):
        # ...
        if error:
            raise ValidationError(self.error_message % number)

Строки форматирования: format_lazy()

str.format()Метод Python не будет работать, если format_stringлюбой из аргументов to str.format() содержит объекты отложенного перевода. Вместо этого вы можете использовать django.utils.text.format_lazy(), который создает ленивый объект, который запускает str.format()метод только тогда, когда результат включен в строку. Например:

from django.utils.text import format_lazy
from django.utils.translation import gettext_lazy
...
name = gettext_lazy('John Lennon')
instrument = gettext_lazy('guitar')
result = format_lazy('{name}: {instrument}', name=name, instrument=instrument)

В этом случае ленивые переводы resultбудут преобразованы в строки, только если они resultиспользуются в строке (обычно во время рендеринга шаблона).

Другие варианты использования слова lazy в отложенных переводах

В любом другом случае, когда вы хотите отложить перевод, но должны передать переводимую строку в качестве аргумента другой функции, вы можете сами обернуть эту функцию внутри ленивого вызова. Например:

from django.utils.functional import lazy
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _

mark_safe_lazy = lazy(mark_safe, str)

А потом позже:

lazy_string = mark_safe_lazy(_("<p>My <strong>string!</strong></p>"))

Локализованные названия языков

get_language_info()

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 Атрибут Правда только для двунаправленных языков.

Источником языковой информации является django.conf.localeмодуль. Аналогичный доступ к этой информации доступен для кода шаблона. См. ниже.

Интернационализация: в коде шаблона

Переводы в шаблонах Django используют два тега шаблона и немного другой синтаксис, чем в коде Python. Чтобы предоставить вашему шаблону доступ к этим тегам, поместите его в верхнюю часть шаблона. Как и все теги шаблонов, этот тег должен быть загружен во все шаблоны, которые используют переводы, даже те шаблоны, которые являются производными от других шаблонов, которые уже загрузили тег.{% load i18n %}i18n

Предупреждение

Переведенные строки не экранируются при отображении в шаблоне. Это позволяет вам включать HTML в переводы, например, для выделения, но потенциально опасные символы (например ") также будут отображаться без изменений.

translateтег шаблона

Шаблонный тег переводит либо постоянную строку (заключенную в одинарные или двойные кавычки) или содержимое переменной:{% translate %}

<title>{% translate "This is the title." %}</title>
<title>{% translate myvar %}</title>

Если noopопция присутствует, поиск переменных все равно выполняется, но перевод пропускается. Это полезно при «заглушке» контента, который потребует перевода в будущем:

<title>{% translate "myvar" noop %}</title>

Внутренние переводы используют gettext()вызов.

В случае, если myvarв тег передается шаблон var (см. Выше), тег сначала преобразует такую ​​переменную в строку во время выполнения, а затем ищет эту строку в каталогах сообщений.

Невозможно смешать переменную шаблона внутри строки внутри . Если для ваших переводов требуются строки с переменными (заполнители), используйте вместо них.{% translate %}{% blocktranslate %}

Если вы хотите получить переведенную строку, не отображая ее, вы можете использовать следующий синтаксис:

{% translate "This is the title" as the_title %}

<title>{{ the_title }}</title>
<meta name="description" content="{{ the_title }}">

На практике вы будете использовать это, чтобы получить строку, которую можно использовать в нескольких местах в шаблоне или чтобы вы могли использовать вывод в качестве аргумента для других тегов или фильтров шаблона:

{% translate "starting point" as start %}
{% translate "end point" as end %}
{% translate "La Grande Boucle" as race %}

<h1>
  <a href="/" title="{% blocktranslate %}Back to '{{ race }}' homepage{% endblocktranslate %}">{{ race }}</a>
</h1>
<p>
{% for stage in tour_stages %}
    {% cycle start end %}: {{ stage }}{% if forloop.counter|divisibleby:2 %}<br>{% else %}, {% endif %}
{% endfor %}
</p>

{% translate %}также поддерживает контекстные маркеры с использованием contextключевого слова:

{% translate "May" context "month name" %}
Изменено в Django 3.1:

transТег был переименован в translate. trans Тег до сих пор поддерживается в качестве псевдонима для обратной совместимости.

blocktranslateтег шаблона

В отличие от translateтега, blocktranslateтег позволяет вам помечать сложные предложения, состоящие из литералов и переменного содержимого для перевода, используя заполнители:

{% blocktranslate %}This string will have {{ value }} inside.{% endblocktranslate %}

Чтобы преобразовать выражение шаблона - например, доступ к атрибутам объекта или использование фильтров шаблона - вам необходимо привязать выражение к локальной переменной для использования в блоке перевода. Примеры:

{% blocktranslate with amount=article.price %}
That will cost $ {{ amount }}.
{% endblocktranslate %}

{% blocktranslate with myvar=value|filter %}
This will have {{ myvar }} inside.
{% endblocktranslate %}

Вы можете использовать несколько выражений внутри одного blocktranslateтега:

{% blocktranslate with book_t=book|title author_t=author|title %}
This is {{ book_t }} by {{ author_t }}
{% endblocktranslate %}

Примечание

По-прежнему поддерживается предыдущий более подробный формат: {% blocktranslate with book|title as book_t and author|title as author_t %}

Другие блочные теги (например, или ) не допускаются внутри тега.{% for %}{% if %}blocktranslate

Если разрешение одного из аргументов блока не удается, blocktranslateвернется к языку по умолчанию, временно отключив текущий активный язык с помощью deactivate_all() функции.

Этот тег также предусматривает множественное число. Чтобы использовать это:

  • Назначьте и свяжите значение счетчика с именем count. Это значение будет использоваться для выбора правильной формы множественного числа.
  • Укажите как единственного и множественного числа , разделив их с тегом в пределах и тегов.{% plural %}{% blocktranslate %}{% endblocktranslate %}

Пример:

{% blocktranslate count counter=list|length %}
There is only one {{ name }} object.
{% plural %}
There are {{ counter }} {{ name }} objects.
{% endblocktranslate %}

Более сложный пример:

{% blocktranslate with amount=article.price count years=i.length %}
That will cost $ {{ amount }} per year.
{% plural %}
That will cost $ {{ amount }} per {{ years }} years.
{% endblocktranslate %}

Когда вы используете как функцию множественного числа, так и привязываете значения к локальным переменным в дополнение к значению счетчика, имейте в виду, что blocktranslate конструкция внутренне преобразуется в ngettextвызов. Это означает, что применимы те же примечания относительно переменных ngettext .

Обратный поиск URL-адресов не может выполняться в пределах blocktranslateи должен быть получен (и сохранен) заранее:

{% url 'path.to.view' arg arg2 as the_url %}
{% blocktranslate %}
This is a URL: {{ the_url }}
{% endblocktranslate %}

Если вы хотите получить переведенную строку, не отображая ее, вы можете использовать следующий синтаксис:

{% blocktranslate asvar the_title %}The title is {{ title }}.{% endblocktranslate %}
<title>{{ the_title }}</title>
<meta name="description" content="{{ the_title }}">

На практике вы будете использовать это, чтобы получить строку, которую можно использовать в нескольких местах в шаблоне, или чтобы вы могли использовать вывод в качестве аргумента для других тегов или фильтров шаблона.

{% blocktranslate %}также поддерживает контекстные маркеры с использованием contextключевого слова:

{% blocktranslate with name=user.username context "greeting" %}Hi {{ name }}{% endblocktranslate %}

Еще одна поддерживаемая функция - это опция. Эта опция удалит символы новой строки из начала и конца содержимого тега, заменит любые пробелы в начале и конце строки и объединит все строки в одну, используя пробел для их разделения. Это очень полезно для отступа содержимого тега без того, чтобы символы отступа попадали в соответствующую запись в PO-файле, что упрощает процесс перевода.{% blocktranslate %}trimmed{% blocktranslate %}{% blocktranslate %}

Например, следующий тег:{% blocktranslate %}

{% blocktranslate trimmed %}
  First sentence.
  Second paragraph.
{% endblocktranslate %}

приведет к записи в PO-файле, по сравнению с , если бы опция не была указана."First sentence. Second paragraph.""\n  First sentence.\n  Second paragraph.\n"trimmed

Изменено в Django 3.1:

blocktransТег был переименован в blocktranslate. blocktrans Тег до сих пор поддерживается в качестве псевдонима для обратной совместимости.

Строковые литералы, передаваемые в теги и фильтры

Вы можете преобразовать строковые литералы, переданные в качестве аргументов, в теги и фильтры, используя знакомый _()синтаксис:

{% some_tag _("Page not found") value|yesno:_("yes,no") %}

В этом случае и тег, и фильтр увидят переведенную строку, поэтому им не нужно знать переводы.

Примечание

В этом примере инфраструктуре перевода будет передана строка "yes,no", а не отдельные строки "yes"и "no". Переведенная строка должна содержать запятую, чтобы код синтаксического анализа фильтра знал, как разделить аргументы. Например, немецкий переводчик может перевести строку "yes,no"как "ja,nein" (сохраняя запятую без изменений).

Комментарии для переводчиков в шаблонах

Как и в случае с кодом Python , эти примечания для переводчиков можно указать с помощью комментариев либо с помощью comment тега:

{% comment %}Translators: View verb{% endcomment %}
{% translate "View" %}

{% comment %}Translators: Short intro blurb{% endcomment %}
<p>{% blocktranslate %}A multiline translatable
literal.{% endblocktranslate %}</p>

или с {##} конструкциями однострочного комментария :

{# Translators: Label of a button that triggers search #}
<button type="submit">{% translate "Go" %}</button>

{# Translators: This is a text of the base template #}
{% blocktranslate %}Ambiguous translatable block of text{% endblocktranslate %}

Примечание

Для полноты картины это соответствующие фрагменты получившегося .poфайла:

#. Translators: View verb
# path/to/template/file.html:10
msgid "View"
msgstr ""

#. Translators: Short intro blurb
# path/to/template/file.html:13
msgid ""
"A multiline translatable"
"literal."
msgstr ""

# ...

#. Translators: Label of a button that triggers search
# path/to/template/file.html:100
msgid "Go"
msgstr ""

#. Translators: This is a text of the base template
# path/to/template/file.html:103
msgid "Ambiguous translatable block of text"
msgstr ""

Переключение языка в шаблонах

Если вы хотите выбрать язык в шаблоне, вы можете использовать languageтег шаблона:

{% load i18n %}

{% get_current_language as LANGUAGE_CODE %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
<p>{% translate "Welcome to our page" %}</p>

{% language 'en' %}
    {% get_current_language as LANGUAGE_CODE %}
    <!-- Current language: {{ LANGUAGE_CODE }} -->
    <p>{% translate "Welcome to our page" %}</p>
{% endlanguage %}

В то время как первое появление «Добро пожаловать на нашу страницу» использует текущий язык, второе всегда будет на английском языке.

Другие теги

Для этих тегов также требуется расширение .{% load i18n %}

get_available_languages

{% get_available_languages as LANGUAGES %}возвращает список кортежей, в котором первый элемент - это код языка, а второй - имя языка (переведенное на текущий активный языковой стандарт).

get_current_language

{% get_current_language as LANGUAGE_CODE %}возвращает предпочтительный язык текущего пользователя в виде строки. Пример: en-us. Посмотрите, как Django обнаруживает языковые предпочтения .

get_current_language_bidi

{% get_current_language_bidi as LANGUAGE_BIDI %}возвращает направление текущей локали. Если True, это язык с письмом справа налево, например иврит, арабский. Если Falseэто язык с написанием слева направо, например английский, французский, немецкий и т. Д.

i18nконтекстный процессор

Если включить django.template.context_processors.i18nконтекстный процессор, то каждый RequestContextбудет иметь доступ к LANGUAGES, LANGUAGE_CODEи , LANGUAGE_BIDIкак определено выше.

get_language_info

Вы также можете получить информацию о любом из доступных языков, используя предоставленные теги шаблонов и фильтры. Чтобы получить информацию об одном языке, используйте тег:{% get_language_info %}

{% get_language_info for LANGUAGE_CODE as lang %}
{% get_language_info for "pl" as lang %}

Затем вы можете получить доступ к информации:

Language code: {{ lang.code }}<br>
Name of language: {{ lang.name_local }}<br>
Name in English: {{ lang.name }}<br>
Bi-directional: {{ lang.bidi }}
Name in the active language: {{ lang.name_translated }}

get_language_info_list

Вы также можете использовать тег шаблона для получения информации для списка языков (например, активных языков, как указано в ). См. Раздел о представлении перенаправления set_language для примера того, как отобразить селектор языка с помощью .{% get_language_info_list %}LANGUAGES{% 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 }} («Немецкий», когда активный язык - чешский)

Интернационализация: в коде JavaScript

Добавление переводов в JavaScript создает некоторые проблемы:

  • Код JavaScript не имеет доступа к gettextреализации.
  • Код JavaScript не имеет доступа к файлам .poили .mo; они должны быть доставлены сервером.
  • Каталоги переводов для JavaScript должны быть как можно меньше.

Django предоставляет интегрированное решение этих проблем: он передает переводы в JavaScript, чтобы вы могли вызывать gettextи т. Д. Из JavaScript.

Основное решение этих проблем - это следующее JavaScriptCatalogпредставление, которое генерирует библиотеку кода JavaScript с функциями, имитирующими gettextинтерфейс, плюс массив строк перевода.

JavaScriptCatalogВид

класс JavaScriptCatalog

Представление, которое создает библиотеку кода JavaScript с функциями, имитирующими gettextинтерфейс, а также массив строк перевода.

Атрибуты

domain

Домен перевода, содержащий строки для добавления в вывод представления. По умолчанию 'djangojs'.

packages

Список установленных приложений. Эти приложения должны содержать каталог. Все эти каталоги плюс все каталоги (которые всегда включены) объединяются в один каталог. По умолчанию , это означает, что в выходных данных JavaScript предоставляются все доступные переводы .application nameslocaleLOCALE_PATHSNoneINSTALLED_APPS

Пример со значениями по умолчанию :

from django.views.i18n import JavaScriptCatalog

urlpatterns = [
    path('jsi18n/', JavaScriptCatalog.as_view(), name='javascript-catalog'),
]

Пример с пользовательскими пакетами :

urlpatterns = [
    path('jsi18n/myapp/',
         JavaScriptCatalog.as_view(packages=['your.app.label']),
         name='javascript-catalog'),
]

Если ваш корневой URLconf использует i18n_patterns(), для правильной генерации каталога JavaScriptCatalogтакже необходимо заключить в оболочку i18n_patterns().

Пример с i18n_patterns() :

from django.conf.urls.i18n import i18n_patterns

urlpatterns = i18n_patterns(
    path('jsi18n/', JavaScriptCatalog.as_view(), name='javascript-catalog'),
)

Приоритет переводов таков, что пакеты, указанные позже в packagesаргументе, имеют более высокий приоритет, чем пакеты , указанные в начале. Это важно в случае несовпадения переводов одного и того же литерала.

Если вы используете несколько JavaScriptCatalogпредставлений на сайте, и некоторые из них определяют одни и те же строки, строки в каталоге, который был загружен последними, имеют приоритет.

Использование каталога переводов JavaScript

Чтобы использовать каталог, извлеките динамически сгенерированный скрипт следующим образом:

<script src="{% url 'javascript-catalog' %}"></script>

При этом используется обратный поиск URL-адреса, чтобы найти URL-адрес представления каталога JavaScript. Когда каталог загружен, ваш код JavaScript может использовать следующие методы:

  • gettext
  • ngettext
  • interpolate
  • get_format
  • gettext_noop
  • pgettext
  • npgettext
  • pluralidx

gettext

gettextФункция ведет себя аналогично стандартному gettext интерфейсу в коде Python:

document.write(gettext('this is to be translated'));

ngettext

ngettextФункция обеспечивает интерфейс к множественному числу слов и фразам:

const objectCount = 1 // or 0, or 2, or 3, ...
const string = ngettext(
    'literal for the singular case',
    'literal for the plural case',
    objectCount
);

interpolate

В interpolateфункции поддерживает динамическое заполнение строки форматирования. Синтаксис интерполяции заимствован из Python, поэтому interpolate функция поддерживает как позиционную, так и именованную интерполяцию:

  • Позиционная интерполяция: objсодержит объект массива JavaScript, значения элементов которого затем последовательно интерполируются в соответствующие им fmtзаполнители в том же порядке, в котором они появляются. Например:

    const formats = ngettext(
      'There is %s object. Remaining: %s',
      'There are %s objects. Remaining: %s',
      11
    );
    const string = interpolate(formats, [11, 20]);
    // string is 'There are 11 objects. Remaining: 20'
    
  • Именованная интерполяция: этот режим выбирается путем передачи необязательного логического namedпараметра как true. objсодержит объект JavaScript или ассоциативный массив. Например:

    const data = {
      count: 10,
      total: 50
    };
    
    const formats = ngettext(
        'Total: %(total)s, there is %(count)s object',
        'there are %(count)s of a total of %(total)s objects',
        data.count
    );
    const string = interpolate(formats, data, true);
    

Однако вы не должны переборщить с интерполяцией строк: это все еще JavaScript, поэтому код должен выполнять повторяющиеся подстановки регулярных выражений. Это не так быстро, как строковая интерполяция в Python, поэтому придерживайтесь тех случаев, когда она вам действительно нужна (например, в сочетании с ngettext созданием правильного множественного числа).

get_format

get_formatФункция имеет доступ к настроенным i18n параметров форматирования и может извлекать строку формата для заданного имени настройки:

document.write(get_format('DATE_FORMAT'));
// 'N j, Y'

У него есть доступ к следующим настройкам:

Это полезно для поддержания согласованности форматирования с отображаемыми 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Вид

класс JSONCatalog

Чтобы использовать другую клиентскую библиотеку для обработки переводов, вы можете воспользоваться преимуществом JSONCatalogпредставления. Он похож на JavaScriptCatalogответ JSON, но возвращает его.

Обратитесь к документации , JavaScriptCatalog чтобы узнать о возможных значениях и их использовании domainи packages атрибутов.

Формат ответа следующий:

{
    "catalog": {
        # Translations catalog
    },
    "formats": {
        # Language formats for date, time, etc.
    },
    "plural": "..."  # Expression for plural forms, or null.
}

Примечание по производительности

Различные представления JavaScript / JSON i18n генерируют каталог из .moфайлов по каждому запросу. Поскольку его вывод постоянен, по крайней мере, для данной версии сайта, он является хорошим кандидатом для кеширования.

Кэширование на стороне сервера снизит нагрузку на ЦП. Это легко реализуется с помощью cache_page()декоратора. Чтобы активировать аннулирование кеша при изменении переводов, укажите префикс ключа, зависящий от версии, как показано в примере ниже, или сопоставьте представление с URL-адресом, зависящим от версии:

from django.views.decorators.cache import cache_page
from django.views.i18n import JavaScriptCatalog

# The value returned by get_version() must change when translations change.
urlpatterns = [
    path('jsi18n/',
         cache_page(86400, key_prefix='js18n-%s' % get_version())(JavaScriptCatalog.as_view()),
         name='javascript-catalog'),
]

Кэширование на стороне клиента сэкономит пропускную способность и ускорит загрузку вашего сайта. Если вы используете ETags ( ConditionalGetMiddleware), вы уже покрыты. В противном случае вы можете применить условные декораторы . В следующем примере кеш становится недействительным при перезапуске сервера приложений:

from django.utils import timezone
from django.views.decorators.http import last_modified
from django.views.i18n import JavaScriptCatalog

last_modified_date = timezone.now()

urlpatterns = [
    path('jsi18n/',
         last_modified(lambda req, **kw: last_modified_date)(JavaScriptCatalog.as_view()),
         name='javascript-catalog'),
]

Вы даже можете предварительно создать каталог JavaScript как часть процедуры развертывания и использовать его как статический файл. Этот радикальный прием реализован в django-statici18n .

Интернационализация: в шаблонах URL

Django предоставляет два механизма для интернационализации шаблонов URL:

  • Добавление языкового префикса в корень шаблонов URL-адресов, чтобы можно LocaleMiddlewareбыло определить язык для активации по запрошенному URL-адресу.
  • Делаем сами шаблоны URL-адресов переводимыми с помощью django.utils.translation.gettext_lazy()функции.

Предупреждение

Использование любой из этих функций требует, чтобы для каждого запроса был установлен активный язык; другими словами, вам нужно иметь django.middleware.locale.LocaleMiddlewareв своем MIDDLEWAREокружении.

Префикс языка в шаблонах URL

i18n_patterns( * URL-адреса , prefix_default_language = True )

Эту функцию можно использовать в корневом URLconf, и Django автоматически добавит текущий активный языковой код ко всем определенным в нем шаблонам URL i18n_patterns().

Установка prefix_default_languageна Falseудаляет префикс из языка по умолчанию ( LANGUAGE_CODE). Это может быть полезно при добавлении переводов на существующий сайт, чтобы текущие URL-адреса не изменились.

Примеры шаблонов URL:

from django.conf.urls.i18n import i18n_patterns
from django.urls import include, path

from about import views as about_views
from news import views as news_views
from sitemap.views import sitemap

urlpatterns = [
    path('sitemap.xml', sitemap, name='sitemap-xml'),
]

news_patterns = ([
    path('', news_views.index, name='index'),
    path('category/<slug:slug>/', news_views.category, name='category'),
    path('<slug:slug>/', news_views.details, name='detail'),
], 'news')

urlpatterns += i18n_patterns(
    path('about/', about_views.main, name='about'),
    path('news/', include(news_patterns, namespace='news')),
)

После определения этих шаблонов URL-адресов Django автоматически добавит языковой префикс к шаблонам URL-адресов, которые были добавлены i18n_patterns функцией. Пример:

>>> from django.urls import reverse
>>> from django.utils.translation import activate

>>> activate('en')
>>> reverse('sitemap-xml')
'/sitemap.xml'
>>> reverse('news:index')
'/en/news/'

>>> activate('nl')
>>> reverse('news:detail', kwargs={'slug': 'news-slug'})
'/nl/news/news-slug/'

С prefix_default_language=Falseи LANGUAGE_CODE='en'URL-адреса будут:

>>> activate('en')
>>> reverse('news:index')
'/news/'

>>> activate('nl')
>>> reverse('news:index')
'/nl/news/'

Предупреждение

i18n_patterns()разрешено только в корневом URLconf. Использование его во включенном URLconf вызовет ImproperlyConfiguredисключение.

Предупреждение

Убедитесь, что у вас нет шаблонов URL без префикса, которые могут конфликтовать с автоматически добавляемым языковым префиксом.

Перевод шаблонов URL

Шаблоны URL-адресов также можно пометить как переводимые с помощью gettext_lazy()функции. Пример:

from django.conf.urls.i18n import i18n_patterns
from django.urls import include, path
from django.utils.translation import gettext_lazy as _

from about import views as about_views
from news import views as news_views
from sitemaps.views import sitemap

urlpatterns = [
    path('sitemap.xml', sitemap, name='sitemap-xml'),
]

news_patterns = ([
    path('', news_views.index, name='index'),
    path(_('category/<slug:slug>/'), news_views.category, name='category'),
    path('<slug:slug>/', news_views.details, name='detail'),
], 'news')

urlpatterns += i18n_patterns(
    path(_('about/'), about_views.main, name='about'),
    path(_('news/'), include(news_patterns, namespace='news')),
)

После того, как вы создали переводы, reverse() функция вернет URL-адрес на активном языке. Пример:

>>> from django.urls import reverse
>>> from django.utils.translation import activate

>>> activate('en')
>>> reverse('news:category', kwargs={'slug': 'recent'})
'/en/news/category/recent/'

>>> activate('nl')
>>> reverse('news:category', kwargs={'slug': 'recent'})
'/nl/nieuws/categorie/recent/'

Предупреждение

В большинстве случаев лучше всего использовать переведенные URL-адреса только в блоке шаблонов с префиксом языкового кода (с использованием i18n_patterns()), чтобы избежать возможности того, что небрежно переведенный URL-адрес вызовет конфликт с нетранслированным шаблоном URL.

Реверс в шаблонах

Если локализованные URL-адреса в шаблонах меняются местами, они всегда используют текущий язык. Для ссылки на URL-адрес на другом языке используйте language тег шаблона. Он включает данный язык в прилагаемом разделе шаблона:

{% load i18n %}

{% get_available_languages as languages %}

{% translate "View this category in:" %}
{% for lang_code, lang_name in languages %}
    {% language lang_code %}
    <a href="{% url 'category' slug=category.slug %}">{{ lang_name }}</a>
    {% endlanguage %}
{% endfor %}

languageТег ожидает код языка в качестве единственного аргумента.

Локализация: как создавать языковые файлы

После того, как строковые литералы приложения были помечены для последующего перевода, сам перевод должен быть записан (или получен). Вот как это работает.

Файлы сообщений

Первый шаг - создать файл сообщений для нового языка. Файл сообщения - это простой текстовый файл, представляющий один язык, который содержит все доступные строки перевода и то, как они должны быть представлены на данном языке. Файлы сообщений имеют .poрасширение.

Django поставляется с инструментом, который автоматизирует создание и обслуживание этих файлов.django-admin makemessages

Утилиты Gettext

makemessagesКоманды (и compilemessagesобсуждаются ниже) команды используют от GNU Gettext набора инструментов: xgettext, msgfmt, msgmergeи msguniq.

Минимальная gettextподдерживаемая версия утилит - 0.15.

Чтобы создать или обновить файл сообщения, выполните эту команду:

django-admin makemessages -l de

… Где de- имя локали для файла сообщения, который вы хотите создать. Например, pt_BRдля бразильского португальского, de_ATавстрийского немецкого или idиндонезийского.

Скрипт следует запускать из одного из двух мест:

  • Корневой каталог вашего проекта Django (тот, который содержит manage.py).
  • Корневой каталог одного из ваших приложений Django.

Сценарий запускается по дереву исходного кода вашего проекта или дереву исходного кода приложения и извлекает все строки, отмеченные для перевода (см. Как Django обнаруживает переводы и убедитесь, что LOCALE_PATHS он настроен правильно). Он создает (или обновляет) файл сообщений в каталоге locale/LANG/LC_MESSAGES. В deпримере это файл locale/de/LC_MESSAGES/django.po.

Когда вы запускаете makemessagesиз корневого каталога вашего проекта, извлеченные строки будут автоматически распределены в соответствующие файлы сообщений. То есть строка, извлеченная из файла приложения, содержащего locale каталог, будет помещена в файл сообщения в этом каталоге. Строка, извлеченная из файла приложения без какого-либо localeкаталога, либо войдет в файл сообщения в каталог, указанный первым, LOCALE_PATHSлибо сгенерирует ошибку, если LOCALE_PATHSона пуста.

По умолчанию проверяет каждый файл с расширением , или . Если вы хотите изменить это значение по умолчанию, используйте параметр или, чтобы указать расширения файлов для проверки:django-admin makemessages.html.txt.py--extension-e

django-admin makemessages -l de -e txt

Разделите несколько расширений запятыми и / или используйте -eили --extension несколько раз:

django-admin makemessages -l de -e html,txt -e xml

Предупреждение

При создании файлов сообщений из исходного кода JavaScript вам необходимо использовать специальный djangojsдомен, а не .-e js

Используете шаблоны Jinja2?

makemessagesне понимает синтаксис шаблонов Jinja2. Чтобы извлечь строки из проекта, содержащего шаблоны Jinja2, используйте извлечение сообщений из Babel .

Вот пример babel.cfgфайла конфигурации:

# Extraction from Python source files
[python: **.py]

# Extraction from Jinja2 templates
[jinja2: **.jinja]
extensions = jinja2.ext.with_

Убедитесь, что вы указали все расширения, которые используете! В противном случае Babel не распознает теги, определенные этими расширениями, и полностью проигнорирует содержащие их шаблоны Jinja2.

Babel предоставляет аналогичные функции makemessages, может заменить его в целом и не зависит от него gettext. Дополнительные сведения см. В документации по работе с каталогами сообщений .

Нет текста?

Если у вас не установлены gettextутилиты, makemessagesбудут созданы пустые файлы. В этом случае либо установите gettextутилиты, либо скопируйте файл сообщений на английском языке ( locale/en/LC_MESSAGES/django.po), если он доступен, и используйте его в качестве отправной точки, то есть пустой файл перевода.

Работаете в Windows?

Если вы используете Windows и вам нужно установить утилиты GNU gettext, чтобы они makemessagesработали, см. Gettext в Windows для получения дополнительной информации.

Каждый .poфайл содержит небольшой бит метаданных, таких как контактная информация лица, обслуживающего перевод, но основная часть файла представляет собой список сообщений - сопоставлений между строками перевода и фактическим переведенным текстом для конкретного языка.

Например, если ваше приложение Django содержит строку перевода для текста , например:"Welcome to my site."

_("Welcome to my site.")

… Тогда будет создан файл, содержащий следующий фрагмент - сообщение:django-admin makemessages.po

#: path/to/python/module.py:23
msgid "Welcome to my site."
msgstr ""

Краткое объяснение:

  • msgidэто строка перевода, которая появляется в источнике. Не меняй это.
  • msgstrэто то место, где вы помещаете перевод для конкретного языка. Он начинается пустым, поэтому вы обязаны его изменить. Убедитесь, что ваш перевод заключен в кавычки.
  • Для удобства каждое сообщение включает в себя в виде строки комментария с префиксом #и расположенной над ней msgidимя файла и номер строки, из которой была взята строка перевода.

Особый случай - длинные сообщения. Там первая строка сразу после msgstr(или msgid) - это пустая строка. Тогда сам контент будет записан в следующие несколько строк по одной строке на строку. Эти строки соединяются напрямую. Не забывайте конечные пробелы внутри строк; в противном случае они будут скреплены без пробелов!

Следите за своей кодировкой

Из-за того, как gettextинструменты работают внутри компании, и поскольку мы хотим разрешить исходные строки, отличные от ASCII, в ядре Django и ваших приложениях, вы должны использовать UTF-8 в качестве кодировки для ваших PO-файлов (по умолчанию при создании PO-файлов). Это означает, что все будут использовать одну и ту же кодировку, что важно, когда Django обрабатывает файлы PO.

Нечеткие записи

makemessagesиногда генерирует записи перевода, помеченные как нечеткие, например, когда перевод выводится из ранее переведенных строк. По умолчанию нечеткие записи не обрабатываются compilemessages.

Чтобы повторно проверить весь исходный код и шаблоны на предмет новых строк перевода и обновить все файлы сообщений для всех языков, запустите следующее:

django-admin makemessages -a

Компиляция файлов сообщений

После создания файла сообщения - и каждый раз, когда вы вносите в него изменения - вам нужно будет скомпилировать его в более эффективную форму для использования в gettext. Сделайте это с помощью утилиты.django-admin compilemessages

Этот инструмент .poобрабатывает все доступные файлы и создает .moфайлы, которые представляют собой двоичные файлы, оптимизированные для использования с помощью gettext. В том же каталоге, из которого вы запустили , запустите так:django-admin makemessagesdjango-admin compilemessages

django-admin compilemessages

Вот и все. Ваши переводы готовы к использованию.

Работаете в Windows?

Если вы используете Windows и вам нужно установить утилиты GNU gettext, чтобы они работали, см. Gettext в Windows для получения дополнительной информации.django-admin compilemessages

Файлы .po: кодировка и использование спецификации.

Django поддерживает только .poфайлы, закодированные в UTF-8, и без какой-либо спецификации (метка порядка байтов), поэтому, если ваш текстовый редактор по умолчанию добавляет такие метки в начало файлов, вам нужно будет перенастроить их.

Устранение неполадок: gettext()неправильно определяет python-formatв строках со знаками процентов

В некоторых случаях, например, в строках со знаком процента, за которым следует пробел и тип преобразования строки (например ), строки неправильно помечаются значком ._("10% interest")gettext()python-format

Если вы попытаетесь скомпилировать файлы сообщений с неправильно помеченными строками, вы получите сообщение об ошибке, например или .number of format specifications in 'msgid' and 'msgstr' does not match'msgstr' is not a valid Python format string, unlike 'msgid'

Чтобы обойти это, вы можете избежать знаков процента, добавив второй знак процента:

from django.utils.translation import gettext as _
output = _("10%% interest")

Или вы можете использовать, no-python-formatчтобы все знаки процента обрабатывались как литералы:

# xgettext:no-python-format
output = _("10% interest")

Создание файлов сообщений из исходного кода JavaScript

Вы создаете и обновляете файлы сообщений так же, как и другие файлы сообщений Django - с помощью инструмента. Единственная разница в том, что вам нужно явно указать, что на языке gettext называется доменом, в данном случае доменом, предоставив параметр, например:django-admin makemessagesdjangojs-d djangojs

django-admin makemessages -d djangojs -l de

Это создаст или обновит файл сообщений для JavaScript для немецкого языка. После обновления файлов сообщений выполните так же, как и с обычными файлами сообщений Django.django-admin compilemessages

gettextв Windows

Это необходимо только для людей, которые хотят извлечь идентификаторы сообщений или скомпилировать файлы сообщений ( .po). Сама работа по переводу включает редактирование существующих файлов этого типа, но если вы хотите создать свои собственные файлы сообщений или хотите протестировать или скомпилировать измененный файл сообщений, загрузите предварительно скомпилированный двоичный установщик .

Вы также можете использовать gettextдвоичные файлы, полученные в другом месте, если команда работает правильно. Не пытайтесь использовать утилиты перевода Django с пакетом, если команда, введенная в командной строке Windows, вызывает всплывающее окно с сообщением «xgettext.exe вызвал ошибку и будет закрыт Windows».xgettext --versiongettextxgettext --version

Настройка makemessagesкоманды

Если вы хотите передать дополнительные параметры xgettext, вам необходимо создать собственную makemessagesкоманду и переопределить ее xgettext_options атрибут:

from django.core.management.commands import makemessages

class Command(makemessages.Command):
    xgettext_options = makemessages.Command.xgettext_options + ['--keyword=mytrans']

Если вам нужна большая гибкость, вы также можете добавить новый аргумент в свою настраиваемую makemessagesкоманду:

from django.core.management.commands import makemessages

class Command(makemessages.Command):

    def add_arguments(self, parser):
        super().add_arguments(parser)
        parser.add_argument(
            '--extra-keyword',
            dest='xgettext_keywords',
            action='append',
        )

    def handle(self, *args, **options):
        xgettext_keywords = options.pop('xgettext_keywords')
        if xgettext_keywords:
            self.xgettext_options = (
                makemessages.Command.xgettext_options[:] +
                ['--keyword=%s' % kwd for kwd in xgettext_keywords]
            )
        super().handle(*args, **options)

Разное

Вид set_languageперенаправления

set_language( запрос )

Для удобства Django поставляется с представлением, django.views.i18n.set_language()которое устанавливает языковые предпочтения пользователя и перенаправляет на заданный URL-адрес или, по умолчанию, обратно на предыдущую страницу.

Активируйте это представление, добавив следующую строку в свой URLconf:

path('i18n/', include('django.conf.urls.i18n')),

(Обратите внимание, что в этом примере представление доступно по адресу /i18n/setlang/.)

Предупреждение

Убедитесь, что вы не включили указанный выше URL-адрес i18n_patterns()- для правильной работы он должен быть независимым от языка.

Представление ожидает вызова через POSTметод с language параметром, установленным в запросе. Если поддержка сеанса включена, представление сохраняет выбор языка в сеансе пользователя. Он также сохраняет выбор языка в файле cookie с именем django_languageпо умолчанию. (Имя можно изменить в LANGUAGE_COOKIE_NAMEнастройках.)

После установки выбора языка Django ищет nextпараметр в файле POSTили GETdata. Если он найден и Django считает его безопасным URL-адресом (т.е. он не указывает на другой хост и использует безопасную схему), будет выполнено перенаправление на этот URL-адрес. В противном случае Django может вернуться к перенаправлению пользователя на URL-адрес из Refererзаголовка или, если он не задан, в /зависимости от характера запроса:

  • Если запрос принимает HTML-контент (на основе его AcceptHTTP-заголовка), откат всегда будет выполняться.
  • Если запрос не принимает HTML, откат будет выполнен, только если nextпараметр был установлен. В противном случае будет возвращен код состояния 204 (без содержимого).
Изменено в Django 3.1:

В более старых версиях различие для отката зависит от того, установлено ли в X-Requested-Withзаголовке значение XMLHttpRequest. Это устанавливается методом jQuery ajax().

Вот пример кода HTML-шаблона:

{% load i18n %}

<form action="{% url 'set_language' %}" method="post">{% csrf_token %}
    <input name="next" type="hidden" value="{{ redirect_to }}">
    <select name="language">
        {% get_current_language as LANGUAGE_CODE %}
        {% get_available_languages as LANGUAGES %}
        {% get_language_info_list for LANGUAGES as languages %}
        {% for language in languages %}
            <option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected{% endif %}>
                {{ language.name_local }} ({{ language.code }})
            </option>
        {% endfor %}
    </select>
    <input type="submit" value="Go">
</form>

В этом примере Django ищет URL-адрес страницы, на которую будет перенаправлен пользователь, в redirect_toпеременной контекста.

Явная установка активного языка

Вы можете явно указать активный язык для текущего сеанса. Возможно, языковые предпочтения пользователя получены, например, из другой системы. Вы уже были представлены django.utils.translation.activate(). Это относится только к текущему потоку. Чтобы сохранить язык для всего сеанса в файле cookie, установите LANGUAGE_COOKIE_NAMEфайл cookie в ответе:

from django.conf import settings
from django.http import HttpResponse
from django.utils import translation
user_language = 'fr'
translation.activate(user_language)
response = HttpResponse(...)
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, user_language)

Обычно вы хотите использовать оба: django.utils.translation.activate() изменяет язык для этого потока, а установка файла cookie сохраняет это предпочтение в будущих запросах.

Использование переводов вне представлений и шаблонов

Хотя Django предоставляет богатый набор инструментов i18n для использования в представлениях и шаблонах, он не ограничивает использование специфичным для Django кодом. Механизмы перевода Django могут использоваться для перевода произвольных текстов на любой язык, поддерживаемый Django (конечно, если существует соответствующий каталог переводов). Вы можете загрузить каталог переводов, активировать его и перевести текст на язык по вашему выбору, но не забудьте вернуться к исходному языку, поскольку активация каталога переводов выполняется для каждого потока, и такое изменение повлияет на код, выполняющийся в том же потоке. .

Например:

from django.utils import translation

def welcome_translated(language):
    cur_language = translation.get_language()
    try:
        translation.activate(language)
        text = translation.gettext('welcome')
    finally:
        translation.activate(cur_language)
    return text

Вызов этой функции со значением 'de'даст вам "Willkommen", независимо от LANGUAGE_CODEязыка, установленного промежуточным программным обеспечением.

Особый интерес представляют функции, django.utils.translation.get_language()которые возвращают язык, используемый в текущем потоке, django.utils.translation.activate()активируют каталог переводов для текущего потока и django.utils.translation.check_for_language() проверяют, поддерживается ли данный язык Django.

Чтобы помочь написать более сжатый код, есть также диспетчер контекста, django.utils.translation.override()который сохраняет текущий язык при входе и восстанавливает его при выходе. С его помощью приведенный выше пример становится:

from django.utils import translation

def welcome_translated(language):
    with translation.override(language):
        return translation.gettext('welcome')

Примечания по реализации

Особенности перевода Django

Механизм перевода Django использует стандартный gettextмодуль, поставляемый с Python. Если вы знаете gettext, вы можете отметить эти особенности в том, как Django выполняет перевод:

  • Строковый домен - djangoили djangojs. Этот строковый домен используется для различения различных программ, которые хранят свои данные в общей библиотеке файлов сообщений (обычно /usr/share/locale/). djangoДомен используется для Python и шаблонов строк перевода и загружается в глобальные каталоги перевода. djangojs Домен используется только для каталогов перевода JavaScript , чтобы убедиться , что те , настолько малы , насколько это возможно.
  • Django не использует в xgettextодиночку. Он использует оболочки Python для xgettextи msgfmt. Это в основном для удобства.

Как Django обнаруживает языковые предпочтения

После того, как вы подготовили свои переводы - или, если вы хотите использовать переводы, поставляемые с Django, - вам необходимо активировать перевод для своего приложения.

За кулисами Django имеет очень гибкую модель принятия решения о том, какой язык следует использовать - для всей установки, для конкретного пользователя или и того, и другого.

Чтобы установить языковые предпочтения для всей установки, установите LANGUAGE_CODE. Django использует этот язык в качестве перевода по умолчанию - последняя попытка, если не удается найти более подходящий перевод с помощью одного из методов, используемых промежуточным программным обеспечением локали (см. Ниже).

Если все, что вам нужно, это запустить Django на своем родном языке, все, что вам нужно сделать, это установить LANGUAGE_CODEи убедиться, что соответствующие файлы сообщений и их скомпилированные версии ( .mo) существуют.

Если вы хотите позволить каждому отдельному пользователю указывать, какой язык он предпочитает, вам также необходимо использовать расширение LocaleMiddleware. LocaleMiddlewareпозволяет выбрать язык на основе данных из запроса. Он настраивает контент для каждого пользователя.

Чтобы использовать LocaleMiddleware, добавьте 'django.middleware.locale.LocaleMiddleware' в свою MIDDLEWAREнастройку. Поскольку порядок промежуточного программного обеспечения имеет значение, следуйте этим рекомендациям:

  • Убедитесь, что это одно из первых установленных промежуточных программ.
  • Он должен появиться после SessionMiddleware, потому LocaleMiddleware что использует данные сеанса. И это должно произойти раньше, CommonMiddleware потому что CommonMiddlewareдля разрешения запрошенного URL-адреса требуется активированный язык.
  • Если пользуетесь CacheMiddleware, ставьте LocaleMiddlewareпосле.

Например, вы MIDDLEWAREмогли бы выглядеть так:

MIDDLEWARE = [
   'django.contrib.sessions.middleware.SessionMiddleware',
   'django.middleware.locale.LocaleMiddleware',
   'django.middleware.common.CommonMiddleware',
]

(Подробнее о промежуточном программном обеспечении см. Документацию по промежуточному программному обеспечению .)

LocaleMiddleware пытается определить языковые предпочтения пользователя, следуя этому алгоритму:

  • Во-первых, он ищет языковой префикс в запрошенном URL. Это выполняется только тогда, когда вы используете i18n_patternsфункцию в корневом URLconf. См. Раздел Интернационализация: в шаблонах URL-адресов для получения дополнительной информации о языковом префиксе и способах интернационализации шаблонов URL-адресов.

  • В противном случае он ищет файл cookie.

    Имя используемого файла cookie устанавливается LANGUAGE_COOKIE_NAME настройкой. (Имя по умолчанию django_language.)

  • В противном случае он просматривает Accept-Languageзаголовок HTTP. Этот заголовок отправляется вашим браузером и сообщает серверу, какие языки вы предпочитаете, в порядке приоритета. Django пробует каждый язык в заголовке, пока не найдет язык с доступными переводами.

  • В противном случае он использует глобальную LANGUAGE_CODEнастройку.

Заметки:

  • В каждом из этих мест предпочтение языка ожидается в стандартном языковом формате в виде строки. Например, бразильский португальский pt-br.

  • Если базовый язык доступен, но указанный подъязык отсутствует, Django использует базовый язык. Например, если пользователь указывает de-at (австрийский немецкий), но deдоступен только Django, Django использует de.

  • LANGUAGESМожно выбрать только языки, перечисленные в настройке. Если вы хотите ограничить выбор языка подмножеством предоставленных языков (поскольку ваше приложение не поддерживает все эти языки), установите LANGUAGESсписок языков. Например:

    LANGUAGES = [
        ('de', _('German')),
        ('en', _('English')),
    ]
    

    В этом примере языки, доступные для автоматического выбора, ограничиваются немецким и английским (и любым подъязыком, например de-chили en-us).

  • Если вы определяете настраиваемую LANGUAGESнастройку, как описано в предыдущем пункте, вы можете пометить имена языков как строки перевода - но используйте gettext_lazy()вместо этого, gettext()чтобы избежать циклического импорта.

    Вот пример файла настроек:

    from django.utils.translation import gettext_lazy as _
    
    LANGUAGES = [
        ('de', _('German')),
        ('en', _('English')),
    ]
    

После LocaleMiddlewareопределения предпочтения пользователя он делает это предпочтение доступным request.LANGUAGE_CODEдля каждого HttpRequest. Не стесняйтесь читать это значение в своем коде просмотра. Вот пример:

from django.http import HttpResponse

def hello_world(request, count):
    if request.LANGUAGE_CODE == 'de-at':
        return HttpResponse("You prefer to read Austrian German.")
    else:
        return HttpResponse("You prefer to read another language.")

Обратите внимание, что при статическом (без промежуточного программного обеспечения) переводе язык используется settings.LANGUAGE_CODE, а при динамическом (промежуточном) переводе - на языке request.LANGUAGE_CODE.

Как Django находит переводы

Во время выполнения Django создает в памяти унифицированный каталог литералов-переводов. Для этого он ищет переводы, следуя этому алгоритму в отношении порядка, в котором он проверяет различные пути к файлам для загрузки скомпилированных файлов сообщений ( .mo), и приоритета нескольких переводов для одного и того же литерала:

  1. Каталоги, перечисленные в, LOCALE_PATHSимеют наивысший приоритет, причем те, которые появляются первыми, имеют более высокий приоритет, чем те, которые появляются позже.
  2. Затем он ищет и использует, если он существует, localeкаталог в каждом из установленных приложений, перечисленных в INSTALLED_APPS. Те, которые появляются первыми, имеют более высокий приоритет, чем те, которые появляются позже.
  3. Наконец, базовый перевод, предоставленный Django, django/conf/locale используется в качестве запасного варианта.

Смотрите также

Переводы литералов, включенных в ресурсы JavaScript, ищутся по аналогичному, но не идентичному алгоритму. Подробнее JavaScriptCatalogсм.

Вы также можете поместить файлы пользовательского формата в LOCALE_PATHSкаталоги, если вы также установили FORMAT_MODULE_PATH.

Во всех случаях ожидается, что имя каталога, содержащего перевод, будет называться с использованием нотации имени локали . Например de, pt_BR, es_ARи т.д. Непереведенные Струны для территориальных языковых вариантов использование перевода родового языка. Например, для непереведенных pt_BRстрок используются pt переводы.

Таким образом, вы можете писать приложения, которые включают свои собственные переводы, и вы можете переопределить базовые переводы в своем проекте. Или вы можете создать большой проект из нескольких приложений и поместить все переводы в один большой общий файл сообщений, специфичный для проекта, который вы составляете. Выбор за вами.

Все репозитории файлов сообщений имеют одинаковую структуру. Они есть:

  • Ищутся все пути, указанные в LOCALE_PATHSвашем файле настроек.<language>/LC_MESSAGES/django.(po|mo)
  • $APPPATH/locale/<language>/LC_MESSAGES/django.(po|mo)
  • $PYTHONPATH/django/conf/locale/<language>/LC_MESSAGES/django.(po|mo)

Для создания файлов сообщений вы используете инструмент. И вы используете для создания двоичных файлов, которые используются .django-admin makemessagesdjango-admin compilemessages.mogettext

Вы также можете запустить, чтобы компилятор обработал все каталоги в ваших настройках.django-admin compilemessages --settings=path.to.settingsLOCALE_PATHS

Использование неанглийского базового языка

Django делает общее предположение, что исходные строки в переводимом проекте написаны на английском языке. Вы можете выбрать другой язык, но должны помнить об определенных ограничениях:

  • gettext предоставляет только две формы множественного числа для исходных сообщений, поэтому вам также потребуется предоставить перевод для базового языка, чтобы включить все формы множественного числа, если правила множественного числа для основного языка отличаются от английского.
  • Если активирован английский вариант и отсутствуют английские строки, резервным языком будет не язык LANGUAGE_CODEпроекта, а исходные строки. Например, англичанин, посещающий сайт с LANGUAGE_CODEустановленным испанским языком и исходными строками, написанными на русском языке, увидит русский текст, а не испанский.

Copyright ©2021 All rights reserved