Представьте: у вас есть 7 команд разработчиков общей численностью более 100 человек. Они одновременно пилят 13 приложений. Работа ведется в 20 репозиториях.

Все приложения нужно переводить. Какие-то на 6 языков, какие-то на 20. А какие-то на 13, но это совсем другой набор языков, в предыдущие 20 он не входит.

У всех разный стек, как следствие разные форматы строк: js, json, ts, yaml или yml. А кое-кто до сих пор хранит свои тексты в базе.

Вы работаете по Agile: ежедневная доставка ценностей, двухнедельные спринты. В DoR включено наличие всех необходимых переводов. Ну и, конечно, переводы нужны были вчера, чтобы успеть протестировать.

image

Есть отдел технических писателей. Кто такой технический писатель? Это человек, который пишет внешнюю документацию, иногда — внутреннюю. Пишет все виды текстов, которые могут увидеть пользователи или партнеры: интерфейсные тексты, тексты писем, ответы API, ошибки. Сопровождает процесс разработки, чтобы быть погруженным в технику и бизнес-логику. И обеспечивает своевременную поставку переводов в приложение.

Еще есть должность копирайтера-переводчика и менеджера по локализации. Это человек, который создает все тексты на английском языке, а также следит за консистентностью переводов, назначает переводчиков, и решает все связанные проблемы.
Внимание, вопрос: как много техписов, копирайтеров и менеджеров по локализации необходимо, чтобы не стопорить разработку и не причинять боль всему техотделу?

В нашем случае мы обошлись 4 техписами и 1 копирайтером-менеджером по локализации. Поставка переводов в среднем укладывается в один рабочий день и никогда не превышает трех рабочих дней. Надеюсь, вам стало интересно.

Как мы к этому пришли


6 лет назад мы работали в Google sheets и БД. То есть, если в процессе разработки появлялись строки на перевод, мы их копировали в табличку, а потом по почте отправляли на перевод. Когда перевод был готов, его вручную заливали в БД. Единственный плюс такого решения — тебе не надо заново выкладывать приложение, чтобы увидеть новые строки. Зато если в переводах ошибка, откатиться назад не получится. Никакой памяти переводов, никаких глоссариев. Консистентность переводов достигается методом пристального взора.

Первая попытка


Первая версия автоматизировать этот процесс выглядела так: когда у разработчика появлялись строки, он добавлял их в новую ветку в специальном репозитории для переводов. Потом в этой же ветке запускался pipeline, который по API отправлял весь diff строк на перевод. Правда, обратно переводы должны были попадать уже в БД, а загружать по API строки с внешнего ресурса во внутреннюю БД не получилось.

Что дала такая интеграция? Был убран шаг, где техническому писателю нужно собирать все в одну таблицу, вручную отправлять, а затем делить полученные переводы по приложениям и по количеству языков. В данном случае строки сразу отправлялись на перевод в рамках проекта, одноименного с приложением, для которого они предназначались. На выходе технический писатель получал набор архивов, по каждому из приложений, для которых велась работа. Это существенно сократило долю ручного труда. К тому же на стороне провайдера была реализована память переводов. Но это решение сохраняло и ряд недостатков: хранение строк в БД не позволяло осуществлять полноценный менеджмент строк на нашей стороне и по-прежнему подразумевало большую долю ручной работы.

Боль и непрерывные локализации


Следующая интеграция принесла много страданий разработчикам. Мне кажется, у тех, кто ее застал, до сих пор при слове “локализация” дергается глаз. Это была первая интеграция с Serge и Smartcat.

image

Тут важно рассказать, что такое Serge и Smartcat.

Serge — это утилита, которая поддерживает git. Она умеет доставать из ветки нужные строки, отправлять их на перевод, а затем возвращать в ту же самую ветку перевод только для этих строк. Еще нужен плагин, который будет вызывать API CAT-системы, в которой мы переводим. Плагин должен получать новые строки у Serge и возвращать Serge готовые переводы.

Smartcat — это CAT-система, с поддержкой глоссария, памяти переводов, плейсхолдеров. Также, Smartcat агрегирует и упрощает процесс взаиморасчетов с фрилансерами, поддерживает подключение вендоров переводов.

На этом шаге мы перенесли строки из БД в репозитории проектов. Теперь строки надо было отправлять прямо из репозитория приложения и возвращать их туда же.

Предполагалось, что это будет работать так: разработчик знает, от какой ветки он создал свою feature-ветку, и diff в ресурсных файлах между этими двумя ветками как раз и есть то, что надо перевести. Когда разработчик имеет набор строк для перевода, он в своей ветке запускает job с конфигом Serge. Serge вычисляет diff, извлекает новые строки, вызывает плагин и отправляет строки на перевод. Когда переводы готовы, разработчик вызывает следующий job: он разворачивает инстанс Serge, созданный на предыдущем шаге, получает готовые переводы и коммитит их в исходную ветку.

Решение оказалось нестабильным: Serge не предназначен для развертывания с нуля при каждом запуске pipeline, разработчики не желали думать о diff-ах между ветками, а плагин Smartcat остро нуждался в обновлении и доработке. Процесс доставки новых строк мог длиться часами. И, увы, не всегда заканчивался успехом.

Теоретически, были автоматизированы все стадии процесса, на деле же сопровождение, вычислении diff-а до запуска pipeline и troubleshooting занимали больше времени, чем выполнение той же задачи полностью вручную.

Свет в конце туннеля


К августу 2018 года мы запустили текущую версию интеграции. У нас есть сервер локализаций. На сервере для каждого репозитория существует свой инстанс Serge. Serge просматривает все ветки в репозитории, отправляет новые строки на перевод и коммитит готовые переводы в исходные ветки. В текущей интеграции все работает быстро и стабильно. После создания ветки для переводов, строки оказываются в Smartcat в течение 5-6 минут. После подтверждения переводов, коммит происходит аналогично, в течение тех же 5-6 минут. Срок поставки переводов ограничен лишь человеческим фактором: загруженностью переводчиков, разницей в часовых поясах и так далее.

В следующих статьях я расскажу, как с нуля настроить интеграцию Serge-Smartcat-Gitlab, и как мы решали разнообразные нестандартные задачи.

Комментарии (14)


  1. wtigga
    27.03.2019 12:47

    С интересом жду следующей статьи про конкретную настройку Serge.


  1. Free_ze
    27.03.2019 13:00

    У всех разный стек, как следствие разные форматы строк: js, json, ts, yaml или yml.

    Соответствие такое себе, разве форматы нужны не затем, чтобы быть универсальными? Не в исходники строки запихивать, разумеется (кто ж до этого додумается?), а именно форматы файлов с данными xml/json/y(a)ml.


    1. NatalyaPavlikova Автор
      27.03.2019 13:14

      Непрерывные локализации интегрировались в уже существующие проекты, и не потребовали менять формат строк. Оно просто работает из коробки с тем, что есть.


      1. Free_ze
        27.03.2019 13:27

        Разумеется. Я бурчу о том, что это гениальное решение, но все же для проблем, которых можно было избежать, проектируя приложения грамотно изначально.


        1. afan
          27.03.2019 21:27

          Это не всегда возможно: когда приложения/платформы разные, то лучше всего использовать стандартные решения и форматы для каждой платформы (.strings для iOS/macOS, XML для Android, JSON для расширений Chrome и т.д.). А возможность работы с ресурсами разных форматах в системе локализации все равно должна быть, и это уже второстепенная деталь имплементации. Хвост (локализация) точно не должен вилять собакой (инженерами). :)


          1. Free_ze
            28.03.2019 11:42

            то лучше всего использовать стандартные решения и форматы для каждой платформы

            В данном случае именно, что не лучше: требует усложнение процесса вместо унификации. Формат хранения не такая принципиально важная штука, чтобы плодить тучу вариаций просто потому, что это дефолт.


            1. afan
              28.03.2019 19:09

              Если вы считаете, что поддержка нескольких общепринятых форматов ресурсов локализации усложняет процесс, то что-то не так с вашим процессом локализации. Это как раз самая незамысловатая часть процесса. А вот перекраивать устоявшиеся практики разработки под платформы (а в некоторых случаях и заставлять инженеров что-то переделывать) — это только наживать проблемы между разработчиками и отделом локализации.

              Я полностью соглашусь с мнением, что интернационализацию и выбор форматов файлов ресурсов лучше продумывать заранее (особенно если платформа разработки позволяет гибкость в этом плане), но и перегибать палку, принуждая всех использовать один и тот же формат, не надо. Да и как я упомянул выше, это просто далеко не всегда возможно.


              1. Free_ze
                28.03.2019 20:45

                Если вы считаете, что поддержка нескольких общепринятых форматов ресурсов локализации усложняет процесс, то что-то не так с вашим процессом локализации.

                Что вы там говорили про хвост, виляющий собакой?)

                А вот перекраивать устоявшиеся практики разработки под платформы

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

                я упомянул выше, это просто далеко не всегда возможно.

                Вы упомянали стандартные инструменты, но не невозможность.


                1. afan
                  28.03.2019 22:14

                  Иногда это именно невозможность. При нативной разработке под Android их XML-формат это единственно возможный вариант работы с ресурсами. Нет, можно, конечно, обойти все практики и придумать на этом месте что-то свое, но при этом придется отказываться от кучи удобств связанных с использованием стандартного формата. Т.е. выбор другого формата приносит гораздо больше проблем, чем решает. Ровно та же картина и с другими платформами. Единого формата ресурсов просто нет и быть не может (и не нужно к нему стремиться). Я как раз за то, чтобы не делать культ карго из унификации и позволить инженерам использовать штатные механизмы работы с их проектами.


                  1. Free_ze
                    29.03.2019 11:00

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

                    Кмк, слишком много категоричности и при этом нет аргументов, которые можно было бы обсудить.
                    Всего доброго!


  1. rebelpolin
    27.03.2019 13:09

    Автор, вы просто супер!


    1. NatalyaPavlikova Автор
      27.03.2019 13:14

      Спасибо)


  1. AnnetPf
    28.03.2019 08:30

    Очень интересно. Жду раскрытия в последующих главах!


  1. DitriXNew
    28.03.2019 09:04

    Вот читаю и плачу. Нам бы ваши проблемы в мире 1С :)
    В 1С вообще нет такого понятия как ресурсный файл, там тупо весь текст в самом коде, как форм так и коде.

    Хотя, мы решили эту проблему.
    Но в отличии от вас мы пошли по пути, когда программист пишет все всегда на 1 языке.
    Далее идет ветка дев, запускаются все тесты (дымовые, сценарные, синтаксические контроли т.д.), после этого, если все ок — запускается пайплайн перевода, который делает клон ветки дева, загружает/обновляет тексты для перевода, переводит, возвращает все вов ременную ветку и потом на этой ветке стартует опять весь цикл сборки с тестами, и вот если он прошел хорошо, то тогда только все собирается и уходит в мастер, а рядом ложится скомпилированный файл.

    Но да, перевод это еще те чудеса, особенно с учетом того, что у нас например идут разные варианты сборок по языкам. Кстати — сами используем смарткет, совет тем кто начинает с ним и читает их апи — не верьте ему :)

    Все таки ерп системы — большие, там нормальное явление сотни тысяч строк и миллионы слов на перевод (конечно есть повторы, но этого не легче, на самом деле, а совсем наоборот).

    Жду вторую статью с переводом.