Comme aucune réponse n'est complète pour 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 n'est peut-être pas un moyen simple / bon de résoudre ce problème car il n'y a pas grand chose à faire, et ce qu'il faut faire est plutôt de changer le code existant que d'utiliser quelque chose de complètement découplé. C'est pourquoi j'ai rédigé les instructions suivantes qui devraient être complètes.
Ma solution est principalement basée sur https://github.com/gunhansancar/ChangeLanguageExample (comme déjà lié par localhost ). C'est le meilleur code que j'ai trouvé pour m'orienter. Quelques remarques:
- Si nécessaire, il fournit 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 changé les paramètres régionaux (en utilisant l'habituel getString(id)
), ce qui n'est pas nécessaire dans l'approche ci-dessous
- Il ne prend en charge que les langues et non les paramètres régionaux complets (qui incluent également les codes de région (pays) et de variante)
Je l'ai un peu changé, découplant la partie qui persiste dans la locale choisie (car on pourrait vouloir le faire séparément, comme suggéré ci-dessous).
Solution
La solution comprend les deux étapes suivantes:
- Modifier définitivement les paramètres régionaux à utiliser par l'application
- Faire en sorte que l'application utilise l'ensemble de 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 un
ListPreference
dans un PreferenceFragment
avec les langues disponibles (doit être conservé 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éez un SettingsFragment
comme celui-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: faire en sorte que l'application utilise les paramètres régionaux personnalisés
Configurez maintenant chaque activité pour utiliser l'ensemble de 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ù se trouve le code important 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();
}
}
}
Ce que ça fait c'est
- Remplacer
attachBaseContext(Context base)
pour utiliser les paramètres régionaux précédemment conservés avecLocaleHelper
- Détecter un changement de paramètres régionaux et recréer l'activité pour mettre à jour ses chaînes
Remarques sur cette solution
La recréation d'une activité ne met pas à jour le titre de l'ActionBar (comme déjà observé ici: https://github.com/gunhansancar/ChangeLanguageExample/issues/1 ).
- Ceci peut être réalisé en ayant simplement un
setTitle(R.string.mytitle)
dans la onCreate()
méthode 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, aucune région (pays) et codes de variante (comme fr-rCA
) sont pris en charge jusqu'à présent. Pour prendre en charge les spécifications régionales 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 afin que je puisse l'inclure dans la solution.