Skip to main content

How to change the language on Android at runtime and don’t go mad

 

How to change the language on Android at runtime and don’t go mad

TL;DR

There is a library called Lingver that implements the presented approach with just a few lined of code. Check it out!

Introduction

The topic is old as the hills, but still is being actively discussed among developers due to frequent API and behavior changes. The goal of this post is to gather all tips and address all pitfalls while implementing this functionality.

Disclaimer

Changing the language on Android at runtime was never officially encouraged or documented. The resource framework automatically selects the resources that best match the device. Such behavior is enough for common applications, so just make sure you have strict reasons to change it before proceeding further.

There are a ton of articles and answers on Stack Overflow but they usually lack enough of explanation. As a result, when this functionality gets broken, developers can’t easily fix it due to the messy API and lots of deprecated things. We don’t want to fall into the same trap, right? That’s why I want to go step by step to a final solution.

Getting started

Make sure that you are already familiar with the following concepts: ResourcesConfiguration, and Locale.

Technically, to get localized data one should use Resources with the desired Locale set in their Configuration. Basically, there are three kinds of resources you should be worried about:

  • resources from Activity.getResources
  • resources from Application.getResources
  • the top level resources

The top level resources are created for a specific package during an application initialization. For instance, Activity titles declared in your manifest are loaded exactly from these resources. Often, all of these resources are the same instance, but it is not always the case.

Let’s see how we can change the locale across different API levels.

Up through API level 16

Changing the language on this stage is pretty straightforward. Consider the following code snippet:

So we have a class LocaleManager that wraps a logic of changing an application locale. Let’s focus on updateResources method. What we do here is update the resources via updateConfiguration with a config that includes the desired locale.

When to update

So far so good, but when to call it exactly you may ask. This part is a little bit tricky:

  • The first place is your “Settings” screen or whatever place you use to change the language in your application. Note that after the locale is changed you still have to reload already fetched strings manually. We will talk how to do it correctly at the end of this section.
  • The other places are onCreate and onConfigurationChanged of your Application. Android resets the locale for the top level resources back to the device default on every application restart and configuration change. So make sure you perform a new update there.

Besides, you should persist information about a selected locale in some disk storage to get it back when you need it. SharedPreferences is a good choice.

Settings screen

Going back to the case with your “Settings” screen. Let’s imagine that you spent some time playing around your app and then changed the locale in your settings screen. The current activity and the other activities in the back stack used the previous locale to show content. You have to somehow refresh them. Well, the simplest way is to clear the existing task and start a new one. This is exactly when the first pitfall comes in.

Some titles are not translated

Pitfall 1. Activity titles are not translated or mixed with different languages!

After the language change, activity titles are not translated properly sometimes even after restarting of an activity.

It took me some time to find out what’s going on. During a launch of an activity, its title (declared in a manifest file) is being loaded from the top level resources and cached. That’s the reason of getting the same title for the next time and ignoring a new locale you set.

How to reproduce

Imagine that your device language is English and your application consists of three activities: AB, and C. You start the activity A and then open B. Titles for both activities are being cached. In the activity B you change the language to Ukrainian and start the activity C. HA! At this point, titles for A and B are cached in English while it is in Ukrainian for C.

Note that this behavior is relevant for all API levels.

How to clear the cache

The simplest way is to restart your application process (check ProcessPhoenix) right after you update the locale. However, it might be not acceptable for some applications as it is quite a heavy task and is far away from a seamless user experience.

Note that a configuration change clears the cache as well. Another dirty hack is to use Java Reflection API. By the way, let me know if you have any better way.

As an alternative, you can set titles manually in onCreate using local activity resources and do not depend on cached entities. You might want to use a workaround in your BaseActivity. See Appendix A.

API level 17

At this point, Android introduces support for bidirectional layouts along with a minor change in the resources API.

Since then, instead of modifying the locale variable directly you should use the setLocale method which additionally sets a layout direction internally. This is how updateResources method looks like now.

public class LocaleManager {

    public static void setLocale(Context c) {
        setNewLocale(c, getLanguage(c));
    }

    public static void setNewLocale(Context c, String language) {
        persistLanguage(c, language);
        updateResources(c, language);
    }

    public static String getLanguage(Context c) { ... }

    private static void persistLanguage(Context c, String language) { ... }

    private static void updateResources(Context context, String language) {
        Locale locale = new Locale(language);
        Locale.setDefault(locale);

        Resources res = context.getResources();
        Configuration config = new Configuration(res.getConfiguration());
        config.locale = locale;
        res.updateConfiguration(config, res.getDisplayMetrics());
    }
}



API level 25

At this point, updateConfiguration for Resources gets deprecated in favor of createConfigurationContext(which was added in API 17).

So what do we change now? Basically, instead of updating the existing resources you need to create a new Context with properly configured Resources and put it as a base one for Application and Activity via attachBaseContextAs a result, all invocations of getResources will be delegated to the new resources instead of the top level instance.

Well, to sum up, we use:

  • updateConfiguration for API < 17
  • createConfigurationContext for API ≥17

I was confused when the lovely activity titles were not translated again for API ≥17. What’s wrong this time?

Pitfall 2. Activity titles are not translated using createConfigurationContext!

Let’s examine what we do step by step to find an issue:

  • We create a special Context which owns a new localized Resources instance.
  • We put this Context as a base one for an application and an activity via attachBaseContext.

Ah! Do you remember the top level resources we talked about previously? It seems that there is no way to update them with the help of createConfigurationContext. Consequently, the application uses the default locale to get titles.

Let’s see what options do we have to fix this behavior:

  • use updateConfiguration for all API levels to update the top level resources ignoring the deprecation
  • use updateConfiguration for API <17 and createConfigurationContext for API ≥17 to respect the deprecation. As a side effect, you have to set activity titles in onCreate manually using local Resources (see Appendix A)

Note that you have to invoke attachBaseContext in the other components like Service to update the resources for them as well. Another pitfall of using createConfigurationContext is that you can’t actually update the resources for Application after you change the language at runtime since attachBaseContext is never called again. Therefore, you have to restart the application to update the resources.

Okay, let’s check API level 26 section to make a final decision.

Note that applyOverrideConfiguration may be used as an alternative to attachBaseContext. It does the pretty similar thing but exists only for Activity.

API level 26

Up to API level 25 your application and activities share the same resources (aka the top level resources) by default. It means that a call of updateConfiguration from any Context will update the resources. However, starting from API 26, resources for an application and an activity are separate entities, so you need to update them separately respectively (for instance, in onCreate of your Application and BaseActivity).

Conclusions

Let’s sum up and see what options we finally have:

  1. Use updateConfiguration for all API levels in onCreate of your Application and BaseActivity to update the resources ignoring the deprecation. Remember to deal with the cache issue in this case.
  2. Use updateConfiguration for API <17 and createConfigurationContext for API ≥17 to respect the deprecation. Additionally, you have to set Activity titles manually using local resources (check Appendix A).

What do you choose? To be a good citizen or prefer a simple solution despite the deprecation?

UPD #1:
If you want to play with the sample app, use a device below API 28. Starting from Android Pie, any usage of non-SDK interfaces is restricted, that’s why accessing the title cache for educational reasons is not possible anymore. https://developer.android.com/about/versions/pie/restrictions-non-sdk-interfaces

Don’t worry, it doesn’t break anything regarding the approach itself.

UPD #2:
There is an issue while using appcompat 1.1.0 which will be fixed in the upcoming releases of the appcompat library. Please, refer to the following issue.

UPD #3
There is a library called Lingver that implements the presented approach with just a few lined of code. Check it out!

UPD #4. WebView
Starting from Android N, there is a weird side effect while using a WebView. For unknown reasons, the very first creation of it resets the application locale to the device default. Therefore, you have to set the desired locale back. See an example of implementation in the sample app.

Please check out a library or a sample project on Github.

Appendix A

This is a possible workaround to set activity titles using local Resources instanceIt intends to break the dependency on the cache and the top level resources.

Additional information

Locale.setDefault / Locale.getDefault

Gets the current value of the default locale for this instance of the Java Virtual Machine. It is used by many locale-sensitive methods if no locale is explicitly specified.

The default locale is used for locale-sensitive operations like formatting/parsing numbers or dates. Usually, it is important to keep it the same as a locale you use for showing content in your application.

LocaleList API

Starting in Android 7.0 (API level 24), Android provides enhanced support for multilingual users, allowing them to select multiple locales in settings.

  • LocaleList API is introduced along with setLocales/getLocales in Configuration.
  • accessing locale variable gets deprecated in favor of getLocales().get(0).

This new API allows developers to create more sophisticated app behavior. For instance, browser apps can avoid offering to translate pages in a language the user already knows.

However, if your goal is to lock the only one specific language, you can ignore this update.

Note that setLocale starts invoking setLocales with a list of just one locale under the hood since API 24.

Comments

All Post

When to Use Waterfall vs. Agile

  We compare the benefits and drawbacks of using two well-known software development methodologies, Waterfall and Agile, and lay out when it might be more suitable to use one over the other – or combine practices of both – for your product initiative. When developing a new software product, your team will need to navigate which development methodology is right for your initiative. In the world of managing  software development  projects, the topic of Agile vs Waterfall is widely debated. Many thought leaders and Agile enthusiasts in the industry have argued Waterfall is dead, however, traditional organizational environments and processes have led to it still being widely used today. A 2017 report from the Project Management Institute shows that  51% of the organizations surveyed use Waterfall either often or always . The reality is, each software development project poses its own unique challenges and requirements. It’s not a matter of deciding which development methodology is “the bes

Flutter form validation: Full guide for you to make Flutter form

  Flutter form validation Getting started with form validation in Flutter The Flutter SDK provides us with an out-of-the-box widget and functionalities to make our lives easier when using form validation. In this article, we’ll cover two approaches to form validation: the form widget and the Provider package. You can find more information on these two approaches in the official Flutter docs. Creating a form in Flutter First, we are going to create a simple login page that has the following fields: Email Name Phone number Password For the validation, we want the users of our app to fill in the correct details in each of these fields. The logic will be defined as such: First, for the name field, we want the user to enter a valid first name and last name, which can be accompanied by initials. For the email field, we want a valid email that contains some characters before the “@” sign, as well as the email domain at the end of the email. For phone number validation, the user is expected to

7 Key Android Concepts

  Although the Android platform is open and customizable, Android users have become accustomed to constructs developed by Google for Android devices. Although the Android platform is open and customizable, Android users have become accustomed to constructs developed by Google for Android devices. Moreover, the use of these Android concepts is vital in developing an application quickly – custom Android designs can take up to 10 times longer! Android UI Controls Android provides a number of standard UI controls that  enable a rich user experience . Designers and developers should thoroughly understand all of these controls for the following reasons: They are faster to implement. It can take up to ten times longer to develop a custom control than to implement a user interface with standard Android controls. They ensure good performance. Custom controls rarely function as expected in their first implementation. By implementing standard controls, you can eliminate the need to test, revise a

How to them the background of the Android options menu items

  “What we’ve got here is… failure to theme. Some views you just can’t reach. So you get what we had here last project, which is the way Android wants it… well, it gets it. I don’t like it any more than you men.” – Captain, Road Prison 36 Some of you might recognize the previous paragraph as the introduction of Guns ‘N Roses’ Civil War or from the movie Cold Hand Luke starring Paul Newman. This is the feeling I get when I try to create a custom theme for an application on Android. The Android SDK does permit some level of theming, which is not really well documented to start with. Other things are hard-coded, “so you get what we had here last project”. Now, one of the things your application will most likely use is the Options menu, which is the menu you see when you press the hard menu key. It is kind of… orange. In our last project, we had to completely remove the orange in favor of our customer’s color scheme, which is on the blue side. I couldn’t find a way to change the menu item

Clean Code and the Art of Exception Handling

  Clean Code and the Art of Exception Handling Exceptions are as old as programming itself. An unhandled exception may cause unexpected behavior, and results can be spectacular. Over time, these errors have contributed to the impression that exceptions are bad. But exceptions are a fundamental element of modern programming. Rather than fearing exceptions, we should embrace them and learn how to benefit from them. In this article, we will discuss how to manage exceptions elegantly, and use them to write clean code that is more maintainable. Exceptions are as old as programming itself. Back in the days when programming was done in hardware, or via low-level programming languages, exceptions were used to alter the flow of the program, and to avoid hardware failures. Today, Wikipedia  defines exceptions as: anomalous or exceptional conditions requiring special processing – often changing the normal flow of program execution specialized programming language constructs or computer hardware m

Android Jetpack Compose

  Jetpack Compose Tutorial for Android: Getting Started Jetpack Compose is Android’s modern toolkit for building native UI. It simplifies and accelerates UI development on Android. Quickly bring your app to life with less code, powerful tools, and intuitive Kotlin APIs. At Google I/O 2019, Google first announced  Jetpack Compose . Jetpack Compose is Google’s response to the declarative UI framework trend, which the Android team is developing to fundamentally change the way developers create UI, making it easier and faster to write, and more performant to run. It is a part of the Jetpack suite of libraries and as such should provide compatibility throughout platform versions, removing the need to avoid certain features, because you’re targeting lower-end devices or older versions of Android. Although it’s still in an alpha , Compose is already making big waves in the Android community. If you want to stay up-to-date on the latest and greatest technology, read on! In this tutorial, you’

Loops in Dart 💪💪💪😎😎😎

       Loops in Dart   💪💪💪😎😎😎 Dart Loops In Programming, loops are used to repeat a block of code until certain conditions are not completed. For, e.g., if you want to print your name 100 times, then rather than typing print(“your name”); 100 times, you can use a loop. There are different types of loop in Dart. They are: For Loop For Each Loop While Loop Do While Loop Info Note : The primary purpose of all loops is to repeat a block of code. Print Your Name 10 Times Without Using Loop Let’s first print the name 10 times without using a loop. void main() { print( "John Doe" ); print( "John Doe" ); print( "John Doe" ); print( "John Doe" ); print( "John Doe" ); print( "John Doe" ); print( "John Doe" ); print( "John Doe" ); print( "John Doe" ); print( "John Doe" ); } Show Output

MVVM architecture, ViewModel and LiveData (Part 1)

  MVVM architecture, ViewModel and LiveData (Part 1) During Google I/O, Google introduced  architecture components  which includes  LiveData  and  ViewModel  which facilitates developing Android app using MVVM pattern. This article discusses how can these components serve an android app that follows MVVM. Quick Definition of MVVM If you are familiar with MVVM, you can skip this section completely. MVVM is one of the architectural patterns which enhances separation of concerns, it allows separating the user interface logic from the business (or the back-end) logic. Its target (with other MVC patterns goal) is to achieve the following principle  “Keeping UI code simple and free of app logic in order to make it easier to manage” . MVVM has mainly the following layers: Model Model represents the data and business logic of the app. One of the recommended implementation strategies of this layer, is to expose its data through observables to be decoupled completely from ViewModel or any other