У вас есть приложение на Django, которое работает на английском языке. Теперь вам нужно, чтобы оно работало и на других языках. Это руководство охватывает весь процесс, от разметки строк в коде до обслуживания полностью локализованного сайта, с практическими примерами на каждом шаге.
Фреймворк интернационалзации Django (обычно называемый i18n) является одним из лучших в своём роде. Он поддерживает языки с письмом справа налево, правила плюрализации, форматы дат и все прочие вещи, которые делают локализацию действительно сложной. Проблема в том, что настройка включает несколько взаимосвязанных частей, и большинство руководств рассматривают лишь одну или две из них.
Что на самом деле включает локализация
Локализация включает не только перевод строк. Правильно локализованное приложение Django обрабатывает:
- Перевод текста (шаблоны, представления, модели, JavaScript)
- Маршрутизацию URL по языкам (
/en/about/,/de/about/) - Форматирование дат, времени и чисел (14/03/2026 vs 03/14/2026)
- Правила плюрализции (в английском 2 формы, в арабском 6, в японском 1)
- Вёрстку справа налево для арабского, иврита, фарси
- Учёт часовых поясов
Большинству проектов на Django нужны только первые три пункта. Но стоит знать, что может фреймворк, прежде чем начинать, чтобы не загнать себя в угол.
Шаг 1: настройте параметры
Начните с настрйки интернационализации в settings.py:
from django.utils.translation import gettext_lazy as _
LANGUAGE_CODE = "en"
USE_I18N = True
USE_L10N = True
USE_TZ = True
LANGUAGES = [
("en", _("English")),
("de", _("German")),
("fr", _("French")),
("ja", _("Japanese")),
("es", _("Spanish")),
]
LOCALE_PATHS = [
BASE_DIR / "locale",
]
LANGUAGES определяет, какие языки поддерживает ваш сайт. Включайте только те языки, которые вы действительно планируете перевести. Добавление 30 языков без их перевода лишь создаёт пустые файлы .po и запутывает структуру URL.
LOCALE_PATHS указывает Django, где искать файлы переводов. По умолчанию это директория locale/ в каждом приложении, но единая директория locale/ на уровне проекта удобнее в управлении.
Шаг 2: добавьте промежточное ПО и настройте URL
Добавьте промежуточное ПО для локали в настройку MIDDLEWARE. Порядок здесь важен:
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.locale.LocaleMiddleware", # after SessionMiddleware
"django.middleware.common.CommonMiddleware",
# ...
]
LocaleMiddleware должен идти после SessionMiddleware (ему нужна сессия для запоминания языковых предпочтений) и перед CommonMiddleware. Неправильный порядок является одной из самых частых причин, по которым переводы Django молча не работают.
Затем оберните ваши URL-паттерны с помощью i18n_patterns:
from django.conf.urls.i18n import i18n_patterns
urlpatterns = [
path("admin/", admin.site.urls),
]
urlpatterns += i18n_patterns(
path("", include("myapp.urls")),
)
Это добавляет код языка в качестве префикса ко всем вашим URL: /en/about/, /de/about/, /fr/about/. Админка остаётся без префикса, так как в большинстве проектов ей не нужна локализация.
Шаг 3: отметьте строки для перевода
Здесь происходит основная часть работы. Каждая строка, обращённая к пользователю, в вашем коде должна быть обёрнута в функцию перевода.
В коде Python
from django.utils.translation import gettext as _
def my_view(request):
message = _("Your order has been placed.")
return render(request, "order.html", {"message": message})
Используйте gettext (с псевдонимом _) в представлениях и функциях. Для строк на уровне модуля, таких как метки полей модели, используйте gettext_lazy:
from django.utils.translation import gettext_lazy as _
class Product(models.Model):
name = models.CharField(_("product name"), max_length=200)
description = models.TextField(_("description"))
class Meta:
verbose_name = _("product")
verbose_name_plural = _("products")
Различие имеет значение: gettext_lazy откладывает поиск перевода до момента, когда строка фактически отображается. Это необхдимо для всего, что вычисляется при импорте (определения моделей, поля форм, имена URL). Использование обычного gettext на уровне модуля приведёт к тому, что перевод будет выполнен один раз при запуске и всем пользователям будет отдаваться неправильный язык.
В шаблонах
{% load i18n %}
<h1>{% trans "Welcome to our store" %}</h1>
<p>
{% blocktrans count items=cart.item_count %}
You have {{ items }} item in your cart.
{% plural %}
You have {{ items }} items in your cart.
{% endblocktrans %}
</p>
{% trans %} работает с простыми строками. {% blocktrans %} работает со строками, содержащими переменные или плюрализацию. Не забывайте {% load i18n %} в начале каждого шаблона, который использует эти теги.
В JavaScript
Django может сгенерировать JavaScript-каталог ваших переводов:
from django.views.i18n import JavaScriptCatalog
urlpatterns += i18n_patterns(
path("jsi18n/", JavaScriptCatalog.as_view(), name="javascript-catalog"),
)
Затем в вашем JavaScript:
document.getElementById("status").textContent = gettext("Loading...");
Подключите скрипт каталога перед любым JavaScript, который использует gettext:
<script src="{% url 'javascript-catalog' %}"></script>
Шаг 4: сгенерируйте и переведите файлы .po
После разметки строк сгенерируйте файлы переводов:
python manage.py makemessages -l de -l fr -l ja -l es
Это создаст файлы .po в вашей директории locale/:
locale/
de/LC_MESSAGES/django.po
fr/LC_MESSAGES/django.po
ja/LC_MESSAGES/django.po
es/LC_MESSAGES/django.po
Каждый файл .po содержит записи следующего вида:
#: myapp/views.py:12
msgid "Your order has been placed."
msgstr ""
В msgstr помещается перевод. Вы можете заполнять их вручную, нанять переводчиков или автоматизровать процесс с помощью инструментов перевода на основе ИИ.
После перевода скомпилируйте файлы .po в бинарные файлы .mo, которые Django может эффективно читать:
python manage.py compilemessages
Запускайте compilemessages каждый раз при обновлении переводов. Пропуск этого шага является ещё одной распространённой причиной, по которой переводы не отображаются. Django читает файлы .mo, а не файлы .po.
Шаг 5: обработка форматирования дат, чисел и времени
Настройка L10N в Django управляет форматированием дат и чисел. При USE_L10N = True Django автоматичски использует форматирование с учётом локали:
{% load l10n %}
<p>Order date: {{ order.created_at }}</p>
<p>Total: {{ order.total }}</p>
Дата, которая отображается как "July 10, 2026" на английском, автоматически покажется как "10. Juli 2026" на немецком и "2026年7月10日" на японском. Числа следуют аналогичным правилам: 1,234.56 на английском становится 1.234,56 на немецком.
Если вам нужно переопределить формат для конкретных значений, используйте фильтры localize и unlocalize:
{% load l10n %}
<!-- Always use locale formatting -->
<p>{{ value|localize }}</p>
<!-- Always use the default (English) format -->
<p>{{ value|unlocalize }}</p>
Шаг 6: добавьте переключатель языка
Пользователям нужен способ сменить язык. Вот простой переключатель языка, который работает с представлением set_language Django:
{% load i18n %}
<form action="{% url 'set_language' %}" method="post">
{% csrf_token %}
<input type="hidden" name="next" value="{{ request.path }}">
<select name="language" onchange="this.form.submit()">
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% for lang_code, lang_name in LANGUAGES %}
<option value="{{ lang_code }}"
{% if lang_code == LANGUAGE_CODE %}selected{% endif %}>
{{ lang_name }}
</option>
{% endfor %}
</select>
</form>
Убедитесь, что URL set_language включён в вашу кофигурацию URL:
urlpatterns = [
path("i18n/", include("django.conf.urls.i18n")),
# ...
]
Он должен быть вне i18n_patterns, так как самому переключателю языка не нужен языковой префикс.
Распространённые ошибки и как их избежать
Использование gettext вместо gettext_lazy в моделях
Это самая распространённая ошибка локализации в проектах Django. Поля моделей, метки форм и всё остальное, определённое на уровне модуля, требует gettext_lazy, а не gettext. Симптом: все пользователи видят переводы на том языке, с которым запустился серверный процесс.
Забыли запустить compilemessages
Вы отредактировали файл .po, перезапустили сервер, но переводы по-прежнему не отображаются. Django читает файл .mo, и он не обновляется автоматически. Добавьте compilemessages в ваш конвейер развёртывания.
Неправилный порядок промежуточного ПО
LocaleMiddleware после CommonMiddleware означает, что Django не может определить язык из префикса URL. Запрос каждый раз будет обрабатываться с языком по умолчанию. Размещайте его между SessionMiddleware и CommonMiddleware.
Жёстко заданные строки в шаблонах
Легко пропустить строки, которые нужно перевести. Проверьте ваши шаблоны на наличие текста вне тегов {% trans %} или {% blocktrans %}. Частые нарушители: метки кнопок, плейсхолдеры полей форм, сообщения об ошибках и текст подвала.
Отсутствие обработки плюрализации
В английском языке две формы множественного числа (1 item, 2 items). В русском три. В арабском шесть. Если вы жёстко пишете "item(s)" вместо использования ngettext или {% blocktrans count %}, ваши переводы будут звучать неуклюже или окажутся грамматически неправильными на других языках.
Автоматизация рабочего процесса перевода
Ручной перевод файлов .po подходит для небольших проектов, но не масштабируется. Типичный проект Django с 200 переводимыми строками на 5 языков означает 1000 переводов для управления. Каждый раз, когда вы добавляете или изменяете строку, нужно обновлять все языки.
Рабочий процесс обычно выглядит так:
- Отметьте новые строки с помощью
_()или{% trans %} - Запустите
makemessagesдля их извлечения - Переведите новые записи в каждом файле
.po - Запустите
compilemessages - Закоммитьте всё
Шаги 3 и 4 замедляют процесс. Переводчики добавляют задержку и стоимость. Ручное копирование в сервисы перевода утомительно и чревато ошибками, особенно когда нужно сохранять форматы плейсхолдеров Django, такие как %(name)s или {count}.
Инструменты перевода на основе ИИ могут автоматизировать шаг 3, оставляя шаг 4 в вашем конвейере CI/CD. Компромисс заключается в том, что переводы ИИ могут нуждаться в проверке на предмет нюансов и терминологии, но для большинства строк интерфейса качество достатчно хорошее для прямого использования.
Тестирование локализации
Перед развёртыванием убедитесь, что ваша локализация действительно работает:
# Generate messages for all configured languages
python manage.py makemessages -a
# Check for missing translations
python manage.py compilemessages
# Start the dev server and switch languages
python manage.py runserver
Что нужно проверить:
- Переключайтесь между всеми поддерживаемыми языками с помощью переключателя
- Убедитесь, что URL содержат правильный языковой префикс
- Проверьте, что даты и числа форматируются корректно для каждой локали
- Ищите непереведённые строки (они будут отображаться на английском)
- Протестируйте языки с письмом справа налево, если вы их поддерживаете
- Убедитесь, что плюрализация работает правильно (протестируйте с 0, 1, 2 и 5+ элементами)
Рассмотрите возможность добавления проверки в CI, которая не проходит, если в каком-либо файле .po есть непереведённые или нечёткие записи. Это предотвращает случайные развёртывания с пропущенными переводами.
Структура проекта
Хорошо организованная настройка локализации выглядит следующим образом:
myproject/
locale/
de/LC_MESSAGES/django.po
fr/LC_MESSAGES/django.po
ja/LC_MESSAGES/django.po
es/LC_MESSAGES/django.po
myapp/
templates/
views.py
models.py
settings.py
urls.py
TRANSLATING.md
Файл TRANSLATING.md необязателен, но полезен. Он документирует ваши терминологические предпочтения, рекомендации по тону и любые специфичные для перевода правила вашего проекта. Если вы используете инструменты перевода на основе ИИ, которые поддерживают контекстные файлы, они могут прочитать этот файл и применить ваши предпочтения автоматически.
Что делать дальше
Если вы начинаете с нуля:
- Добавьте настройки и промежуточное ПО из шагов 1 и 2
- Выберите самый важный шаблон и отметьте его строки (шаг 3)
- Сгенерируйте файлы
.poсначала для одного языка (шаг 4) - Переведите этот один язык и убедитесь, что всё работает от начала до конца
- Затем расширяйте на другие шаблоны и языки
Если у вас уже есть частично локализованное приложение:
- Запустите
makemessages -aдля повторной генерации всех файлов.po - Проверьте наличие нечётких или непереведённых строк
- Заполните пропущенные переводы
- Настройте проверку в CI, чтобы новые непереведённые строки обнаруживались до того, как попадут в продакшен
Начинать с малого и расширяться лучше, чем пытаться локализовать всё сразу. Добейтесь полной работы одного языка, а затем добавляйте остальные.