Переводим интерфейсы на полсотни языков. Sketch

59cbae92d0813432765213.jpeg
Герои сериала «Шерлок»


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


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


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


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


Перечисленные инструменты призваны помочь в процессе локализации сайта Badoo, двух мобильных приложений Badoo (для Android и iOS), а также партнёрских приложений Chappy, Bumble, Huggle, Hot or Not на 47 языков. Это огромный фронт работ.


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


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


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


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


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


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


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


59cbcd8b3120e035270530.png


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


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


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


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


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


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


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


59cbcb8e5a65b963410531.png


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


59cbd3187d282658459260.png


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


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


59cca7f74e7e5945728552.png


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


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


$ grep -l ‘iPhone 7’ pages/* 


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


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

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


Не будем касаться вопросов разархивирования и чтения JSON из файлов, но стоит сказать о формате Property Lists (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 запрошенного скриншота, а в значении записано представление скриншота (напомню, мы запрашивали скриншот для iPhone SE) в Base64.


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



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


59cbb717b2ada606625949.png


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


Заключение


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


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


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

© Habrahabr.ru