Conversion By Translation - Changing Your Android App Language At Runtime
You have an awesome app with lots of cool features, one of which is multi-language support for your users. Everything works great but your Product Manager wants to improve conversion by prompting users to choose the app’s language on the first launch as part of the onboarding process.
With a couple of years of Android experience under your belt, you're thinking,
“How hard can it be?”
On Android, there is no official support, documentation or API to change an entire app’s language at runtime. When a user opens an app, the resource framework automatically selects the resources that best match the device language settings. In Google Maps, for example, you can not change the street names on the map without changing the entire device language.
In order to change an app’s language (or
Locale in Android terms), we need to be familiar with 2 different mechanisms that will help us achieve our goal:
This class describes all device configuration information that can impact the resources the application retrieves. This includes user-specified configuration options (locale list and scaling)
Basically, this is the class that Android is gettings its
Locale information from.
Overriding this class with the
Locale that represents the user’s selected language will change the folder Android is looking for its resources (e.g strings.xml).
Gets the current value of the default locale for this instance of the Java Virtual Machine.
Locale is usually used when using operations like formatting text, parsing numbers or handling dates. So it is important to override it, otherwise, an app may be displayed in the correct language but things like dates and currency will be broken.
From API 17 and above, the preferred way to override a
Configuration is by wrapping the currently used
Context in a new
Context using the method below:
We can combine all the actions we need to take into a single function:
This method should be called inside the
attachBaseContext() method in the
Application class and in each Activity.
As far as the Android system implementation goes, we are done!
(or so I thought? more on that later)
A few things you should do in order to achieve this:
- The selected language should be persisted in
Room/SharedPreferences/etcin order to retrieve it later (
CreateLocaleFromSavedLanguage()method in the snippet above)
- Optional: Load fresh data from the server based on the selected language, like translated strings or specific business logic directed to the selected language users
- Reflect the language change by rebuilding your UI in a nice and clean way
A Game of Cat & माउस्
The feature is done and you confidently mark it as ready for QA testing. 💪
As the days pass, bug tickets pile up, each with a different scenario that made the app look like it is partially translated to the selected language and partially using the device language.
Below are 2 example scenarios, there are probably more weird cases, so keep that in mind if you decide to go on this road.
#1: System Language Change
If the user changes the device’s main language while your app is in the background, your app
Locale will be overridden by the Android system to reflect the new system language.
If your app is using
WebView in order to display web pages (yeah I hate it too) then you will notice strange behavior every time a
WebView is used.
WebView creation, the Application
Locale gets overridden with the system
Locale which messed up the translations. 🤔
The reason behind this is starting with Android N, the Chrome app will be used to render any/all Webviews in third-party Android apps. Because Chrome is an Android app in itself, running in its own sandboxed process, it will not be bound to the
Locale set by your app. Instead, Chrome will revert to the primary device
The fix, in this case, is again to override the
Locale after a
WebView is created using the technique we wrote above. One way of doing this is by using a custom
Hopefully, this issue should be resolved in Android 10, where the Chrome app will no longer be the
After a few iterations, everything looks good. The Product Manager is happy, your users are happy and even you feel quite pleased with implementing a feature that is not officially supported. 👏
With that said, future Android versions can change how this solution affects your app and even introduce new edge cases that require special care, it is up to you to decide if the investment is worthwhile.