Ustaw ustawienia regionalne programowo

Moja aplikacja obsługuje 3 (wkrótce 4) języki. Ponieważ kilka locale są dość podobne chciałbym dać użytkownikowi możliwość zmiany locale w mojej aplikacji, na przykład osoba włoska może wolą Hiszpański niż Angielski.

Czy istnieje sposób, aby użytkownik mógł wybrać spośród ustawień regionalnych dostępnych dla aplikacji, a następnie zmienić, które Ustawienia regionalne są używane? Nie widzę problemu, aby ustawić ustawienia regionalne dla każdej aktywności, ponieważ jest to proste zadanie do wykonania w klasie bazowej.

Author: Cœur, 2011-02-13

9 answers

Dla osób wciąż szukających tej odpowiedzi, ponieważ configuration.locale jest przestarzała, możesz teraz używać z API 24:

configuration.setLocale(locale);

Weź pod uwagę, że minSkdVersion dla tej metody jest API 17.

Pełny przykładowy kod:

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

Nie zapominaj, że jeśli zmienisz ustawienia regionalne z bieżącą aktywnością, będziesz musiał go ponownie uruchomić, aby zmiany weszły w życie.

Edycja 11 maja 2018

Od postu @ CookieMonster, możesz mieć problemy z utrzymaniem locale zmiana w wyższych wersjach API. Jeśli tak, dodaj następujący kod do podstawowej aktywności, aby zaktualizować ustawienia regionalne kontekstu przy każdym tworzeniu aktywności:

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

Jeśli tego użyjesz, nie zapomnij zapisać języka do SharedPreferences, gdy ustawisz ustawienia regionalne za pomocą setLocate(locale)

 50
Author: Ricardo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-13 13:38:09

Hope this help (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());
 165
Author: Rubycon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-26 06:17:13
@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);
    }
}

Użyj tej metody pomocniczej, aby wymusić określone ustawienia regionalne.

[[1]] UDPATE 22 SIE 2017. Lepsze wykorzystanie takie podejście.
 12
Author: localhost,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-22 14:29:01

Miałem problem z programowym ustawianiem locale z urządzeniami który ma Android OS N i wyższy . Dla mnie rozwiązaniem było napisanie tego kodu w mojej podstawowej aktywności:

(Jeśli nie masz podstawowej aktywności, powinieneś wprowadzić te zmiany we wszystkich swoich działaniach)

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

Zauważ, że tutaj nie wystarczy zadzwonić

createConfigurationContext(configuration)

Musisz również uzyskać kontekst, który zwraca ta metoda, a następnie ustawić ten kontekst w metodzie attachBaseContext.

 10
Author: CookieMonster,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-15 15:10:44

Ponieważ żadna odpowiedź nie jest kompletna dla obecnego sposobu rozwiązania tego problemu, staram się podać instrukcje dotyczące kompletnego rozwiązania. Proszę skomentować, jeśli czegoś brakuje lub można by zrobić to lepiej.

Informacje ogólne

Po pierwsze, istnieją biblioteki, które chcą rozwiązać problem, ale wszystkie wydają się przestarzałe lub brakuje niektórych funkcji:]}

Dalej myślę, że pisanie biblioteki może nie być dobrym / łatwym sposobem rozwiązania tego problemu, ponieważ nie ma zbyt wiele do zrobienia, a to, co należy zrobić, to raczej zmiana istniejącego kodu niż użycie czegoś całkowicie odsprzęgniętego. Dlatego skomponowałem następujące instrukcje, które powinny być kompletne.

Moje rozwiązanie opiera się głównie na https://github.com/gunhansancar/ChangeLanguageExample (jako już połączone przez localhost ). Jest to najlepszy kod, który znalazłem, aby zorientować się w. Niektóre uwagi:

  • w razie potrzeby udostępnia różne implementacje, aby zmienić ustawienia regionalne dla Android N (i powyżej) i poniżej
  • używa metody updateViews() w każdej aktywności, aby ręcznie zaktualizować wszystkie łańcuchy znaków po zmianie ustawień regionalnych (używając zwykłego getString(id)), co nie jest konieczne w podejściu pokazanym poniżej
  • [29]}obsługuje tylko języki, a nie pełne lokalizacje (które obejmują również region (kraj) i kody wariantowe)

Zmieniłem go trochę, oddzielając część, która utrzymuje wybrane locale (jak ktoś może chcieć to zrobić osobno, zgodnie z sugestią poniżej).

Rozwiązanie

Rozwiązanie składa się z dwóch następujących etapów:

  • trwale zmienić ustawienia regionalne, które mają być używane przez aplikację
  • Aby aplikacja korzystała z niestandardowych ustawień regionalnych, bez ponownego uruchamiania [36]}

Krok 1: Zmień ustawienia regionalne

Użyj klasy LocaleHelper, opartej na gunhansancar ' s LocaleHelper :

  • Dodaj ListPreference w PreferenceFragment z dostępnymi językami (należy zachować, gdy języki powinny być dodawane później)
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;
    }
}

Utwórz SettingsFragment jak poniżej:

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

Utwórz zasób locales.xml z listą wszystkich lokalizacji z dostępnymi tłumaczeniami w następujący sposób ( lista kodów regionalnych):

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

W twoim PreferenceScreen możesz użyć poniższej sekcji, aby umożliwić użytkownikowi wybór dostępnych języków:

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

Który używa następujących ciągów z 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>
Krok 2: spraw, aby aplikacja korzystała z niestandardowych ustawień regionalnych

Teraz ustaw każdy Aktywność, aby użyć niestandardowego zestawu ustawień regionalnych. Najprostszym sposobem na to jest posiadanie wspólnej klasy bazowej dla wszystkich działań z następującym kodem (gdzie ważny kod znajduje się w attachBaseContext(Context base) i 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();
        }
    }
}

To co robi to

  • Override attachBaseContext(Context base) aby użyć ustawień regionalnych, które zostały wcześniej zapisane w LocaleHelper
  • wykrywa zmianę ustawień regionalnych i odtwarza aktywność, aby zaktualizować jej ciągi znaków

Uwagi na temat tego rozwiązania

  • Odtwarzanie an Activity nie aktualizuje tytułu ActionBar (jak to już zostało zaobserwowane tutaj: https://github.com/gunhansancar/ChangeLanguageExample/issues/1).

    • można to osiągnąć po prostu mając setTitle(R.string.mytitle) w metodzie onCreate() każdej aktywności.
  • Pozwala użytkownikowi wybrać domyślne ustawienia regionalne systemu, a także domyślne ustawienia regionalne aplikacji (które można nazwać, w tym przypadku "English").

  • Tylko kody językowe, bez regionu (kraju) i kody wariantowe (jak fr-rCA) są do tej pory obsługiwane. Aby obsługiwać pełne specyfikacje lokalne, można użyć parsera podobnego do tego w Android-Languages library (który obsługuje region, ale nie ma kodów wariantowych).

    • jeśli ktoś znajdzie lub napisał dobry parser, dodaj komentarz, abym mógł go uwzględnić w rozwiązaniu.
 7
Author: user905686,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-08 14:05:14

Dodaj klasę pomocniczą następującą metodą:

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

    }
}

I wywołaj to w swojej aktywności startowej, np. MainActivity.java:

public void onCreate(Bundle savedInstanceState) {
    ...
    LanguageHelper.setAppLocale("fa", this);
    ...
}
 3
Author: Hadid Graphics,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-14 06:07:14

Proste i łatwe

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

Gdzie " en "To kod języka, A" US " to kod kraju.

 1
Author: Makvin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-23 04:47:47
 /**
 * 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
    }
}
 0
Author: Zbigniew Mazur,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-09 10:07:31

Umieść ten kod w swojej aktywności

 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
Author: Xurshid Raxmatov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-19 19:30:15