下面以添加 西班牙语 为例,首先在 res 右键 “new”,选择 “Values resource file”。
左侧选中 “Locale”,点击 “ >> ”,然后 “Chosen qualifiers” 面板中就会出现 “Locale” 这栏
西班牙语是 “es”,在 “Language” 面板中最开始是没有搜索框的,直接聚焦后输入,搜索框就会自动出现了。然后在右边的 “Specific Region Only” 面版中 选择 “Any Region”。
语言 | 英语名称 | 中文名称 | 国家码 | 国标 | ||||
---|---|---|---|---|---|---|---|---|
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 |
下面我做一个支持默认、中文、英文、西班牙语的实例
- @string/language_default
- @string/language_zh
- @string/language_en
- @string/language_es
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);
}
}
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
Context类本身是一个纯abstract类,它有两个具体的实现子类:ContextImpl和ContextWrapper。其中ContextWrapper类,如其名所言,这只是一个包装而已,ContextWrapper构造函数中必须包含一个真正的Context引用,同时ContextWrapper中提供了attachBaseContext()用于给ContextWrapper对象中指定真正的Context对象,调用ContextWrapper的方法都会被转向其所包含的真正的Context对象。