Our thoughts, knowledge, insights and opinions

Good practices for Android app localization

Often when we develop our brand new apps, we focus on delivering as soon as possible, which leads us to take shortcuts, including not thinking about internationalization. When we decide we want to have our app translated, it can be quite a pain if we don’t think about that from the very beginning. Let’s take a look at what we do wrong and what we can do if we’ve already taken shortcuts.

Internationalization vs localization

We often use “internationalization” and “localization” terms as synonyms, but it’s actually not the case. We should think about internationalization as adapting your app to be able to be translated to other locales. Localization, on the other hand, is a process of adapting it to a specific locale. Listen to Fragmented Podcast[049] on that matter.

Localization basics

This paragraph will cover basics of how Android handles packages in terms of localization. If you know that already, you can go ahead and skip it.

Android lets you provide resources based on language currently picked on the device. It is a pretty straightforward procedure - all language-specific data has to land in a directory with a proper suffix. Let’s say you have your app internationalized, all your English strings are in values/strings.xml and you want to add Italian translation. What you need to do is create a values-it directory and simply add strings.xml file there with the same XML keys and translated values. For different languages, you only need to change the suffix in the directory name. If you have different data than strings that need to be translated, same rules apply. For drawables, you simply need to create drawable-it directory and put your drawables there. In following paragraphs we’ll look through good practices regarding internationalization and localization of Android applications.

Common issues with internationalization/localization

Let’s face it - we tend to be lazy sometimes. Sometimes the “right” solution is only two clicks away, but we consider it redundant and don’t bother. If we ignore localization-related issues from the very beginning, it can create a massive amount of work when we do choose to localize the app. Be warned, most of the steps included here will again be basic ones :)

Hardcoding strings

When adding strings in layouts, it’s often tempting to hardcode strings. Don’t do that. Fortunately, Android Studio at least warns us here and provides us with a simple procedure to “unhardcode” the strings. Simply move the keyboard cursor on the hardcoded string in XML file (like android:text="Some Text") and then:

to extract that. By the way, if you need to check something only in the visual layout editor without creating a string, simply use tools:text=”Example text”. It will render this text inside Android Studio layout editor only, without creating row in strings.xml file.

Margins and paddings

To avoid problems with right-to-left handling, remember to use margins and paddings with start/end in it (android:layout_marginStart, android:layout_marginEnd) instead of left/right (android:layout_marginLeft, android:layout_marginRight) for API >= 17. For lower API, you unfortunately have to use both specifiers (e.g. left AND start)… or, if you’re using databinding you could use:

Then, in XML layout file just surround your entire layout with <layout>...</layout> tags. You’ll be able to use simple one-liner: app:layout_marginStart="@{@dimen/start_dimen}" instead of two attributes.

Placeholders in strings

Generally, it is better to use placeholders whenever possible instead of concatenating strings “by hand” in Java code. Let’s take a look at two quick examples. First, let’s say you want to greet the user by saying “Hello, User!”. You should definitely go for:

Instead of: because in other languages %s might have to be placed somewhere else. Another quick example: let’s say you have a list of names that you want to show to the user. Again, you could just concatenate them in code with a separator of your choice, e.g. a comma. What you should do instead though is to save a separator and use it during concatenation. Why? In different language, the separator could simply be different.

Clear Communication with Translator

Let’s say you’ve prepared all the strings and you’re ready to internationalize your application. Before you send them to the translator, hold on for a second. There are a few traps we can run into.

XML format

An inexperienced translator might be unfamiliar with XML format. In this case you have to tell him/her what exactly needs to be translated (the value, between <string></string>, not the key <string name="this_should_not_be_translated">). There are also formatting options like %s, %d, %1$s, %2$f which you need to explain to the translator. Generally, he/she should not mangle with them unless it is necessary to move them in a different place (depending on the language). Another thing is escaping apostrophes: translator needs to know, that it is needed to add \ before apostrophes like That\'s good!.

Providing context

If all we give the translator is a raw strings.xml file with keys that look just like values: the translator might lack the context necessary to translate them properly. There are a few ways to provide one. In an ideal world, each string should be commented. Probably no programmer likes to and wants to do it, but it can be really helpful to the translator and can help avoid erratic translations.

You should also keep in mind, that sometimes there are situations where the amount of space for the strings is limited (e.g. on a button) and it’d be a good idea to somehow point that out (e.g. via a comment). This way he/she can know if they should reach for a way to shorten the string (e.g. use abbreviations) or can just go for the best translation they can give. You should also consider prefixing the keys of strings with a screen name like: This way it’d be easier for the translator to find the screen on the designs and provide accurate translations. It’s also a good idea to give the translator an up-to-date document with application screenshots so he/she can review the translating work on a “living organism” (more information on how to provide a report with screenshots in Checking translations.

RTL tips and tricks

During the process of localization, we should keep in mind the RTL (right-to-left) languages group. Just quick recap: on Android 4.1, limited support for RTL languages was introduced, and then in 4.2 they became fully supported by the system. To turn RTL support on for our app, we have to enable it in the Manifest file by adding android:supportsRtl="true" in <application> object. Unfortunately, setting this flag doesn’t automagically make your application RTL ready - we should also revisit its layouts. Next few paragraphs will show you how to handle the most common problems.

Icon mirroring

It is the common use case for a RTL language (e.g. Arabic) to flip the icons horizontally (mirror them). For example, an arrow which points left in LTR has to point right in RTL. You can generate duplicates and keep versions in proper directories (e.g. drawable-ar), but fortunately, there is an easy way around. You just need to use styles.

Lists mirroring

ViewPager, HorizontalScrollView, RecyclerView (horizontal case) are often used to show lists of several views in a form of a horizontal scrollable list. When using them, we have to take care of the proper RTL localization process.

RecyclerView (horizontal case)

All you need to do in order to properly setup RecyclerView for RTL is to call setReverseLayout method on LayoutManager instance. This method takes a boolean parameter, and in that case you should pass true to tell RecyclerView that an entire layout should be reversed.

ViewPager, HorizontalScrollView

When using ViewPager or HorizontalScrollView, you have to take a look at an attribute called android:layoutDirection and related view instance method setLayoutDirection(int). There are several presets that you can utilize in the application. You can find more information in Android documentation.

Working with numbers

The next thing that we need to keep in mind during localization is how we present the numbers in the UI. For most cases, showing commonly used, “our” numbers as they are, like “1231” is enough. Sometimes we have to take care of other variants, though, like numbers used in Arabic. A good idea would be to consider user’s locale when showing numbers. And of course there is an easy way to achieve that. What you need to do is to simply use NumberFormat class. Take a look at the following code example below:

The output of that code is:

Checking translations

Before rolling out our application, it would be a good idea to check all screens with all translations. One way to check it is to go through the entire app flow manually and look for any probable inconsistent translations. But this approach has major flaws. We are developers, not translators, so… we can’t really check if translations are correct. Another approach would be to send the .apk file straight to the translator and let him/her do the checking. It could be fine, but in many cases, translator won’t know the entire app flow and might miss some screens.

Fortunately, there’s another, more elegant solution. Let’s use our developer’s skills and make our life easier, using instrumentation tests. This way we can create a report with screenshots of all screens in the app, with the whole flow, so neither us nor the translator has to manually walk through the application. We’ll use Espresso and another the great library called Spoon. Spoon allows us to take screenshots and build beautifully formatted reports as a simple HTML page. When we have reports ready, we can send them to the translator and he/she can verify them. We’ve killed two birds with one stone: we have tests for the app flow and we can verify final translations.

Espresso test with different locales

In this example we’ll show how to create tests, which will handle various locale scenarios. As mentioned above, we’d also want a report with screenshots per locale (for the translator, and for us to take a quick look if everything fits). The most convenient way is to use one, simple annotation per method with as little code as possible. Our annotation will look like:

We’ll achieve that by using custom TestRule and one custom annotation. First, let’s create custom TestRule like this:

And next we create a custom annotation:

To hook up all these things together, we have to add a rule as part of test class:

To run instrumentation tests and generate UI report you have to run them via command line:

At the end you will get report like this one:

Spoon Report

Pseudolocalization

Another trick we could use to check our app against different locales is the boolean flag pseudoLocalesEnabled which can be set inside app/build.gradle file. What it basically does is it can generate pseudo-random strings translations based on your current content of the strings.xml files. To show this in action you have to enable specific locale in device settings:

  • en_XA, which adds tons of glyphs as well as inflates the length of each string:

English-XA

  • en_XB, which reverses every string and sets the locale to right-to-left:

English-XB

More information about pseudoLocalesEnabled you can find here Pseudolocalization: Visiting Android’s Bizarro World and here Pseudo-localization testing in Android.

Useful tools

We think it is worth taking a look at multiple services/tools, that can significantly help you with the organization of translation process. They usually offer a simple web tool, in which one can add context to each key (string), add another language, and easily import/export translation files ready for Android or iOS. It is also not uncommon for these services to offer translation work if you need an experienced translator for a particular language. Sometimes they even let you change translations in runtime and roll out an update of texts without uploading an update to the store (interesting how that works, right? ;)) Keep in mind though, there are multiple solutions available, and those below are just examples.

Below you can find other useful resources that will help you with localizing and testing your applications.

If you want to know how other platforms handle localization, check our previous post on localizing iOS apps.

You like this post? Want to stay updated? Follow us on Twitter or subscribe to our Feed.