Demystifying Locale on Android
By: Julien Salvi
Tags: Android App Development, Android, Localization, AndroidDev, Android Development

Most of the Locales that are used in Android applications are built as the following language-region where the language tag is two or three letter code generally from ISO 639-1, and the region tag is a two-letter code from ISO 3166–1 alpha-2. For example, the tag for English spoken in United Kingdom is en-GB where en is for English language and GB for the region.

More complex Locales can be created using the script, variant or extension subtags. Let’s have a look at the script and variant subtags.

Script is a four-letter script code from ISO 15924 that describes the typeface to use for a given text. For example, Latn for Latin alphabet and Cyrl for Cyrillic writing system.

sr-Latn-RS for Serbian written with Latin alphabet

sr-Cyrl-RS for Serbian written in Cyrillic

Variant is a five to eight-letter code or a four-character code starting with a digit that represents a regional dialect of a primary language. This subtag must come after the language, script or region subtags.

ca-ES-valencia for Catalan spoken in Valencia

zh-Latn-TW-pinyin for Chinese language spoken in Taiwan in pinyin

On Android, such complex Locale can be built using Locale.getLanguageTag() by passing the full tag to the function or by using the builder Locale.Builder() if the tag is split into subtags. The builder will give you access to each subtag in order to define a new Locale.

Use Locales in Android applications

In this section, the focus will be made on the language resolution for the applications and how to deal with the number format and its currencies. Starting with the Android 7, multiple languages can be set on the device settings.

Language resolution

Let’s see how Android applications handle language switching and resolve Locales when a new language is settled into the app or to the operating system. There are many tools that allow us to manage translations and language switch for our Android applications(Lokalise, Phrase, Lingver…).

Let’s assume that we are using Lokalise for managing the app translations and an Android application implements the following languages we see below. Only Lollipop devices and versions above can target 3-letter code language tag, otherwise it will fallback to the default value.

res

|-values

|-values-fr-rFR // French from France

|-values-de // German

|-values-es // Spanish

|-values-es-rAR // Spanish from Argentina

|-values-iro // Iroquoian languages

And the user has set these languages on the operating system:

1 Français (Canada)

2 Italiano (Italia)

3 Español (Argentina)

4 English (United States)

Prior to Android 7, the Locale resolution would have failed because French-Canadian fr-CA does not match any language in the application even if the application does support French. Default values will be used for displaying string resources in that case.

With Android 7 and above, things got better with major improvements of the language resolution and the support of multiple languages on the device. Thus, for the same application, the resolution will be different. The app will look at the resource that matches the exact tag at first, then the parent ( fr here for instance) and finally all children of fr . As fr-FR is implemented into the application, all texts will be displayed in French but without Canadian variants.

If no languages match the first language, the application will use the second and so on, until a Locale matches the support languages. Otherwise, the default resources will be used.

For a better and faster resolution make sure to use the default language tags ( fr , es …) when setting up your app resources and then localized tags.

Number format and currencies

Locales are very important when you are dealing with numbers and currencies. In order to be displayed correctly, numbers and prices must match the language of the application for consistency and reliability. The example below will show you some differences between the languages:

When there is no currency is available the generic symbol ¤ is used, if Locale.ROOT is provided for example.

Common pitfalls with Locale

Locales can lead to tricky errors on Android when trying to apply the right Locale for your applications, supporting new languages or using WebViews.

Beware third party libraries

In most of applications, all developers are using third party libraries and some of them can embed resource files such as strings.xml . If some of these strings are used in the application, it can lead to a sentence written with multiple languages.

Let’s say you are using the string resource R.string.abc_search_hint from the Material Design Component library. The application developed and distributed worldwide supports English (as default), French and German. If the device is set to French, the application will be displayed in French, same behavior for German and if the device is set to another language it should fallback to English, right? Well, not really…

If the code below is not set in your build.gradle you will have some surprises when launching the application. For example, on devices set to Italian, your texts from your resources will be in English (as you do not support Italian language) but the text from the library will be displayed in Italian as the library supports the language!

Be sure to scope the language you are using in your application to avoid displaying text with multiple languages.

Pro tip: when filtering resource files with resConfig , don’t forget to update it when new languages are supported. It will save you some time not figuring out why your app is not translated in the new language.

Difference between app Locale and device Locale

In most cases, calling Locale.getDefault() will allow the user to know the current language of the application that is usually the same as the device language. But it is a common a mistake to use this method to get the device language, especially if the application can switch language at runtime!

Indeed, Locale.getDefault() is the current value of the default locale for the instance of the JVM, and not the language the user set in the phone settings. Switching language will set a new value to Locale.getDefault() but the device language won’t be changed! So if you want to get the current device Locale use the methods below:

Even if the values might be the same most of the time, be sure to know the difference between these two methods.

Fear the WebView!

Starting with Android 7, Google decided to change the way WebView is launched and use Google Chrome instead. And there is a huge impact in your apps if multiple languages are supported. Opening a WebView will revert the application Locale to the device default language, so if you allowed your user to select a different language for your app, other than the device’s one, it will break your translations.

This issue has already been reported to Google but won’t be fixed as, according the Google, it is working as intended. So a workaround has to be implemented to avoid this behavior.