177 votes

Définir le Locale de façon programmatique

Mon application supporte 3 (bientôt 4) langues. Comme plusieurs langues sont assez similaires, j'aimerais donner à l'utilisateur la possibilité de changer de langue dans mon application. Par exemple, un Italien pourrait préférer l'espagnol à l'anglais.

Existe-t-il un moyen pour l'utilisateur de choisir parmi les paramètres linguistiques disponibles pour l'application et de modifier ensuite le paramètre utilisé ? Je ne vois pas de problème à définir la locale pour chaque activité, car il s'agit d'une tâche simple à réaliser dans une classe de base.

1 votes

Si vous avez besoin d'un moyen de restaurer ultérieurement la locale par défaut ou si vous avez besoin d'une préférence linguistique contenant une liste de langues, et si vous voulez changer la locale de manière plus pratique, cela peut être utile : github.com/delight-im/Android-Languages

194voto

Igor Khomenko Points 7233

J'espère que cela vous aidera (dans le curriculum vitae) :

Locale locale = new Locale("ru");
Locale.setDefault(locale);
Configuration config = getBaseContext().getResources().getConfiguration();
config.locale = locale;
getBaseContext().getResources().updateConfiguration(config,
      getBaseContext().getResources().getDisplayMetrics());

3 votes

Cela doit donc être défini pour chaque activité ?

7 votes

1. est-il obligatoire d'utiliser getBaseContext() ou est-il préférable d'utiliser le contexte de l'application ? 2. ce code doit-il être appelé dans chaque activité ? merci.

13 votes

J'ai placé ce code dans onCreate() de mon Activity launcher (et nulle part ailleurs) et j'ai été agréablement surpris de voir que la locale s'appliquait à toute l'application. Ceci dans une application ciblant 4.3 avec un minSDK de 14 (ICS).

185voto

Ricardo Points 3750

Pour les personnes qui cherchent encore cette réponse, puisque configuration.locale a été déprécié depuis l'API 24, vous pouvez maintenant utiliser :

configuration.setLocale(locale);

Tenez compte du fait que la version minimale de cette méthode est l'API 17.

Code d'exemple complet :

@SuppressWarnings("deprecation")
private void setLocale(Locale locale){
    SharedPrefUtils.saveLocale(locale); // optional - Helper method to save the selected language to SharedPreferences in case you might need to attach to activity context (you will need to code this)
    Resources resources = getResources();
    Configuration configuration = resources.getConfiguration();
    DisplayMetrics displayMetrics = resources.getDisplayMetrics();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1){
        configuration.setLocale(locale);
    } else{
        configuration.locale=locale;
    }
    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N){
        getApplicationContext().createConfigurationContext(configuration);
    } else {
        resources.updateConfiguration(configuration,displayMetrics);
    }
}

N'oubliez pas que, si vous modifiez les paramètres régionaux d'une activité en cours, vous devrez la redémarrer pour que les modifications soient prises en compte.

EDIT 11 MAI 2018

D'après le post de @CookieMonster, vous pourriez avoir des problèmes pour conserver le changement de locale dans les versions supérieures de l'API. Si c'est le cas, ajoutez le code suivant à votre activité de base (BaseActivity extends AppCompatActivity / autres activités) afin de mettre à jour le contexte local à chaque création d'activité :

@Override
protected void attachBaseContext(Context base) {
     super.attachBaseContext(updateBaseContextLocale(base));
}

private Context updateBaseContextLocale(Context context) {
    String language = SharedPrefUtils.getSavedLanguage(); // Helper method to get saved language from SharedPreferences
    Locale locale = new Locale(language);
    Locale.setDefault(locale);

    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {
        return updateResourcesLocale(context, locale);
    }

    return updateResourcesLocaleLegacy(context, locale);
}

@TargetApi(Build.VERSION_CODES.N_MR1)
private Context updateResourcesLocale(Context context, Locale locale) {
    Configuration configuration = new Configuration(context.getResources().getConfiguration());
    configuration.setLocale(locale);
    return context.createConfigurationContext(configuration);
}

@SuppressWarnings("deprecation")
private Context updateResourcesLocaleLegacy(Context context, Locale locale) {
    Resources resources = context.getResources();
    Configuration configuration = resources.getConfiguration();
    configuration.locale = locale;
    resources.updateConfiguration(configuration, resources.getDisplayMetrics());
    return context;
}

Si vous utilisez cette méthode, n'oubliez pas d'enregistrer la langue dans SharedPreferences lorsque vous définissez les paramètres linguistiques avec la commande setLocale(locale)

EDIT 7 AVRIL 2020

Il se peut que vous rencontriez des problèmes sous Android 6 et 7, et cela est dû à un problème dans les bibliothèques androidx lors de la gestion du mode nuit. Pour cela, vous devrez également remplacer applyOverrideConfiguration dans votre activité de base et mettez à jour la locale de la configuration au cas où une nouvelle locale serait créée.

Exemple de code :

@Override
public void applyOverrideConfiguration(Configuration overrideConfiguration) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
        // update overrideConfiguration with your locale  
        setLocale(overrideConfiguration) // you will need to implement this
    }
    super.applyOverrideConfiguration(overrideConfiguration);
}

29voto

CookieMonster Points 1320

J'ai eu un problème avec le paramétrage programmatique de la locale avec les périphériques. qui a Android OS N et supérieur . Pour moi, la solution était d'écrire ce code dans mon activité de base :

(si vous n'avez pas d'activité de base, vous devez effectuer ces changements dans toutes vos activités).

@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(updateBaseContextLocale(base));
}

private Context updateBaseContextLocale(Context context) {
    String language = SharedPref.getInstance().getSavedLanguage();
    Locale locale = new Locale(language);
    Locale.setDefault(locale);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        return updateResourcesLocale(context, locale);
    }

    return updateResourcesLocaleLegacy(context, locale);
}

@TargetApi(Build.VERSION_CODES.N)
private Context updateResourcesLocale(Context context, Locale locale) {
    Configuration configuration = context.getResources().getConfiguration();
    configuration.setLocale(locale);
    return context.createConfigurationContext(configuration);
}

@SuppressWarnings("deprecation")
private Context updateResourcesLocaleLegacy(Context context, Locale locale) {
    Resources resources = context.getResources();
    Configuration configuration = resources.getConfiguration();
    configuration.locale = locale;
    resources.updateConfiguration(configuration, resources.getDisplayMetrics());
    return context;
}

Notez qu'ici il ne suffit pas d'appeler

createConfigurationContext(configuration)

vous devez également obtenir le contexte que cette méthode renvoie, puis définir ce contexte dans le fichier attachBaseContext méthode.

27voto

user905686 Points 769

Comme aucune réponse n'est complète pour la façon actuelle de résoudre ce problème, j'essaie de donner des instructions pour une solution complète. Veuillez commenter si quelque chose manque ou pourrait être mieux fait.

Informations générales

Tout d'abord, il existe quelques bibliothèques qui veulent résoudre le problème mais elles semblent toutes dépassées ou il leur manque certaines fonctionnalités :

En outre, je pense que l'écriture d'une bibliothèque n'est pas un moyen facile de résoudre ce problème, car il n'y a pas grand-chose à faire, et ce qui doit être fait est plutôt de modifier le code existant que d'utiliser quelque chose de complètement découplé. Par conséquent, j'ai composé les instructions suivantes qui devraient être complètes.

Ma solution est principalement basée sur https://github.com/gunhansancar/ChangeLanguageExample (comme déjà évoqué par localhost ). C'est le meilleur code que j'ai trouvé pour s'orienter. Quelques remarques :

  • Le cas échéant, il fournit différentes implémentations pour changer de locale pour Android N (et supérieur) et inférieur
  • Il utilise une méthode updateViews() dans chaque activité pour mettre à jour manuellement toutes les chaînes de caractères après un changement de locale (en utilisant l'habituel getString(id) ), ce qui n'est pas nécessaire dans l'approche présentée ci-dessous.
  • Il ne prend en charge que les langues et non les locales complètes (qui comprennent également les codes de région (pays) et de variante).

Je l'ai modifié un peu, en découplant la partie qui persiste la locale choisie (car on pourrait vouloir le faire séparément, comme suggéré ci-dessous).

Solution

La solution consiste à suivre les deux étapes suivantes :

  • Changez de façon permanente la locale utilisée par l'application.
  • Faire en sorte que l'application utilise les paramètres régionaux personnalisés, sans redémarrage.

Étape 1 : modifier les paramètres linguistiques

Utilisez la classe LocaleHelper sur la base de LocaleHelper de gunhansancar :

  • Ajouter un ListPreference dans un PreferenceFragment avec les langues disponibles (doit être maintenu lorsque des langues doivent être ajoutées ultérieurement)

    import android.annotation.TargetApi; import android.content.Context; import android.content.SharedPreferences; import android.content.res.Configuration; import android.content.res.Resources; import android.os.Build; import android.preference.PreferenceManager;

    import java.util.Locale;

    import mypackage.SettingsFragment;

    /**

    • Manages setting of the app's locale. */ public class LocaleHelper {

      public static Context onAttach(Context context) { String locale = getPersistedLocale(context); return setLocale(context, locale); }

      public static String getPersistedLocale(Context context) { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); return preferences.getString(SettingsFragment.KEY_PREF_LANGUAGE, ""); }

      /**

      • Set the app's locale to the one specified by the given String.
      • @param context
      • @param localeSpec a locale specification as used for Android resources (NOTE: does not
      • support country and variant codes so far); the special string "system" sets
      • the locale to the locale specified in system settings
      • @return */ public static Context setLocale(Context context, String localeSpec) { Locale locale; if (localeSpec.equals("system")) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { locale = Resources.getSystem().getConfiguration().getLocales().get(0); } else { //noinspection deprecation locale = Resources.getSystem().getConfiguration().locale; } } else { locale = new Locale(localeSpec); } Locale.setDefault(locale); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { return updateResources(context, locale); } else { return updateResourcesLegacy(context, locale); } }

      @TargetApi(Build.VERSION_CODES.N) private static Context updateResources(Context context, Locale locale) { Configuration configuration = context.getResources().getConfiguration(); configuration.setLocale(locale); configuration.setLayoutDirection(locale);

      return context.createConfigurationContext(configuration);

      }

      @SuppressWarnings("deprecation") private static Context updateResourcesLegacy(Context context, Locale locale) { Resources resources = context.getResources();

      Configuration configuration = resources.getConfiguration();
      configuration.locale = locale;
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
          configuration.setLayoutDirection(locale);
      }
      
      resources.updateConfiguration(configuration, resources.getDisplayMetrics());
      
      return context;

      } }

Créer un SettingsFragment comme les suivantes :

import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import mypackage.LocaleHelper;
import mypackage.R;

/**
 * Fragment containing the app's main settings.
 */
public class SettingsFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener {
    public static final String KEY_PREF_LANGUAGE = "pref_key_language";

    public SettingsFragment() {
        // Required empty public constructor
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.preferences);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_settings, container, false);
        return view;
    }

    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        switch (key) {
            case KEY_PREF_LANGUAGE:
                LocaleHelper.setLocale(getContext(), PreferenceManager.getDefaultSharedPreferences(getContext()).getString(key, ""));
                getActivity().recreate(); // necessary here because this Activity is currently running and thus a recreate() in onResume() would be too late
                break;
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        // documentation requires that a reference to the listener is kept as long as it may be called, which is the case as it can only be called from this Fragment
        getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
    }

    @Override
    public void onPause() {
        super.onPause();
        getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
    }
}

Créer une ressource locales.xml listant toutes les langues avec les traductions disponibles de la manière suivante ( liste des codes locaux ):

<!-- Lists available locales used for setting the locale manually.
     For now only language codes (locale codes without country and variant) are supported.
     Has to be in sync with "settings_language_values" in strings.xml (the entries must correspond).
  -->
<resources>
    <string name="system_locale" translatable="false">system</string>
    <string name="default_locale" translatable="false"></string>
    <string-array name="locales">
        <item>@string/system_locale</item> <!-- system setting -->
        <item>@string/default_locale</item> <!-- default locale -->
        <item>de</item>
    </string-array>
</resources>

Dans votre PreferenceScreen vous pouvez utiliser la section suivante pour permettre à l'utilisateur de sélectionner les langues disponibles :

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <PreferenceCategory
        android:title="@string/preferences_category_general">
        <ListPreference
            android:key="pref_key_language"
            android:title="@string/preferences_language"
            android:dialogTitle="@string/preferences_language"
            android:entries="@array/settings_language_values"
            android:entryValues="@array/locales"
            android:defaultValue="@string/system_locale"
            android:summary="%s">
        </ListPreference>
    </PreferenceCategory>
</PreferenceScreen>

qui utilise les chaînes suivantes de strings.xml :

<string name="preferences_category_general">General</string>
<string name="preferences_language">Language</string>
<!-- NOTE: Has to correspond to array "locales" in locales.xml (elements in same orderwith) -->
<string-array name="settings_language_values">
    <item>Default (System setting)</item>
    <item>English</item>
    <item>German</item>
</string-array>

Étape 2 : Faire en sorte que l'application utilise la locale personnalisée

Maintenant, configurez chaque activité pour utiliser le jeu de paramètres régionaux personnalisés. Le moyen le plus simple d'y parvenir est d'avoir une classe de base commune à toutes les activités avec le code suivant (où le code important se trouve dans la section attachBaseContext(Context base) y onResume() ):

import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;

import mypackage.LocaleHelper;
import mypackage.R;

/**
 * {@link AppCompatActivity} with main menu in the action bar. Automatically recreates
 * the activity when the locale has changed.
 */
public class MenuAppCompatActivity extends AppCompatActivity {
    private String initialLocale;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initialLocale = LocaleHelper.getPersistedLocale(this);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menu_settings:
                Intent intent = new Intent(this, SettingsActivity.class);
                startActivity(intent);
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(LocaleHelper.onAttach(base));
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (initialLocale != null && !initialLocale.equals(LocaleHelper.getPersistedLocale(this))) {
            recreate();
        }
    }
}

Ce qu'il fait, c'est

  • Remplacer attachBaseContext(Context base) pour utiliser la locale précédemment persistée avec LocaleHelper
  • Détecter un changement de la locale et recréer l'activité pour mettre à jour ses chaînes de caractères

Remarques sur cette solution

  • La recréation d'une activité ne met pas à jour le titre de la barre d'action (comme déjà observé ici : https://github.com/gunhansancar/ChangeLanguageExample/issues/1 ).

    • Pour ce faire, il suffit de disposer d'un setTitle(R.string.mytitle) dans le onCreate() méthode de chaque activité.
  • Il permet à l'utilisateur de choisir la locale par défaut du système, ainsi que la locale par défaut de l'application (qui peut être nommée, dans ce cas "English").

  • Seuls les codes de langue, sans les codes de région (pays) et de variante (tels que fr-rCA ) sont pris en charge jusqu'à présent. Pour prendre en charge les spécifications locales complètes, un analyseur syntaxique similaire à celui de l'application Bibliothèque Android-Langues peut être utilisé (qui supporte la région mais pas les codes de variantes).

    • Si quelqu'un trouve ou a écrit un bon analyseur, ajoutez un commentaire pour que je puisse l'inclure dans la solution.

16voto

localhost Points 3916
@SuppressWarnings("deprecation")
public static void forceLocale(Context context, String localeCode) {
    String localeCodeLowerCase = localeCode.toLowerCase();

    Resources resources = context.getApplicationContext().getResources();
    Configuration overrideConfiguration = resources.getConfiguration();
    Locale overrideLocale = new Locale(localeCodeLowerCase);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
        overrideConfiguration.setLocale(overrideLocale);
    } else {
        overrideConfiguration.locale = overrideLocale;
    }

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        context.getApplicationContext().createConfigurationContext(overrideConfiguration);
    } else {
        resources.updateConfiguration(overrideConfiguration, null);
    }
}

Il suffit d'utiliser cette méthode d'aide pour forcer une locale spécifique.

UDPATE 22 AOÛT 2017. Meilleure utilisation cette approche .

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