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.
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