- How to Change the Whole App Language in Android Programmatically?
- Example
- Step by Step Implementation
- Conversion By Translation: Changing Your Android App Language At Runtime
- The Problem
- Prerequisite
- Configuration Class
- Locale.setDefault() Method
- The Solution
- A Game of Cat & माउस्
- #1: System Language Change
- #2: WebView
- Conclusion
- How to change the language on Android at runtime and don’t go mad
- Introduction
- Getting started
- Up through API level 16
- When to update
- Settings screen
- Pitfall 1. Activity titles are not translated or mixed with different languages!
- How to reproduce
- How to clear the cache
- API level 17
- API level 25
- Pitfall 2. Activity titles are not translated using createConfigurationContext!
- API level 26
- Conclusions
- Appendix A
- Additional information
- Locale.setDefault / Locale.getDefault
- LocaleList API
How to Change the Whole App Language in Android Programmatically?
Android 7.0(API level 24) provides support for multilingual users, allowing the users to select multiple locales in the setting. A Locale object represents a specific geographical, political, or cultural region. Operations that required these Locale to perform a task are called locale-sensitive and uses the Locale to tailor information for the user. For example, displaying a number is a locale-sensitive operation so, the number should be formatted according to the conventions of the user’s native region, culture, or country.
Example
In this example, we are going to create a simple application in which the user has the option to select his desired language-English or Hindi. This will change the language of the whole application. A sample GIF is given below to get an idea about what we are going to do in this article. Note that we are going to implement this project using the Java language.
Step by Step Implementation
Step 1: Create A New Project
To create a new project in Android Studio please refer to How to Create/Start a New Project in Android Studio. Note that select Java as the programming language.
Step 2: Create Resource Files
In this step, we are required to create a string resource file for the Hindi language. Go to app > res > values > right-click > New > Value Resource File and name it as strings. Now, we have to choose qualifiers as Locale from the available list and select the language as Hindi from the drop-down list. Below is the picture of the steps to be performed.
Источник
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?”
The Problem
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.
Prerequisite
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:
Configuration Class
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).
Locale.setDefault() Method
Gets the current value of the default locale for this instance of the Java Virtual Machine.
The Default 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.
The Solution
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/etc in order to retrieve it later ( CreateLocaleFromSavedLanguage() method in the snippet above)
- Reflect the language change by re-creating your Activity or Fragment (this will result in calling attachBaseContext() and modifying the current Context )
- 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
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.
The fix, in this case, is to override the onConfigurationChanged() method in the Application class and override the Locale using the updateConfiguration() method found in Resource class.
#2: WebView
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.
After each 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 Locale .
Source: https://stackoverflow.com/a/40675539/5516215
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 WebView :
Hopefully, this issue should be resolved in Android 10, where the Chrome app will no longer be the WebView provider.
Source: https://www.xda-developers.com/google-chrome-no-longer-webview-provider-android-10/
Conclusion
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.
Источник
How to change the language on Android at runtime and don’t go mad
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.
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: Resources , Configuration , 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.
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: A, B, 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.
N ote 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.
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 attachBaseContext . As 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 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 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)
N ote 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:
- 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.
- Use updateConfiguration for API 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 instance . It 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.
Источник