Android应用内语言切换失效:未添加系统语言时无法切换
Android应用内语言切换失效问题
我用以下代码实现Android应用内语言切换:当系统「添加语言」列表包含英语(en)和法语(fr)时,切换正常;但把对应语言从系统列表移除后,切换就失效了。按网上示例,无需系统添加语言也能实现应用内切换,请问哪里错了?怎么实现无需系统添加语言的应用内切换?
现有实现代码
LocalUtil.kt
object LocalUtil { fun applyLanguageContext(context: Context, locale: Locale?): Context { if (locale == null) return context if (locale == getLocale(context.resources.configuration)) return context return try { setupLocale(locale) val resources = context.resources val configuration = getOverridingConfig(locale, resources) updateResources(context, resources, configuration) context.createConfigurationContext(configuration) } catch (e: Exception) { e.printStackTrace() context } } private fun updateResources( context: Context, resources: Resources, config: Configuration ) { if (context.applicationContext !== context) { resources.updateConfiguration(config, resources.displayMetrics) } } private fun setupLocale(locale: Locale) { Locale.setDefault(locale) LocaleList.setDefault(LocaleList(locale)) } private fun getOverridingConfig(locale: Locale, resources: Resources): Configuration { val configuration = resources.configuration configuration.setLocales(LocaleList(locale)) return configuration } private fun getLocale(configuration: Configuration): Locale { return configuration.locales.get(0) } }
MyApp.kt
class MyApp: Application() { override fun getApplicationContext(): Context { val context = super.getApplicationContext() return LocalUtil.applyLanguageContext(context, Locale(LANGUAGE)) } override fun attachBaseContext(newBase: Context) { super.attachBaseContext(LocalUtil.applyLanguageContext(newBase, Locale(LANGUAGE))) } companion object { var LANGUAGE = "en" } }
MainActivity.kt
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } fun setChangeLangBtn(view: View) { val changeLangBtn: Button = findViewById(R.id.change_lang_btn) changeLangBtn.setOnClickListener { if (MyApp.LANGUAGE == "en") MyApp.LANGUAGE = "fr" else MyApp.LANGUAGE = "en" reloadActivity() } } fun reloadActivity() { val intent = Intent(this, javaClass).apply { addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION) } startActivity(intent) recreate() } override fun getBaseContext(): Context { return LocalUtil.applyLanguageContext(super.getBaseContext(), Locale(MyApp.LANGUAGE)) } override fun getApplicationContext(): Context { val context = super.getApplicationContext() return LocalUtil.applyLanguageContext(context, Locale(MyApp.LANGUAGE)) } override fun attachBaseContext(newBase: Context) { super.attachBaseContext(LocalUtil.applyLanguageContext(newBase, Locale(MyApp.LANGUAGE))) } }
问题原因
- 系统语言过滤机制:Android 7.0+ 会自动过滤系统未安装的语言资源,导致应用无法加载对应语言的
strings.xml文件。 - 废弃API使用:
resources.updateConfiguration已被废弃,且仅修改非ApplicationContext的资源不彻底。 - Context处理冗余:重复重写
getApplicationContext、getBaseContext会导致上下文混乱,反而影响语言切换生效。 - Activity重启逻辑冗余:同时调用
startActivity和recreate会导致不必要的页面重建,甚至冲突。
修正方案
1. 重构LocalUtil,绕过系统语言过滤
核心是通过createConfigurationContext创建独立的上下文,强制加载指定语言资源:
object LocalUtil { fun applyLanguageContext(context: Context, locale: Locale?): Context { if (locale == null) return context val currentLocale = getLocale(context.resources.configuration) if (locale == currentLocale) return context return try { // 强制设置默认Locale Locale.setDefault(locale) // 创建包含指定Locale的配置 val config = Configuration(context.resources.configuration).apply { setLocales(LocaleList(locale)) } // 返回包装后的上下文,绕过系统资源过滤 context.createConfigurationContext(config) } catch (e: Exception) { e.printStackTrace() context } } // 兼容高低版本的Locale获取 private fun getLocale(configuration: Configuration): Locale { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { configuration.locales[0] } else { @Suppress("DEPRECATION") configuration.locale } } }
2. 简化Application和Activity的Context处理
只需要在attachBaseContext中处理语言切换,无需重写其他Context获取方法:
MyApp.kt 修改
class MyApp : Application() { companion object { var LANGUAGE = "en" } override fun attachBaseContext(newBase: Context) { val targetLocale = Locale(LANGUAGE) super.attachBaseContext(LocalUtil.applyLanguageContext(newBase, targetLocale)) } }
MainActivity.kt 修改
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } fun setChangeLangBtn(view: View) { findViewById<Button>(R.id.change_lang_btn).setOnClickListener { MyApp.LANGUAGE = if (MyApp.LANGUAGE == "en") "fr" else "en" // 仅调用recreate即可完成页面重建 recreate() } } override fun attachBaseContext(newBase: Context) { val targetLocale = Locale(MyApp.LANGUAGE) super.attachBaseContext(LocalUtil.applyLanguageContext(newBase, targetLocale)) } }
3. 确保资源文件存在
在res目录下创建对应语言的资源文件夹,确保包含对应语言的字符串:
values-en/strings.xml(英语资源)values-fr/strings.xml(法语资源)
关键说明
- Android 7.0+ 系统的语言过滤机制会忽略系统未安装的语言,通过
createConfigurationContext创建的上下文可以绕过此限制,直接加载应用内的语言资源。 - 不要使用废弃的
resources.updateConfiguration方法,改用createConfigurationContext是官方推荐的正确方式。 - 仅在
attachBaseContext中处理语言切换即可,避免重写其他Context方法导致的上下文不一致问题。
内容的提问来源于stack exchange,提问作者PhilBlais
相关产品推荐
相关产品推荐

