我使用以下代码在我的应用中设置特定语言。语言将保存到应用内的SharedPreferences
。 它完全符合API级别23。 Android N SharedPreferences
效果也很好,它会返回正确的语言代码字符串,但不会更改区域设置(设置默认语言)电话)。可能有什么不对?
更新1:当我在Log.v("MyLog", config.locale.toString());
之后立即使用res.updateConfiguration(config, dm)
时,它会返回正确的区域设置,但应用的语言不会更改。
更新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
答案 0 :(得分:10)
创建一个新的类扩展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));
}
完成活动并再次启动,新的语言环境将生效。
答案 1 :(得分:3)
当我开始以SDK 29为目标时,我遇到了这个问题。我创建的LocaleHelper适用于从我的最小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中,我将此方法称为。它开始工作。但是在某些情况下,如果我们从最近的任务中杀死该应用程序然后启动它,则第一个屏幕未本地化。因此,为了解决这个问题,我在应用程序类中创建了一个静态temp变量,用于保存初始应用程序实例的值。
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和Pixel设备中进行了测试。在所有这些方面,应用程序内语言选择均能正常工作。
很长的帖子,很抱歉,但是请提出建议以优化方式! 谢谢!
编辑1: 所以我在Android 7.1(sdk 25)上无法正常工作时遇到了这个问题。我向甘汉寻求帮助,然后进行了更改。
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
}
现在终于可以了。 谢谢甘汉!
答案 2 :(得分:1)
This approach will work on all api level device, Make sure to recreate activity on changing language programatically.
1。对BaseBaseContext使用Base Activity来设置语言环境语言 并将此活动扩展到所有活动
open class BaseAppCompactActivity() : AppCompatActivity() {
override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(LocaleHelper.onAttach(newBase))
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
}
2. Use Application attachBaseContext and onConfigurationChanged to set the locale language
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) {
/ ***如果设备语言被更改,也可以处理语言** /
super.onConfigurationChanged(newConfig);
}
3. Use Locale Helper for handling language changes ,this approch work on all device
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
}
}
答案 3 :(得分:1)
我在使用 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)上测试了这个解决方案,它运行得很好:)