Multi-lingual iOS & Android apps

Borut Tomazin
5 min readSep 13, 2018

Imagine an app that will be distributed around the world and needs to be translated in as many languages as possible. Easy right? Yeah, it’s not rocket science, but I’ll give a few tips and tricks on how to manage single or multilingual apps with ease.

As an iOS developer, the main focus here will be a localization of an iOS app. But I’ll give some tips for the Android part also.

iOS and android both have similart ways to handle localizations in apps. On iOS you need to create localizable.strings and on android strings.xml file. You would tipicaly create one of this file per language e.g. if you need to support English and German languages you will create {en,de}/localizable.strings for iOS and/or values-{en,de}/strings.xml . After that you create key-value entries where key is like and identificator for translation and value is an actual translation.

iOS and Android both have similar ways to handle localizations in apps. On iOS, you need to create localizable.string file and strings.xml on Android. You would typically create one of this file per language, e.g., if you need to support English and German languages you will create {en,de}/localizable.string for iOS or values-{en,de}/localizable.string for Android. After that, you create key-value entries where the key is like an identification for translation and value is an actual translation.

Here is an example of localizable.strings file on iOS

/* General */
"title" = "Title";
"subtitle" = "Subtitle";
/* Login *?
"login_button" = "Login";
"login_forgot_password" = "Forgot Password";

And example of strings.xml file on android

<!-- General -->
<string name="title">Title</string>
<string name="subtitle">Subtitle</string>
<!-- Login -->
<string name="login_button">Login</string>
<string name="login_forgot_password">Forgot Password</string>

Now imagine you have 1000+ entries in localization file like above. Again, easy right? Yes, you need to add key/value pairs, and you are good. But what if I tell you that we’ll be supporting German, Spanish, Italian, French and some other languages down the road? You’ll probably just copy/paste this files, renamed them and updated translations right? Well, this is where this article will help you out so you will not end up in a sync-up mess or forget to add/remove translations to/from all files.

The first thing you need to get familiar with an excellent library called Babelish. It is written in ruby and supports conversions for iOS and Android among others. The Babelish main purpose is to pick CSV file with translations and convert it to destination format (strings or XML in our case).

Install Babelish

The easiest way to install this library is to create Gemfile (or update if already exists in the project) with the following lines.

# Project Gemfile
source "https://rubygems.org"
# other gems
gem 'babelish'

After saving a Gemfile, just run gem install bundler and bundle install from the Console.

The next thing is to configure a config file .babelish that Babelish will use for conversion. An example is available on GitHub. Here I am sharing an example from one of my iOS projects

filename: "Project Localization"langs:
English: "en"
German: "de"
fetch: true
sheet: 0
excluded_states: ["SKIP", "android"]
comments_column: 0
state_column: 4
keys_column: 1
default_lang: "ENGLISH"
output_dir: "../Project/Resources/Localization/"
output_basenames:
- Localizable
ignore_lang_path: false
stripping: true
csv_separator: ","

Notice filename and fetch properties. Both are used to load a CSV from a Google Sheets. If you want to load translations from a local CSV file, change the filename to a local file path and set fetch to false. If you are not going to use Google Sheets, you can skip the next chapter.

Creating a Google Sheets

Head over to Sheets and create a new blank document.

Let’s walk you through the document briefly and explain all the columns.

  • Section - a grouping key, this is a comment that will separate each group with an empty line
  • KEY - localization key that will be used in the app to get out the translation
  • iOS/Android - these columns are not needed but are nice to have to know if a key was added to either platform (when using the same translations for both platforms)
  • Action - I use this column to define which platform key/value pair should be exported. If you put in iOS, it will be only generated for *.strings file. If you put in Android, it will be only generated for *.xml file. There is another option to put in SKIP. When you put that into the comment column, this line will not be exported for any platform. This is defined in the config with key `excluded_states`.
  • Notes - you can put here some extra notes or explanation.
  • English/German - a column per language. In this example, we have translations for English and German. When you want a new language, add a new column, name it and put it in the config file under langs.

The magic

Now that we have a spreadsheet ready with all the translations, we need one final step. Create localizable files that our apps will consume.

We need to run the script (below) that will generate files for us. The script should be in the same folder as the config file (or modify the path to the script). Note that config must be set per project. So if you have iOS and Android project, you’ll need to create two configs.

Call ./Localize.sh without params to generate files for iOS platform.

Generated strings files on iOS
/* General */
"general_success" = "Success";
"general_continue" = "Continue";
/* Login */
"login_button" = "iOS only export";
--------------------------------------------------------------------/* General */
"general_success" = "Erfolg";
"general_continue" = "Fortsetzen";
/* Login */
"login_button" = "iOS nur exportieren";

Call .Localize.sh android to generate files for android platform.

<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- General -->
<string name="general_success">Success</string>
<string name="general_continue">Continue</string>
<!-- Login -->
<string name="login_button">Android only export</string>
</resources>
--------------------------------------------------------------------<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- General -->
<string name="general_success">Erfolg</string>
<string name="general_continue">Fortsetzen</string>
<!-- Login -->
<string name="login_button">Android nur exportieren</string>
</resources>

Conclusion

I’ve seen a lot of projects doing localization wrong. I don’t say it’s hard, it’s just hard to make it right and easy to manage. It all complicates after you start localizing your app for the second, third or even more languages.

I’m using this technique in all projects I’ve been working on for a while now. I use this approach even if the app is monolingual. The main reason I’ve decided practicing this is feasibility to change existing translations or add new ones.

--

--