66 votes

Android N change de langue par programmation

Je l'ai trouvé vraiment bizarre bug qui est reproduit uniquement sur Android N appareils.

Au tour de mon application, il est possible de changer la langue. Voici le code qui change.

 public void update(Locale locale) {

    Locale.setDefault(locale);

    Configuration configuration = res.getConfiguration();

    if (BuildUtils.isAtLeast24Api()) {
        LocaleList localeList = new LocaleList(locale);

        LocaleList.setDefault(localeList);
        configuration.setLocales(localeList);
        configuration.setLocale(locale);

    } else if (BuildUtils.isAtLeast17Api()){
        configuration.setLocale(locale);

    } else {
        configuration.locale = locale;
    }

    res.updateConfiguration(configuration, res.getDisplayMetrics());
}

Ce code fonctionne très bien dans l'activité de mon tour ( avec recreate() appel), mais dans toutes les prochaines activités de toutes les ressources de Chaîne sont mauvais. La rotation de l'écran fixe. Que puis-je faire avec ce problème? Dois-je changer les paramètres régionaux pour Android N différemment ou c'est juste le système de bug?

P. S. Voici ce que j'ai trouvé. Lors du premier démarrage de MainActivity (ce qui est après ma tournée) Locale.getDefault() est correct, mais les ressources sont mauvais. Mais dans d'autres activités, il me donne mal de paramètres Régionaux et le mal les ressources de ce lieu. Après la rotation de l'écran (ou peut-être quelques autres changement de configuration) Locale.getDefault() est correct.

117voto

Kuva Points 1107

Ok. Enfin j'ai réussi à trouver une solution.

D'abord, vous devez savoir que dans 25 API Resources.updateConfiguration(...) est obsolète. Donc, au lieu de cela, vous pouvez faire quelque chose comme ceci:

1) Vous devez créer votre propre ContextWrapper qui va l'emporter sur tous les paramètres de configuration dans baseContext. Par exemple, c'est la mienne ContextWrapper que les changements de paramètres Régionaux correctement. Attention sur context.createConfigurationContext(configuration) méthode.

public class ContextWrapper extends android.content.ContextWrapper {

public ContextWrapper(Context base) {
    super(base);
}

public static ContextWrapper wrap(Context context, Locale newLocale) {

    Resources res = context.getResources();
    Configuration configuration = res.getConfiguration();

    if (BuildUtils.isAtLeast24Api()) {
        configuration.setLocale(newLocale);

        LocaleList localeList = new LocaleList(newLocale);
        LocaleList.setDefault(localeList);
        configuration.setLocales(localeList);

        context = context.createConfigurationContext(configuration);

    } else if (BuildUtils.isAtLeast17Api()) {
        configuration.setLocale(newLocale);
        context = context.createConfigurationContext(configuration);

    } else {
        configuration.locale = newLocale;
        res.updateConfiguration(configuration, res.getDisplayMetrics());
    }

    return new ContextWrapper(context);
}}

2) Voici ce que vous devez faire dans votre BaseActivity:

  @Override
protected void attachBaseContext(Context newBase) {

    Locale newLocale;
    // .. create or get your new Locale object here.

    Context context = ContextWrapper.wrap(newBase, newLocale);
    super.attachBaseContext(context);
}

Note:

N'oubliez pas de recréer votre activité si vous souhaitez modifier les paramètres Régionaux dans votre Application quelque part. Vous pouvez annuler toute configuration que vous souhaitez utiliser cette solution.

25voto

thyzz Points 358

Inspiré par les différents codes (j'.e: notre Stackoverflow frères (shout out boys)), j'avais produit une version beaucoup plus simple. L' ContextWrapper extension est inutile.

D'abord disons que vous avez 2 boutons pour les 2 langues, FR et KH. Dans le onClick pour les boutons enregistrer le code de langue en SharedPreferences, puis appelez l'activité recreate() méthode.

Exemple:

@Override
public void onClick(View v) {
    switch(v.getId()) {
        case R.id.btn_lang_en:
            //save "en" to SharedPref here
            break;
        case R.id.btn_lang_kh:
            //save "kh" to SharedPref here
            break;

        default:
        break;
    }
    getActivity().recreate();
}

Puis créer une méthode statique qui renvoie ContextWrapper, peut-être dans une classe Utils (coz c'est ce que j'ai fait, lul).

public static ContextWrapper changeLang(Context context, String lang_code){
    Locale sysLocale;

    Resources rs = context.getResources();
    Configuration config = rs.getConfiguration();

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        sysLocale = config.getLocales().get(0);
    } else {
        sysLocale = config.locale;
    }
    if (!lang_code.equals("") && !sysLocale.getLanguage().equals(lang_code)) {
        Locale locale = new Locale(lang_code);
        Locale.setDefault(locale);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            config.setLocale(locale);
        } else {
            config.locale = locale;
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            context = context.createConfigurationContext(config);
        } else {
            context.getResources().updateConfiguration(config, context.getResources().getDisplayMetrics());
        }
    }

    return new ContextWrapper(context);
}

Enfin, charger le code de langue à partir d' SharedPreferences dans l'ENSEMBLE de l'ACTIVITÉ DE l' attachBaseContext(Context newBase) méthode.

@Override
protected void attachBaseContext(Context newBase) {
    String lang_code = "en"; //load it from SharedPref
    Context context = Utils.changeLang(newBase, lang_code);
    super.attachBaseContext(context);
}

BONUS: Pour économiser la paume de la sueur sur le clavier, j'ai créé un LangSupportBaseActivity classe qui étend l' Activity et utiliser le dernier bloc de code. Et j'ai toutes les autres activités s'étend LangSupportBaseActivity.

Exemple:

public class LangSupportBaseActivity extends Activity{
    ...blab blab blab so on and so forth lines of neccessary code

    @Override
    protected void attachBaseContext(Context newBase) {
        String lang_code = "en"; //load it from SharedPref
        Context context = Utils.changeLang(newBase, lang_code);
        super.attachBaseContext(context);
    }
}

public class HomeActivity extends LangSupportBaseActivity{
    ...blab blab blab
}

2voto

QuantumTiger Points 727

Les réponses ci-dessus m'ont mis sur la bonne piste, mais a laissé un couple de questions

  1. Sur android 7 et 9 je pourrais heureusement le changement de toute autre langue que la langue par défaut de l'application. Quand j'ai changé de retour à la langue par défaut de l'application, il a montré la dernière langue sélectionnée n'est pas surprenant que ce qui a remplacé la valeur par défaut (même si il est intéressant de noter que ce n'était pas un problème sur Android 8!).
  2. Pour RTL langues, il n'a pas de mise à jour les mises à RTL

Pour résoudre le premier article que j'ai stocké les paramètres régionaux par défaut sur l'application de démarrage.

Remarque Si votre langue par défaut est réglé sur "fr", puis les lieux de "frfr" ou "frfr" les deux besoin de faire correspondre les paramètres régionaux par défaut (sauf si vous fournissez de séparer les localisations pour eux). De même, dans l'exemple ci-dessous si le téléphone de l'utilisateur paramètres régionaux est d'arLY (arabe Libyenne) puis le defLanguage doit être "ar" pas "d'arLY"

private Locale defLocale = Locale.getDefault();
private Locale locale = Locale.getDefault();
public static myApplication myApp;
public static Resources res;
private static String defLanguage = Locale.getDefault().getLanguage() + Locale.getDefault().getCountry();
private static sLanguage = "en";
private static final Set<String> SUPPORTEDLANGUAGES = new HashSet<>(Arrays.asList(new String[]{"en", "ar", "arEG"})); 

@Override
protected void attachBaseContext(Context base) {
  if (myApp == null) myApp = this;
  if (base == null) super.attachBaseContext(this);
  else super.attachBaseContext(setLocale(base));
}

@Override
public void onCreate() {
  myApp = this;

  if (!SUPPORTEDLANGUAGES.contains(test)) {
    // The default locale (eg enUS) is not in the supported list - lets see if the language is
    if (SUPPORTEDLANGUAGES.contains(defLanguage.substring(0,2))) {
      defLanguage = defLanguage.substring(0,2);
    }
  }
}

private static void setLanguage(String sLang) {
  Configuration baseCfg = myApp.getBaseContext().getResources().getConfiguration();
  if ( sLang.length() > 2 ) {
    String s[] = sLang.split("_");
    myApp.locale = new Locale(s[0],s[1]);
    sLanguage = s[0] + s[1];
  }
  else {
    myApp.locale = new Locale(sLang);
    sLanguage = sLang;
  }
}

public static Context setLocale(Context ctx) {
  Locale.setDefault(myApp.locale);
  Resources tempRes = ctx.getResources();
  Configuration config = tempRes.getConfiguration();

  if (Build.VERSION.SDK_INT >= 24) {
    // If changing to the app default language, set locale to the default locale
    if (sLanguage.equals(myApp.defLanguage)) {
      config.setLocale(myApp.defLocale);
      // restored the default locale as well
      Locale.setDefault(myApp.defLocale);
    }
    else config.setLocale(myApp.locale);

    ctx = ctx.createConfigurationContext(config);

    // update the resources object to point to the current localisation
    res = ctx.getResources();
  } else {
    config.locale = myApp.locale;
    tempRes.updateConfiguration(config, tempRes.getDisplayMetrics());
  }

  return ctx;
}

Pour résoudre le RTL questions, j'ai étendu AppCompatActivity que par des Fragments de commentaires dans cette réponse

public class myCompatActivity extends AppCompatActivity {
  @Override
  protected void attachBaseContext(Context base) {
    super.attachBaseContext(myApplication.setLocale(base));
  }

  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (Build.VERSION.SDK_INT >= 17) {
      getWindow().getDecorView().setLayoutDirection(myApplication.isRTL() ?
              View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
    }
  }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X