Da für den aktuellen Weg zur Lösung dieses Problems keine Antwort vollständig ist, versuche ich, Anweisungen für eine vollständige Lösung zu geben. Bitte kommentieren Sie, wenn etwas fehlt oder besser gemacht werden könnte.
Allgemeine Information
Erstens gibt es einige Bibliotheken, die das Problem lösen möchten, aber alle scheinen veraltet zu sein oder es fehlen einige Funktionen:
Außerdem denke ich, dass das Schreiben einer Bibliothek möglicherweise keine gute / einfache Möglichkeit ist, dieses Problem zu lösen, da nicht viel zu tun ist und der vorhandene Code eher geändert werden muss, als etwas vollständig Entkoppeltes zu verwenden. Deshalb habe ich die folgenden Anweisungen verfasst, die vollständig sein sollten.
Meine Lösung basiert hauptsächlich auf https://github.com/gunhansancar/ChangeLanguageExample (wie bereits von localhost verlinkt ). Es ist der beste Code, an dem ich mich orientieren konnte. Einige Anmerkungen:
- Bei Bedarf werden verschiedene Implementierungen bereitgestellt, um das Gebietsschema für Android N (und höher) und darunter zu ändern
updateViews()
In jeder Aktivität wird eine Methode verwendet , um alle Zeichenfolgen nach dem Ändern des Gebietsschemas (unter Verwendung des üblichen getString(id)
) manuell zu aktualisieren, was bei dem unten gezeigten Ansatz nicht erforderlich ist
- Es werden nur Sprachen und keine vollständigen Gebietsschemas unterstützt (einschließlich Regions- (Land) und Variantencodes).
Ich habe es ein wenig geändert und den Teil entkoppelt, der das ausgewählte Gebietsschema beibehält (da man dies möglicherweise separat tun möchte, wie unten vorgeschlagen).
Lösung
Die Lösung besteht aus den folgenden zwei Schritten:
- Ändern Sie dauerhaft das Gebietsschema, das von der App verwendet werden soll
- Lassen Sie die App das benutzerdefinierte Gebietsschemaset verwenden, ohne es neu zu starten
Schritt 1: Ändern Sie das Gebietsschema
Verwenden Sie die Klasse LocaleHelper
, die auf gunhansancars LocaleHelper basiert :
- Fügen Sie ein
ListPreference
in a PreferenceFragment
mit den verfügbaren Sprachen hinzu (muss beibehalten werden, wenn Sprachen später hinzugefügt werden sollen)
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;
}
}
Erstellen Sie SettingsFragment
wie folgt:
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);
}
}
Erstellen Sie eine Ressource, locales.xml
in der alle Gebietsschemas mit verfügbaren Übersetzungen wie folgt aufgelistet sind ( Liste der Gebietsschemacodes ):
<!-- 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>
In Ihrem können PreferenceScreen
Sie den folgenden Abschnitt verwenden, damit der Benutzer die verfügbaren Sprachen auswählen kann:
<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>
Dabei werden die folgenden Zeichenfolgen verwendet 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>
Schritt 2: Lassen Sie die App das benutzerdefinierte Gebietsschema verwenden
Richten Sie nun jede Aktivität so ein, dass das benutzerdefinierte Gebietsschemaset verwendet wird. Der einfachste Weg, dies zu erreichen, besteht darin, eine gemeinsame Basisklasse für alle Aktivitäten mit dem folgenden Code zu haben (wobei sich der wichtige Code in attachBaseContext(Context base)
und befindet 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();
}
}
}
Was es tut ist
- Überschreiben
attachBaseContext(Context base)
, um das zuvor beibehaltene Gebietsschema zu verwendenLocaleHelper
- Erkennen Sie eine Änderung des Gebietsschemas und erstellen Sie die Aktivität neu, um die Zeichenfolgen zu aktualisieren
Hinweise zu dieser Lösung
Durch das erneute Erstellen einer Aktivität wird der Titel der Aktionsleiste nicht aktualisiert (wie hier bereits erwähnt: https://github.com/gunhansancar/ChangeLanguageExample/issues/1 ).
- Dies kann durch einfach ein mit erreicht werden
setTitle(R.string.mytitle)
in dem onCreate()
Verfahren jeder Aktivität.
Hiermit kann der Benutzer das Standardgebietsschema des Systems sowie das Standardgebietsschema der App auswählen (das benannt werden kann, in diesem Fall "Englisch").
fr-rCA
Bisher werden nur Sprachcodes, keine Region (Land) und Variantencodes (wie ) unterstützt. Zur Unterstützung der vollständigen Gebietsschemaspezifikationen kann ein Parser verwendet werden, der dem in der Android-Languages-Bibliothek ähnelt (der Regions-, aber keine Variantencodes unterstützt).
- Wenn jemand einen guten Parser findet oder geschrieben hat, fügen Sie einen Kommentar hinzu, damit ich ihn in die Lösung aufnehmen kann.