Is it possible to change the language of an app programmatically while still using Android resources?
If not, is it possible to request a resource in an specific lan
Time for a due update.
First off, the deprecated list with the API in which it was deprecated:
configuration.locale
(API 17)updateConfiguration(configuration, displaymetrics)
(API 17)The thing no question answered recently has gotten right is the usage of the new method.
createConfigurationContext is the new method for updateConfiguration.
Some have used it standalone like this:
Configuration overrideConfiguration = ctx.getResources().getConfiguration();
Locale locale = new Locale("en_US");
overrideConfiguration.setLocale(locale);
createConfigurationContext(overrideConfiguration);
... but that doesn't work. Why? The method returns a context, which then is used to handle Strings.xml translations and other localized resources (images, layouts, whatever).
The proper usage is like this:
Configuration overrideConfiguration = ctx.getResources().getConfiguration();
Locale locale = new Locale("en_US");
overrideConfiguration.setLocale(locale);
//the configuration can be used for other stuff as well
Context context = createConfigurationContext(overrideConfiguration);
Resources resources = context.getResources();
If you just copy-pasted that into your IDE, you may see a warning that the API requires you targeting API 17 or above. This can be worked around by putting it in a method and adding the annotation @TargetApi(17)
But wait. What about the older API's?
You need to create another method using updateConfiguration without the TargetApi annotation.
Resources res = YourApplication.getInstance().getResources();
// Change locale settings in the app.
DisplayMetrics dm = res.getDisplayMetrics();
android.content.res.Configuration conf = res.getConfiguration();
conf.locale = new Locale("th");
res.updateConfiguration(conf, dm);
You don't need to return a context here.
Now, managing these can be difficult. In API 17+ you need the context created (or the resources from the context created) to get the appropriate resources based on localization. How do you handle this?
Well, this is the way I do it:
/**
* Full locale list: https://stackoverflow.com/questions/7973023/what-is-the-list-of-supported-languages-locales-on-android
* @param lang language code (e.g. en_US)
* @return the context
* PLEASE READ: This method can be changed for usage outside an Activity. Simply add a COntext to the arguments
*/
public Context setLanguage(String lang/*, Context c*/){
Context c = AndroidLauncher.this;//remove if the context argument is passed. This is a utility line, can be removed totally by replacing calls to c with the activity (if argument Context isn't passed)
int API = Build.VERSION.SDK_INT;
if(API >= 17){
return setLanguage17(lang, c);
}else{
return setLanguageLegacy(lang, c);
}
}
/**
* Set language for API 17
* @param lang
* @param c
* @return
*/
@TargetApi(17)
public Context setLanguage17(String lang, Context c){
Configuration overrideConfiguration = c.getResources().getConfiguration();
Locale locale = new Locale(lang);
Locale.setDefault(locale);
overrideConfiguration.setLocale(locale);
//the configuration can be used for other stuff as well
Context context = createConfigurationContext(overrideConfiguration);//"local variable is redundant" if the below line is uncommented, it is needed
//Resources resources = context.getResources();//If you want to pass the resources instead of a Context, uncomment this line and put it somewhere useful
return context;
}
public Context setLanguageLegacy(String lang, Context c){
Resources res = c.getResources();
// Change locale settings in the app.
DisplayMetrics dm = res.getDisplayMetrics();//Utility line
android.content.res.Configuration conf = res.getConfiguration();
conf.locale = new Locale(lang);//setLocale requires API 17+ - just like createConfigurationContext
Locale.setDefault(conf.locale);
res.updateConfiguration(conf, dm);
//Using this method you don't need to modify the Context itself. Setting it at the start of the app is enough. As you
//target both API's though, you want to return the context as you have no clue what is called. Now you can use the Context
//supplied for both things
return c;
}
This code works by having one method that makes calls to the appropriate method based on what API. This is something I have done with a lot of different deprecated calls (including Html.fromHtml). You have one method that takes in the arguments needed, which then splits it into one of two (or three or more) methods and returns the appropriate result based on API level. It is flexible as you do't have to check multiple times, the "entry" method does it for you. The entry-method here is setLanguage
PLEASE READ THIS BEFORE USING IT
You need to use the Context returned when you get resources. Why? I have seen other answers here who use createConfigurationContext and doesn't use the context it returns. To get it to work like that, updateConfiguration has to be called. Which is deprecated. Use the context returned by the method to get resources.
Example usage:
Constructor or somewhere similar:
ctx = getLanguage(lang);//lang is loaded or generated. How you get the String lang is not something this answer handles (nor will handle in the future)
And then, whereever you want to get resources you do:
String fromResources = ctx.getString(R.string.helloworld);
Using any other context will (in theory) break this.
AFAIK you still have to use an activity context to show dialogs or Toasts. for that you can use an instance of an activity (if you are outside)
And finally, use recreate()
on the activity to refresh the content. Shortcut to not have to create an intent to refresh.