Android(安卓)多语言支持
添加多国语言文件
下面以添加 西班牙语 为例,首先在 res 右键 “new”,选择 “Values resource file”。
左侧选中 “Locale”,点击 “ >> ”,然后 “Chosen qualifiers” 面板中就会出现 “Locale” 这栏
西班牙语是 “es”,在 “Language” 面板中最开始是没有搜索框的,直接聚焦后输入,搜索框就会自动出现了。然后在右边的 “Specific Region Only” 面版中 选择 “Any Region”。
多语言表(转自 https://blog.csdn.net/u010586698/article/details/56673379)
语言 | 英语名称 | 中文名称 | 国家码 | 国标 | ||||
---|---|---|---|---|---|---|---|---|
Language | English Name | Chinese Name | Code | GB | ICS | JB | KK | L |
العربية | Arabic(Israel) | 阿拉伯语(以色列) | ar_IL | Y | Y | Y | Y | Y |
Arabic(Egypt) | 阿拉伯语(埃及) | ar_EG | GD_MN | GD_MN | GD_MN | GD_MN | GD_MN | |
中文 | Chinese Simplified | 中文简体 | zh_CN | Y | Y | Y | Y | Y |
中文 | Chinese Tradition | 中文繁体 | zh_TW | Y | Y | Y | Y | Y |
中文 | Chinese | 中文(香港) | zh_HK | N | N | P | P | Y |
Nederlands | Dutch (Netherlands) | 荷兰语 | nl_NL | Y | Y | Y | Y | Y |
Nederlands | Dutch (Netherlands) | 荷兰语(比利时) | nl_BE | GD_MN | GD_MN | GD_MN | GD_MN | GD_MN |
English | English | English(United States) | 英语(美国) | en_US | Y | Y | Y | Y |
English | English(Australia) | 英语(澳大利亚) | en_AU | GD_MN | GD_MN | GD_MN | GD_MN | GD_MN |
English | English(Canada) | 英语(加拿大) | en_CA | GD_MN | GD_MN | GD_MN | GD_MN | GD_MN |
English | English(India) | 英语(印度) | en_IN | GD_MN | GD_MN | GD_MN | GD_MN | GD_MN |
English | English(Ireland) | 英语(爱尔兰) | en_IE | GD_MN | GD_MN | GD_MN | GD_MN | GD_MN |
English | English(New Zealand) | 英语(新西兰) | en_NZ | GD_MN | GD_MN | GD_MN | GD_MN | GD_MN |
English | English(Singapore) | 英语(新加波) | en_SG | GD_MN | GD_MN | GD_MN | GD_MN | GD_MN |
English | English(South Africa) | 英语(南非) | en_ZA | GD_MN | GD_MN | GD_MN | GD_MN | GD_MN |
English | English(United Kingdom) | 英语(英国) | en_GB | GD_MN | GD_MN | GD_MN | Y | Y |
Français | French | 法语 | fr_FR | Y | Y | Y | Y | Y |
Français | French | 法语(比利时) | fr_BE | GD_MN | GD_MN | GD_MN | GD_MN | GD_MN |
Français | French | 法语(加拿大) | fr_CA | GD_MN | GD_MN | GD_MN | GD_MN | GD_MN |
Français | French | 法语(瑞士) | fr_CH | GD_MN | GD_MN | GD_MN | GD_MN | GD_MN |
Deutsch | German | 德语 | de_DE | Y | Y | Y | Y | Y |
Deutsch | German | 德语(列支敦斯登) | de_LI | GD_MN | GD_MN | GD_MN | GD_MN | GD_MN |
Deutsch | German | 德语(奥地利) | de_AT | GD_MN | GD_MN | GD_MN | Y | Y |
Deutsch | German | 德语(瑞士) | de_CH | GD_MN | GD_MN | GD_MN | GD_MN | GD_MN |
Italiano | Italian | 意大利语 | it_IT | Y | Y | Y | Y | Y |
Italiano | Italian | 意大利语(瑞士) | it_CH | GD_MN | GD_MN | GD_MN | GD_MN | GD_MN |
Protuguês | Portuguese | 葡萄牙语(巴西) | Pt_BR | Y | Y | Y | Y | Y |
Protuguês | Portuguese | 葡萄牙语 | Pt_PT | Y | Y | Y | Y | Y |
Español | Spanish | 西班牙语 | es_ES | Y | Y | Y | Y | Y |
Español | Spanish | 西班牙语(美国) | es_US | GD_MN | GD_MN | P | Y | Y |
বাংলা | Bengali | 孟加拉语 | bn_BD | Y | N | N | N | Y |
বাংলা | Bengali | 孟加拉语(印度) | bn_IN | Y | Y | Y | Y | Y |
hrvatski | Croatian | 克罗地亚语 | hr_HR | GD_MN | GD_MN | P | Y | Y |
čeština | Czech | 捷克语 | cs_CZ | Y | Y | Y | Y | Y |
Dansk | Danish | 丹麦语 | da_DK | GD_MN | GD_MN | P | Y | Y |
ελληνικά | Greek | 希腊语 | el_GR | GD_MN | Y | Y | Y | Y |
עברית | Hebrew | 希伯来语(以色列) | he_IL | GD_MN | GD_MN | GD_MN | GD_MN | GD_MN |
עברית | Hebrew | 希伯来语(以色列) | iw_IL | GD_MN | GD_MN | Y | Y | Y |
हिंदी | Hindi | 印度语 | hi_IN | Y | Y | Y | Y | Y |
Magyar | Hungarian | 匈牙利语 | hu_HU | GD_MN | Y | Y | Y | Y |
Indonesian | 印度尼西亚语 | in_ID | Y | Y | Y | Y | Y | |
日本語の言語 | Japanese | 日语 | ja_JP | GD_MN | GD_MN | P | P | Y |
한국의 | Korean | 韩语(朝鲜语) | ko_KR | GD_MN | GD_MN | Y | Y | Y |
Bahasa Melayu | Malay | 马来语 | ms_MY | Y | Y | Y | Y | Y |
فارسی | Perisan | 波斯语 | fa_IR | Y | Y | Y | Y | Y |
Polski | Polish | 波兰语 | Pl_PL | GD_MN | GD_MN | P | Y | Y |
româna | Romanian | 罗马尼亚语 | ro_RO | GD_MN | Y | Y | Y | Y |
Русский | Russian | 俄罗斯语 | ru_RU | Y | Y | Y | Y | Y |
српски | Serbian | 塞尔维亚语 | sr_RS | GD_MN | GD_MN | P | P | Y |
Svenska | Swedish | 瑞典语 | sv_SE | GD_MN | GD_MN | P | Y | Y |
ไทย | Thai | 泰语 | th_TH | Y | Y | Y | Y | Y |
Türkçe | Turkey | 土耳其语 | tr_TR | Y | Y | Y | Y | Y |
اردو | Urdu | 乌尔都语 | ur_PK | Y | Y | Y | Y | Y |
tiếng việt | Vietnamese | 越南语 | vi_VN | Y | Y | Y | Y | Y |
catalá | Catalan | 加泰隆语(西班牙) | ca_ES | GD_MN | GD_MN | P | P | Y |
latviešu | Latviesu | 拉脱维亚语 | lv_LV | GD_MN | GD_MN | P | Y | Y |
Lietuvių | Lithuanian | 立陶宛语 | lt_LT | GD_MN | GD_MN | P | Y | Y |
Norsk bokmal | Norwegian | 挪威语 | Nb_NO | GD_MN | GD_MN | P | Y | Y |
Slovenčina | slovencina | 斯洛伐克语 | sk_SK | GD_MN | GD_MN | P | Y | Y |
Slovenščina | Slovenian | 斯洛文尼亚语 | sl_SI | GD_MN | GD_MN | P | P | Y |
български | bulgarian | 保加利亚语 | bg_BG | GD_MN | GD_MN | P | Y | Y |
українська | Ukrainian | 乌克兰语 | uk_UA | GD_MN | GD_MN | P | Y | Y |
Tagalog | Filipino | 菲律宾语 | tl_PH | GD_MN | Y | Y | Y | Y |
Suomi | Finnish | 芬兰语 | fi_FI | GD_MN | GD_MN | P | Y | Y |
Afrikaans | Afrikaans | 南非语 | af_ZA | N | GD_MN | GD_MN | GD_MN | GD_MN |
Rumantsch | Romansh | 罗曼什语(瑞士) | rm_CH | GD_MN | GD_MN | GD_MN | GD_MN | GD_MN |
ဗမာ | Burmese(Zawgyi) | 缅甸语(民间) | my_ZG | N | N | P | P | Y |
ဗမာ | Burmese(Paduak) | 缅甸语(官方) | my_MM | N | N | Y | Y | Y |
ខ្មែរ | Khmer | 柬埔寨语 | km_KH | N | N | Y | Y | Y |
አማርኛ | Amharic | 阿姆哈拉语(埃塞俄比亚) | am_ET | N | GD_MN | GD_MN | GD_MN | GD_MN |
беларуская | Belarusian | 白俄罗斯语 | be_BY | N | GD_MN | GD_MN | GD_MN | GD_MN |
eesti | Estonian | 爱沙尼亚语 | et_EE | N | GD_MN | P | P | Y |
Kiswahili | Swahili | 斯瓦希里语(坦桑尼亚) | sw_TZ | N | GD_MN | GD_MN | GD_MN | GD_MN |
isiZulu | Zulu | 祖鲁语(南非) | zu_ZA | N | GD_MN | GD_MN | GD_MN | GD_MN |
azərbaycanca | Azerbaijani | 阿塞拜疆语 | az_AZ | N | N | N | GD_MN | GD_MN |
Հայերէն | Armenian | 亚美尼亚语(亚美尼亚) | hy_AM | N | N | P | P | Y |
ქართული | Georgian | 格鲁吉亚语(格鲁吉亚) | ka_GE | N | N | N | GD_MN | GD_MN |
ລາວ | Laotian | 老挝语(老挝) | lo_LA | N | N | N | GD_MN | GD_MN |
Монгол | Mongolian | 蒙古语 | mn_MN | N | N | N | GD_MN | GD_MN |
नेपाली | Nepali | 尼泊尔语 | ne_NP | N | N | N | GD_MN | GD_MN |
қазақ тілі | Kazakh | 哈萨克语 | kk_KZ | N | N | P | P | Y |
Galego | Galician | 加利西亚语 | gl-rES | N | N | N | N | GD_MN |
íslenska | Icelandic | 冰岛语 | is-rIS | N | N | N | N | GD_MN |
ಕನ್ನಡ | Kannada | 坎纳达语 | kn-rIN | N | N | N | N | GD_MN |
кыргыз тили; قىرعىز تىلى | Kyrgyz | 吉尔吉斯语 | ky-rKG | N | N | N | N | GD_MN |
മലയാളം | Malayalam | 马拉亚拉姆语 | ml-rIN | N | N | N | N | GD_MN |
मराठी | Marathi | 马拉提语/马拉地语 | mr-rIN | N | N | N | N | GD_MN |
தமிழ் | Tamil | 泰米尔语 | ta-rIN | N | N | N | N | GD_MN |
македонски јазик | Macedonian | 马其顿语 | mk-rMK | N | N | N | N | GD_MN |
తెలుగు | Telugu | 泰卢固语 | te-rIN | N | N | N | N | GD_MN |
Ўзбек тили | Uzbek | 乌兹别克语 | uz-rUZ | N | N | N | N | GD_MN |
Euskara | Basque | 巴斯克语 | eu-rES | N | N | N | N | GD_MN |
සිංහල | Sinhala | 僧加罗语(斯里兰卡) | si_LK | N | N | N | N | N |
下面我做一个支持默认、中文、英文、西班牙语的实例
分别创建如下文件
实现方案:
- 通过数组存储字符串的ID
arrays.xml
<?xml version="1.0" encoding="utf-8"?> - @string/language_default
- @string/language_zh
- @string/language_en
- @string/language_es
- 切换语言的工具类(安卓7为分界,有两种不同的处理方案)
LanguageUtil.java
public class LanguageUtil { private static final String SP_NAME = "LanguageSP"; private static final String LANGUAGE_INDEX = "LanguageIndex"; private static LanguageUtil instance; private Context context; private int[] languageIds; public static LanguageUtil getInstance(Context context) { if (instance == null) { instance = new LanguageUtil(context); } else { instance.context = context; } return instance; } private LanguageUtil(Context context) { this.context = context; final Resources resources = context.getResources(); TypedArray ta = resources.obtainTypedArray(R.array.language_ids); languageIds = new int[ta.length()]; for (byte i = 0; i < ta.length(); i++) { languageIds[i] = ta.getResourceId(i, 0); } } /** * 获取语言的字符串数组 */ private String[] getLanguages() { final String[] languages = new String[languageIds.length]; for (byte i = 0; i < languageIds.length; i++) { languages[i] = context.getString(languageIds[i]); } return languages; } /** * 根据索引拿到国际化区域类 */ private Locale getLocale(int index) { Locale locale; switch(languageIds[index]) { case R.string.language_en: locale = Locale.ENGLISH; break; case R.string.language_es: locale = new Locale("es"); break; case R.string.language_zh: locale = Locale.CHINA; break; default: if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { locale = LocaleList.getDefault().get(0); } else { locale = Locale.getDefault(); } break; } return locale; } /** * 在 API 24(Android 7.0)后需要在重启后替换 Context 才能切换语言 * ContextWrapper中提供了attachBaseContext()用于给ContextWrapper对象中指定真正的Context对象,调用ContextWrapper的方法都会被转向其所包含的真正的Context对象 */ private Context attachBaseContext(int index) { Resources resources = context.getResources(); Configuration configuration = resources.getConfiguration(); Locale locale = getLocale(index); configuration.setLocale(locale); if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { configuration.setLocale(locale); context = context.createConfigurationContext(configuration); } else { DisplayMetrics dm = resources.getDisplayMetrics(); resources.updateConfiguration(configuration, dm); } return context; } /** * 重启 Activity */ private void recreate(final Activity activity) {// activity.runOnUiThread(new Runnable() {// @Override// public void run() {//重启 Activity(这个方法一定要在主线程中被调用)//注意:recreate 这个方法有的问题,有时候会闪屏,就是一个界面跳两次出来//recreate之后不能finish,会崩溃// activity.recreate();// }// }); //重启 Activity Intent intent = new Intent(context, activity.getClass()); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); activity.startActivity(intent); activity.finish(); } private void saveIndexToPreference(int index) { SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor et = sp.edit(); et.putInt(LANGUAGE_INDEX, index); et.commit(); } private int getIndexFromPreference() { SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); return sp.getInt(LANGUAGE_INDEX, 0); } public void changeLanguage(int index, Activity activity) { saveIndexToPreference(index); attachBaseContext(index); recreate(activity); } /** * 每次Activity启动时都会去寻找默认的语言配置,所以必须在分配Context之前进行预处理,修改成你所需要的配置。 * 这个预处理方法就是 attachBaseContent */ public Context restoreLanguage() { int index = getIndexFromPreference(); return attachBaseContext(index); }}
- 所有Activity都必须继承一个基类,在基类的 attachBaseContext 方法中切换语言
BaseInternationalActivity.java
public abstract class BaseInternationalActivity extends AppCompatActivity { @Override protected void attachBaseContext(Context newBase) { newBase= LanguageUtil.getInstance(newBase).restoreLanguage(); super.attachBaseContext(newBase); }}
注意事项
上面的方法只会更新 activity 的 context,对于 application 和 service 的 context 并不会被改变。
虽然 application 和 service 也有 attachBaseContext,只会在初次打开时被替换。
而且这里只用了 activity.recreate() 影响的只会是当前的 Activity, 其它的 activity 启动的时候还是使用默认的 context。
所以如果要用到 service 里面的 context,我的做法是 比如在 bind 的时候重新设置它的context
attachBaseContext (转自 https://www.jianshu.com/p/94e0f9ab3f1d)
Context类本身是一个纯abstract类,它有两个具体的实现子类:ContextImpl和ContextWrapper。其中ContextWrapper类,如其名所言,这只是一个包装而已,ContextWrapper构造函数中必须包含一个真正的Context引用,同时ContextWrapper中提供了attachBaseContext()用于给ContextWrapper对象中指定真正的Context对象,调用ContextWrapper的方法都会被转向其所包含的真正的Context对象。
更多相关文章
- Android(安卓)APP 中英文切换
- Android遇上Kotlin | Kotlin若只如初见
- Android(安卓)客户端与服务端JSP相互传递中文
- Android学习笔记之解决下载中中文文件名问题
- Android(安卓)socket通信,读去中文汉字乱码问题解决。
- Android(安卓)中文 API(123) ―― AbsListView(二)
- Android(安卓)SDK 3.0正式放出 —— 我的使用感受(2/23更新)
- Android(安卓)Permission 中文说明
- TSwitch 中文简繁显示支持(XE6 Android)