Вступлени. Как это вообще работает
В этой статье я опишу процесс и возможные области переводов на Django. Так процесс переводов HTML файлов и Python скриптов сильно отличается от того же перевода JS файлов.
Так же при локализации надо понимать, как именно ты хочешь показать другую версию языка. И хорошей идеей будет отобразить это непосредственно в URL. Есть варианты:
- Создать отдельный сайт с соответствующим доменом. По типу example.de. Это слишком дорого и не практично.
- Добавить поддомен. По типу ru.example.com
- Добавить директорию. По типу example.com/en/
- Или использовать URL параметр example.com?loc=en
У каждого из этих вариантов есть свои плюсы и минусы. И я не из тех, кто может говорить об этом. Всё довольно подробно описано здесь, в блоге гугла. Я опробовал только вариант с директориями, ибо ... ну, у меня один сервер и ограниченный бюджет. Поэтому в этой статье вы найдёте именно этот способ отображения переводов.
В любом случае перевод сайта на django можно разделить на 4 условные задачи. Это перевод строк в python коде, перевод django-моделей, перевод строк в шаблонах и перевод строк в javascript коде.
И сам процесс можно разделить на следующие этапы:
- Помечаем(выделяем) необходимые строки для перевода.
- Собираем все помеченные(выделенные) строки.
- Пишем переводы для них
- Компилируем специальный *.mo файл
Но перед тем, как начать переводить необходимо донастроить свой сайт. Без этого никак.
Базовая настройка
В первую очередь подключим соответствующий мидлвари. Его основная роль это редиректы на соответствующие локализованные страницы и вставка тех строк перевода, которые нужны для текущей локали сайта.
В файле settings.py:
Ещё нужно записать код языка по умолчанию, он же текущий, и подготовить список поддерживаемых локализаций(переводов) сайта. Так же добавь глобальную переменную USE_I18N. Последний пункт не обязателен, он включён по умолчанию, но я просто люблю чтобы всё было явно настроено.
Последним штрихом будет, указание локализуемых маршрутов. В данном случае, не имеет смысла локализовывать все api или admin пути, ну и уж тем более allauth пути. Это то, что останется не тронутым. То есть, будем локализовывать наш фронтенд. Это страницы about, contacts, главная и страницы подтверждения почты и сброса пароля.
В файле Website/urls.py добавь следующие строчки:
Кстати, функцию i18n_patterns можно использовать только в главном urls.py. Такие дела.
Предварительная настройка завершена.
Локализация сайта по этапам
Все дальнейшие подглавы будут описывать процесс локализации сайта. Начиная от сбора строчек для перевода заканчивая их компиляцией.
Выделяем строчки для перевода
Нам нужно указать что, собственно говоря, нужно перевести и для каждой части сайта, будь то шаблон, или python код, или javascript код, оно будет разным.
Переводы в python коде
Для того чтобы пометить строчки для перевода, необходимо импортировать следующие функции: gettext и gettext_lazy.
Дальше, нужно просто обернуть нужную тебе строчку в gettext или gettext_lazy. Сразу замечу, не вокруг каждой строки возможно обернуть функцию gettext. Например, переменной или вычисляемым значениям. То есть, например:
В данном примере, django сможет обнаружить только первую строку "Usual string". Все остальные он проигнорирует.
Я так же сделал импорт такой функции как gettext_lazy. В чём его особенность и когда его применять? В общем и целом единственное отличие их друг от друга состоит в том, что второй берёт строку из *.po файла, только тогда, когда эта строка используется на сервере. Так по крайней мере написано на официальном сайте django.
Эту функцию (gettext_lazy) стоит применять, при необходимости перевода django моделей и его аттрибутов, описаний для админки. Но в 90% случаев, gettext вам хватит. Плюс, как я заметил раньше, чтобы сделать модели интернациональными, потребуется нечто большее чем встроенные утилиты django.
Переводы в шаблонах
Для перевода текста в шаблонах тебе нужно знать только три тега:
В начале шаблона тебе нужно будет загрузить специальный модуль, i18n. Каждый раз, когда тебе будет нужно перевести некий текст в шаблоне, загружай модуль. Даже если этот шаблон наследуется от другого шаблона, который уже загрузил i18n.
После того как модуль i18n был загружен. У тебя есть два варианта:
- Первый это использовать однострочный перевод.
- Второй использовать многострочный перевод.
Разница между ними такова, что во втором случае между тегами всё попадёт под перевод, в том числе и другие теги. Будь в курсе.
Переводы в JS файлах
Настройка переводов javascript кода, наверное, самое сложное и муторное. Хотя на первый взгляд, вроде и туториал есть и не требуется ничего экстра ординарного. Но об этом позже. Давай я сначала покажу как настроить переводы JS через django.
Первое, что нужно сделать это подключить соответствующие пути в urls.py. Причём замечу, если ты пользуешься функцией i18n_patterns, то и подключать JavaScriptCatalog класс нужно там.
Класс JavaScriptCatalog, будет генерировать для нас мини-js библиотеку с каталогом строчек перевода. Да, это поубавит несколько скорость загрузки страницы поэтому в будущем надо будет не забыть кешировать это. Теперь подключим этот скрипт.
Настройка бэкенда для перевода готова. Теперь нужно пометить строки, которые мы хотим перевести. Итак, нам потребуется только функция gettext(), котора определена в подключённом ранее JS-файле. Например, вот так:
Вот таким вот образом мы помечаем строки для перевода. То есть просто оборачиваем их в соответствующую функцию gettext.
Собираем строки
После настроек для переводов шаблонов, python и javascript, нужно собрать все отмеченные строки в файлы *.po. Предварительно создав директорию locale в каждом приложении в котором есть строки для перевода. Если все необходимые директории (locale) для переводов были созданы, нужно запустить следующую команду:
Если ты помечал строчки в JS, то необходимо запустить ещё одну команду для сбора строк и из JS файлов. В этой команде мы используем флаг -d со значением djangojs.
Пожалуйста, ещё используй флаг -i или --ignore. Смотри, я использую Node.js и ReactJS. Все они находятся в директории node_modules и не только они на самом деле. Если ты не поставишь данный флаг, django со всей ответственностью проверит каждый JS-файл в твоём проекте, а их будет десятки тысяч.
Переводы, как их заполнять
В результате этих двух команд мы получим файлы django.po - от первой команды и djangojs.po - в качестве результата второй команды. Там будут вот такие строки, например:
Тебе нужно будет заполнить всё что в "" после msgstr. Ничего не трогай внутри msgid, это первое. И второе, при переводе многострочных элементов (это те которые начинаются с "\n") после msgid - msgstr так же должен начинаться с "\n".
В любом случае, если и будут ошибки при компиляции, то они очень информативны и у тебя не будет проблем их решить самостоятельно. Кстати о компиляции.
Компиляция переводов
Последним этапом переводов является их компиляция в *.mo файлы. Чтобы скомпилировать переводы, вне зависимости от того переводы это шаблонов, python или js кода, используй следующую команду:
Поздравляю! Мы закончили переводить наш сайт и теперь он может поддерживать множество других языков, вне зависимости, какие и где эти строчки находятся. Но если, качество переводов JS файлов тебя не устраивает, или ты столкнулся с проблемой переводов JS файлов на React, он просто их не находит, то продолжи читать следующую главу про переводы JS строк через i18next реакт модуль.
Переводы в JS файлах, через i18next фреймворк
В отличии перевода JS файлов через Django, настроить реакт для этого, гораздо сложнее. Но зато нахождение всех необходимых строчек гарантированно. Установим необходимы пакеты для начала:
Разберём каждый пакет, который установили:
- i18next - функциональное ядро для переводов
- react-i18next - связующее звено между React и i18next
- i18next-browser-languagedetector - позволяет определять текущий язык пользователя в браузере и его изменение
- i18next-http-backend - занимается тем, что доставляет переводы до конечного клиента
- i18next-parser - собирает все строчки для переводов
Теперь всё это нужно будет настроить. Начнём с парсера, создай файл i18next-parser.config.js в Frontend директории, это там где у тебя node_modules директория. После вставь следующий контент.
Если сравнивать с оригиналом, я многое что поменял.
- Во-первых, поменял лексер, lexers: для JS файлов.
- Во-вторых добавил локали, locales:, для Английского, Русского, Белорусского, Немецкого, Испанского и Французского.
- В третьих, я изменил путь сохранения результатов, output. Я помещаю все файлы переводов в директорию static. Спрашивается, почему? Когда я размещу на сервере этот сайт, все статические файлы должны обслуживаться этим сервером. Соответственно, все эти файлы должны будут перемещены в другую локацию. И у джанго уже есть встроенная команда для копирования всех статических файлов в эту локацию - collectstatic. Правильно, в том числе и наши локализации.
- В четвёртых, я проставил все значения для сепараторов на false. Сделал я это потому, что не использую их функции переводов, как это демонстрируется в их туториале. Я оборачиваю строчки и предложения в соответствующую функцию для перевода, вместо того, чтобы придумывать некие особенные ключи для каждого случая.
Следующее, что нам нужно сделать, это создать файл i18n.js рядом с index.js. И вставить следующий контент:
В этом файле мы настраиваем модуль i18n. Определяем как мы будем доставлять до конечного пользователя статические файлы (Backend модуль) и как мы будем определять язык пользователя (LanguageDetector модуль). Во-втором случае важно указать, чтобы 'path' было первым, ведь именно так, мы указываем изменение языка.
Делаем импорт данного компонента:
Теперь на примере одного из компонентов, я покажу как пометить строчки, которые необходимо перевести. В первую очередь делаем импорт функции перевода:
Дальше в функции-компонента, используем хук:
И теперь, чтобы обернуть необходимую строку для перевода:
Как ты мог заметить, я отправляю функцию t дальше, в качестве аргумента обработчика onConfirmEmail. Сделал я это потому, что переводы необходимы не только в реакт компонентах, но и в обычных обработчиках и функциях. Я не смог найти другой способ глобально определить функцию перевода, поэтому в каждой функции, которая нуждается в переводе я определяю дополнительный параметр - обработчики переводов t.
Соберём все выделенные мною строчки в файлы переводов. Для этого мы и устанавливали i18n-parser модуль. И чтобы и дальше нам было просто разрабатывать наш сайт, напишем отдельный скрипт для выполннения переводов в package.json.
Необходимо указать только, где и что искать используя специальный паттерн. ** - значит ищи во всех папках и подпапках. *.js - значит ищи любые файлы с расширением .js
Запустим скрипт, и он найдёт нам и за нас все строчки для переводов и создаст все, ранее указанные, файлы локализации.
Все файлы переводов ты найдёшь по пути static/locales/LOCALE/translation.json. И вот пример, сгенерированного файла локализации для русского языка:
Дальше посмотрим на вывод в консоль и то как выглядит страница при переводе на немецкий язык.

На примере одного из моих сайтов.
Как видно из консоли мы сделали XMLHttpRequest (или XHR) запрос два раза, чтобы получить немецкую и английскую локализацию. Почему две, а не одну? Всё потому, что мы указали в i18n.js, fallbackLng: 'en' . То есть если не удастся получить немецкую локализацию, мы будем использовать локаль по умолчанию.
Ещё видно, как модуль LanguageDetector сработал и заметил изменение языка пользователя. Всё, переводы JS кода на React завершены.
Эпилог
В этой статье мы с тобой разобрали, все возможные случаи, где нужно будет сделать перевод. Переводы в шаблонах или python коде не представляют сложность.
Хотя настройка и создание переводов для JS кода, тоже легко, только нужно учитывать, что он хорошо работает с ванильным JS. Со всем остальным могут возникнуть некоторые трудности. Так для нормальных переводов React кода, потребуется устанавливать дополнительные пакеты.
Ну и в заключении хочу добавить, что перевод дело не простое и долгое. И надеюсь я смог хоть немного облегчить тебе работу с ними.