问题
I'm using the following code to set specific language in my app. Language is saved into SharedPreferences
within the app. And it works perfectly up to API level 23. With Android N SharedPreferences
works well too, it returns the correct language code-string, but it does not change the locale (sets default language of the phone). What could be wrong?
Update 1: When I use Log.v("MyLog", config.locale.toString());
immediately after res.updateConfiguration(config, dm)
it returns correct locale, but language of the app does not changed.
Update 2: I also mentioned that if I change locale and then restart the activity (using new intent and finish on the old one), it changes the language properly, it even shows correct language after rotation. But when I close the app and open it again, I get default language. It's weird.
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);
//...
}
//...
}
Update 3: THE ANSWER IS ALSO HERE: https://stackoverflow.com/a/40849142/3935063
回答1:
Create a new class extends 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);
}
}
override Activity's attachBaseContext method
@Override
protected void attachBaseContext(Context newBase) {
Locale languageType = LanguageUtil.getLanguageType(mContext);
super.attachBaseContext(MyContextWrapper.wrap(newBase, languageType));
}
finish the activity and start it again,new locale will become effective.
demo:https://github.com/fanturbo/MultiLanguageDemo
回答2:
This approach will work on all api level device, Make sure to recreate activity on changing language programatically.
1.Use Base Activity for attachBaseContext to set the locale language And extend this activity for all activities
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) {
/*** also handle chnage language if device language chnaged **/
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
}
}
来源:https://stackoverflow.com/questions/38997356/change-language-programmatically-android-n-7-0-api-24