以编程方式设置区域设置

Rol*_*and 118 android locale user-defined

我的应用程序支持3种(很快4种)语言.由于几个语言环境非常相似,我想让用户选择在我的应用程序中更改语言环境,例如意大利人可能更喜欢西班牙语而不是英语.

有没有办法让用户在可用于应用程序的语言环境中进行选择,然后更改使用的语言环境?我不认为为每个Activity设置语言环境是一个问题,因为它是在基类中执行的简单任务.

Rub*_*con 175

希望这个帮助(在onResume上):

Locale locale = new Locale("ru");
Locale.setDefault(locale);
Configuration config = getBaseContext().getResources().getConfiguration();
config.locale = locale;
getBaseContext().getResources().updateConfiguration(config,
      getBaseContext().getResources().getDisplayMetrics());
Run Code Online (Sandbox Code Playgroud)

  • 我将此代码放入我的启动器Activity的onCreate()(并且没有其他地方),并且惊喜地看到该区域设置应用于整个应用程序.这是一个针对4.3的应用,minSDK为14(ICS). (10认同)
  • 无需创建新的Configuration对象.您可以使用当前配置并更新它:getResources().getConfiguration() (8认同)
  • 1.必须使用getBaseContext()或者更好地使用应用程序上下文?2.应在每个活动中调用此代码?谢谢. (5认同)
  • 因此,必须为每个活动设置此设置吗? (2认同)

Ric*_*rdo 84

对于仍在寻找此答案的人,由于configuration.locale已弃用,您现在可以使用API​​ 24:

configuration.setLocale(locale);
Run Code Online (Sandbox Code Playgroud)

考虑到此方法的minSkdVersion是API 17.

完整示例代码:

@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);
    }
}
Run Code Online (Sandbox Code Playgroud)

不要忘记,如果使用正在运行的Activity更改语言环境,则需要重新启动它才能使更改生效.

编辑2018年5月11日

从@ CookieMonster的帖子开始,您可能无法在较高的API版本中保持区域设置更改.如果是这样,请将以下代码添加到Base Activity,以便在每次创建Activity时更新上下文区域设置:

@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;
}
Run Code Online (Sandbox Code Playgroud)

如果您使用此功能,请不要忘记在设置语言环境时将语言保存到SharedPreferences setLocate(locale)

  • `appcompat:1.1.0` 的问题可以通过 `appcompat:1.2.0-alpha02` 和代码 `Set<Locale> set = new LinkedHashSet<>(); 来修复 // 将目标语言环境置于列表的前面 set.add(locale); LocaleList all = LocaleList.getDefault(); for (int i = 0; i < all.size(); i++) { // 附加用户支持的其他语言环境 set.add(all.get(i)); Locale[] locales = set.toArray(new Locale[0]); `@TargetApi(Build.VERSION_CODES.N) updateResourcesLocale()` 内的configuration.setLocales(new LocaleList(locales));` (6认同)
  • 但现在,不推荐使用updateConfiguration. (4认同)
  • 这适用于活动,但有没有办法更新应用程序上下文? (4认同)
  • 将“androidx.appcompat:appcompat:”版本从“1.0.2”更改为“1.1.0”后,不适用于 Android 7,但适用于 Android 9。 (4认同)
  • 我和“1.1.0” androidx 遇到同样的问题 (4认同)
  • 对我来说同样的问题。当我更改为 androidx.appcompat:appcompat:1.1.0' lib 后 (2认同)
  • 很抱歉,我在之前的评论中忘记了如何更改配置:`Configuration configuration = new Configuration(context.getResources().getConfiguration());` (2认同)

use*_*686 17

由于目前解决此问题的方法没有完整的答案,我尝试给出完整解决方案的说明.如果缺少某些内容或者可以做得更好,请发表评论.

一般信息

首先,存在一些想要解决问题的库,但它们似乎都已过时或缺少某些功能:

此外,我认为编写库可能不是解决此问题的好/简单方法,因为没有太多工作要做,而且必须要做的是改变现有代码而不是使用完全解耦的东西.因此,我编写了以下应该完成的说明.

我的解决方案主要基于https://github.com/gunhansancar/ChangeLanguageExample(已经通过localhost链接).这是我发现的最佳代码.一些评论:

  • 根据需要,它提供了不同的实现来更改Android N(及更高版本)及更低版本的区域设置
  • 它使用updateViews()每个Activity中的一个方法在更改区域设置(使用常规getString(id))后手动更新所有字符串,这在下面显示的方法中是不必要的
  • 它只支持语言而不是完整的语言环境(也包括区域(国家)和变体代码)

我改变了一点,解除了持续选择的语言环境的部分(正如人们可能希望单独进行的那样,如下所示).

解决方案包括以下两个步骤:

  • 永久更改应用程序使用的区域设置
  • 使应用程序使用自定义区域设置,而无需重新启动

第1步:更改区域设置

使用该类LocaleHelper,基于gunhansancar的LocaleHelper:

  • 添加ListPreferencePreferenceFragment与可用的语言(有语言时,应在以后添加到保)
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;
    }
}
Run Code Online (Sandbox Code Playgroud)

创建SettingsFragment如下所示:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

locales.xml使用以下方式创建列出具有可用翻译的所有语言环境的资源(语言环境代码列表):

<!-- 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>
Run Code Online (Sandbox Code Playgroud)

在你PreferenceScreen可以使用下面的部分,让用户选择可用的语言:

<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>
Run Code Online (Sandbox Code Playgroud)

它使用以下字符串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>
Run Code Online (Sandbox Code Playgroud)

第2步:使应用程序使用自定义区域设置

现在设置每个Activity以使用自定义区域设置.做到这一点最简单的方法是有一个与下面的代码的所有活动的共同基类(这里的重要代码是在attachBaseContext(Context base)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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

它的作用是什么

  • 覆盖attachBaseContext(Context base)以使用以前持久化的语言环境LocaleHelper
  • 检测区域设置的更改并重新创建活动以更新其字符串

关于此解决方案的说明

  • 重新创建活动不会更新ActionBar的标题(如下所示:https://github.com/gunhansancar/ChangeLanguageExample/issues/1).

    • 这可以通过简单地setTitle(R.string.mytitle)onCreate()每个活动的方法中实现.
  • 它允许用户选择系统默认语言环境,以及应用程序的默认语言环境(可以命名,在本例中为"英语").

  • fr-rCA到目前为止,仅支持语言代码,不支持区域(国家/地区)和变体代码(例如).要支持完整的语言环境规范,可以使用类似于Android-Languages库中的解析器(它支持区域但不支持变体代码).

    • 如果有人找到或编写了一个好的解析器,请添加注释,以便我可以将其包含在解决方案中.

  • 优秀但噩梦之王 (2认同)
  • 地狱不,我的应用程序已经太复杂了,这种方法在未来维护将是一场噩梦。 (2认同)

loc*_*ost 15

@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);
    }
}
Run Code Online (Sandbox Code Playgroud)

只需使用此帮助程序方法强制使用特定的语言环境.

UDPATE 2017年8月22日.更好地使用这种方法.


Coo*_*ter 14

我在使用Android OS N及更高版本的设备以编程方式设置区域设置时出现问题.对我来说,解决方案是在我的基本活动中编写此代码:

(如果您没有基本活动,那么您应该在所有活动中进行这些更改)

@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;
}
Run Code Online (Sandbox Code Playgroud)

请注意,这里打电话是不够的

createConfigurationContext(configuration)
Run Code Online (Sandbox Code Playgroud)

您还需要获取此方法返回的上下文,然后在attachBaseContext方法中设置此上下文.

  • 在 Android N 下的版本中不起作用,因为必须在 onCreate() 而不是 AttachBaseContext() 中调用 resources.updateConfiguration (5认同)
  • 此代码在高于7的android上非常有效,但在低于N的版本中不起作用。你有什么解决方案? (2认同)

小智 5

使用以下方法添加辅助类:

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

    }
}
Run Code Online (Sandbox Code Playgroud)

并在您的启动活动中调用它,例如MainActivity.java

public void onCreate(Bundle savedInstanceState) {
    ...
    LanguageHelper.setAppLocale("fa", this);
    ...
}
Run Code Online (Sandbox Code Playgroud)


nny*_*ges 5

适用于 API16 到 API28 只需将此方法放置在以下位置:

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;
Run Code Online (Sandbox Code Playgroud)

使用以下命令将此代码插入到您的所有活动中:

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

或者在需要新上下文的片段、适配器等上调用 localeUpdateResources。

致谢:雅罗斯拉夫·别列赞斯基


Bur*_*lek 5

有一个超级简单的方法。

在 BaseActivity、Activity 或 Fragment 中覆盖 attachBaseContext

 override fun attachBaseContext(context: Context) {
    super.attachBaseContext(context.changeLocale("tr"))
}
Run Code Online (Sandbox Code Playgroud)

延期

fun Context.changeLocale(language:String): Context {
    val locale = Locale(language)
    Locale.setDefault(locale)
    val config = this.resources.configuration
    config.setLocale(locale)
    return createConfigurationContext(config)
}
Run Code Online (Sandbox Code Playgroud)