preface

To be international, an app needs to support at least two languages, Chinese and English, and with Google’s update, Android will be able to set the preferred language for the current language. Therefore, this paper is based on this. The multi-language switching scheme is as follows: fixed text content of App, following system, Chinese and English, three kinds of switching, restart the App after selection to take effect;

This article code reference link, thanks to the original author ~ but directly using the link in the article tool class may have a little problem in the system compatibility, I in the project practice process to improve, share

Special note: The tool class is written by Java, the page in the project and the related Application class are written by Kotlin, then I may not express my ideas well, please understand

Specific steps

One, switch language code logic

1. Initialize in onCreate of the Application to determine which language is displayed in the app based on the locally saved multi-language option (user selected)

2. Select the appropriate language from the Language Settings screen, persist the language options, and restart the application (return to the first Activity).

The operations such as obtaining the preferred language of the system, setting language information, registering the Activity life cycle to listen for callbacks, and modifying the language Settings in the application can be used in a tool class. Here is the code for the multilanguage switch utility class: SPUtils is a utility class for SharedPreferences, an operation in the project

public class MultiLanguageUtils {

    /** * Modify in-app language Settings *@paramLanguage language *@param* / area area
    public static void changeLanguage(Context context,String language, String area) {
        if (TextUtils.isEmpty(language) && TextUtils.isEmpty(area)) {
            // If the language and locale are empty, follow the system
            SPUtils.getInstance().put(Constants.SP_LANGUAGE,"");
            SPUtils.getInstance().put(Constants.SP_LANGUAGE,"");
        } else {
            // Not empty, modify app language, persistent language options information
            Locale newLocale = newLocale(language, area); setAppLanguage(context,newLocale); saveLanguageSetting(context, newLocale); }}/** * Update the application language (core) *@param context
     * @param locale
     */
    private static void setAppLanguage(Context context, Locale locale) {
        Resources resources = context.getResources();
        DisplayMetrics metrics = resources.getDisplayMetrics();
        Configuration configuration = resources.getConfiguration();
        //Android 7.0 + method
        if (Build.VERSION.SDK_INT >= 24) {
            configuration.setLocale(locale);
            configuration.setLocales(new LocaleList(locale));
            context.createConfigurationContext(configuration);
            // updateConfiguration this method, though many bloggers say that version is invalid
            // But my production environment androidX+Android Q environment, must add this sentence, can restart the App to switch language
            resources.updateConfiguration(configuration,metrics);
           
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            //Android 4.1
            configuration.setLocale(locale);
            resources.updateConfiguration(configuration,metrics);
        } else{ configuration.locale = locale; resources.updateConfiguration(configuration,metrics); }}/** * follows the system language */
    public static Context attachBaseContext(Context context) {
        String spLanguage = SPUtils.getInstance().getString(Constants.SP_LANGUAGE , "");
        String spCountry = SPUtils.getInstance().getString(Constants.SP_COUNTRY,"");
        if(! TextUtils.isEmpty(spLanguage)&&! TextUtils.isEmpty(spCountry)){ Locale locale =new Locale(spLanguage, spCountry);
            setAppLanguage(context, locale);
        }
        return context;
    }

    /** * Check whether multilingual information stored in SharedPrefences is the same as that stored in app */
    public static boolean isSameWithSetting(Context context) {
        Locale locale = getAppLocale(context);
        String language = locale.getLanguage();
        String country = locale.getCountry();
        String sp_language = SPUtils.getInstance().getString(Constants.SP_LANGUAGE , "");
        String sp_country = SPUtils.getInstance().getString(Constants.SP_COUNTRY,"");
        if (language.equals(sp_language) && country.equals(sp_country)) {
            return true;
        } else {
            return false; }}/** * Save multilingual information to sp */
    public static void saveLanguageSetting(Context context, Locale locale) {
        SPUtils.getInstance().put(Constants.SP_LANGUAGE , locale.getLanguage());
        SPUtils.getInstance().put(Constants.SP_COUNTRY , locale.getCountry());
    }

    /** * get the application language */
    public static Locale getAppLocale(Context context){
        Locale local;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            local =context.getResources().getConfiguration().getLocales().get(0);
        } else {
            local =context.getResources().getConfiguration().locale;
        }
        return local;
    }

    /** * Get the system language */
    public static LocaleListCompat getSystemLanguage(a){
        Configuration configuration = Resources.getSystem().getConfiguration();
        LocaleListCompat locales = ConfigurationCompat.getLocales(configuration);
        return locales;
    }

    // Register the Activity lifecycle in the Application implementation class to listen for callbacks
    //registerActivityLifecycleCallbacks(callbacks);
    public static  Application.ActivityLifecycleCallbacks callbacks = new Application.ActivityLifecycleCallbacks() {
        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
          String language = SPUtils.getInstance().getString(Constants.SP_LANGUAGE , "");
          String country  = SPUtils.getInstance().getString(Constants.SP_COUNTRY  ,"");
          if(! TextUtils.isEmpty(language) && ! TextUtils.isEmpty(country)) {// Force an application language change
                if(! isSameWithSetting(activity)) { Locale locale =newLocale(language, country); setAppLanguage(activity,locale); }}}@Override
        public void onActivityStarted(Activity activity) {}@Override
        public void onActivityResumed(Activity activity) {}@Override
        public void onActivityPaused(Activity activity) {}@Override
        public void onActivityStopped(Activity activity) {}@Override
        public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}@Override
        public void onActivityDestroyed(Activity activity) {}};/** * Set language information ** This method is recommended in attachBaseContext and onConfigurationChange. AttachBaseContext ensures that the language information is modified when the page is loaded. OnConfigurationChange corresponds to the problem of updating a Resource during a vertical/horizontal switch@param context application context
     */
    public static void setConfiguration(Context context) {
        if (context == null) {
            return;
        }
        /* * To prevent passing in a non-applicationContext, we need to do a mandatory conversion to avoid onConfigurationChange problems. * Because onConfigurationChange is triggered and the system updates the Resources in ApplicationContext, it is possible that the language display will be inconsistent if the page contains Runtime resources * (resources dynamically loaded at Runtime). * /
        Context appContext = context.getApplicationContext();
        Locale preferredLocale = getSysPreferredLocale();
        Configuration configuration = appContext.getResources().getConfiguration();
        if (Build.VERSION.SDK_INT >= 17) {
            configuration.setLocale(preferredLocale);
        } else {
            configuration.locale = preferredLocale;
        }
        // Update the language Settings in the context
        Resources resources = appContext.getResources();
        DisplayMetrics dm = resources.getDisplayMetrics();
        resources.updateConfiguration(configuration, dm);
    }

    Note: this method gets the system preferred language ** that is actually set by the user and not adjusted by the API@return* /
    public static   Locale getSysPreferredLocale(a) {
        Locale locale;
        // get the default language of the system directly below 7.0
        if (Build.VERSION.SDK_INT < 24) {
            // Equivalent to context.getResources().getConfiguration().locale;
            locale = Locale.getDefault();
            // Get system preferred language above 7.0
        } else {
            /* * The following two methods are equivalent, GetResources ().getConfiguration().getLocales() * 1.context.getResources().getConfiguration().getLocales() * 2.LocaleList.getAdjustedDefault() */
            // Get the list of languages actually set by the user
            locale = LocaleList.getDefault().get(0);
        }
        returnlocale; }}Copy the code

Here are the methods (core procedures) that are called in the language Settings page, with detailed comments

ActivityUtils is a tool class for activities in the project. I used this code to close all activities and display the home page Activity to restart the Activity. You can do this in other ways

class MultiLanguageFragment : BaseFragment() {
    // Enumerate classes to determine user-specific options
     enum class MultiLanguage{
        FOLLOW_SYSTEM,
        SIMPLIFY_CHINESE,
        ENGLISH
    }
    private var flag = - 1

    override fun initViews(a) {
       tbChangeLanguage.addRightTextButton(getString(R.string.save), Constants.BUTTON_SAVE_MULTI_LANGUAGE )
            .setOnClickListener {
                // Record which language is selected, perform the language switch
               when(flag){
                  MultiLanguage.FOLLOW_SYSTEM.ordinal -> {
                     // Get the primary language of the mobile system
                     // There can be logic here. If the first language of the system is not provided by APP, then the system language is judged sequentially whether it conforms to the language provided by APP
                     val locale: Locale = MultiLanguageUtils.getSystemLanguage()[0]
                     val language: String = locale.language
                     val country: String = locale.country
                     // Change the in-app language to the phone system language
                     MultiLanguageUtils.changeLanguage(activity, language, country)
                     // Clear the SP data so that the application can keep the switch synchronously when the system switches languages
                     // Example: When the system is converted to English, the application language also becomes English
                     MultiLanguageUtils.changeLanguage(activity, null.null)
                     // Restart the APP to the first Activity
                     ActivityUtils.finishAllActivities()
                     ActivityUtils.startActivity(this.requireActivity()::class.java)
                    }
                   
                  // Select simplified Chinese
                  MultiLanguage.SIMPLIFY_CHINESE.ordinal -> {
                    MultiLanguageUtils.changeLanguage(this.requireActivity(), "zh"."ZH")
                    ActivityUtils.finishAllActivities()
                    ActivityUtils.startActivity(this.requireActivity()::class.java)
                   }
                  //选择English  
                  MultiLanguage.ENGLISH.ordinal -> {
                    MultiLanguageUtils.changeLanguage(_mActivity, "en"."US")
                    ActivityUtils.finishAllActivities()
                    ActivityUtils.startActivity(this.requireActivity()::class.java)
                  }
                   
                  else -> {
                        ToastUtils.showLong(getString(R.string.none_chosen_language))
                    }
                }
        }
    }
}
Copy the code

The implementation class of the Application in the App sets the language switch callback for each Activity to ensure that the application can be restarted after restarting

class App : Application() {

    override fun onCreate(a) {
        super.onCreate()
        // Multilingual Settings
        registerActivityLifecycleCallbacks(MultiLanguageUtils.callbacks)
        context = applicationContext
    }
Copy the code

2. Content to be prepared for language switch

This language content actually refers to the creation of the provided multilanguage switch ready resource folder, the most basic is the definition of multilanguage string content

Values -en, values-en, and …………Copy the code

Here is the new method: Right-click in the res folder new Android Resource File, select Locale in the first mullet, and click the >> button. In the second box, click Locale(?). , in the third box select the corresponding language, in the fourth box select the language locale

As shown in the figure below 👇

After creating Chinese and English resources, you can see them in the RES package, 👇

Generally speaking, strings defined in the values package of the system need to be interpreted in the language of strings with corresponding IDS in all values package strings synchronously; otherwise, the corresponding language interpretation may not be found after multi-language switching. The diagram below:

3. Extra chores

For example, according to the design diagram, some control with text specified size width and height, after language switch, may not accommodate the length of the text switch, if the plug-in to Chinese language directly translated into other languages, not processed in advance, there may be line feed, no display and other problems.

For example, as shown below:

In Simplified Chinese: The text of the first item in the bottom navigation bar is “Meeting Management”.

Switch to English: The first item “Meeting Management” in the bottom navigation bar is defined as Meeting Management in English. You can see that the original location of Meeting Management in Simplified Chinese is not enough, and ellipsis appears. (full) :

But goose design generally only a set, if you want to make more languages, XX control width need not write a fixed or switch language content after the impact of the page, if the language after the text will be beyond the scope, how to solve, these things to plan and communicate more ~

conclusion

In general, Android’s multi-language manual switching within the App is not too difficult to implement.

However, specific implementation details vary in specific projects, and this article provides a simple and general approach.

Thank you for watching