Мультиязычный сайт на Django: короткое руководство
За полтора-два часа вы настроите переключение языков, переведёте интерфейс и подготовите модели к переводу контента. Разбираем на Django 5.x без сторонних пакетов и с одним проверенным для контента.

К концу этого руководства у вас будет сайт на Django 5.x с двумя языками (русский и английский), переключателем в шапке, переведёнными надписями интерфейса и URL вида /en/about/ и /ru/about/. Строки в шаблонах и в коде будут подхватывать язык из адреса, а тексты в базе — храниться на обоих языках. На чистом проекте это занимает полтора-два часа, из них большая часть уходит на сам перевод строк, а не на настройку.
Django делит задачу на две части, и их важно не путать. Перевод интерфейса (кнопки, подписи, сообщения) решается встроенным механизмом i18n и файлами .po. Перевод контента из базы (названия товаров, тексты статей) встроенными средствами не делается — для него нужен либо отдельный пакет, либо ручная схема с дополнительными полями. Здесь разбираем оба, начиная с интерфейса.
Что нужно до начала
- Django 5.x и работающий проект (хотя бы одно приложение и один шаблон).
- Установленный компилятор переводов gettext. На Linux — пакет
gettext, на macOS —brew install gettext, на Windows проще всего взять готовые бинарники и добавить их в PATH. Без него командаcompilemessagesне заработает. - Доступ к
settings.pyи корневомуurls.pyпроекта.
Шаг 1. Включаем i18n в настройках
Механизм интернационализации в Django по умолчанию частично включён, но нам нужны конкретные параметры. Откройте settings.py.
# settings.py (Django 5.x)
from django.utils.translation import gettext_lazy as _
USE_I18N = True # включает систему перевода
USE_TZ = True
LANGUAGE_CODE = "ru"
# Языки, между которыми переключаемся
LANGUAGES = [
("ru", _("Русский")),
("en", _("English")),
]
# Где лежат файлы переводов
LOCALE_PATHS = [
BASE_DIR / "locale",
]Теперь добавьте LocaleMiddleware в MIDDLEWARE. Порядок важен: он идёт после SessionMiddleware и перед CommonMiddleware.
# settings.py
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.locale.LocaleMiddleware", # добавили сюда
"django.middleware.common.CommonMiddleware",
# ...остальное как было
]Проверка: запустите python manage.py runserver. Ошибок при старте быть не должно. Если видите ImproperlyConfigured про порядок middleware — проверьте, что LocaleMiddleware стоит именно между двумя указанными.
Шаг 2. Добавляем языковой префикс в URL
Чтобы адреса выглядели как /ru/ и /en/, оберните маршруты в i18n_patterns. В корневом urls.py:
# urls.py
from django.conf.urls.i18n import i18n_patterns
from django.urls import path, include
# Эти адреса без языкового префикса — например, служебный переключатель
urlpatterns = [
path("i18n/", include("django.conf.urls.i18n")),
]
# А эти — с префиксом /ru/ и /en/
urlpatterns += i18n_patterns(
path("about/", include("pages.urls")),
prefix_default_language=True,
)Параметр prefix_default_language=True означает, что даже язык по умолчанию получит префикс (/ru/about/). Если поставить False, русский будет отдаваться на /about/ без префикса — это иногда удобнее для SEO, но тогда следите за дублями и редиректами.
Проверка: откройте /en/about/ и /ru/about/ — обе должны отдавать страницу. Адрес /about/ без префикса при True сделает редирект на язык из заголовка браузера.
Шаг 3. Помечаем строки для перевода
В шаблонах загрузите тег i18n и оборачивайте текст в {% translate %} (короткий алиас — {% trans %}).
{% load i18n %}
<h1>{% translate "О компании" %}</h1>
<p>{% blocktranslate %}Мы делаем сайты с 2015 года.{% endblocktranslate %}</p>В Python-коде используйте gettext_lazy для строк, которые вычисляются на этапе импорта (поля моделей, choices), и обычный gettext внутри вьюх.
from django.utils.translation import gettext_lazy as _
class Article(models.Model):
STATUS_CHOICES = [
("draft", _("Черновик")),
("published", _("Опубликовано")),
]Шаг 4. Собираем и переводим файлы
Создайте каталог locale в корне проекта, затем выполните команду сбора строк:
python manage.py makemessages -l enПоявится файл locale/en/LC_MESSAGES/django.po. Откройте его — увидите пары msgid (исходная строка) и пустой msgstr. Заполните переводы:
msgid "О компании"
msgstr "About us"Скомпилируйте .po в бинарный .mo, который читает Django:
python manage.py compilemessagesПроверка: перезапустите сервер, откройте /en/about/ — заголовок должен смениться на английский. Если он остался русским, чаще всего дело в незапущенном compilemessages или в том, что gettext не установлен.
Шаг 5. Переключатель языка
Форма отправляет POST на встроенный view set_language, который мы подключили как i18n/ в шаге 2.
{% load i18n %}
<form action="{% url 'set_language' %}" method="post">
{% csrf_token %}
<input name="next" type="hidden" value="{{ request.path }}">
{% get_current_language as CURRENT %}
{% get_available_languages as LANGS %}
<select name="language" onchange="this.form.submit()">
{% for code, name in LANGS %}
<option value="{{ code }}"{% if code == CURRENT %} selected{% endif %}>{{ name }}</option>
{% endfor %}
</select>
</form>Выбранный язык сохраняется в сессии и cookie, а LocaleMiddleware подхватывает его при следующих запросах.
Перевод контента из базы
Здесь встроенный механизм заканчивается. Названия и тексты в БД он не трогает — gettext работает только со строками в коде и шаблонах. Есть два рабочих пути.
Главная развилка: переводите ли вы фиксированный интерфейс или пользовательский контент. Первое — файлы .po. Второе — либо поля в модели, либо специализированный пакет. Смешивать их одним инструментом не получится.
| Подход | Что даёт | Чем платите |
|---|---|---|
Отдельные поля в модели (title_ru, title_en) | Ноль зависимостей, полный контроль, простая миграция | Ручной выбор поля по языку, разрастание модели при многих языках |
| Пакет django-modeltranslation | Прозрачная подмена поля по активному языку, работа в админке | Внешняя зависимость, поля-колонки в БД, миграции при добавлении языка |
Для двух языков и пары моделей ручные поля обходятся дешевле. Когда языков четыре и больше, а моделей десятки — берите пакет, чтобы не писать логику выбора вручную. Установка и точный синтаксис регистрации моделей у django-modeltranslation описаны в его документации; проверьте совместимость версии с вашим Django перед установкой.
Если не получилось
- Строки не переводятся, остаются русскими. Скорее всего не запущен
compilemessagesили после правки.poсервер не перезапущен. Проверьте, что рядом сdjango.poпоявилсяdjango.mo. - CommandError: Can't find msguniq / xgettext. Не установлен
gettextлибо его бинарники не в PATH. На Windows это самая частая причина. - Все URL внезапно требуют префикс и старые ссылки ломаются. Вы обернули в
i18n_patternsте маршруты, которым префикс не нужен (API, вебхуки). Оставьте их в обычномurlpatternsвне обёртки. - Строка помечена, но не попала в .po. Забыли
{% load i18n %}в начале шаблона или использовалиgettextтам, где нуженgettext_lazy(например, в определении модели на этапе импорта). - Переключатель меняет язык, но страница остаётся прежней. В скрытом поле
nextнет текущего пути либо middleware стоит в неправильном месте.
Что делать дальше
После настройки основного механизма настройте отдачу hreflang в шаблоне, чтобы поисковики связывали языковые версии страниц. Добавьте Content-Language в ответы и проверьте, что sitemap генерирует записи для каждого языка. Если контента в базе много — заведите отдельную задачу на инвалидацию кеша при переключении языка: закешированная страница на одном языке не должна отдаваться на запрос другого. Локализация дат, чисел и множественного числа ({% blocktranslate count %}) — отдельная тема, здесь мы её не разворачивали.
Инженер-технолог: Перевод технических мануалов с CN/EN на русский ГОСТовский язык.
Спросить за 20 ₽Источники: Django documentation — Translation, Django documentation — Internationalization and localization
Частые вопросы
У меня Django 4.2, всё сработает так же?
Обязательно ставить gettext, или можно без него?
Нужно ли перезапускать сервер после каждого изменения перевода?
Можно ли отдавать язык по умолчанию без префикса в URL?
Как перевести данные, которые пользователи вводят через админку?
Переводы работают локально, но на проде строки остались на исходном языке. Почему?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.