iOS - Localizing strings in a project19 Feb 2021
Localization is an often overlooked problem. We start a project, we know that we only have to support one language and we approach our string-handling based on that requirement. When the time comes to support a second language, the project grew so much that adapting to that second language causes pain and frustration.
Doing it right from the beginning
iOS and Xcode support localized strings in a rudimentary-but-pragmatic way: we can define a
Localizable.strings file, and localize that file in n languages. For each language we will have a key-value file that will contain all our strings:
// German strings "ok_button_title" = "OK"; "cancel_button_title" = "Abbrechen"; /*Main Screen*/ "main_title" = "Willkommen";
// Spanish strings "ok_button_title" = "Vale"; "cancel_button_title" = "Cancelar"; /*Main Screen*/ "main_title" = "Bienvenido";
For accessing these strings all we need to do is to use
NSLocalizedString(key, comment: "") and pass the key we are interested in. Eg.
NSLocalizedString("ok_button_title", comment: "")
This is going to work nicely and, if we were to launch the app in German or Spanish, the system will know which strings to use.
Getting they keys under control
The main drawback of that approach is that keys are strings and if they start going around, that could be prone to error.
This can be easily solved as well. Let’s define a new type called
Strings which will contain all our keys:
We can even define an extension to get the localized string for every key in the
Now all we need to do to use a string in a
UILabel, for example, is to call:
Bringing it to the next level: extracting the strings
Keeping strings and wordings up to date is usually a burden for developers. Designers would produce a mockup which some wordings than then the product manager will need to check/update. The developer is usually left with the task of making sure that these wordings are in sync. If a project is run in several platforms (iOS, Android, web…), keeping the wordings up to date and synchronized can get out of control easily.
A wiser approach would be to move these strings out of the project and place them in a centralized tool that every platform could access. You could build such a service or you can use a commercial solution. For the sake of this tutorial I’m going to use POEditor: an online tool which allows you define terms and their translations and provides an API to access that information.
Setting up the project
This step is very straight-forward: we create a project, we add new entries (called terms in POEditor) and provide the translations. That’s all it is.
Automatizing the fetching of the strings
The underlying idea is that, on every build, we want to fetch the latest strings, generating our
Strings enum automatically (listing all available keys), and download the translations in the different supported languages.
To do so I’m using Python, hacking my way into it (please, don’t judge my Python knowledge: I know nothing about nothing).
The script does two main things: first, it fetches all the terms listed in the project. With that we create our
Strings class (we are also taking care of the case conversion from snake to camel case).
The second step is to download the files containing the translations.
This script is run during the build phase:
This can be easily improved, but with a couple of neat tricks we were able to create a setup where developers don’t really need to worry about strings anymore. A PM or marketing person could go into that tool and update these strings and the app will pick-up on the changes during the next build.
The tool also supports other formats for the export file (JSON, Android XML, etc.), so this script could be easily adapted to your project/platform requirements.
Thanks for reading! ✌️