Internationalize your App (III)
Welcome to the third part of this series of articles in which we are explaining a posible approach to a repetitive problem. How do I internationalize my mobile App?
Let’s say you are not starting a project from scratch and you have the same app for Android and iOS, each of them with their own translations and their own string keys (if any in iOS). Unfortunately you are where we were a few months ago. A long and tortuous path is just in front of you. Let’s see how to approach the process. Target: having a single source of truth for iOS and Android and, in the process, having a set of strings that you could use for any other platform as frontend web, desktop app, etc.
Step by step process
Think about a suitable naming convention for the string IDs
Since we are going to have a single source of truth, it is better if the string IDs have a meaningful name. In our case we thought a geed choice would be:
This three part separation takes into account the screen in which the string appears, the component within the string and lastly a descriptive name. For instance, for a given string ID
start_page.header.greeting_title we are saying that the string appears in the
start_page string within the
header component and the string content is the
greeting_title. As we laid out, the .po will be used by the Product people to inspect and modify the strings, so a meaningful string ID will provide a valuable context for them.
Localize any existing string in the app
It sounds obvious. Nonetheless we found most of the strings (for the iOS platform) were hardcoded in the Swift classes as well as in the .xib and storyboards. Take advantage of the non existence of these strings to name them properly based on the naming convention agreed in the previous step.
Match string keys
This is maybe the most tedious task for the whole process. The objective of having a single source of truth is sharing strings between platforms, so it would not make sense if we have the same strings duplicated in the code, one for Android and the other for iOS. Personal tip: avoid using “ios” or “android”, its naming should be framework agnostic and we will end up with strings that are only used in one platforms. That is not a big deal.
To achieve this we created a little matching script which checked wether there were repeated values between the two platforms. Once we had that list we just had to decide which one we took and named it accordingly.
Add missing strings
We need to migrate to the .po file from a fully functional string file (xml or .strings) so at this point we will choose one as a single source of truth (we chose the iOS .strings file) and move all the strings to that file, which implies taking the strings from Android which are not already in the file and move them to the .strings file. Now we have a single source of truth, but not in the .po file yet but in the iOS .strings file.
Create .po files
Now it is time to generate the .po file (which will be our single source of truth from now on). To achieve so we relied on the localise.biz tool. Thankfully we avoided to create another script to convert from .strings to .po. Upload the generated .po to a new Git repository and voilá, you got your strings repository ready to be converted to any platform dependant format (Android and iOS for the time being).
Weblate installation [optional]
At this point is enough to manage an App strings from a single file, but if you want to empower the Product people or even you have translators at your disposal, it is a good idea to have a translator tool that allows them to easily manage the bunch of strings.
We chose Weblate because it is an open source tool that, thanks to Docker, is easily installable in your Linux server. It has paid plans to work as SAAS.
This series of articles will not cover any scripting code, it is just about an approach to a problem that any of you can implement in your own way. Make sure to create a set of scripts that are able to convert a .po file to .xml and .strings files. Personal tip that helped us a lot: rely on regex as much as possible.
Once we have completed the “step by step” process we are in a scenario in which we already have a single source of truth at our disposal. Time to generate the platform dependant strings files (.xml and .strings) with the string framework we have just created. But, how do we integrate the generation process within our regular workflow?
We got it in Android through a build.gradle task that is invoked during the Gradle initialization phase, which means it is also invoked when compiling the project.
For iOS we followed a similar approach by adding a pre build phase to the Podfile.
This code by itself do nothing. Actually it invokes a Ruby file in which we make the call to
generate_strings.py as we did in Android.
Thank you for reading. If you want to read about further actions and improvements that we could apply to the solution, I encourage you to stay tuned.