Connect with us

Разработка

Как iOS-приложение Tinder сократило размер локализаций на 95%

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

Опубликовано

/

     
     

iOS-приложение Tinder используется более чем в 190 странах мира. Для того чтобы работать в каждой из этих стран, нам необходимо обеспечить локализованный опыт. Важнейшим аспектом локализации является отображение правильного языка для локали текущего пользователя, какой бы она ни была. На практике это означает, что в любой момент времени мы можем поддерживать более 50 языков для любой функции, которую мы предоставляем нашим конечным пользователям. Со временем доставка всех поддерживаемых локалей нашим пользователям, особенно на рынках с более ограниченными сетевыми возможностями, стала сопряжена с существенными затратами.

Проблема

Локализация на платформах Apple использует общий шаблон пути к файлу каталога для обеспечения локализации строк. Каждая папка называется по поддерживаемой локали: например, en.lproj соответствует английскому языку США, fr.lproj — французскому, а затем каждый файл strings/stringdict, находящийся в этой папке, будет использоваться для локализации на нужном языке. Ниже приведено визуальное представление этого отображения:

Эта структура повторяется для каждого таргета, использующего локализацию в графе сборки, что означает, что на каждую цель будет приходиться не менее 50 файлов Localized.strings и Localized.stringdicts. Для приложений, использующих статическую линковку, это означает, что каждый из этих файлов должен быть отнесен по имени к цели, в которой он находится, поскольку мы не можем допустить коллизии путей файлов в конечном пакете iOS App Store Package (IPA). Например, если есть две цели TargetA и TargetB, то дерево локализованных файлов должно выглядеть примерно так, чтобы статическая линковка работала:

То, что мы имеем на данный момент, будет работать, но есть проблема, связанная с процессом подписания кода Apple. Минимальный размер одного файла при подписании кода составляет 4 КБ, а это значит, что каждый локализованный файл, независимо от его содержания, будет занимать в конечном IPA не менее 4 КБ из-за подписи.

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

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

После удаления этого неиспользуемого содержимого мы получаем примерно следующее:

На этом этапе все выглядит лучше, но теперь мы видим, что для каждого файла локализации мы повторяем ключ снова и снова для каждого поддерживаемого языка. Это можно оптимизировать с помощью инструмента SmallStrings от Emerge.

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

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

Решение

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

  1. Объединить все файлы локализации в один файл для каждого языка
  2. Минифицировать этот окончательный объединенный файл локализации с помощью SmallStrings от Emerge

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

Объединить все файлы локализации в один файл для каждого языка

Для выполнения этой части работы мы реализовали новое правило под названием namespaced_strings. Это правило собирает локализации для каждой цели в графе и возвращает провайдер, содержащий версию каждого файла локализации с расставленными именами.

Псевдокод Starlark можно посмотреть ниже:

Затем, используя этот Provider, мы создали Aspect и еще одно правило для его запуска. Этот Aspect может обойти весь наш граф сборки, собрать провайдеров строк с именами, а затем объединить все файлы локализации в один файл для каждого языка.

Благодаря нашему новому правилу merge_strings у нас теперь есть способ транзитивно собрать все namespaced строки для всего приложения.

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

Минифицируем этот окончательный объединенный файл локализации с помощью SmallStrings от Emerge

Теперь, когда у нас есть единый файл локализации для каждого языка, у нас есть необходимые настройки для поддержки использования инструмента SmallStrings от Emerge. Первым делом нам нужно было перенести инструментарий, основанный на Ruby, в Swift, поскольку мы не хотим поддерживать цепочку инструментов Ruby в сборке. После того как мы перенесли логику фронтенда инструмента на Swift, мы создали цели cc_binary и cc_library для кода на C, используемого для выполнения сжатия LZFSE. После того как эти цели были созданы, мы могли создать новые правила, которые могли бы получить наш провайдер NamespacedStringsInfo и выполнить необходимую минификацию:

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

Влияние

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

Свернуть все файлы локализации в один файл для каждого языка

  • Изменение размера загрузки: -5,5 МБ
  • Изменение размера установки: -30,1 МБ

Минификация этого окончательного объединенного файла локализации с помощью SmallStrings от Emerge

  • Изменение размера загрузки: -5.2MB
  • Изменение размера при установке: -21.2MB

В целом эти усилия привели к уменьшению размера загружаемого файла на 10,7 МБ и уменьшению размера устанавливаемого приложения на 51,3 МБ без какого-либо ущерба для разработчиков или конечных пользователей.

Выводы

Вот несколько выводов из этой работы:

  1. Генерация кода может быть невероятно мощным инструментом для абстрагирования от больших изменений, таких как эти.
  2. Упаковка множества файлов размером менее 4 КБ может значительно увеличить размер вашего приложения, поэтому очень важно уменьшить количество мелких файлов, упакованных в конечный IPA.
  3. Тем, кто заинтересован в таком подходе, следует разработать тесты для упакованных файлов локализации до и после этого изменения, чтобы убедиться, что ни одна строка по ошибке не потерялась. Эти тесты также являются хорошим способом доказать, что количество файлов локализации было уменьшено в результате изменения.
  4. Герметичная песочница Bazel может прозрачно предоставлять улучшения нашим разработчикам и пользователям. Вместо того, чтобы фиксировать результаты этих действий по минимизации, мы можем использовать песочницу для скрытия этих деталей реализации. Иначе было бы невозможно перестроить наш конвейер переводов, чтобы приспособить этот рабочий процесс к одной платформе.
  5. В результате этой работы мы получили еще больше преимуществ, таких как динамическое удаление неиспользуемых строк. Благодаря этому подходу мы видим способ добиться этого и получить еще больше улучшений.

Для тех, кто интересуется этим подходом, пожалуйста, обратитесь к репозиторию SmallStrings.

Источник

Если вы нашли опечатку - выделите ее и нажмите Ctrl + Enter! Для связи с нами вы можете использовать info@apptractor.ru.
Telegram

Популярное

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: