Как добавить переводы для django сайта (python, js, шаблоны и модели) SearchResultParser ч. 5
06.02.2025
17.03.2025
17 минут
118
0
0
0
0
Вступление
Ты не поверишь, но я уже давно хотел написать данную статью. А знаешь почему? Потому что это первое что я сделал для своего сайта и первое в чём я разобрался. В этой статье мы будем переводить мой сайт парсинга результатов поиска. И я попытаюсь охватить все возможные проблемы и ситуации которые могут приключиться с тобой, когда ты тоже захочешь локализовать свой сайт.
Как это вообще работает
При локализации надо понимать, как именно ты хочешь показать другую версию языка. И хорошей идеей будет отобразить это непосредственно в URL. Есть варианты:
- Создать отдельный сайт с соответствующим доменом. По типу example.de. Это слишком дорого и не практично.
- Добавить поддомен. По типу ru.example.com
- Добавить директорию. По типу example.com/en/
- Или использовать URL параметр example.com?loc=en
У каждого из этих вариантов есть свои плюсы и минусы. И я не из тех, кто может говорить об этом. Всё довольно подробно описано здесь, в блоге гугла. Я опробовал только вариант с директориями, ибо ... ну, у меня один сервер и ограниченный бюджет. Поэтому в этой статье вы найдёте именно этот способ отображения переводов.
В любом случае, перевод сайта на django можно разделить на 4 условные группы. Это перевод строк в пайтон, перевод django-моделей, перевод строк в шаблонах и перевод строк в javascript коде.
Переводы строк для сайта, можно разделить на следующие этапы:
- Помечаем(выделяем) необходимые строки для перевода.
- Собираем все помеченные(выделенные) строки.
- Пишем переводы для них
- Компилируем специальный *.mo файл
Базовая настройка
Но перед тем как мы начнём выделять и переводить, нужно ещё немного поднастроить наш сайт. И в первую очередь подключим соответствующий мидлвари. Его основная роль это редиректы на соответствующие локализованные страницы и вставка тех строк перевода, которые нужны для текущей локали сайта.
В файле settings.py:
Ещё нужно записать код языка по умолчанию, он же текущий и подготовить список поддерживаемых локализаций(переводов) сайта. Так же добавь глобальную переменную USE_I18N. Последний пункт не обязателен, он включён по умолчанию, но я просто люблю чтобы всё было явно настроено. Так же в файле settings.py, где-нибудь в конце добавь:
Так как, этот сайт, по факту, обычное веб-приложение, то есть контент там публиковаться не будет, я могу позволить себе написать через гугл переводчик все необходимые переводы.
Последним штрихом будет, указание от каких путей мы хотим локализации. В данном случае, не имеет смысла локализовывать все api или admin пути, ну и уж тем более allauth пути. Это то, что останется не тронутым. То есть, будем локализовывать наш фронтенд. Это страницы about, contacts, главная и страницы подтверждения почты и сброса пароля.
В файле Website/urls.py добавь следующие строчки:
Кстати, функцию i18n_patterns можно использовать только в главном urls.py. Такие дела.
Предварительная настройка завершена. Теперь можно проверить как она работает. Зайди на мой сайт(Когда я размещу данный сайт на хостинге здесь появится ссылка), а если ты делаешь это параллельно мне, то открыв браузер по пути localhost:8000, тебя должно перенаправить на localhost:8000/en/.
Ты можешь зайти на любую языковую версию своего сайта, если ты конечно её добавил в LANGUAGES. Конечно, пока не на что смотреть, но потерпи до переводов. Всё будет \( ̄︶ ̄*\))
Выделяем строчки для перевода
Нам нужно указать что, собственно говоря, нужно перевести и для каждой части сайта, будь то шаблон, или python код, или javascript код, оно будет разным.
Переводы в python коде
Для того чтобы пометить строчки для перевода, необходимо импортировать следующие функции: gettext и gettext_lazy.
Дальше, нужно просто обернуть нужную тебе строчку в gettext или gettext_lazy. Сразу замечу, не вокруг каждой строки возможно обернуть функцию gettext. Например, переменной или вычисляемым значениям. То есть:
В данном примере, django сможет обнаружить только первую строку "Usual string". Все остальные он проигнорирует.
Я так же сделал импорт такой функции как gettext_lazy. В чём его особенность и когда его применять? В общем и целом единственное отличие их друг от друга состоит в том, что второй берёт строку из *.po файла, только тогда, когда эта строка используется на сервере. Так по крайней мере написано на официальном сайте django.
Эту функцию стоит применять, при необходимости перевода django моделей и его аттрибутов, описаний для админки. Но в 90% случаев, gettext вам хватит. Плюс, как я заметил раньше, чтобы сделать модели интернациональными, потребуется нечто большее чем встроенные утилиты django.
На текущий момент разработки сайта, SearchResultParsre, на стороне бэкенда нет ни одной строчки, которую необходимо было бы перевести. А если у вашего проекта есть такие строчки, то просто оберните их в gettext и идём дальше.
Переводы в шаблонах
Для перевода текста в шаблонах тебе нужно знать только три тега:
В начале шаблона тебе нужно будет загрузить специальный модуль, i18n. Каждый раз, когда тебе будет нужно перевести некий текст в шаблоне, загружай модуль. Даже если этот шаблон наследуется от другого шаблона, который уже загрузил i18n.
После того как модуль i18n был загружен. У тебя есть два варианта. Первый это использовать однострочный перевод . Второй использовать многострочный перевод . Разница между ними такова, что между тегами и всё попадёт под перевод, в том числе и другие теги. Будь в курсе.
Теперь выделим все необходимые строчки для перевода в app.html, base.html, about.html и contacts.html. Покажу на примере как будет выглядеть уже обработанный шаблон; файл Frontend/templates/Frontend/about.html:
Переводы в JS файлах, используя Django
Настройка переводов javascript кода, наверное, самое сложное и муторное. Хотя на первый взгляд, вроде и туториал есть и не требуется ничего экстра ординарного. Но об этом позже. Давай я сначала покажу как настроить переводы JS через django.
Первое, что нужно сделать это подключить соответствующие пути в Website/urls.py. Причём замечу, если ты пользуешься функцией i18n_patterns, то и подключать JavaScriptCatalog класс нужно там.
Класс JavaScriptCatalog, будет генерировать для нас мини-js библиотеку с каталогом строчек перевода. Да, это поубавит несколько скорость загрузки страницы поэтому в будущем надо будет не забыть кешировать это. Теперь подключи её в base.html шаблоне и мы готовы переводить.
Настройка бэкенда для перевода готова. Теперь нужно пометить строки, которые мы хотим перевести. Итак, нам потребуется только функция gettext(). Давай покажу на примере одной из моих функций, onReqeustPasswordReset:
Так же в JSX нужно будет оборачивать функцию gettext в такие вот скобочки {}. Пример из функции fetchProfile:
И по сути всё, мы готовы и можно собирать строки и компилировать их в *.mo файлы. Давай посмотрим, что получилось. Вот страница оригинала, на английском:

А вот переведённая страница на русский. Найдёшь что-нибудь странное?

Ага, не всё переведено. Даже больше, django не смог найти мои строчки для перевода. По этому я и не смог записать перевод. Почему так? Скорее всего из-за Реакта, он ведь собирается в один пакет и используется именно он, а не написанные мною компоненты.
Я перерыл интернет для решения данной проблемы и нашёл парня у которого была похожая проблема. На медиуме он сделал соответствующий пост о том как заставить команду makemessages находить строчки. Если вкратце, ему пришлось лезть в исходный код Django и добавлять параметр --language=python при указанном домене djangojs. По какой-то, не известной мне причине, xgettext находит больше строчек для перевода при указанном языковом флаге python. ¯\(°_o)/¯
В любом случае, данный способ мне не подходит, слишком не надёжный. Да и ломать Django ради того, чтобы перевести пару строчек ... Нее, лучше я реализую перевод JS файлов через React. Об этом через одну главу. А пока ...
Собираем, Переводим и Компилируем переводы
После настроек для переводов шаблонов, python и javascrip кода (используя django), нужно собрать все отмеченные строки в файлы *.po. Предварительно создав директорию locale в каждом приложении в котором есть строки для перевода. После создания нужно запустить следующую команду
Если ты ещё переводил строчки в JS, то необходимо запустить ещё одну команду. В этой команде мы используем флаг -d со значением djangojs. Это обязательный флаг если ты хочешь чтобы это сработало.
Пожалуйста, ещё используй флаг -i или --ignore. Смотри, я использую Node.js и ReactJS. Все они находятся в директории node_modules и не только они на самом деле. Если ты не поставишь данный флаг django со всей ответственностью проверит каждый грёбаный файл в твоём проекте, а их будет десятки тысяч. И это займёт время, так что я тебя предупредил.
В результате этих двух команд мы получим файлы django.po - от первой команды и djangojs.po - в качестве результата второй команды. Там будут вот такие строки:
Просто хочу сделать пару замечаний, когда ты начнёшь делать переводы. Ничего не трогай внутри msgid, это первое. И второе, при переводе многострочных элементов (это те которые начинаются с "\n") после msgid, msgstr так же должен начинаться с "\n".
В любом случае, если и будут ошибки при компиляции, то они очень информативны и у тебя не будет проблем их решить самостоятельно.
Последним этапом переводов является их компиляция в *.mo файлы. Чтобы скомпилировать переводы, вне зависимости от того переводы это шаблонов, python или js кода, используй следующую команду:
Поздравляю! Мы закончили переводить наш сайт и теперь он может поддерживать множество других языков, вне зависимости, какие и где эти строчки находятся. Но если, качество переводов JS файлов тебя не устраивает и ты столкнулся с той же проблемой, что и я, то продолжи читать следующую главу про переводы 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' было первым, ведь именно так, мы указываем изменение языка.
Делаем импорт данного компонента:
В самом верху, ибо нам нужно будет его использовать в других модулях
Теперь на примере компонента EmailVerify.js я покажу как пометить строчки, которые необходимо перевести. В первую очередь делаем импорт функции перевода:
Дальше в функции-компонента, используем хук:
И теперь, чтобы обернуть необходимую строку для перевода:
Как ты мог заметить, я отправляю функцию t дальше, в качестве аргумента обработчика onConfirmEmail. Сделал я это потому, что переводы необходимы не только в реакт компонентах, но и в обычных обработчиках и функциях. Я не смог найти другой способ глобально определить функцию перевода, поэтому в каждой функции, которая нуждается в переводе я определяю дополнительный параметр - обработчики переводов t.
Полный пример:
А теперь пометь все свои строчки, которым требуется перевод. Я конечно же не буду вываливать всё сюда, но уже переведённую версию сайта ты найдёшь в этом архиве.
Соберём все выделенные мною строчки в файлы переводов. Для этого мы и устанавливали i18n-parser модуль. И чтобы и дальше нам было просто разрабатывать наш сайт, напишем отдельный скрипт для выполннения переводов в package.json.
Необходимо указать только, где и что искать используя специальный паттерн. ** - значит ищи во всех папках и подпапках. *.js - значит ищи любые файлы с расширением .js
Запустим скрипт, и он найдёт нам и за нас все строчки для переводов и создаст все, ранее указанные, файлы локализации.
Все файлы переводов ты найдёшь по пути static/locales/LOCALE/translation.json. И вот пример, сгенерированного файла локализации для русского языка:
Дальше посмотрим на вывод в консоль и то как выглядит страница при переводе на немецкий язык.

Как видно из консоли мы сделали XMLHttpRequest (или XHR) запрос два раза, чтобы получить немецкую и английскую локализацию. Почему две, а не одну? Всё потому, что мы указали в i18n.js, fallbackLng: 'en' . То есть если не удастся получить немецкую локализацию, мы будем использовать локаль по умолчанию.
Ещё видно, как модуль LanguageDetector сработал и заметил изменение языка пользователя. Всё, переводы JS кода завершены.
Переводы в django-моделях
В отличие от переводов строчек, перевод полей django моделей подразумевает создание дополнительных миграций для переводимой модели. Как это работает? Всё довольно просто, если поле необходимо перевести на другой язык, то создаётся копия этого поля, но с приставкой локали, на которую необходимо его перевести. Ну а в конце создать и применить миграцию.
Когда и при каких обстоятельствах это может пригодиться? Это на самом деле очень простой вопрос, на который я отвечу так. У тебя есть статья и ты хочешь написать её на 2-х языках. НУ и тебе нужны два разных заголовка для одной статьи. Это не возможно сделать используя такие встроенные команды в django как makemessages и compilemessages.
Ну давай настроим переводы моделей. Для начала установи пакет modeltranslation:
Подключи установленное приложение в settings.py. Его лучше всего подключать первым в списке.
Чтобы данный модуль знал для каких языков требуется создать переведённые модели, у тебя должны быть указаны языки в LANGUAGES (Смотри в начале статьи). Дальше нужно будет создать файл translation.py в директории приложения, модели которого ты хочешь перевести.
Вот пример содержания этого файла:
В этом файле мы указываем для какой модели хотим сделать переводы, и какие поля мы хотим перевести. В конце просто регистрируем указанную переведённую модель.
Осталось только сделать миграцию базы данных и и применить эти миграции.
На этом можно было бы и закончить, но давай покажу ещё изменённый файл admin.py для отображения переведённой модели в django-админке.
Если мы хотим поддерживать 2 языка, например английский и русский, то каждое поле для перевода будет иметь три варианта, один изначальный плюс ещё два с приставками *_en и *_ru. Главное, что запомнить, не использовать изначальное поле, а его варианты с приставками.Вот поэтому я и исключаю данные поля из админки.
Эпилог
В этой статье мы с тобой разобрали, все возможные случаи, где нужно будет сделать перевод. Переводы в шаблонах или python коде не представляют сложность. Хотя настройка и создание переводов для JS кода, тоже легко, только нужно учитывать, что он хорошо работает с ванильным JS. Со всем остальным могут возникнуть трудности и не точности.
Про переводы моделей. На твоём месте я бы старался избегать этого. Но Дим, как допустим писать посты на нескольких языках? Ведь будет создаваться много дублей пустых страниц. Например, статья по адресу /ru/articles/art1 будет иметь вариант /en/articles/art1. И если я написал статью на русском по первому адресу, то и на втором тоже должна быть статья. Я ведь не смогу сделать ещё одну статью с идентичным URL. А, собственно говоря, и не нужно, скажу я тебе. Пишем, ту же самую статью, но по новому адресу, например /en/articles/art1_but_in_english/, а со страницы /en/articles/art1 настраиваем 301 редирект на неё. Вот как пример.
Ну и в заключении хочу добавить, что перевод дело не простое и долгое. Вот даже сейчас я сижу и жду чуда, авось статья сама себя переведёт. В любом случае, вот обновлённая версия сайта(архив), со всеми переводами и настройками. Увидимся в следующей части.
\( ̄︶ ̄*\))
Комментарии
(0)
Отправить
Сейчас тут пусто. Буть первым (o゚v゚)ノ