Герои сериала «Шерлок»


Привет! Я Алексей Тимин, инженер из команды локализации Badoo. В этом посте я расскажу вам о том, как мы помогаем переводчикам в их нелёгком труде, и о новом Open Source-решении, позволяющем генерировать скриншоты дизайна, подготовленного в Sketch, для разных языков.


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


В команде локализации Badoo всего лишь два разработчика, но мы держимся и создаём очень интересные инструменты:


  • Translation Memory
  • Collaborative Translation Platform, доступной по адресу https://translate.badoo.com/,
  • функционал предоставления корректного перевода конкретному пользователю (в зависимости от языка, пола, склонения, числа),
  • система подготовки переводов для A/B-тестирования (да, мы проводим тестирование вариантов перевода, чтобы определить, какая формулировка лучше),
  • десяток интерфейсов для работы переводчиков,
  • сбор статистики по работе переводчиков и использованию ими инструментов.

Перечисленные инструменты призваны помочь в процессе локализации сайта и мобильных приложений Badoo на 47 языков. Это огромный фронт работ.


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


Процесс перевода выглядит так:


  1. Дизайнеры рисуют.
  2. Программисты программируют.
  3. Переводчики переводят.
  4. Релиз.

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


По нашей задумке, процесс должен выглядеть так:


  1. Дизайнеры рисуют.
  2. Программисты программируют, а переводчики – переводят и могут каким-то способом сразу видеть результат.
  3. Релиз.

Чтобы вам было проще представить проблему, привожу схемы процесса до и после. Красным на первой схеме выделен оптимизируемый участок:



Наши дизайнеры работают в графическом редакторе Sketch. Мы выяснили, что идущая вместе с ним утилита sketch-tool умеет генерировать скриншоты, а это значит, при добавлении перевода есть возможность сразу показывать скриншот переводчику! Но возник вопрос: как заменить исходные тексты в дизайне, чтобы получить локализованный скриншот?


В перерывах между вечеринками мы обсуждали возможные варианты реализации идеи. И выход был найден.


Давайте разберёмся, как устроен .sketch-файл изнутри.


Представление данных внутри .sketch-файла


В 43-й версии Sketch разработчики стали использовать новый формат .sketch-файла для «лучшей интеграции со сторонними сервисами».


Логически в подготовленном в Sketch дизайне выделяются Pages, Artboards и графические элементы. Каждой сущности – Pages, Artboards и графическим элементам – один раз (в момент создания) присваивается уникальный идентификатор (UUID), который впоследствии не меняется.


Схематично связи между сущностями можно изобразить так:



Смотрите картинку ниже, чтобы понять, что есть что в интерфейсе Sketch: iPhone SE и iPhone 7 – две из возможных Artboards, a Page 1 – это одна из возможных Pages.



Сохранённый в .sketch-файл дизайн представляет собой ZIP-архив, внутри которого находятся директории с PNG- и JSON-файлами. Выглядит просто?


Если мы разархивируем .sketch-файл, то дерево директорий получится примерно таким:



Информация о каждой Page и связанных объектах Artboard хранится в pages/*.json. Именем файла служит UUID объекта Page, каждому объекту Page соответствует один файл.


Мы можем запросто открыть любой pages/*.json и отредактировать, например, название одного из Artboards. Чтобы определить конкретный файл для редактирования, запускаем:


$ grep -l ‘iPhone 7’ pages/* 

И если изменить название – не проблема, то изменить, допустим, текст «Нажми меня» на кнопке уже сложнее. После блуждания по форумам и безрезультатных поисков подходящей библиотеки мы поняли, что многие другие люди тоже ищут решение.


…смерть его на конце иглы, та игла в яйце, то яйцо в утке, та утка в зайце, тот заяц в сундуке, а сундук стоит на высоком дубу...
«Царевна-лягушка»

Текст на кнопке упакован в бинарный plist, закодированный в строку Base64, являющуюся значением атрибута сериализованного JS-объекта, находящегося в одном из файлов, сжатых ZIP-ом.


Не будем касаться вопросов разархивирования и чтения JSON из файлов, но стоит сказать о формате Property List (bplist на схеме выше). Чтобы модифицировать текст «Нажми меня», можно использовать утилиту plutil. Она позволяет вставить новое и удалить старое значение некоего свойства, а ещё с её помощью можно преобразовать plist из бинарного вида в XML и обратно. XML – удобный формат, для работы с ним существует множество инструментов. Также возможен экспорт в JSON, но, во-первых, при этом происходит потеря типов данных, а во-вторых, не всегда plist может быть сконвертирован в JSON. Например, с plist-ом из .sketch-файла экспорт в JSON не сработал.


Итак, мы разобрались с внутренним представлением и наконец переходим к вариантам реализации нашей задумки.


Выбор подходящей реализации


Вариант 1. Ленивое решение


Мы пытались рассказать переводчикам про JSON, Base64 и bplist, научить их самостоятельно заменять тексты переводами и делать скриншоты. Но когда им показали консольную команду экспорта превью


$ sketchtool export artboards --items='42C53146-F9BF-4EEE-A4F8-BB489F0A3CDA,BF38A95A-F0CD-452E-BE26-E346EBD349CE' --formats=png --save-for-web example_design.sketch

поняли, что этот вариант не годится.


(Шутка, ничего переводчикам мы не рассказывали, а сразу перешли ко второму варианту).


Вариант 2. True way


Переводчики не должны думать о технических вопросах. Просто нужно, чтобы при сохранении перевода им был предоставлен скриншот.


Для этого мы решили разработать сервис, минимальным функционалом которого стало бы:


  1. Определение в .sketch-файле UUID графических элементов, содержащих текст.
  2. Генерация скриншотов с локализованным текстом.

Проект получил название Sketch Modifier и был опубликован на GitHub.


Работа со Sketch Modifier


Чтобы начать использовать Sketch Modifier, нужно установить Node.js. на macOS (где конечно уже должен быть установлен Sketch https://www.sketchapp.com/). Да, Sketch есть только под macOS. Но если ваш дизайнер работает в Sketch, то как минимум один Mac у вас есть.


Рассмотрим процесс работы со Sketch Modifier по шагам.


Шаг 1. Установка


Находите компьютер под управлением macOS. Скачиваете и устанавливаете на него Node.js, что совсем просто.


Далее скачиваете архив или клонируете репозиторий с GitHub командой


$ git clone https://github.com/AlexTimin/sketch-modifier.git

Переходите в директорию проекта


$ cd sketch-modifier

Устанавливаете зависимости с помощью npm:


$ npm install

И, наконец, запускаете сервер


$ ./bin/www

Всё, теперь по адресу http://localhost:3000 у вас должен отзываться сервер. Можете перейти по этому адресу в браузере и проверить.


Шаг 2. Загрузка .sketch-файла и определение исходных текстов


Для примера возьмем example_design.sketch и загрузим его в систему. Для этого нужно отправить запрос из директории, в которую вы сохранили example_design.sketch:


$ curl -F 'data=@example_design.sketch' http://localhost:3000/add-sketch/

.sketch-файлу будет присвоен UUID. В ответ вы получите JSON следующего вида:


{
    "8a2009c5-36ca-4328-87d6-16aa0c2e2800": {  // присвоенный example_design.sketch UUID, у вас он будет другой
        "5A0F429A-C974-460A-9482-93AED7456850": {  // Page 1 UUID
            "C1C29749-B967-494D-8D7E-A484EAB37534": {  // iPhone SE Artboard UUID
                "E335D359-9DF3-4DCC-8B79-E77E38824714": "Нажми меня" // UUID текста на кнопке
            }
            … // информация по другим Artboards
        }
        … // информация по другим Pages
    }
}

Можете сохранить эти данные себе в базу, отправить в /dev/null или сделать ещё что-нибудь интересное. Но мы сохраняем их в базу.


Шаг 3. Генерация переведённых превью


Чтобы заменить текст, нужно отправить запрос на адрес http://localhost:3000/generate-preview/ с указанием параметров screens и textReplaces. Список необходимых команд будет ниже, а пока разберёмся со структурой параметров запроса.


В параметре screens мы указываем список UUID тех Artboards, скриншоты которых хотим получить. Значение параметра имеет такую структуру:


{
    Example Design UUID: [  // example_design.sketch
        Artboard UUID,  // iPhone SE
        ... 
    ]
}

В textReplaces мы указываем UUID текстовых элементов и новый текст. Значение параметра имеет такую структуру:


{
    Text UUID: "новый текст, перевод",
    ...
}

Итак, формируем запрос для генерации скриншота. Заменим текст «Нажми меня» на «Start the party!», например. Для этого нам понадобится файл generate-preview-request-data, в котором мы укажем значения параметров запроса.


Содержимое файла generate-preview-request-data:


textReplaces={
    "E335D359-9DF3-4DCC-8B79-E77E38824714": "Start the  party!"
}&screens={
    "8a2009c5-36ca-4328-87d6-16aa0c2e2800" : [
        "C1C29749-B967-494D-8D7E-A484EAB37534"
    ]
}

Выполняем команду из директории, в которую вы сохранили файл generate-preview-request-data.


$ curl -X POST -d "@generate-preview-request-data" http://localhost:3000/generate-preview/

В ответ вы получите скриншоты в Base64. Структура ответа будет такая:


{
    "C1C29749-B967-494D-8D7E-A484EAB37534": "data:image/7HYUIY786GFFDASeY+...;base64", 
    ...
}

Наверное, вы догадались, что ключом в структуре ответа является UUID запрошенного скриншота, а в значении записано представление скриншота в Base64 (напомню, мы запрашивали скриншот для iPhone SE).


Если вы сохраните, допустим, в example.html следующий код с подставленным Base64-представлением картинки


<img src="скриншот в Base64">


а потом откроете example.html в браузере, то увидите переведённый скриншот:



C помощью Sketch Modifier вы можете делать скриншоты до локализации, в процессе и после локализации, что очень важно. Вы будете видеть, как ведёт себя дизайн при использовании реальных текстов, и понимать, что нужно доработать.


Заключение


Мы настраиваем работу со Sketch Modifier таким образом, что дизайн загружается один раз, а скриншоты генерируются при сохранении переводов. Переводчики будут сразу видеть, умещаются ли выбранные формулировки переводов в заданных областях. А значит, о проблемах будет известно заранее.


Исходный код реализации находится на GitHub.


Пользуйтесь, советуйте способы усовершенствования, пишите отзывы.

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


  1. mSnus
    29.09.2017 05:42
    +1

    Будем надеяться, что Sketch как-нибудь сам собой помрёт, со всеми его несовместимостями, неудобствами и одноплатформенностью. И не придётся городить вот эти супер технологичные костыли.


    Товарищи, я вам аплодирую, но это напоминает мне времена DOS и разбор двоичных форматов, к которым не было описаний). Если с этим инструментом столько возни, неужели в современном мире нельзя выбрать другой инструмент, у которого нет таких проблем с совместимостью?


    1. aatimin Автор
      29.09.2017 11:46
      +1

      Спасибо!

      Sketch — это выбор дизайнеров, в нём быстрее и удобнее мобильные интерфейсы создавать. Одноплатформенность для нас не проблема, а удобств, наверно, все-таки больше, чем неудобств)

      Нельзя не отметить, что разработчики улучшают Sketch. Списывать со счетов его очень рано.

      По поводу костылей и возни не могу согласиться. Мы хотели локализованные скриншоты параллельно с разработкой — мы получили. Но как обычно, кастомные вещи требуют времени, которого в данном случае было потрачено разумное количество.


  1. andrewiWD
    29.09.2017 09:56

    Занимательный процесс. Т.е. два прогера по 5К брутто-зарплаты в месяц пилят систему локализации, анимируют скриншоты в скетче вместо существующих на рынке решений типа Smartling, Onesky, Lokalise с симуляцией экранов вживую по 100-200 баксов в месяц, куда и редакторы, и память переводов и другие плюшки входят. Зачем?!


    1. aatimin Автор
      29.09.2017 12:43
      +1

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

      Да и сама система переводов существует уже очень давно. Зачем создавались указанные вами сервисы, если была система переводов Badoo?)


    1. unel
      29.09.2017 13:37

      с симуляцией экранов вживую

      Что-то я посмотрел на эти сервисы и не увидел там информации про "симуляцию экранов вживую", как и объёма подготовительной работы, которую она потребует =(
      Можете подсказать, в каких разделах мне это посмотреть?


      1. andrewiWD
        02.10.2017 14:53

        1. unel
          02.10.2017 18:00

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

          Автор же статьи как раз повествует о способе, позволяющем локализаторам переводить (и проверять, что они нормально заходят) строки ещё до того, как программисты сверстают это самое приложение