Software localization
How to Share Translations Between iOS and Android
While there are platform-specific tools for the localization process available (e. g.Xcode'c the integrated XLIFF export).
However, if both apps offer the same functionality and display pretty much the same content you can really benefit from shared translation data.
Sharing translation data between an iOS and an Android app leads to new challenges that I will address in this blog post.
I will sketch out how to share translations across both platforms as well as how to set up a continuous localization workflow for this.
Overcoming Cross-compatibility Obstacles
Before we get started I highly recommend to check out the official localization documentation for iOS and Android.
Xcode provides a feature called “Base internationalization” to help you develop your internationalized iOS app. It is used to extract user-facing strings from .storyboard and .xib files. It relieves translation editors from having to modify .storyboard and .xib files inside Xcode.
However, there is a drawback. Base internationalization generates string files with cryptic names referencing the quite cryptic object ids of the interface builder:
"CH7-Yv-ryk.text" = "Nice text";
These cryptic segment names lose any contextual information and are not helpful to translators.
Imagine using such a segment identifier in a shared setup with an Android app. Your Android team would have a really hard time finding out where this segment should actually be located inside their application because it lacks any information on its context. It even gets worse if the copy is quite short or used in multiple locations with the segment name offering no help at all.
In general, developers on both platforms will benefit from well structured and meaningful key names.
You should abandon the thought of using Base internationalization in Xcode when you intend to share your translations between both apps. At least if you want to keep your whole team happy.
iOS and Android rely on different formats and structures for their localization files. Even if you have generated a nice String file for your iOS app that contains meaningful key names, you can’t just include that file in your Android app or vice versa.
Before adding an existing iOS locale file to an Android app, you need to convert your localization files between the iOS Localizable Strings and Android XML format.
Placeholder Handling in iOS and Android
For example, consider the case if you use interpolation variables in your locale file. Android and iOS basically share the same c-style placeholder style but unlucky us they differ in their formatting syntax.
Let’s assume in your app you want to display a greeting based on the user’s ‘name’. You want to insert that name into the string programmatically to greet every user individually with their own name using an interpolation placeholder. Unfortunately, iOS and Android don’t use the same syntax in that case. In iOS the typical placeholder would be %@
, however, Android uses the placeholder syntax %s
. So in order to work properly on both platforms, strings containing placeholders need to be converted correctly to the syntax of the respective platform.
Now, you could start writing a small script to convert the possible placeholder formats inside the locale files, however, a modern localization management system like Phrase handles this automatically for you. For the placeholder example, Phrase enables you to download your translations correctly converted into the format you want at any time without you having to care about specific conversion rules.
Phrase also offers an Xcode and an Android studio plugin (Beta) to allow simple synchronization with our online Translation Center.
Continuous Localization with Phrase
Setup
Starting with Phrase, the onboarding wizard will guide you through the initial steps including the creation of your project. Complete the wizard and created your first project. You can either initially upload your existing locale files now or do it using our plugin or client later. However, you should ensure that the c-style placeholder format is activated in your project to use Phrase’s placeholder conversion feature.
After setting up your account and initial project, the next step is to download and install the Phrase plugins for Xcode (iOS) and Android Studio. As described in the previous section, we won’t use the Base Internationalization feature workflow in Xcode (Note that the guide for the Xcode plugin relies on the Base Internationalization feature using the XLIFF format).
If you have followed the plugin installation guides, you should now have one .phraseapp.yml configuration file inside your iOS app’s folder and one inside the Android app. At Phrase we call the upload of locale files `push` and the export or download `pull`. You’re now basically ready to push and pull your translations using the plugin or the Phrase Client directly for each specific platform. However, since we want to enable a workflow to share translations between iOS and Android there is one necessary step left. We need to customize the .phraseapp.yml a bit for each app, so let me show you how in the next step. There is a format option ‘convert_placeholder’ which we need to activate for the pull-command as shown in this example configuration file:
For Android:
phraseapp: access_token: 930dfe3c2736d745c610c46b3d050b5526a45fcs5d01d007e5103a481f9a40cs file_format: xml project_id: 3dbc6dc4d79eaa48a7af7ebc7ea12ef5 pull: targets: - file: ./app/src/main/res/values/strings.xml params: locale_id: 17326bf9e3eb422fe1a33bb2faas7af1 format_options: { convert_placeholder: true } - file: ./app/src/main/res/values-es/strings.xml params: locale_id: 273dxbf9e3eb422fe1a33bb2fad87af1 format_options: { convert_placeholder: true } - file: ./app/src/main/res/values-fr-rCA/strings.xml params: locale_id: 37326bf9e3eb422fe1a33bb2fad87aas format_options: { convert_placeholder: true } push: sources: - file: ./app/src/main/res/values/strings.xml params: {locale_id: 17326bf9e3eb422fe1a33bb2faas7af1}
For iOS:
phraseapp: access_token: 930dfe3c2736d745c610c46b3d050b5526a45fcs5d01d007e5103a481f9a40cs project_id: 3dbc6dc4d79eaa48a7af7ebc7ea12ef5 file_format: strings pull: targets: - file: ./en.lproj/Localizable.strings params: locale_id: 17326bf9e3eb422fe1a33bb2faas7af1 format_options: { convert_placeholder: true } - file: ./es.lproj/Localizable.strings params: locale_id: 273dxbf9e3eb422fe1a33bb2fad87af1 format_options: { convert_placeholder: true } - file: ./fr-CA.lproj/Localizable.strings params: locale_id: 37326bf9e3eb422fe1a33bb2fad87aas format_options: { convert_placeholder: true } push: sources: - file: ./en.lproj/Localizable.strings params: {locale_id: 17326bf9e3eb422fe1a33bb2faas7af1}
I assume in the provided example configuration files that you want to translate your apps from English (en) to Spanish (es) and to Canadian French (fr-CA). Therefore, we have created the corresponding locales inside our Phrase project beforehand.
Check out the Phrase client configuration documentation for more details on possible options.
It’s time for your first upload! Use the plugins or the Phrase Client from the command line to push your locale files from one of your projects to Phrase. You will notice that only one locale will be uploaded as shown in this example output of the Android Studio plugin:
In the example configuration file, I have explicitly added only one push-source for the original copy (also called default locale within our system). I highly recommend to not push all locales in your continuous workflow. It’s a good idea to manage all copy and translation changes within Phrase from now on and to use push only to introduce additional translation segments.
If you have existing translations for any other locale, you can of course still initially push them using the Phrase client or uploading them inside the using the Translation Center.
Now, your translators can start editing or adding translated copy inside Phrase. Should you need professional translation services, you can even order professional translations through our software that are usually delivered within a couple of hours.
Whenever translations are updated you can use the Phrase plugin to `pull` those copy changes into your two applications. You can also integrate this as a build or release step. As an example, you can simply add a hook to update your locales when building your iOS app in Xcode. Just add a ‘run script’ in your ‘build phases’ of your chosen target that executes ‘phraseapp pull’.
If you add new features that require completely new translation copy, you can just add the required keys to the source locale file. Using the `push` command of the plugin, the newly created segments will be uploaded to Phrase. The new segments will be created and are available for translators in the Translation Center immediately.
An improved process
Congratulations, with this simple setup you have now enabled a continuous internationalization workflow that ensures that your iOS and Android app will always share the same set of translations.
Your Android and iOS apps are now prepared to use shared translation data. Updating the apps’ copy is as easy as ‘pull’ing the updated locale file from Phrase. Your localization team can finally work on copy without having to bother your engineers too much. Similarly, your engineers do not have to worry about the translation process and can spend their time on developing the software.