Мультиязычность и перевод в Django

4 марта 2016 г. 22:40

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

1. Настраиваем settings.py

Несколько простых строк в settings.py позволят добавить многоязычность сайту:

LANGUAGE_CODE = 'ru'  # язык сайта по умолчанию

LANGUAGES = (
    ('ru', 'Russian'),
    ('en', 'English'),
)

USE_I18N = True  # активация системы перевода django

# месторасположение файлов перевода
LOCALE_PATHS = (
    'locale',
    # os.path.join(PROJECT_DIR, 'locale'),
)

Если вы используете django-cms, то добавьте ещё этот милдварь 'cms.middleware.language.LanguageCookieMiddleware', . Это полезно для запоминания, какой язык был определён, и при последующем заходе на сайт будет активизироваться именно этот язык.

2. Подготавливаем слова и фразы для перевода

В моделях:

from django.utils.translation import ugettext_lazy as _
...
title = models.CharField(max_length=100, verbose_name=_('Title'))

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

В формах:

from django.utils.translation import ugettext_lazy as _
...
title = forms.CharField(label=_('Title'), max_length=100)

В представлениях:

from django.utils.translation import ugettext as _
...
text = _('My variable')

В шаблонах:

{% load i18n %}
...
{% trans 'Hello' %} - для перевода простой фразы
{% trans "Title" context "my title" %} - для перевода простой фразы с контекстом
{% blocktrans %}Hello, dear {{ request.user.username }}{% endblocktrans %} - для перевода фразы блока с дополнительными переменными, тегами и фильтрами
{% custom_tag _('some text') value|yesno:_('yes,no') %} - для перевода фразы в тегах и фильтрах
Внимание
Не используйте знак "|" (вертикальная черта) при переводе фразы в фильтре или теге (к примеру, {{ value|some_filter:_('foo|bar') }}), потому что в файле перевода появится что-то подобное FFFFFFFXXXXXXXX и перевод работать не будет.

Пример работы с уже переведёнными словами другими приложениями, например, Name

# -*- coding: utf-8 -*-
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import pgettext_lazy
from django import forms


class ContactForm(forms.Form):
    name = forms.CharField(max_length=255, label=pgettext_lazy('form label', 'Name'))
    phone = forms.CharField(max_length=100, label=_('Phone'))
Замечание
Обратите внимание, что я не использовал оператор as для сокращения обращения к функции pgettext_lazy. Почему-то с pgettext_lazy это не работает. То есть, нельзя записать для сокращения что-то вроде from django.utils.translation import ugettext_lazy as _p и использовать name = forms.CharField(max_length=9, label=_p('form label', 'Name')) - перевод в файл не добавится.

Если бы мы использовали как обычно _('Name'), то наш бы перевод не применился, так как другое приложение (в моём случае это был django-taggit) перевело бы так: "Название", а в форме обратной связи нам нужно перевести как "Имя".

Чтобы использовался наш перевод, нужно выполнить один из вариантов:

  1. Переместить наше приложение перед другими приложениями, которые переводят наше используемое слово.
  2. Использовать pgettext_lazy, который создаёт для слова контекст.

Немного подробнее про pgettext_lazy

По контексту (первый аргумент функции pgettext_lazy) Django определяет вариант перевода.

Например, мы можем Name в одном случае будет "Название", в другом - "Имя", в третьем - "Наименование".

name = pgettext_lazy('form label', 'Name')  # name = Имя (отправителя)
name = pgettext_lazy('article label', 'Name')  # name = Наименование (детали)
name = pgettext_lazy('city label', 'Name')  # name = Название (города)

3. Создаём файлы для перевода и компилируем

Для создания файл перевода нужно запустить команду makemessages с указанием языка опцией -l. Таким образом, нужно вызвать команду столько раз сколько у вас языков (если ошибаюсь, поправьте), например:

$ python manage.py makemessages -l ru
$ python manage.py makemessages -l en

Возможен запуск с конкретного приложения:

$ cd my_app
/my_app $ python ../manage.py makemessages -l en

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

CommandError: errors happened while running xgettext on CompoundDoc.py
xgettext: ./env/lib/python2.7/site-packages/xlwt/CompoundDoc.py:1: Кодировка "windows-1252" неизвестна.  Продолжение работы с ASCII.
xgettext: Non-ASCII string at ./env/lib/python2.7/site-packages/xlwt/CompoundDoc.py:209.
          Please specify the source encoding through --from-code.

Я держу папку виртуального окружения в корне проекта, поэтому команда makemessages обошла все установленные зависимости для проекта и встретила проблему с переводом пакета xlwt. Для решения проблемы нужно исключить ненужные папки для команды, например, так:

$ python manage.py makemessages -l en -i env -i services

Команда исключит папки env и services.

После того как папки с языками создались для обновления перевода всех языков можно воспользоваться командой:

$ python manage.py makemessages -a
# или
$ python manage.py makemessages -a -i env -i services

Ключ -a указывает, чтобы команда обновила перевод для всех существующих языков.

Фрагмент файла 'ru/LC_MESSAGES/django.po':

msgctxt "form label"
msgid "Name"
msgstr "Имя"

Обратите внимание, что если мы использовали pgettext_lazy, то в файле django.po будет ещё добавлен msgctxt для переводимого слова, например:

msgctxt "form label"
msgid "Name"
msgstr "Имя"

msgctxt "article label"
msgid "Name"
msgstr "Наименование"

msgctxt "city label"
msgid "Name"
msgstr "Название"

Иногда django может пометить фразы как fuzzy. Например:

#: settings.py:306
msgid "Maltsev Artem Blog"
msgstr "Блог Мальцева Артёма"

#: templates/base.html:11
#, fuzzy
#| msgid "Maltsev Artem Blog"
msgid "Maltsev Artem"
msgstr "Блог Мальцева Артёма"

Django облегчает нам работу с переводом: если находит похожий msgid, то использует его msgstr, то есть его перевод. Таким образом, нам остаётся всего лишь подправить перевод и удалить строчку "#, fuzzy", иначе перевод не будет применён. Строку, начинающуюся с "#| msgid ..." (после "#, fuzzy"), можно тоже удалить, чтобы место не занимала. В итоге наш пример преобразуется следующим образом:

#: settings.py:306
msgid "Maltsev Artem Blog"
msgstr "Блог Мальцева Артёма"

#: templates/base.html:11
msgid "Maltsev Artem"
msgstr "Мальцев Артём"

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

$ python manage.py compilemessages

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

~/projects/vivazzi/spec $ python ../manage.py compilemessages

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

4. Добавляем в шаблон код смены языка

Начиная с Django 1.9 переключатель языка можно добавить так:

from django.conf import settings
from django.conf.urls import include, url
from django.conf.urls.i18n import i18n_patterns
from django.contrib import admin
from django.conf.urls.static import static

admin.autodiscover()

i18n_urls = (
    url(r'^admin/', include(admin.site.urls)),
    ...
    url(r'^i18n/', include('django.conf.urls.i18n')),
)

urlpatterns = [
    ...
]

urlpatterns.extend(i18n_patterns(*i18n_urls, prefix_default_language=False))
urlpatterns.extend(static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT))

Как написано в документации, для работоспособности переключателя не нужно url(r'^i18n/', include('django.conf.urls.i18n')) применять с функцией i18n_patterns. Но у меня почему-то не сработало, а вот код выше вполне работает. Я использую django-cms и может быть здесь идёт какой-то конфликт (ниже вы увидите соответствующий код). Если знаете в чём дело, пишите в комментариях, буду рад подправить статью.

Затем можно добавить сам 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="{% trans 'Change' %}" />
</form>

Это пример взят из документации и немного изменён (добавил перевод кнопки).

Если вы используете django-cms, то всё ещё проще: достаточно просто использовать в шаблоне следующий код (без дополнительных подключений django.conf.urls.i18n урлов):

{% load i18n menu_tags %}

{% for language in languages %}
<li{% if current_language == language.0 %} class="active"{% endif %}>
    <a href="{% page_language_url language.0 %}" title="{% trans 'Change language to' %} {{ language.1 }}">{{ language.0|upper }}</a>
</li>
{% endfor %}

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

Дополнительные материалы по теме

Частичное использование языкового префикса для отдельных url - 1. Поддержка нескольких языков сайта

Оцените статью

4,5 из 5 (всего 2 оценки)

Поля, отмеченные звёздочкой ( * ) , являются обязательными.

Спасибо за ваш отзыв!

После нажатия кнопки "Отправить" ваше сообщение будет доставлено мне на почту.

Автор статьи

Права на использование материала, расположенного на этой странице http://vivazzi.pro/it/translate-django/:

Разрешается копировать материал с указанием её автора и ссылки на оригинал без использования параметра rel="nofollow" в теге <a>. Использование:

Автор статьи: Мальцев Артём
Ссылка на статью: <a href="http://vivazzi.pro/it/translate-django/">http://vivazzi.pro/it/translate-django/</a>

Подробнее: Правила использования сайта

Комментариев: 2

Александр Б.
Александр Б.

23.06.2018 6:18 #

китайский не подгружается((

Ответить

Артём Мальцев
Артём Мальцев автор

24.06.2018 2:21 #

Александр, с китайским не имел дело. Расскажите по-подробнее, что именно не загружается. Какое поведение, трейсбек и пр.

Ответить

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

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

Отправить

На данный момент нет специального поиска, поэтому я предлагаю воспользоваться обычной поисковой системой, например, Google, добавив "vivazzi" после своего запроса.

Попробуйте