История одной интернационализации
Исходные данные
В рамках одной компании живут и развиваются несколько идеологически связанных веб-сервисов с русскоязычным контентом. В один прекрасный момент им суждено было ступить на прекрасный, но тернистый путь интернационализации. Причём список поддерживаемых языков планировалось расширять с завидной интенсивностью.
Нам предстояло пройти следующие этапы:
- Подготовка проектов к интернационализации;
- Интеграция в workflow разработки процесса перевода текстов;
- Выбор и настройка инструментов для работы с сервисом переводов;
- Налаживание взаимодействия между разработчиками и переводчиками.
О проблемах и решениях, имевших место в рамках этой задачи, (а также немного о пляшущих гномиках) я расскажу ниже.
Первый шаг
Он трудный самый. Не являясь сторонниками преждевременных оптимизаций (а также не имея на это времени), мы решили пойти по простому “протоптанному” пути.
Что у нас в арсенале? Проекты реализованы на базе PHP фреймворка Yii, который имеет встроенный механизм интернационализации. Что ж, отлично, было бы глупо его не использовать.
Поехали: каждый проект обзавёлся “языковой” директорией, где в начале присутствовал только исходный и всеми любимый RU. Затем все тексты в шаблонах, сообщения об ошибках и прочий интернационализируемый контент переехал туда в виде словарей в формате ключ — значение. Тут был извлечён урок первый “Капитанский”: задумывайтесь об интернализации проектов заранее. Если сегодня нет требования поддержать ещё один язык, то оно может появиться завтра. Первый шаг не должен быть трудным самым.
Так как сервисы имели общий идеологический фундамент, мы сразу столкнулись с большим количеством ключей-дубликатов, разбросанных по словарям в разных проектах. Логичным решением было бы устранение дублей и “расшаривание” общих словарей для всех проектов. Это понизит стоимость внесения изменений в существующие переводы, а также поспособствует при разработке новых проектов в этой группе.
В итоге, общие словари были вынесены на уровень фреймворка и мы пришли к следующей схеме:
Фуф. Кажется, наши проекты готовы “переводиться”. Посмотрим, что из этого получится.
Интернационализируй это
Оу, забыл упомянуть про то, что у нас была своя команда переводчиков. Правда, ранее они не взаимодействовали с разработкой веб-сервисов, занимались своими задачами, со своим инструментарием и по своим процессам. Но всё равно это было чудесно, ведь работа с внешними подрядчиками ещё больше усложнила бы нам жизнь.
Первое время мы работали с переводчиками в режиме чёрного ящика. Разработчики упаковывали словари в архив (предварительно собрав их со всех проектов), отправляли их письмом переводчикам и, получив предварительный прогноз по срокам, ждали подобный же архив с переведёнными словарями.
И вот, настал долгожданный момент, когда наши сервисы обзавелись второй языковой версией.
Ура, в небе заиграла радуга и гномики пустились в пляс. Но о чём-то я умолчал, не правда ли? Между упаковкой словарей в архив и плясом гномиков жизнь не останавливалась и были слышны уже первые тревожные звоночки.
Во-первых, переводы переводами, но есть и другие задачи. Каждый проект, попавший под интернационализацию, более или менее активно развивался. Делались новые фичи, вносились багфиксы, а зачастую это затрагивало контентую чать: добавлялись новые текста, редактировались уже существующие. Первый полный перевод длился около трёх недель и за это время словари в репозиториях разработчиков претерпели достаточно большие изменения.
Тут мы извлекли урок номер два: продумайте синхронизацию процессов разработки и переводов. В нашем случае разумней было бы отправлять словари на перевод небольшими частями, чтобы через пару дней можно было бы получить их назад и добавить в репозиторий разработчиков. На это время необходимо замораживать все работы, затрагивающие эти словари. Это позволило бы избежать столь болезненного ручного объединения двух версий словарей, выделения не переведенных или отредактированных ключей и повторной отправки их на перевод.
Следующей заметной проблемой стали подобные вопросы переводчиков, повторяющиеся с пугающей регулярностью: “У вас тут есть фраза “Невозможно открыть счёт”. Имеется в виду финансовый счёт или счёт в спортивном состязании?”. Да, у нас не футбольный симулятор, но позаботиться о том, чтобы переводчик имел доступ к предметной области надо было заранее. Итак, урок третий: качественный перевод невозможен без контекста.
С некоторым запозданием мы предоставили команде переводчиков доступы на наши тестовые стенды и дали им возможность воспроизводить некоторые пользовательские истории, важные для корректных переводов. Дополнительно к этому были проведены итоговые ревью сервисов с уже подключенной новой языковой версией.
После долгожданного релиза настало время задуматься об изменениях в привычном всем процессе разработки с учётом нового “интернационального” статуса сервисов.
Трудности перевода
Сделал фичу — переведи её! Вот девиз, озаглавивший новую эпоху в жизни нашей команды. Вопрос о том, как именно переводить лежал на плечах разработчика. В некоторых случаях ты мог блеснуть знаниями языка и самостоятельно добавить в словари что-то типа “Hello, {userName}!”. Но в большинстве ситуаций приходилось вновь и вновь дёргать переводчиков.
Это увеличило объём ручной работы, время разработки фичей и, конечно, время тестирования. Достаточно часто стали происходить случаи, когда небрежно добавленный перевод, скрытый в недрах какого нибудь сервиса обнаруживался уже постфактум и вынуждал команду форсировать полноценный релизный цикл, включавший исключительно правки переводов.
Результатом бурного обсуждения этой проблемы стал вынос словарей из всех проектов в отдельную библиотеку:
Это позволило нам проводить “контентные” релизы отдельно, не затрагивая функциональную часть. Плюс, из репозиториев проектов был устранён шум, связанный с работой над переводами. Конечно же, подобное изменение не обошлось нам бесплатно. Были расширены модули интернационализации на уровне фреймворка, доработана система сборки и конфигурации билд-сервера. Но, как итог, эти шаги мы посчитали оправданными.
В преддверии появления ещё одной языковой версии обоснованные опасения вызывал протокол взаимодействия разработчиков и переводчиков.
Со стороны переводчиков были озвучены следующие проблемы:
- Отсутствие единого формата поступающих переводов. Разработчики присылали словари, куски словарей, предложения и даже отдельные слова;
- Неэффективное при данной схеме использование собственных лингвистических инструментов;
- Отсутствие доступа к текущему репозиторию с переводами для ревью, обсуждений, просмотра истории изменений.
Разработчики же в основном жаловались на большое количество ручных операций, а также на необходимость проверять присланные словари на соблюдение синтаксиса и правил форматирования.
Нам нужен был централизованный сервис для работы с переводами, и мы приступили к поискам.
Автоматизируй это
Первым делом был составлен список требований, которым должен будет удовлетворять сервис нашей мечты:
- Доступность для разработчиков, переводчиков и других заинтересованных лиц;
- Возможность разграничивать права в рамках аккаунтов, групп, проектов;
- Поддержка необходимых нам форматов словарей;
- Полноценное внешнее API;
- Дополнительный функционал для осуществления переводов:
- Фильтрация не переведенных ключей;
- Отслеживание истории переводов;
- Возможность обсуждения конкретных кейсов;
- Поиск дубликатов и похожих фраз;
- Гибкая модель нотификаций:
- Переводчикам — о появлении новых ключей и о редактировании имеющихся;
- Разработчикам — о статусе осуществления переводов и о появившихся у переводчиков вопросах;
- Вменяемый интерфейс, не вызывающий приступов эпилепсии.
Беглая оценка времени и ресурсов, которые нам пришлось бы потратить на разработку и поддержку собственного сервиса ограничила направление поисков готовыми решениями.
Приятно удивило не только наличие подобных сервисов, но и их функциональное разнообразие. Обзор этого сегмента рынка будет хорошей темой для другой статьи, поэтому, прокрутив время вперёд, я скажу лишь, что нам наиболее подошёл вот этот. Необходимо лишь отметить урок четвёртый: выбирая внешний сервис интернационализации, предварительно проверьте его на тестовом проекте. Только лишь собрав отзывы от разработчиков и переводчиков, оценив надёжность сервиса, уровень поддержки и стоимость его использования, можно принять осмысленное решение, о котором в последствии не придётся жалеть.
К моменту, когда мы проводили презентацию нового сервиса, команда разработки уже имплементировала его API и мы имели настроенное зеркало нашего репозитория с переводами.
Само собой, первичным оставался наш собственный репозиторий со словарями, в внешний сервис стал всего лишь его “веб мордой”. Если в один злосчастный день он расстворится в воздухе, мы, конечно, расстроимся, но быстро откатимся к старой схеме, не блокируя процесс разработки.
Правила работ по интернационализации приобрели следующий вид:
- Появились новые текста.
- Разработчик добавляет их в словари “основного” языка (в нашем случае — русский);
- Посредством API разрабочик отправляет словари в сервис переводов;
- Переводчики получают нотификацию о появлении не переведенных текстов и переводят их;
- Разработчик получает нотификацию о поступлении переводов и опять же с помощью API проводит синхронизации словарей и обновляет репозиторий;
- Изменились существующие текста:
- Стараемся перевести ситуацию к п.1, т.е. не редактируя имеющиеся ключи в словарях, добавить новые и использовать их;
- Если предыдущий вариант не подходит, то разработчик вносит изменения непосредственно в ключи словаря “основного” языка и отправляет их в сервис переводов;
- Умный сервис понимает, что изменился текст уже существующих ключей и помечает их в остальных языках как требующие повторного перевода;
- Переводчик имеет возможность как оставить старый перевод (например, если просто была исправлена грамматическая ашибка), так и указать новый;
- Разработчик получает нотификацию и проводит синхронизацию словарей;
- Обнаружена ошибка в переводе:
- Обнаруживший (это может быть и разработчик) самостоятельно или с помощью переводчика редактирует его на сервисе переводов;
- При следующей синхронизации словарей это изменение попадает в репозиторий (при необходимости, синхронизация форсируется прямым обращением к разработчикам);
- Добавляется поддержка нового языка:
- В сервисе переводов добавляется новый язык. Все его словари изначально отмечены как не переведённые;
- В зависимости от наличия в штате переводчиков, владеющих данным языком, перевод осуществляется либо своими силами, либо с помощью привлечения сторонних переводчиков путём предоставления им ограниченного доступа к сервису;
- После завершения переводов разработчики вносят изменения в конфиг проектов, чтобы при синхронизации словарей новый язык был добавлен в репозиторий и далее участвовал в общем процессе.
Эти нехитрые правила сделали нашу жизнь чуть-чуть лучше, а также частично устранили нервный тик при упоминании слова интернационализация. Значительное уменьшение количества ручных операций, а также упорядочивание протокола взаимодействия команд снизило накладные расходы на интернационализацию при разработке фичей, а также устранило большинство ошибок, связанных с человеческим фактором.
Урок пятый и заключительный: как и большинство технологических процессов, процесс интернационализации может быть автоматизирован. Степень этой автоматизации следует определять, исходя из конкретной ситуации и потребностей проектов.
Резюме
Для проскролливших и для тех, кто прочитав до конца, забыл о чём шла речь в начале:
- задумывайтесь об интернализации проектов заранее;
- продумайте синхронизацию процессов разработки и переводов;
- качественный перевод невозможен без контекста;
- выбирая внешний сервис интернационализации, предварительно проверьте его на тестовом проекте;
- как и большинство технологических процессов, процесс интернационализации может быть автоматизирован.
За рамками этой статьи остался не менее интересный аспект интернационализации веб сервисов — автоматизация тестирования в мире мультиязычности. Но об этом в другой раз.
Надеюсь, описанный мною опыт будет полезен будущим интернационализаторам.
Спасибо.
Автор: psyduckinattack