1. Train of thought???
How to realize the function of multi language switching?
Firstly, the basic operation flow is: the user selects the language, the multilingual input method adapts according to the language, and the interface adapts according to the selected language. So the question is, how does the input method apk choose the corresponding input method according to the current language? How to select a language to change the entire interface? The last question is how to adapt the layout interface when importing resource strings?
With these problems, we will solve them one by one, and finally realize the function of multilingualism.
2. First find the keyboard - > solve the input method problem
In view of the fact that the project cannot be connected to the Internet and can only be on the existing platform, many domestic input methods, such as Baidu input method and Sogou input method, cannot be used. So I thought about climbing the wall to find the input method in Google mall. Fortunately, I was very lucky to find Gborad. This input method can switch multiple languages without networking, which is very convenient, but one bad thing is that this input method has a large memory.
Conclusion: there are many ways to find resources. Generally, there will be more foreign resources.
3. How to realize the multi language switching function?
Q: how does the input method apk select the corresponding input method according to the current language???
Answer: to install the input method, how to get the input name? How to install? What is the installation idea? First of all, we should be clear and judge first
Is there an input method currently? If not, install it again. The installation input method is to use the command line to install. The files should be copied from the assets directory to the installable path of the sd card
Go install. After installation, different input methods should be adapted according to the language of the current system, such as Google Pinyin input method for Chinese interface and g board input for other languages
Law.
/** * Install Chinese input method */ public static void installChineseInputSoft() { ArrayList<String> inputMethodList = new ArrayList<>(); InputMethodManager imm = (InputMethodManager) Utils.getApp().getSystemService(Context.INPUT_METHOD_SERVICE); List<InputMethodInfo> mInputMethodProperties = imm.getInputMethodList(); for (int i = 0; i < mInputMethodProperties.size(); i++) { inputMethodList.add(mInputMethodProperties.get(i).getId()); } LogUtils.dTag(TAG, inputMethodList);//Find the name of the input method installed in the current system, which will be used later //First, judge whether there is a Chinese input method, if (inputMethodList.contains("com.google.android.inputmethod.pinyin/.PinyinIME")) { return; } // If not, judge whether there is Google pingyin.apk in the config directory String pingYinApkName = "googlepinyin.apk"; String configApkName = PathConstants.APK_INSTALL_SDCARD_PATH + File.separator + pingYinApkName; if (FileUtils.isFileExists(configApkName)) { runInstallApk(configApkName); return; } // If there is no APK, copy and paste ThreadUtils.executeByIo(new ThreadUtils.SimpleTask<Void>() { @Override public Void doInBackground() throws Throwable { boolean isCopy = ResourceUtils.copyFileFromAssets(pingYinApkName, configApkName); LogUtils.dTag(TAG, "APK Copy results:" + isCopy); //After copying, perform script installation if (isCopy) { runInstallApk(configApkName); } return null; } @Override public void onSuccess(Void aVoid) { } }); } /** * Installing Gboard IME */ public static void installGboradInputSoft() { ArrayList<String> inputMethodList = new ArrayList<>(); InputMethodManager imm = (InputMethodManager) Utils.getApp().getSystemService(Context.INPUT_METHOD_SERVICE); List<InputMethodInfo> mInputMethodProperties = imm.getInputMethodList(); for (int i = 0; i < mInputMethodProperties.size(); i++) { inputMethodList.add(mInputMethodProperties.get(i).getId()); } //First, judge whether there is a Gboard input method, if (inputMethodList.contains("com.google.android.inputmethod.latin/com.android.inputmethod.latin.LatinIME")) { return; } // If not, judge whether there is Google pingyin.apk in the config directory String pingYinApkName = "Gboard.apk"; String configApkName = PathConstants.APK_INSTALL_SDCARD_PATH + File.separator + pingYinApkName; if (FileUtils.isFileExists(configApkName)) { runInstallApk(configApkName); return; } // If there is no APK, copy and paste ThreadUtils.executeByIo(new ThreadUtils.SimpleTask<Void>() { @Override public Void doInBackground() throws Throwable { boolean isCopy = ResourceUtils.copyFileFromAssets(pingYinApkName, configApkName); LogUtils.dTag(TAG, "APK Copy results:" + isCopy); //After copying, perform script installation if (isCopy) { runInstallApk(configApkName); } return null; } @Override public void onSuccess(Void aVoid) { } }); } private static void runInstallApk(String configApkName) { long beforeTime = System.currentTimeMillis(); //If the APK is available, install it silently String installCommand = "pm install -r " + configApkName; ShellUtils.execCmdAsync(installCommand, false, new Utils.Consumer<ShellUtils.CommandResult>() { @Override public void accept(ShellUtils.CommandResult commandResult) { long useTime = System.currentTimeMillis() - beforeTime; LogUtils.dTag(TAG, "Installation results:" + commandResult + ",errorMsg:" + commandResult.errorMsg + ",time=" + useTime); //It is possible that the installation is not successful. At this time, you should delete the APK to avoid that the installation will not succeed in the future if (commandResult.result != 0 || !"Success".equals(commandResult.successMsg)) { boolean deleteApk = FileUtils.delete(configApkName); LogUtils.dTag(TAG, "Installation was unsuccessful:delete apk" + deleteApk); } //After the installation, switch the language switchInputSoftKeyboard(); } }); }
Q: how to choose the input method???
A: just install the input method. However, the purpose has not been achieved. After installing the input method, you should switch the input method according to the current system configuration. At present, there are only three input methods: the system comes with, Chinese and other countries. How to adapt to other countries? At this time, some customized configuration variables will be used.
First, define an interface of system related configuration constants, which contains some constants of language configuration for switching between multiple languages.
public interface ConfigConstants { /** * The default language is English, and the subscript is 0, which corresponds to the system in arrays_ language */ int LANGUAGE_DEFAULT = 0; /** * Simplified Chinese, subscript 1 */ int LANGUAGE_SIMPLIFIED_CHINESE = 1; /** * Brazilian Portuguese */ int LANGUAGE_BRAZILIAN_PORTUGUESE = 2; /** * French */ int LANGUAGE_FRENCH = 3; /** * Spanish */ int LANGUAGE_SPANISH = 4; /** * Russian */ int LANGUAGE_RUSSIAN = 5; /** * sp Name of */ String SP_NAME = "system_config"; /** * Currently set language */ String SP_KEY_LANGUAGE = "language"; }
Then switch the soft keyboard according to the system related configuration constants.
The software disk problem has been solved, and the next step is the interface problem./** * Get the language of the current system configuration, subscript and system in arrays_ Language Association * * @return The language set by the current system. The default value is 0, which means English */ public static int getCurrentLanguageConfig() { return SPUtils.getInstance(ConfigConstants.SP_NAME).getInt(ConfigConstants.SP_KEY_LANGUAGE, 0); } /** * Toggle soft keyboard */ public static void switchInputSoftKeyboard() { try { ArrayList<String> Array_mInputMethodId = new ArrayList<>(); InputMethodManager imm = (InputMethodManager) Utils.getApp().getSystemService(Context.INPUT_METHOD_SERVICE); List<InputMethodInfo> mInputMethodProperties = imm.getInputMethodList(); for (int i = 0; i < mInputMethodProperties.size(); i++) { Array_mInputMethodId.add(mInputMethodProperties.get(i).getId()); } LogUtils.dTag(TAG, Array_mInputMethodId);//Find the input method name installed on the current system switch (getCurrentLanguageConfig()) {//Switch the input method according to the current system configuration case ConfigConstants.LANGUAGE_SIMPLIFIED_CHINESE: // if (Array_mInputMethodId.contains("com.iflytek.inputmethod/.FlyIME")) { if (Array_mInputMethodId.contains("com.google.android.inputmethod.pinyin/.PinyinIME")) { Settings.Secure.putString(Utils.getApp().getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD , "com.google.android.inputmethod.pinyin/.PinyinIME"); // , "com.iflytek.inputmethod/.FlyIME"); } else if (Array_mInputMethodId.contains("com.android.inputmethod.latin/.LatinIME")) { Settings.Secure.putString(Utils.getApp().getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD , "com.android.inputmethod.latin/.LatinIME"); } break; case ConfigConstants.LANGUAGE_BRAZILIAN_PORTUGUESE: case ConfigConstants.LANGUAGE_FRENCH: case ConfigConstants.LANGUAGE_SPANISH: case ConfigConstants.LANGUAGE_RUSSIAN: if (Array_mInputMethodId.contains("com.android.inputmethod.latin/.LatinIME")) { Settings.Secure.putString(Utils.getApp().getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD , "com.android.inputmethod.latin/.LatinIME"); } break; case ConfigConstants.LANGUAGE_DEFAULT: default: if (Array_mInputMethodId.contains("com.android.inputmethod.latin/.LatinIME")) { Settings.Secure.putString(Utils.getApp().getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD , "com.android.inputmethod.latin/.LatinIME"); } break; } } catch (Exception e) { LogUtils.eTag(TAG, e.toString()); } }
Q: how to select a language to change the entire interface?
Answer: first understand Android multilingual international adaptation (compatible with 7.0) This blog is written in great detail to understand the whole process of multilingual switching. Then back to our question: you need to use the class Locale to set the system string resources (Locale is a class in JavaSE that represents the type of local language and can be used for calendar, number and string localization). Load the string resources (get) according to the currently set language, set the configuration language, and finally save the language.
/** * Get the language of the current system configuration, subscript and system in arrays_ Language Association * * @return The language set by the current system. The default value is 0, which means English */ public static int getCurrentLanguageConfig() { return SPUtils.getInstance(ConfigConstants.SP_NAME).getInt(ConfigConstants.SP_KEY_LANGUAGE, 0); } /** * Gets the current language configuration * * @return The locale corresponding to the configuration attribute is English by default */ public static Locale getCurrentLocale() { switch (getCurrentLanguageConfig()) { case ConfigConstants.LANGUAGE_DEFAULT: return Locale.US; case ConfigConstants.LANGUAGE_SIMPLIFIED_CHINESE: return Locale.SIMPLIFIED_CHINESE; case ConfigConstants.LANGUAGE_BRAZILIAN_PORTUGUESE: return new Locale("pt", "BR"); case ConfigConstants.LANGUAGE_FRENCH: return new Locale("fr", "FR"); case ConfigConstants.LANGUAGE_SPANISH: return new Locale("es", "ES"); case ConfigConstants.LANGUAGE_RUSSIAN: return new Locale("ru", "RU"); } return Locale.US; } /** * Set the language currently saved by the system * * @param context context */ public static void setSystemLanguage(Context context) { updateLanguage(context, getLocale(getCurrentLanguageConfig())); } /** * Update and set language * * @param context context * @param locale Configuration object */ private static void updateLanguage(Context context, Locale locale) { Resources resources = context.getResources(); Configuration config = resources.getConfiguration(); Locale contextLocale = config.locale; // Switch system language changeSystemLanguage(locale); LogUtils.dTag("language", "update language:" + contextLocale.getDisplayName()); if (!isSameLocale(contextLocale, locale)) { DisplayMetrics dm = resources.getDisplayMetrics(); config.setLocale(locale); if (context instanceof Application) { Context newContext = context.createConfigurationContext(config); try { Field mBaseField = ContextWrapper.class.getDeclaredField("mBase"); mBaseField.setAccessible(true); mBaseField.set(context, newContext); } catch (Exception var8) { var8.printStackTrace(); } } resources.updateConfiguration(config, dm); LogUtils.dTag("language", "Updated language:" + config.locale); } } private static boolean isSameLocale(Locale l0, Locale l1) { return StringUtils.equals(l1.getLanguage(), l0.getLanguage()) && StringUtils.equals(l1.getCountry(), l0.getCountry()); } /** * @param locale The system language is set through the externally set language. In order to meet the requirements of adapting the keyboards of different countries when switching different languages * Later, it was found that after switching the system language, you can only use the input method provided by the A ndroid system, and the system will adapt to different countries, so as to select the input method for countries */ private static void changeSystemLanguage(Locale locale) { if (locale != null) { try { Class classActivityManagerNative = Class.forName("android.app.ActivityManagerNative"); Method getDefault = classActivityManagerNative.getDeclaredMethod("getDefault"); Object objIActivityManager = getDefault.invoke(classActivityManagerNative); Class classIActivityManager = Class.forName("android.app.IActivityManager"); Method getConfiguration = classIActivityManager.getDeclaredMethod("getConfiguration"); Configuration config = (Configuration) getConfiguration.invoke(objIActivityManager); config.setLocale(locale); //config.userSetLocale = true; Class clzConfig = Class.forName("android.content.res.Configuration"); java.lang.reflect.Field userSetLocale = clzConfig.getField("userSetLocale"); userSetLocale.set(config, true); Class[] clzParams = {Configuration.class}; Method updateConfiguration = classIActivityManager.getDeclaredMethod("updateConfiguration", clzParams); updateConfiguration.invoke(objIActivityManager, config); BackupManager.dataChanged("com.android.providers.settings"); } catch (Exception e) { LogUtils.dTag(TAG, "changeSystemLanguage: " + e.getLocalizedMessage()); } } } /** * Saves the language of the current selection/** * Saves the language of the current selection * * @param languageConfig Language to be set, 0 English, 1 Chinese */ public static void saveSystemLanguage(int languageConfig) { SPUtils.getInstance(ConfigConstants.SP_NAME).put(ConfigConstants.SP_KEY_LANGUAGE, languageConfig, true); } * * @param languageConfig Language to be set,0 English, 1 Chinese */ public static void saveSystemLanguage(int languageConfig) { SPUtils.getInstance(ConfigConstants.SP_NAME).put(ConfigConstants.SP_KEY_LANGUAGE, languageConfig, true); } //Implement the following methods in the base class Activity @Override public void onConfigurationChanged(Configuration newConfig) { newConfig.setLocale(ResUtils.getCurrentLocale()); super.onConfigurationChanged(newConfig); ResUtils.setSystemLanguage(this); } @Override protected void attachBaseContext(Context newBase) { ResUtils.setSystemLanguage(newBase); }
I still don't know much about many details. I'll update this article later.
4. Import multilingual resource strings
There is a problem in this process, for some European countries. The decimal point in mathematics is represented by "," in their country. After switching to the French interface, the system will automatically convert the decimal point into ",". After querying the reason, it is found that the problem caused by calling NumberUtil.format without considering the country region switching. The final solution is to use Locale.ENGLISH uniformly and display the data in decimal point format.
My solution is to fix the region when calling NumberUtil: Locale.ENGLISH
private static final ThreadLocal<DecimalFormat> DF_THREAD_LOCAL = new ThreadLocal<DecimalFormat>() { protected DecimalFormat initialValue() { return (DecimalFormat)NumberFormat.getInstance(Locale.ENGLISH); } }; }
5. Reflection???!!!
In retrospect, multi language switching involves system aspects, such as the adaptation of multi language input methods. You can first consider two aspects. Try to use the system change area to see whether the input method of the system can be changed. If this method fails, you can use external resources, such as importing other multi languages, to solve the problem of mismatch of switching language input methods.
Because I know little about Android, I took a slow road before reaching the end. In the process, I accidentally found a fast path. When I finished this short section of fast road, I fell into a small pit at the end of the time difference.
Even after the development of a small function, I still have many vague concepts. If I start from scratch, I still don't know how to write. It can be seen that my own programming ability is very limited, and I lack of mastery. I know more and don't know more.
The process of development is like this. We will encounter all kinds of unexpected problems. The key is whether we can persist consistently and patiently and believe that we have the thinking and ability to solve problems.
Program life will encounter various problems and obstacles on this road, some can be solved, some can't be solved temporarily, sometimes the idea is clear, sometimes a mess, just as the code is written by people, life comes out by itself. Since you can optimize the code and solve bug s, isn't life the same?
I hope everyone can take root and sprout in the field they love.