web-dev-qa-db-fra.com

Définir les paramètres régionaux par programmation

Mon application prend en charge 3 (bientôt 4) langues. Étant donné que plusieurs paramètres régionaux sont assez similaires, j'aimerais donner à l'utilisateur la possibilité de modifier les paramètres régionaux dans mon application. Par exemple, une personne italienne pourrait préférer l'espagnol à l'anglais.

Existe-t-il un moyen pour l'utilisateur de choisir parmi les paramètres régionaux disponibles pour l'application, puis de modifier les paramètres régionaux utilisés? Je ne vois pas comme un problème de définir les paramètres régionaux pour chaque activité puisqu'il s'agit d'une tâche simple à effectuer dans une classe de base.

99
Roland

Pour ceux qui recherchent toujours cette réponse, puisque configuration.locale est obsolète, vous pouvez maintenant utiliser l'API 24:

configuration.setLocale(locale);

Notez que minSkdVersion pour cette méthode est API 17.

Exemple de code 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 avec une activité en cours d'exécution, vous devrez le redémarrer pour que les modifications prennent effet.

EDIT 11 MAI 2018

À partir du message de @ CookieMonster, vous pourriez avoir des problèmes pour conserver les changements de paramètres régionaux dans les versions d'API supérieures. Si tel est le cas, ajoutez le code suivant à votre activité de base afin de mettre à jour les paramètres régionaux de contexte à 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)
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;
}

Si vous utilisez ceci, n'oubliez pas de sauvegarder la langue dans SharedPreferences lorsque vous définissez les paramètres régionaux avec setLocate(locale)

66
Ricardo

J'espère que cette aide (dans onResume):

Locale locale = new Locale("ru");
Locale.setDefault(locale);
Configuration config = getBaseContext().getResources().getConfiguration();
config.locale = locale;
getBaseContext().getResources().updateConfiguration(config,
      getBaseContext().getResources().getDisplayMetrics());
169
Rubycon
@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);
    }
}

Utilisez simplement cette méthode d'assistance pour forcer des paramètres régionaux spécifiques.

UDPATE 22 AUG 2017 . Mieux utiliser cette approche.

13
localhost

J'ai eu un problème avec la définition de paramètres régionaux par programme avec les appareils Qui a Android OS N et supérieur . Pour moi, la solution écrivait ce code dans mon activité de base:

(si vous n'avez pas d'activité de base, vous devriez apporter ces modifications à 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 renvoyé par cette méthode, puis définir ce contexte dans la méthode attachBaseContext.

13
CookieMonster

Comme aucune réponse n’est complète sur la manière 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 amélioré.

Informations générales

Premièrement, il existe des bibliothèques qui veulent résoudre le problème, mais elles semblent toutes obsolètes ou manquent de certaines fonctionnalités:

De plus, je pense qu'écrire une bibliothèque peut ne pas être un moyen simple et efficace de résoudre ce problème car il n'y a pas grand chose à faire et il faut plutôt changer le code existant plutôt que d'utiliser quelque chose de complètement découplé. les instructions suivantes qui devraient être complètes.

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

  • Si nécessaire, il propose différentes implémentations pour modifier les paramètres régionaux 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 après avoir modifié les paramètres régionaux (à l'aide de la fonction getString(id) habituelle), ce qui n'est pas nécessaire dans l'approche indiquée ci-dessous.
  • Il ne prend en charge que les langues et pas les paramètres régionaux complets (qui incluent également les codes de région et de variante)

Je l'ai un peu modifié en découplant la partie qui conserve les paramètres régionaux choisis (comme on pourrait le faire séparément, comme suggéré ci-dessous).

Solution

La solution comprend les deux étapes suivantes:

  • Changer définitivement les paramètres régionaux à utiliser par l'application
  • Faire en sorte que l'application utilise les paramètres régionaux personnalisés sans redémarrer

Étape 1: Modifier les paramètres régionaux

Utilisez la classe LocaleHelper, basée sur LocaleHelper de gunhansancar :

  • Ajouter une ListPreference dans une PreferenceFragment avec les langues disponibles (doit être conservée lorsque les 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éez une SettingsFragment comme celle-ci:

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éez une ressource locales.xml répertoriant tous les paramètres régionaux avec les traductions disponibles de la manière suivante ( liste des codes de paramètres régionaux ):

<!-- 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: Amener l'application à utiliser les paramètres régionaux personnalisés

Configurez maintenant chaque activité pour qu'elle utilise les paramètres régionaux personnalisés. Le moyen le plus simple d'y parvenir est d'avoir une classe de base commune pour toutes les activités avec le code suivant (où le code important est dans attachBaseContext(Context base) et 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();
        }
    }
}

Qu'est-ce qu'il fait est

  • Remplacer attachBaseContext(Context base) pour utiliser les paramètres régionaux précédemment persistés avec LocaleHelper
  • Détecter un changement de paramètres régionaux et recréer l'activité pour mettre à jour ses chaînes

Notes sur cette solution

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

    • Ceci peut être réalisé simplement en ayant une setTitle(R.string.mytitle) dans la méthode onCreate() de chaque activité.
  • Il permet à l'utilisateur de choisir les paramètres régionaux par défaut du système, ainsi que les paramètres régionaux par défaut de l'application (qui peuvent être nommés, dans ce cas, "anglais").

  • Seuls les codes de langue, pas de région (pays) et les codes de variante (tels que fr-rCA) sont pris en charge jusqu'à présent. Pour prendre en charge les spécifications de paramètres régionaux complètes, un analyseur similaire à celui de la bibliothèque Android-Languages peut être utilisé (qui prend en charge la région mais pas les codes de variante).

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

Ajoutez une classe d'assistance avec la méthode suivante:

public class LanguageHelper {
    public static final void setAppLocale(String language, Activity activity) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            Resources resources = activity.getResources();
            Configuration configuration = resources.getConfiguration();
            configuration.setLocale(new Locale(language));
            activity.getApplicationContext().createConfigurationContext(configuration);
        } else {
            Locale locale = new Locale(language);
            Locale.setDefault(locale);
            Configuration config = activity.getResources().getConfiguration();
            config.locale = locale;
            activity.getResources().updateConfiguration(config,
                    activity.getResources().getDisplayMetrics());
        }

    }
}

Et appelez-le dans votre activité de démarrage, comme MainActivity.Java:

public void onCreate(Bundle savedInstanceState) {
    ...
    LanguageHelper.setAppLocale("fa", this);
    ...
}
4
Hadid Graphics

simple et facile

Locale locale = new Locale("en", "US");
Resources res = getResources();
DisplayMetrics dm = res.getDisplayMetrics();
Configuration conf = res.getConfiguration();
conf.locale = locale;
res.updateConfiguration(conf, dm);

où "en" est le code de langue et "US" le code de pays. 

1
Makvin
 /**
 * Requests the system to update the list of system locales.
 * Note that the system looks halted for a while during the Locale migration,
 * so the caller need to take care of it.
 */
public static void updateLocales(LocaleList locales) {
    try {
        final IActivityManager am = ActivityManager.getService();
        final Configuration config = am.getConfiguration();

        config.setLocales(locales);
        config.userSetLocale = true;

        am.updatePersistentConfiguration(config);
    } catch (RemoteException e) {
        // Intentionally left blank
    }
}
1
Zbigniew Mazur

Mettez ce code dans votre activité 

 if (id==R.id.uz)
    {
        LocaleHelper.setLocale(MainActivity.this, mLanguageCode);

        //It is required to recreate the activity to reflect the change in UI.
        recreate();
        return true;
    }
    if (id == R.id.ru) {

        LocaleHelper.setLocale(MainActivity.this, mLanguageCode);

        //It is required to recreate the activity to reflect the change in UI.
        recreate();
    }
0
Xurshid Raxmatov

Valable pour API16 à API28 Il suffit de placer cette méthode à un endroit où:

    Context newContext = context;

        Locale locale = new Locale(languageCode);
        Locale.setDefault(locale);

        Resources resources = context.getResources();
        Configuration config = new Configuration(resources.getConfiguration());

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {

        config.setLocale(locale);
                newContext = context.createConfigurationContext(config);

        } else {

        config.locale = locale;
                resources.updateConfiguration(config, resources.getDisplayMetrics());
        }

    return newContext;
}

Insérez ce code dans toutes vos activités en utilisant:

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(localeUpdateResources(base, "<-- language code -->"));
    }

ou appelez localeUpdateResources sur des fragments, des adaptateurs, etc., là où vous avez besoin du nouveau contexte.

Crédits: Yaroslav Berezanskyi

0
nnyerges