Automated Mobile Localization Process

People prefer using applications in their native language, therefore localization is an important way to reach more markets and attract more users. Based on our experience, we can confidently state that localization boosts registrations, app usage and revenue.

In the charts below you can see significant increase of purchases for Italy and Turkey once the app was localized for those languages.

Payments Italy Payments Turkey

What we have

In this article I will explain how we keep our development speed and manage to ship fully localized new features so often.

Localization request

Once a feature is refined and ready for development, designers align on all strings with product communication specialists and marketing. Then developers take all new strings in the feature and add them to a separate localization repository on GitHub. After all strings are added to the localization repository, a job on CI exports them to an external translation tool. In our case the tool is Transifex. Transifex is a service for management and collaboration with translators.

All strings in the localization repository are stored in the YAML format. This keeps localization files in platform-independent format. Here is an example of strings:

mob_bw_buy_coach_title: Unlock Your Coach Now
mob_bw_buy_coach_subtitle: Personal to You

and_bw_buy_coach_period_week: '%s / Week'
ios_bw_buy_coach_period_week: '%@ / Week'

Each string has a meaningful key to the localization team and gives a context about where the string appears. Usually the same strings are used for both platforms, but in cases where a string is different across platforms (like in the above example with different formatting), we define two strings and use the prefix and or ios. Once strings are added and exported to the localization tool, translators can start translating them.

Developers in their turn add the same key of each string into the platform localization file (strings.xml on Android, Localizable.strings on iOS). This is needed in order to pick up translation later for each locale.

Translation integration

After all translations strings are ready, they need to be added to the app. Doing this manually would be error prone and time consuming. Therefore, it is done by an automated CI job which runs everyday and consists of several Shell and Python scripts. It does the following:

  1. takes a .yml file with the translations for specific locale;
  2. does a loop over all keys for default locale (values/strings.xml);
  3. for each key it takes a value (translation) from .yml file;
  4. if a translation not found, the value from the relevant English string is used;
  5. saves to the separate map;
  6. when the loop is done, the map is serialised to strings.xml file of appropriate locale (e.g. values-de/strings.xml, values-fr/strings.xml etc.);
  7. repeats all steps for all supported locales;

Below you can see the function for matching strings with translations.

def match(language_code, strings_xml_keys, english_strings, input_yaml):

    translated_strings = input_yaml[language_code]

    for key in strings_xml_keys:

        if key in translated_strings:
            value = translated_strings[key]
        else:
            value = english_strings[key]

    ...

It is worth mentioning that if the app supports multiple plural forms, the script should also take into account the file with default plurals forms. In a .yml file plurals are represented as a map:

mob_bw_buy_coach_purchase_month_plurals:
  one: '%d Month'
  other: '%d Months’

Finally, the new strings.xml file for each locale is ready. CI then creates a pull request to the matching application repository. Developers only need to approve and merge the pull request and the translations are added to the application.

Clean up translations

Over time localized strings can become redundant and should be removed from resource files like strings.xml for Android, but also from the .yml file in the localization repository. We have automated this: Once a month our CI runs a script that detects unused strings and removes them from the .yml file. The clean up script is similar to the translations integration script and afterwards pull request will be created to the application repository for removing unused strings for all supported locales.