Sample code for Android multilingual adaptation (7.0 + compatible)

  • 2021-08-21 21:33:08
  • OfStack

1. Preface

1. Android itself provides a framework and API for multilingual adaptation. We can just use it.

2. The language change must be recreate Activity. At present, there is no way not to rebuild. Commonly used App is also reconstructed and can be seen.

3. Compatibility issues. Now that more and more devices are Android 7.0 +, the Android version of new phones will be higher (Android 8.0 +), so adaptation is necessary.

4. At present, most related articles on the Internet are incompatible with 7.0 +, and there are a lot of specific methods.

2. Specific practices

1. Multilingual files

Folder naming refers to the following blog (there are many on the Internet):

Multilingual value folder naming

value puts English resource files by default, and the simplified Chinese folder is named values-zh-rCN. The settings that do not need translation translatable are as follows:


<string name="app_name_english" translatable="false">You App English Name</string>

2. Multilingual tool classes


public class LanguageUtils {
 public static final String CHINESE_SIMPLE = "zh_CN";
 public static final String ENGLISH = "en";
 public static final String AUTO = "auto";
 private static final String TAG = "LanguageUtils";
 //public static final String[] LOCALES = Utils.getContext().getResources().getStringArray(R.array.locales);

 private LanguageUtils() {
  throw new UnsupportedOperationException("u can't instantiate me...");
 }

 public static void setSystemDefaultLocale(Locale locale) {

 }

 public static boolean isSetValue(Context context) {
  Locale currentLocale = context.getResources().getConfiguration().locale;
  return currentLocale.equals(getSetLocale());
 }

 private static Locale getSetLocale() {
  String locale = SPUtils.getInstance(BaseConstants.SP.NAME_APP_SETTINGS).getString(BaseConstants.SP.KEY_LANGUAGE, LanguageUtils.AUTO);
  if (locale.equals(LanguageUtils.AUTO)) {
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    return Resources.getSystem().getConfiguration().getLocales().get(0);// Fixed the problem of getting system default error 
   } else {
    return Locale.getDefault();
   }
  }
  String[] array = locale.split("_");
  String language = array[0];
  if (array.length > 1) {
   String country = array[1];
   return new Locale(language, country);
  }
  return new Locale(language);
 }

 public static int getSetIndex() {
  String languageSet = SPUtils.getInstance(BaseConstants.SP.NAME_APP_SETTINGS).getString(BaseConstants.SP.KEY_LANGUAGE, LanguageUtils.AUTO);
  int localeIndex = 0;
  switch (languageSet) {
   case LanguageUtils.AUTO:
    localeIndex = 0;
    break;
   case LanguageUtils.CHINESE_SIMPLE:
    localeIndex = 1;
    break;
   case LanguageUtils.ENGLISH:
    localeIndex = 2;
    break;
  }
  return localeIndex;
 }

 public static Context wrapContext(Context context) {
  Resources resources = context.getResources();
  Locale locale = LanguageUtils.getSetLocale();

  Configuration configuration = resources.getConfiguration();
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
   configuration.setLocale(locale);
   LocaleList localeList = new LocaleList(locale);
   LocaleList.setDefault(localeList);
   configuration.setLocales(localeList);
  } else {
   configuration.setLocale(locale);
  }
  return context.createConfigurationContext(configuration);
 }

 public static void applyChange(Context context) {
  Resources res = context.getResources();
  DisplayMetrics dm = res.getDisplayMetrics();
  Configuration conf = res.getConfiguration();

  Locale locale = getSetLocale();
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
   conf.setLocale(locale);
   LocaleList localeList = new LocaleList(locale);
   LocaleList.setDefault(localeList);
   conf.setLocales(localeList);
  } else {
   conf.setLocale(locale);
  }
  res.updateConfiguration(conf, dm);
 }
}

3. Code analysis & Compatible with 7.0 +

3.1. How to get the language settings of the system, that is, 7.0 + If you choose auto, you can switch correctly.


if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    return Resources.getSystem().getConfiguration().getLocales().get(0);// Fixed the problem of getting system default error 
   } else {
    return Locale.getDefault();
   }

If you read this article, you may have read many other related articles on the Internet. You should know that there is a strange thing about 7.0 + system:

If you switch the language (say English) in app and the language and system settings (say Chinese) are different, then when you switch the language again and select auto, it will be wrong to get it through Locale. getDefault (), or it will be wrong to get it through LocaleList. get (0), and the ranking of your previously selected language (English) has been advanced. The solution of some articles is to persist the system settings when app is turned on, so that switching the language of app will not affect your access to the system settings, but this is unnecessary and too troublesome (I should not know the above method).

7.0 + system settings also see the difference, before, set the system language can be directly selected, now you have to add first, and then sort, ranked in the first is the system display language!

3.2. Write an BaseActivity as the parent class of all Activity

Create a new BaseActivity for inheritance, override:


@Override
 protected void attachBaseContext(Context newBase) {
  super.attachBaseContext(LanguageUtils.wrapContext(newBase));
 }

Then after switching languages, you want recreate Activity. Where this is called depends on the specific requirements. You can empty the stack like WeChat, and then restart it directly to the main interface, or you can set the interface recreate, but other Activity in the stack should also find a way to notify recreate.

3.3. Shielding system settings change

If the language option of app is not auto, when the system language setting is modified, app should not change with the system, but be displayed according to its own set language. Write a class that inherits from Application (note that it should be configured in manifest, otherwise it will be invalid)


public class MyApp extends Application {
 private Configuration deltaConfig;

@Override
 public void onConfigurationChanged(Configuration newConfig) {
  LogUtils.d(TAG, " Called onConfigurationChanged");
  int diff = newConfig.diff(deltaConfig);
  String languageSet = SPUtils.getInstance(AppConstants.SP.NAME_APP_SETTINGS).getString(AppConstants.SP.KEY_LANGUAGE, LanguageUtils.AUTO);
  if (languageSet.equals(LanguageUtils.AUTO)) {// Look app Is the language setting auto, If it is, it doesn't matter, directly super
   super.onConfigurationChanged(newConfig);
   deltaConfig = newConfig;
  } else if (diff != ActivityInfo.CONFIG_LOCALE) {// This Configuration Change is not language, and if not, it doesn't matter 
   super.onConfigurationChanged(newConfig);
   deltaConfig = newConfig;
  }
   // The system settings language is invalid here 
   // Equivalent to omitting 
   //else{
   // return;
   //}
 }

 @Override
 public void onCreate() {
  super.onCreate();
  //app Record system settings when open 
  deltaConfig = getApplicationContext().getResources().getConfiguration();
  LanguageUtils.applyChange(getApplicationContext());
  }
 }
}

3.4. Other issues

Context for Application will also be updated


LanguageUtils.applyChange(context);
LanguageUtils.applyChange(context.getApplicationContext());

But even so, there are still some problems, mainly because:

If the Title of Activity is defined in manifest, the following label:


<activity
   android:name=".ui.activity.AboutActivity"
   android:launchMode="singleTop"
   android:label="@string/lable_activity_about"
   android:theme="@style/AppTheme.NoActionBar"/>

Then, even if you update ApplicationContext, some Activity may not work, and it doesn't work at all, so this can't be reproduced (it's fascinating). I don't know if the system bug (the test system is 1 plus 3 hydrogen OS 8.0), or is there a better way to write it?

To solve this problem, just use setTitle () in activity oncreate (). There will be no problem in this way.


Related articles: