Poiché nessuna risposta è completa per l'attuale modo di risolvere questo problema, provo a dare istruzioni per una soluzione completa. Si prega di commentare se manca qualcosa o potrebbe essere fatto meglio.
Informazione Generale
Innanzitutto, esistono alcune librerie che vogliono risolvere il problema ma sembrano tutte obsolete o mancano alcune funzionalità:
Inoltre, penso che scrivere una libreria potrebbe non essere un modo buono / facile per risolvere questo problema perché non c'è molto da fare, e ciò che deve essere fatto è piuttosto cambiare il codice esistente piuttosto che usare qualcosa di completamente disaccoppiato. Pertanto ho composto le seguenti istruzioni che dovrebbero essere complete.
La mia soluzione si basa principalmente su https://github.com/gunhansancar/ChangeLanguageExample (come già collegato da localhost ). È il miglior codice che ho trovato su cui orientarmi. Alcune osservazioni:
- Se necessario, fornisce diverse implementazioni per modificare le impostazioni locali per Android N (e versioni successive) e versioni precedenti
- Utilizza un metodo
updateViews()
in ogni attività per aggiornare manualmente tutte le stringhe dopo aver modificato getString(id)
le impostazioni locali (usando il solito ) che non è necessario nell'approccio mostrato di seguito
- Supporta solo le lingue e non le localizzazioni complete (che includono anche i codici regione (paese) e variante)
L'ho cambiato un po ', disaccoppiando la parte che persiste la locale prescelta (come si potrebbe desiderare di fare separatamente, come suggerito di seguito).
Soluzione
La soluzione è composta dai seguenti due passaggi:
- Modifica in modo permanente le impostazioni internazionali da utilizzare dall'app
- Fai in modo che l'app usi le impostazioni internazionali personalizzate, senza riavviare
Passaggio 1: modifica le impostazioni internazionali
Usa la classe LocaleHelper
, basata su LocaleHelper di gunhansancar :
- Aggiungi a
ListPreference
in a PreferenceFragment
con le lingue disponibili (deve essere mantenuto quando le lingue devono essere aggiunte in seguito)
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;
}
}
Crea un SettingsFragment
simile come segue:
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);
}
}
Creare un locales.xml
elenco di risorse con tutte le versioni locali con le traduzioni disponibili nel modo seguente ( elenco di codici locali ):
<!-- 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>
Nella tua PreferenceScreen
è possibile utilizzare la seguente sezione per consentire all'utente di selezionare le lingue disponibili:
<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>
che utilizza le seguenti stringhe da 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>
Passaggio 2: fai in modo che l'app usi le impostazioni internazionali personalizzate
Ora imposta ciascuna attività per utilizzare il set di impostazioni internazionali personalizzato. Il modo più semplice per ottenere questo risultato è avere una classe base comune per tutte le attività con il seguente codice (dove si trova il codice importante attachBaseContext(Context base)
e 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();
}
}
}
Quello che fa è
- Sostituisci
attachBaseContext(Context base)
per utilizzare le impostazioni internazionali precedentemente persistentiLocaleHelper
- Rileva una modifica delle impostazioni locali e ricrea l'attività per aggiornarne le stringhe
Note su questa soluzione
La ricreazione di un'attività non aggiorna il titolo di ActionBar (come già osservato qui: https://github.com/gunhansancar/ChangeLanguageExample/issues/1 ).
- Ciò può essere ottenuto semplicemente inserendo
setTitle(R.string.mytitle)
il onCreate()
metodo di ciascuna attività.
Consente all'utente di scegliere le impostazioni internazionali predefinite del sistema, nonché le impostazioni internazionali predefinite dell'app (che può essere denominata, in questo caso "inglese").
fr-rCA
Finora sono supportati solo codici di lingua, nessuna regione (paese) e codici variante (come ). Per supportare le specifiche locali complete, è possibile utilizzare un parser simile a quello della libreria Android-Languages (che supporta la regione ma non i codici variante).
- Se qualcuno trova o ha scritto un buon parser, aggiungi un commento in modo da poterlo includere nella soluzione.