【问题标题】:Change language programmatically (Android N 7.0 - API 24)以编程方式更改语言 (Android N 7.0 - API 24)
【发布时间】:2016-12-24 03:42:03
【问题描述】:

我正在使用以下代码在我的应用中设置特定语言。语言保存在应用程序内的SharedPreferences 中。 它在 API 级别 23 之前可以完美运行。 使用 Android N SharedPreferences 也可以正常工作,它返回正确的语言代码字符串,但不会更改语言环境(设置手机的默认语言)。有什么问题?

更新 1:当我在 res.updateConfiguration(config, dm) 之后立即使用 Log.v("MyLog", config.locale.toString()); 时,它会返回正确的语言环境,但应用程序的语言不会改变。

更新 2: 我还提到,如果我更改语言环境然后重新启动活动(使用新意图并在旧意图上完成),它会正确更改语言,甚至在之后显示正确的语言回转。但是当我关闭应用程序并再次打开它时,我会得到默认语言。这很奇怪。

public class ActivityMain extends AppCompatActivity {

    //...
    @Override
    protected void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState);
        // Set locale
        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
        String lang = pref.getString(ActivityMain.LANGUAGE_SAVED, "no_language");
        if (!lang.equals("no_language")) {
            Resources res = context.getResources();
            Locale locale = new Locale(lang);
            Locale.setDefault(locale);
            DisplayMetrics dm = res.getDisplayMetrics();
            Configuration config = res.getConfiguration();
            if (Build.VERSION.SDK_INT >= 17) {
                config.setLocale(locale);
            } else {
                config.locale = locale;
            }
        }
        res.updateConfiguration(config, dm);

        setContentView(R.layout.activity_main);
        //...
    } 
    //... 
}

更新 3: 答案也在这里: https://stackoverflow.com/a/40849142/3935063

【问题讨论】:

标签: android locale android-7.0-nougat


【解决方案1】:

这种方法适用于所有 api 级别的设备,确保以编程方式重新创建更改语言的活动。

1.使用Base Activity为attachBaseContext设置语言环境,并将该Activity扩展为所有Activity

open class BaseAppCompactActivity() : AppCompatActivity() {
    override fun attachBaseContext(newBase: Context) {
        super.attachBaseContext(LocaleHelper.onAttach(newBase))

    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

    }
}

2.使用Application attachBaseContext 和onConfigurationChanged 设置locale语言

public class MyApplication extends Application {
    private static MyApplication application;

    @Override
    public void onCreate() {
        super.onCreate();
    }

    public static MyApplication getApplication () {
        return application;
    }

    /**
     * overide to change local sothat language can be chnaged from android device  nogaut and above
     */
    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(LocaleHelper.INSTANCE.onAttach(base));
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        /**
         * also handle change  language if  device language changed
         */
        super.onConfigurationChanged(newConfig);

    }
}

3.使用 Locale Helper 处理语言变化,此方法适用于所有设备

object LocaleHelper {
    fun onAttach(context: Context, defaultLanguage: String): Context {
        return setLocale(context, defaultLanguage)
    }

fun setLocale(context: Context, language: String): Context {
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        updateResources(context, language)
    } else updateResourcesLegacy(context, language)

}


@TargetApi(Build.VERSION_CODES.N)
private fun updateResources(context: Context, language: String): Context {
    val locale = Locale(language)
    Locale.setDefault(locale)

    val configuration = context.getResources().getConfiguration()
    configuration.setLocale(locale)
    configuration.setLayoutDirection(locale)

    return context.createConfigurationContext(configuration)
}

private fun updateResourcesLegacy(context: Context, language: String): Context {
    val locale = Locale(language)
    Locale.setDefault(locale)

    val resources = context.getResources()

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

【讨论】:

【解决方案2】:

我遇到了同样的问题,但使用的是 Android 5、6 和 7。经过大量搜索,解决方案在这篇文章中:Change Locale not work after migrate to Androidx(参见 Ehsan Shadi 的评论,而不是“解决方案”)。

首先,您必须将您的库 appcompat 更新为 1.2.0(针对语言环境进行修复,请参见此处:https://developer.android.com/jetpack/androidx/releases/appcompat#1.2.0),并且您必须在基础活动中覆盖 applyOverrideConfiguration 函数。

我已经在所有 API(19 到 30 - Android 4.4 到 11)上测试了这个解决方案,它运行良好 :)

【讨论】:

    【解决方案3】:

    当我开始以 SDK 29 为目标时,我遇到了这个问题。我创建的 LocaleHelper 适用于从我的 min SDK(21) 到 29 的每个版本,但特别是在 Android N 上它不能正常工作。因此,在搜索了许多堆栈溢出答案并访问 https://issuetracker.google.com/issues/37123942 之后,我设法找到/修改了一个现在适用于所有 android 版本的解决方案。

    所以,首先我求助于https://gunhansancar.com/change-language-programmatically-in-android/ 来获得一个语言环境助手。然后我将其修改为我的需要,如下所示:

     object LocaleHelper {
        const val TAG = "LocaleHelper"
    
        fun updateLocale(base: Context): Context {
            Log.e(TAG, "updateLocale: called");
            val preferenceHelper = PreferenceHelper(base)
            preferenceHelper.getStringPreference(PreferenceHelper.KEY_LANGUAGE).let {
                return if (it.isNotEmpty()) {
                    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {
                        updateResources(base, it)
                    } else {
                        updateResourcesLegacy(base, it)
                    }
                } else {
                    base
                }
            }
        }
    
        private fun updateResources(base: Context, language: String): Context{
            val loc = Locale(language)
            Locale.setDefault(loc)
            val configuration = base.resources.configuration
            configuration.setLocale(loc)
            return base.createConfigurationContext(configuration)
        }
    
        @Suppress("DEPRECATION")
        private fun updateResourcesLegacy(base: Context, language: String): Context{
            val locale = Locale(language)
            Locale.setDefault(locale)
            val configuration = base.resources.configuration
            configuration.locale = locale
            configuration.setLayoutDirection(locale)
            base.resources.updateConfiguration(configuration, base.resources.displayMetrics)
    //                    Log.e(TAG, "updateLocale: returning for below N")
            return base
        }
    }
    

    然后,在基础活动中,重写 attachBaseContext 方法,如:

       /**
         * While attaching the base context, make sure to attach it using locale helper.
         * This helps in getting localized resources in every activity
         */
        override fun attachBaseContext(newBase: Context?) {
            var context = newBase
            newBase?.let {
                context = LocaleHelper.updateLocale(it)
            }
            super.attachBaseContext(context)
        }
    

    但我发现这曾经适用于牛轧糖及以上而不是棉花糖和棒棒糖。所以,我尝试了这个:

    override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            //Update the locale here before loading the layout to get localized strings.
            LocaleHelper.updateLocale(this)
    

    在基础活动的 oncreate 中,我调用了这个方法。它开始工作了。但是在某些情况下,如果我们从最近的任务中终止应用程序然后启动它,则第一个屏幕没有本地化。因此,为了解决这个问题,我在 app 类中创建了一个静态临时变量,用于保存初始 app 实例的值。

    companion object {
            val TAG = "App"
            var isFirstLoad = true
        }
    

    在我的应用程序的第一个屏幕中,我在 oncreate 中执行了此操作:

    override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            //Need to call this so that when the application is opened for first time and we need to update the locale.
            if(App.isFirstLoad) {
                LocaleHelper.updateLocale(this)
                App.isFirstLoad = false
            }
            setContentView(R.layout.activity_dash_board)
    

    现在我在 redmi 4a (Android 7) 上进行了测试,它以前无法在其上运行,sdk 21、23、27、29 的模拟器以及 redmi note 5 pro 和像素设备。在所有这些中,应用内语言选择都正常工作。

    抱歉,帖子太长了,但请建议优化方式! 谢谢!

    编辑 1: 所以我遇到了这个问题,它不能在 android 7.1 (sdk 25) 上运行。我向 gunhan 寻求帮助,然后进行了更改。

    override fun applyOverrideConfiguration(overrideConfiguration: Configuration?) {
        super.applyOverrideConfiguration(LocaleHelper.applyOverrideConfiguration(baseContext, overrideConfiguration))
    }
    

    我在我的 LocaleHelper 中添加了这个功能:

    fun applyOverrideConfiguration(base: Context, overrideConfiguration: Configuration?): Configuration? {
        if (overrideConfiguration != null && Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
            val uiMode = overrideConfiguration.uiMode
            overrideConfiguration.setTo(base.resources.configuration)
            overrideConfiguration.uiMode = uiMode
        }
        return overrideConfiguration
    }
    

    现在它终于开始工作了。 谢谢Gunhan!

    【讨论】:

    • 正是我想要的! ,它作为一个魅力,是 Localehelper 的一个很好的补丁。
    【解决方案4】:

    创建一个新类扩展 ContextWrapper

    public class MyContextWrapper extends ContextWrapper {
        public MyContextWrapper(Context base) {
            super(base);
        }
    
        @TargetApi(Build.VERSION_CODES.N)
        public static ContextWrapper wrap(Context context, Locale newLocale) {
            Resources res = context.getResources();
            Configuration configuration = res.getConfiguration();
    
            if (VersionUtils.isAfter24()) {
                configuration.setLocale(newLocale);
    
                LocaleList localeList = new LocaleList(newLocale);
                LocaleList.setDefault(localeList);
                configuration.setLocales(localeList);
    
                context = context.createConfigurationContext(configuration);
    
            } else if (VersionUtils.isAfter17()) {
                configuration.setLocale(newLocale);
                context = context.createConfigurationContext(configuration);
    
            } else {
                configuration.locale = newLocale;
                res.updateConfiguration(configuration, res.getDisplayMetrics());
            }
    
            return new ContextWrapper(context);
        }
    }
    

    覆盖 Activity 的 attachBaseContext 方法

    @Override
    protected void attachBaseContext(Context newBase) {
        Locale languageType = LanguageUtil.getLanguageType(mContext);
        super.attachBaseContext(MyContextWrapper.wrap(newBase, languageType));
    }
    

    完成活动并重新开始,新的语言环境将生效。

    演示:https://github.com/fanturbo/MultiLanguageDemo

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多