Как стать автором
Обновить

TransMaintain — инструмент для поддержания файлов переводов интерфейса Symfony проектов в консистентном состоянии

Время на прочтение6 мин
Количество просмотров1K

"Боль это боль, как ее ты не назови.
Это страх, там где страх, места нет любви."
Агата Кристи

Сколько проектов не проходило через мои руки — всегда одно и то же — файлы переводов в ужасном состоянии. Лучше всего эту проблему выражают слова Агаты кристи в начале статьи. Здесь не будут разбираться лингвистические нюансы и качество переводов. Предположим у нас имеются отменные переводы. Под катом разбираются те проблемы, которые можно проконтролировать техническими средствами и инструменты предназначенные для этого. Материал рассчитан на людей имеющих опыт работы с Symfony и в целом с консолью Linux. Также предполагается, что вы умеете подключать сторонние бандыл в проект. Поэтому часть вопросов не разбирается с позиции “и так всё понятно”.


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

  1. Отсутствие части переводов. Даже если добавлены переводы для одного-двух языков, то не добавлены для остальных.

  2. Не добавлены переводы вовсе.

  3. Использование похожих, но тем не менее разных ключей для одних и тех же переводов.

  4. Разные переводы для одних и тех же ключей (дубликаты ключей). В результате получаем, что нужный перевод есть, но отображается не то, что ожидаем.

Но это внешнее проявление проблемы. Что лежит в её основе?

Посидев и подумав что мешает разработчикам поддерживать их в хорошем состоянии я выделил для себя 3 основных причины:

  1. Всем людям удобно пользоваться отсортированными списками. Но что реально видит перед собой разработчик в файлах? Всё перемешано. Один кинул в спешке всё как получилось. Другой просто поленился упорядочить ключи. Сверху наслоились мерджи веток и решение конфликтов в них. И вот уже в файлах чёрт ногу сломит.

  2. С первой проблемой тесно связана вторая: разносить переводы по файлам реально долго и нудно. Явно не то, чем хотел бы заниматься разработчик, хоть это и часть его работы. И далеко не всегда у разработчика есть время привести всё в порядок. И всё равно есть вероятность механической ошибки.

  3. Где же наши тесты? Ну правда. Есть прекрасные инструменты для тестирования кода. Например PHPUnit. Как насчёт тестов файлов с переводами, которые можно легко настроить в CI проекта? И пусть каждый мердж-реквест проверяется на предмет того, что всё добавлено.

Первым делом я стал искать существующие инструменты. Казалось, что проблема стара как мир и уже должно быть написано 100500 инструментов. Посмотрел, что предоставляет нам стандартный пакет “symfony/translation”. Погуглил инструменты для разработчиков, на сколько мне хватило терпения, но того что искал не нашёл. Если в комментариях дадут ссылки на существующие инструменты, предназначенные для решения проблем описанных в статье, то буду очень благодарен.

Имея такие вводные я решил создать свой инструмент. Если модераторы пропустят, то здесь будет ссылка на репозиторий. В противном случае ищите проект на Github-е. В текущей реализации проект представляет собой бандл для Symfony 4.4+ проектов. Ниже описано какие конкретно проблемы решает пакет и как им пользоваться.

Разбираемся с кашей в файлах

Первое, на что я нацелился, — это “каша” в файлах. На всех последних проектах, где я работал и работаю переводы хранятся в YAML файлах. С них и начал. Их особенность — возможность хранения ключей в виде структурированного удобочитаемого дерева. При загрузке в траслейтор они склеиваются через точку. Кроме плюсов, это дает возможность задать разные переводы для одних и тех же ключей даже не заметив этого. Значит нужно сделать две вещи:

  1. Преобразовать ключи. Где возможно, разделить ключи склеенные через точку, а где-то наоборот склеить, чтобы построить более компактное дерево.

  2. Отсортировать по ключам.

Так проще искать и применять существующие ключи. И подобные ключи (с опечатками и дубли) окажутся рядом и их будет проще исправить. Кроме удобства чтения и поиска существующих ключей, мы получаем ещё и меньше конфликтов при мердже веток.

Для этого была создана соответствующая консольная команда:

aeliot_trans_maintain:yaml:transform PATH_TO_FILE_IN PATH_TO_FILE_OUT

Вызывается как обычная команда Symfony. Если передан только один аргумент, то PATH_TO_FILE_OUT становится равным PATH_TO_FILE_IN. Это даёт простор. Добавляем несколько стандартных команд Linux типа find или grep и вот уже можно преобразовать все нужные файлы. Например, можно преобразовать все YAML файлы в определённой директории:

find PATH_TO_DIRECTORY -type f \( -iname \*.yml -o -iname \*.yaml \) | sort | xargs  -I {} -t  php  bin/console aeliot_trans_maintain:yaml:transform $1{}

Находим и переводим всё, что пропущено

Дальше нужно добавить пропущенные переводы. Проще всего — это взять список пропущенных переводов и закинуть в переводчик, например Google Translate. Он неплохо справляется с переводами на европейские языки, например с английского на немецкий или французский. Можно сразу с ключами. Потом поправить ключи и вставить в нужный файл.

Для этой задачи тоже создана команда:

bin/console aeliot_trans_maintain:yaml:export_missed_translations DOMAIN DONOR_LOCALE FILTER_BY_LOCALE

Где:

  • DOMAIN — обрабатываемый домен. 

  • DONOR_LOCALE — локаль из которой берутся переводы 

  • FILTER_BY_LOCALE — локаль для которой идёт выгрузка. Если указана, то будут выгружены только переводы, пропущенные в этой локале. Иначе все, что пропущены хотя бы в одной локали этого домена.

Ключи будут к склеенном виде. Полученные данные выгружаются в StdOut. Можно направить его сразу во временный файл. Например:

bin/console a:y:e messages en de > ./donor.txt

Если кто-то ещё не знал, то имена консольных команд в Symfony можно сокращать. Например как здесь по первым буквам.

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

Внимание! У всех этих команд есть одно ограничение. Поскольку для создания YAML файлов используется стандартный Symfony дампер (\Symfony\Component\Yaml\Yaml::dump()), то все переводы состоящие из одного слова без спецсимволов не будут заковычены. Да, пока не идеально. В дальнейшем это планируется исправить.

Декоратор для транслейтора

Ещё один инструмент для того, чтобы все переводы были добавлены — декоратор для стандартного транслейтора. Включается в настройках бандла (ключ конфига: insert_missed_keys). После его включения все ключи, вызванные во время работы проекта, но пропущенные в файлах переводов, будут добавлены в соответствующие домен и локаль.

На текущий момент принимает значения:

  • no — отключен. Значение по умолчанию.

  • end — вставлять найденные ключи в конец файл в склеенном виде

  • merge — встроить в дерево ключей. Экспериментальный вариант и будет дорабатываться.

На текущий момент рекомендованы к использованию только значения: no и end. Также рекомендуется включать декоратор только в dev режиме и только на время, т.к. сильно замедляет работу сайта. Из-за того, что постоянно изменяются файлы переводов и из-за этого постоянно сбрасывается и пересобирается кэш.

Пример подключения в конфиге:

parameters:
   env(TRANS_MAINTAIN_INSERT_MISSED_KEYS): no

aeliot_trans_maintain:
   insert_missed_keys: "%env(TRANS_MAINTAIN_INSERT_MISSED_KEYS)%"

После этого легко включать/отключать декоратор транслейтора изменяя значение ключа TRANS_MAINTAIN_INSERT_MISSED_KEYS в .env или .env.local файлах без риска закоментить чего-нибудь лишнего.

Отлично. Файлы преобразовали. Ошибки исправили. Пропущенные переводы добавили. Всё бы хорошо… но консистентность переводов всё ещё на совести разработчика. А как же тесты? Куда без них в современно проекте?

Автоматическое тестирование переводов в проекте

Для тестирования переводов создана команда:

aeliot_trans_maintain:lint:yaml KEY_1 KEY_2 KEY_N

Возвращает статус 0 если проблем не найдено и 1 если есть проблемы. И в StdOut отдаёт таблицы отчётов. В качестве аргументов принимает ключи проверок или названия пресетов. Может принимать сразу несколько аргументов.

Пресеты:

  • base — выполнить основные проверки подходящие большинству проектов (рекомендовано).

  • all — выполнить все возможные проверки. Зарезервировано на будущее. Сейчас то же, что base за тем исключением, что all должен быть единственным аргументом команды.

Ключи проверок:

  • files_missed — проверяет существование файлов домена перевода для всех локалей, использованных в проекте. Выводит таблицу доменов переводов и локалей, пропущенных для каждого домена. Если домен представлен во всех локалях, то в список не попадёт.

  • keys_missed — проверяет наборы ключей, использованных в разных локалях одного домена. Выводит домено, результирующих ключей (конкатенированных через точку) и список локалей, где они пропущены. В список попадут только ключи, отсутствующие, хотя бы в одной локале.

  • keys_duplicated — проверяет наличие дубликатов ключей. Выводит таблицу домен, локаль, ключ.

Ключи проверок могут идти в комбинации с base. Если указать один и тот же улюч несколько раз, то проверка будет произведена однократно.

Теперь запуск этой команды можно настроить в CI и ни один мердж не пройдёт с неполными переводами. Это конечно не защищает от того, если какой-то ключ перевода не добавлен ни в один файл, но уже сильно снижает число проблем.

Планы на будущее. Послесловие

В ближайших планах:

  1. Добавить возможность проверки на соответствие ключей определённому паттерну и возможность проверки отдельных доменов. На тот случай, если на проекте разным доменам предъявляются разные требования.

  2. Расширить совместимость с более старыми проектами.

  3. Так же планируется реализовать использование API Google/Yandex для автоматических переводов. Для ряда проектов это вполне приемлемо.

Проект открытый. Буду рад любому участию и здоровой критике в комментариях.

Теги:
Хабы:
Всего голосов 4: ↑4 и ↓0+4
Комментарии0

Публикации

Истории

Работа

Ближайшие события

15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань