webentwicklung-frage-antwort-db.com.de

Gebietsschema programmgesteuert einstellen

Meine App unterstützt 3 (bald 4) Sprachen. Da sich mehrere Gebietsschemas sehr ähnlich sind, möchte ich dem Benutzer die Möglichkeit geben, das Gebietsschema in meiner Anwendung zu ändern. Eine italienische Person könnte beispielsweise Spanisch lieber als Englisch.

Gibt es eine Möglichkeit für den Benutzer, unter den für die Anwendung verfügbaren Gebietsschemas auszuwählen und dann das verwendete Gebietsschema zu ändern? Ich sehe es nicht als Problem an, das Gebietsschema für jede Aktivität festzulegen, da dies eine einfache Aufgabe in einer Basisklasse ist.

99
Roland

Für Leute, die immer noch nach dieser Antwort suchen, können Sie nun, da configuration.locale veraltet ist, nun API 24 verwenden:

configuration.setLocale(locale);

Beachten Sie, dass die minSkdVersion für diese Methode API 17 ist.

Vollständiger Beispielcode:

@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);
    }
}

Vergessen Sie nicht, dass Sie das Gebietsschema bei laufender Aktivität ändern müssen, damit die Änderungen wirksam werden.

EDIT 11. MAI 2018

Seit @ CookieMonster's Post haben Sie möglicherweise Probleme, die Gebietsschemaänderung in höheren API-Versionen beizubehalten. Wenn dies der Fall ist, fügen Sie der Basisaktivität den folgenden Code hinzu, sodass Sie das Kontextgebietsschema bei jeder Aktivitätserstellung aktualisieren:

@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;
}

Wenn Sie dies verwenden, vergessen Sie nicht, die Sprache in SharedPreferences zu speichern, wenn Sie das Gebietsschema mit setLocate(locale) festlegen.

66
Ricardo

Hoffe diese Hilfe (in 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);
    }
}

Verwenden Sie diese Hilfsmethode, um ein bestimmtes Gebietsschema zu erzwingen.

UDPATE 22 AUG 2017 . Besser verwenden diesen Ansatz.

13
localhost

Ich hatte ein Problem mit der programmgesteuerten Einstellung des Gebietsschemas mit Geräten Das hat Android OS N und höher . Für mich bestand die Lösung darin, diesen Code in meine Basisaktivität zu schreiben:

(Wenn Sie keine Basisaktivität haben, sollten Sie diese Änderungen in allen Ihren Aktivitäten vornehmen.)

@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;
}

beachten Sie, dass es hier nicht reicht, anzurufen 

createConfigurationContext(configuration)

sie müssen auch den Kontext abrufen, den diese Methode zurückgibt, und diesen Kontext dann in der attachBaseContext-Methode festlegen.

13
CookieMonster

Da für die aktuelle Lösung dieses Problems keine Antwort vollständig ist, versuche ich, 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 einige Funktionen fehlen:

Des Weiteren denke ich, dass das Schreiben einer Bibliothek möglicherweise kein guter/einfacher Weg ist, dieses Problem zu lösen, da nicht viel zu tun ist, und es ist eher notwendig, vorhandenen Code zu ändern, als etwas völlig entkoppeltes zu verwenden ... Deshalb komponierte ich Die folgenden Anweisungen sollten vollständig sein.

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 stehen verschiedene Implementierungen zum Ändern des Gebietsschemas für Android N (und höher) und darunter zur Verfügung
  • Es verwendet eine Methode updateViews() in jeder Aktivität, um alle Zeichenfolgen nach dem Ändern des Gebietsschemas (unter Verwendung der üblichen getString(id)) manuell zu aktualisieren, was in der unten gezeigten Vorgehensweise nicht erforderlich ist
  • Es unterstützt nur Sprachen und nicht vollständige Gebietsschemas (die auch Regions- (Länder-) und Variantencodes enthalten).

Ich habe es ein wenig geändert, indem ich den Teil entkoppelte, der das ausgewählte Gebietsschema beibehält (wie 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
  • Stellen Sie sicher, dass die App den benutzerdefinierten Gebietsschemasatz verwendet, ohne neu zu starten

Schritt 1: Ändern Sie das Gebietsschema

Verwenden Sie die Klasse LocaleHelper, basierend auf gunhansancars LocaleHelper :

  • Fügen Sie eine ListPreference in einer PreferenceFragment mit den verfügbaren Sprachen hinzu (muss gepflegt 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 eine 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, die alle Gebietsschemas mit verfügbaren Übersetzungen auf folgende Weise auflistet ( 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 Ihrer PreferenceScreen können Sie den folgenden Abschnitt verwenden, um den Benutzer die verfügbaren Sprachen auswählen zu lassen:

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

welche die folgenden Zeichenketten von strings.xml verwendet:

<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: Legen Sie fest, dass die App das benutzerdefinierte Gebietsschema verwendet

Richten Sie nun jede Aktivität so ein, dass die benutzerdefinierte Ländereinstellung verwendet wird. Der einfachste Weg, dies zu erreichen, ist eine gemeinsame Basisklasse für alle Aktivitäten mit dem folgenden Code (wobei der wichtige Code in attachBaseContext(Context base) und onResume() steht):

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 Sie attachBaseContext(Context base), um das zuvor mit LocaleHelper beharrte Gebietsschema zu verwenden.
  • 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 Wiederherstellen einer Aktivität wird der Titel der ActionBar nicht aktualisiert (wie hier bereits beschrieben: https://github.com/gunhansancar/ChangeLanguageExample/issues/1 ).

    • Dies kann erreicht werden, indem einfach eine setTitle(R.string.mytitle) in der onCreate()-Methode jeder Aktivität verwendet wird.
  • Der Benutzer kann das Standardgebietsschema des Systems sowie das Standardgebietsschema der App auswählen (die in diesem Fall als "Englisch" bezeichnet werden kann).

  • Bisher werden nur Sprachcodes, keine Regionen (Länder) und Variantencodes (wie fr-rCA) unterstützt. Um vollständige Länderspezifikationen zu unterstützen, kann ein Parser verwendet werden, der dem in der Android-Languages-Bibliothek ähnelt (der Region, aber keine abweichenden Codes unterstützt).

    • Wenn jemand einen guten Parser findet oder geschrieben hat, fügen Sie einen Kommentar hinzu, damit er in die Lösung aufgenommen werden kann.
9
user905686

Fügen Sie eine Hilfsklasse mit der folgenden Methode hinzu:

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());
        }

    }
}

Und nennen Sie es in Ihrer Startaktivität wie MainActivity.Java:

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

simpel und einfach

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

dabei ist "en" der Sprachcode und "US" der Ländercode. 

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

Fügen Sie diesen Code in Ihre Aktivität ein 

 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

Gültig für API16 bis API28. Platzieren Sie diese Methode an folgenden Stellen:

    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;
}

Fügen Sie diesen Code in all Ihre Aktivitäten ein:

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

oder rufen Sie localeUpdateResources für Fragmente, Adapter usw. auf, wenn Sie den neuen Kontext benötigen.

Credits: Jaroslaw Beresanski

0
nnyerges