Change app language programmatically in Android

2018-12-31 00:49发布

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 language?

I would like to let the user change the language of the app from the app.

30条回答
与风俱净
2楼-- · 2018-12-31 00:56

Create a class Extends Application and create a static method. Then you can call this method in all activities before setContentView().

public class MyApp extends Application {

@Override
public void onCreate() {
    super.onCreate();
}

public static void setLocaleFa (Context context){
    Locale locale = new Locale("fa"); 
    Locale.setDefault(locale);
    Configuration config = new Configuration();
    config.locale = locale;
    context.getApplicationContext().getResources().updateConfiguration(config, null);
}

public static void setLocaleEn (Context context){
    Locale locale = new Locale("en_US"); 
    Locale.setDefault(locale);
    Configuration config = new Configuration();
    config.locale = locale;
    context.getApplicationContext().getResources().updateConfiguration(config, null);
}

}

Usage in activities:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    MyApp.setLocaleFa(MainActivity.this);
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.activity_main);
}
查看更多
柔情千种
3楼-- · 2018-12-31 00:56
/*change language at Run-time*/
//use method like that:
//setLocale("en");
 public void setLocale(String lang) { 
  myLocale = new Locale(lang);         
  Resources res = getResources();         
  DisplayMetrics dm = res.getDisplayMetrics();         
  Configuration conf = res.getConfiguration();         
  conf.locale = myLocale;         
  res.updateConfiguration(conf, dm);         
  Intent refresh = new Intent(this, AndroidLocalize.class);         
  startActivity(refresh); 
 }
查看更多
十年一品温如言
4楼-- · 2018-12-31 00:57

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.

查看更多
孤独寂梦人
5楼-- · 2018-12-31 00:58

For Android 7.0 Nougat (and lower) follow this article:

Change Language Programatically in Android

Old answer
This include RTL/LTR support:

public static void changeLocale(Context context, Locale locale) {
    Configuration conf = context.getResources().getConfiguration();
    conf.locale = locale;
    Locale.setDefault(locale);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
       conf.setLayoutDirection(conf.locale);
    }

    context.getResources().updateConfiguration(conf, context.getResources().getDisplayMetrics());
}
查看更多
孤独总比滥情好
6楼-- · 2018-12-31 01:00

I encountered the same problem: I needed to set my language to a language chosen in my app.

My fix was this:

  1. Keep your strings in your XML file, don't extract it to resources
  2. Make an exact copy of your XML and rename it to _languagecode, like _fr (use lowercase!)
  3. Fix your translations in your XML copy
  4. In code you check your app-level language and inflate the relevant XML

Example:

 String languageInitials = MyAppconfig.currentLanguageInitials();
        if (languageInitials.equals("NL")) {
            view = inflater.inflate(R.layout.mylayout_nl, container, false);
        } else {
            view = inflater.inflate(R.layout.fragment_mylayout_fr, container, false);
        }

From these XML's, you can still extract the needed strings to resources.

查看更多
孤独寂梦人
7楼-- · 2018-12-31 01:01

It's really work:

fa = Persian, en = English

Enter your language code in languageToLoad variable:

import android.app.Activity;
import android.content.res.Configuration;
import android.os.Bundle;

public class Main extends Activity {
  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    String languageToLoad  = "fa"; // your language
    Locale locale = new Locale(languageToLoad); 
    Locale.setDefault(locale);
    Configuration config = new Configuration();
    config.locale = locale;
    getBaseContext().getResources().updateConfiguration(config, 
      getBaseContext().getResources().getDisplayMetrics());
    this.setContentView(R.layout.main);
  }
}

You can find an example here

查看更多
登录 后发表回答