国际化不仅包含多语言翻译,还包含各地的使用习惯和习俗等。软件语言的切换选项在代码实现中的唯一标识符参照:
每种语言代码由两个小写字母表示(由 ISO-639
标准定义)每个国家/地区代码由两个大写字母表示(由 ISO-3166
标准定义)。
序号 | 语言_国家 | 语言ID |
---|---|---|
1 |
南非荷兰语 |
af |
2 |
阿拉伯语(阿拉伯联合酋长国) |
ar_AE |
3 |
阿拉伯语(巴林) |
ar_BH |
4 |
阿拉伯语(阿尔及利亚) |
ar_DZ |
5 |
阿拉伯语(埃及) |
ar_EG |
6 |
阿拉伯语(伊拉克) |
ar_IQ |
7 |
阿拉伯语(约旦) |
ar_JO |
8 |
阿拉伯语(科威特) |
ar_KW |
9 |
阿拉伯语(黎巴嫩) |
ar_LB |
10 |
阿拉伯语(利比亚) |
ar_LY |
11 |
阿拉伯语(摩洛哥) |
ar_MA |
12 |
阿拉伯语(阿曼) |
ar_OM |
13 |
阿拉伯语(卡塔尔) |
ar_QA |
14 |
阿拉伯语(沙特阿拉伯) |
ar_SA |
15 |
阿拉伯语(叙利亚) |
ar_SY |
16 |
阿拉伯语(突尼斯) |
ar_TN |
17 |
阿拉伯语(也门) |
ar_YE |
18 |
贝劳语 |
be |
19 |
保加利亚语 |
bg |
20 |
加泰罗尼亚语 |
ca |
21 |
捷克语 |
cs |
22 |
丹麦语 |
da |
23 |
德语(标准) |
de |
24 |
德语(奥地利) |
de_AT |
25 |
德语(瑞士) |
de_CH |
26 |
德语(列支敦士登) |
de_LI |
27 |
德语(卢森堡) |
de_LU |
28 |
希腊语 |
el |
29 |
英语 |
en |
30 |
英语(加勒比) |
en |
31 |
英语(澳大利亚) |
en_AU |
32 |
英语(伯利兹) |
en_BZ |
33 |
英语(加拿大) |
en_CA |
34 |
英语(英国) |
en_GB |
35 |
英语(爱尔兰) |
en_IE |
36 |
英语(牙买加) |
en_JM |
37 |
英语(新西兰) |
en_NZ |
38 |
英语(特立尼达) |
en_TT |
39 |
英语(美国) |
en_US |
40 |
英语(南非) |
en_ZA |
41 |
西班牙语(西班牙传统) |
es |
42 |
西班牙语(西班牙现代) |
es |
43 |
西班牙语(阿根廷) |
es_AR |
44 |
西班牙语(玻利维亚) |
es_BO |
45 |
西班牙语(智利) |
es_CL |
46 |
西班牙语(哥伦比亚) |
es_CO |
47 |
西班牙语(哥斯达黎加) |
es_CR |
48 |
西班牙语(多米尼加共和国) |
es_DO |
49 |
西班牙语(厄瓜多尔) |
es_EC |
50 |
西班牙语(危地马拉) |
es_GT |
51 |
西班牙语(洪都拉斯) |
es_HN |
52 |
西班牙语(墨西哥) |
es_MX |
53 |
西班牙语(尼加拉瓜) |
es_NI |
54 |
西班牙语(巴拿马) |
es_PA |
55 |
西班牙语(秘鲁) |
es_PE |
56 |
西班牙语(波多黎各) |
es_PR |
57 |
西班牙语(巴拉圭) |
es_PY |
58 |
西班牙语(萨尔瓦多) |
es_SV |
59 |
西班牙语(乌拉圭) |
es_UY |
60 |
西班牙语(委内瑞拉) |
es_VE |
61 |
爱沙尼亚语 |
et |
62 |
巴斯克语 |
eu |
63 |
波斯语 |
fa |
64 |
芬兰语 |
fi |
65 |
法罗语 |
fo |
66 |
法语(标准) |
fr |
67 |
法语(比利时) |
fr_BE |
68 |
法语(加拿大) |
fr_CA |
69 |
法语(瑞士) |
fr_CH |
70 |
法语(卢森堡) |
fr_LU |
71 |
盖尔语(苏格兰) |
gd |
72 |
盖尔语(爱尔兰) |
gd_IE |
73 |
希伯来语 |
he |
74 |
北印度语 |
hi |
75 |
克罗地亚语 |
hr |
76 |
匈牙利语 |
hu |
77 |
印度尼西亚语 |
in |
78 |
冰岛语 |
is |
79 |
意大利语(标准) |
it |
80 |
意大利语(瑞士) |
it_CH |
81 |
日语 |
ja |
82 |
依地语 |
ji |
83 |
朝鲜语 |
ko |
84 |
朝鲜语(韩国) |
ko |
85 |
立陶宛语 |
lt |
86 |
拉脱维亚语 |
lv |
87 |
FYRO |
mk |
88 |
马来西亚语 |
ms |
89 |
马耳他语 |
mt |
90 |
荷兰语(标准) |
nl |
91 |
荷兰语(比利时) |
nl_BE |
92 |
挪威语(博克马尔) |
no |
93 |
挪威语(尼诺斯克) |
no |
94 |
波兰语 |
pl |
95 |
葡萄牙语(葡萄牙) |
pt |
96 |
葡萄牙语(巴西) |
pt_BR |
97 |
拉丁语系 |
rm |
98 |
罗马尼亚语 |
ro |
99 |
罗马尼亚语(摩尔达维亚) |
ro_MO |
100 |
俄语 |
ru |
101 |
俄语(摩尔达维亚) |
ru_MO |
102 |
索布语 |
sb |
103 |
斯洛伐克语 |
sk |
104 |
斯洛文尼亚语 |
sl |
105 |
阿尔巴尼亚语 |
sq |
106 |
塞尔维亚语(拉丁) |
sr |
107 |
塞尔维亚语(西里尔) |
sr |
108 |
瑞典语 |
sv |
109 |
瑞典语(芬兰) |
sv_FI |
110 |
苏图语 |
sx |
111 |
萨摩斯语(拉普兰) |
sz |
112 |
泰语 |
th |
113 |
瓦纳语 |
tn |
114 |
土耳其语 |
tr |
115 |
汤加语 |
ts |
116 |
乌克兰语 |
uk |
117 |
乌尔都语 |
ur |
118 |
文达语 |
ve |
119 |
越南语 |
vi |
120 |
科萨语 |
xh |
121 |
中文(中华人民共和国) |
zh_CN |
122 |
中文(中国香港特别行政区) |
zh_HK |
123 |
中文(新加坡) |
zh_SG |
124 |
中文(中国台湾) |
zh_TW |
125 |
祖鲁语 |
zu |
🎯 分级实施落地
为了保证用户体验和便于维护,特作出以下要求,但考虑到实施和检验成本,将要求分为三级
-
必须
:绝大多数地区用户可接受的最低标准,甚至部分功能无法使用。-
举例:
-
UI 界面展示内容,如 UserName 和用户名
-
UI 界面提示文本所在控件大小适应文本长度,避免展示不全
-
UI 页面所有内容、进程名、路径等不要包含有歧义的文本,如 Storage Server 在德国可能认为纳粹、Laputa 在电影是天空之城,西班牙语却是J女
-
翻译内容准确:中文博大精深,直接下意识翻译在一些语言可能不通顺
-
统一字符编码为 UTF-8
-
基础服务,有的用高德,有的用 Google
-
联系&服务确保本地能使用,如手机号+86前缀等
-
相关社区,如国内知乎等,部分国家 Twitter等
-
工作日/节假日不同,组织结构差异
-
字段校验规则,如手机号、邮箱、证件号、车牌号等
-
服务端日志:便于其他地区人员排查 & 部署
-
-
建议
:部分行业/地区需要的功能,但不是大多数地区必须的。-
举例:
-
日期展示格式,中:年月日,美:月日年,英:日月年,可统一为 yyyy-MM-dd
-
特殊日历,泰国佛历、伊朗 伊斯兰历
-
货币限制,不要只限制本币,有些地区有多币种,可以的话统一设置汇率提供一定程度币种转换参考更好
-
安装界面
-
接口中的错误提示
-
非展示信息等
-
核心功能日期格式、时区支持用户选择,尤其是跨冬夏令时的操作
-
特殊国家特殊行业法规,如数据存储时长,用户隐私说明/许可,宕机时长,专利、
-
-
可选优化
:如果有会提升用户体验。-
举例:
-
交互方式(如日期输入格式与选择习惯)
-
UI 对齐方向,考虑到阿拉伯、希伯来、波斯语阅读顺序,可定制支持文本调整右对齐
-
🔗 规范约束
多端通用约束
APP、前端、后端、小程序都需要遵循这些约束
可视化页面相关约束
APP、小程序、Web 页面开发都需要遵循的约束。
可视化界面
-
软件安装或完成时,要有语言切换的选项(可重启后生效)。
-
预加载界面避免使用文字:未获取到语言设置前(如:Web 页面在首次加载时),预加载页面、浏览器标题等信息尽量避免使用文字,推荐使用表示加载中的动图;必须使用文字的选用
Loading
。 -
长文案缩略与鼠标悬停提示:客户端界面所有文案在非中文环境下布局合理、样式正常,对于文案较长的情况,需要对文案进行缩略 +
…
处理,同时增加鼠标悬停提示(Html 的Title
属性),确保用户通过Title
可以查看到完整文案。-
某些国家的语言(如德语、俄语等)翻译后的字符特别长,平均长度是英语的 1.5 到 2 倍,界面在显示文案时会因为超长字符出现破坏页面布局、样式或超长字符无法显示全的情况,需要定制兼容。
-
-
保存用户语言偏好:在用户切换语言后,重启浏览器或重启服务器的操作后,用户再次进入时仍要使用上次选择的语言。
-
尽量不直接使用带有文字的图片(用户上传图片除外),即使需要,也要保证图片中的文字可翻译,建议文字是在无文字的图片上进行二次叠加的。
可视化文案信息处理
-
软件界面展示的文本字段需要以唯一的字符Key来标识文本字段。
-
字符 Key 与其对应的语言只允许增加,不允许修改与删除。
-
在软件通信传输过程中,不允许直接的文本传输(用户输入文本并写入数据库的除外),应将文本转换成翻译标识
Key
后再进行传输,由表示层进行翻译展示。
Key 规则如下:
-
Key中只能包含ASCII字符,包含数字、英文字母、“_”、“-”、“.”,不能包含中文等非ASCII字符
-
字符 key 必须采用 ASCII 码编写,仅可包含【数字、英文字母、下划线(
_
)、中划线(-
)、英文句号(.
)】,尽量避免使 用容易产生混淆的组合,如 0 和 1,两者与小写英文字母 o 和 l 。 -
Key 最大长度限制为 128 位。
-
代码中只能使用字符
Key
,展示层负责翻译显示。 -
在代码、脚本等文件中除注释外,不应存在任何的特定语言文本信息(如:中文),涉及的文件类型包含但不仅限于:.java、.js、.json、.properties、.sql、.vue、.html、.xml、.ui、.h、.cpp 等。
-
不允许用多个
Key
来拼接组成词或句子,语法不一定正确。 -
用于界面显示的多语言
Key
包含三部分组成,格式定义为<应用标识>.<i18nKey>.<类型标识>
。
-
类型 | 标识 | 说明 |
---|---|---|
名称 |
name |
名词短语,如:name、label、tab、menu、type 等。 |
句子 |
msg |
描述语句,如 msg、desc(description)、intro(introduction)、info(information)、sug(suggestion)、warn(warning) 等。 |
按钮 |
button |
动词短语,如 btn(button)。 |
时间、时区国际化
时间格式可配置,如 周起止时间
、 显示样式
软件进程对进程外部任何软件(内部系统其它服务/对外/对前端)的接口,涉及到时间传递的(包括请求参数和返回值), 都要以 ISO 8601
标准时间格式( yyyy-MM-dd’T’HH:mm:ss.sss [+-]hh:mm
, 零时区时为 yyyy-MM-dd’T’HH:mm:ss.sssZ
)进行传输。
-
默认时间格式选用
ISO 8601
标准时间格式。-
带时区:
yyyy-MMdd<T>HH:mm:ss.sss[+|-]hh:mm
-
不带时区:
yyyy-MM-dd<T>HH:mm:ss.sssZ
-
-
传输、存储时采用默认格式,终端展示时允许修改格式。
标准时间转为本地时间:
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
Date date = format.parse("2020-08-08T00:00:00+08:30");
💻 编码和使用 [1]
-
合理利用请求上下文、线程上下文保存语言标识,避免在业务方法入参中包含业务无关的参数。
Java 中创建语言标识方式
// 带有语言和国家/地区信息的本地化对象
Locale locale1 = new Locale("zh","CN");
// 只有语言信息的本地化对象
Locale locale2 = new Locale("zh");
// 等同于Locale("zh","CN")
Locale locale3 = Locale.CHINA;
// 等同于Locale("zh")
Locale locale4 = Locale.CHINESE;
// 获取本地系统默认的本地化对象
Locale locale5 = Locale.getDefault();
JDK 中提供了几个支持本地化的格式化操作工具类:
-
NumberFormat
-
NumberFormat.getCurrencyInstance(Locale.CHINA).format(xxx); (
¥123,456.78
)
-
-
DateFormat
-
MessageFormat
MessageFormat 的使用
// 信息格式化串
String pattern1 = "{0},你好!你于{1}在工商银行存入{2} 元。";
String pattern2 = "At {1,time,short} On{1,date,long},{0} paid {2,number, currency}.";
// 用于动态替换占位符的参数
Object[] params = {"John", new GregorianCalendar().getTime(),1.0E3};
// 使用默认本地化对象格式化信息
String msg1 = MessageFormat.format(pattern1,params);
// 使用指定的本地化对象格式化信息
MessageFormat mf = new MessageFormat(pattern2,Locale.US);
String msg2 = mf.format(params);
System.out.println(msg1);
System.out.println(msg2);
输出如下
John,你好!你于07-1-8 下午9:58在工商银行存入1,000元。 At 9:58 PM OnJanuary 8, 2007,John paid $1,000.00. ResourceBoundle
如果应用系统中某些信息需要支持国际化功能,则必须向希望支持的不同本地化类型分别提供对应的资源文件,并以规范的方式进行命名。
资源文件的命名规范
<资源名><语言代码><国家/地区代码>.properties
-
语言代码和国家/地区代码都是可选的。
-
<资源名>.properties
命名的国际化资源文件是默认的资源文件(某个本地化类型在系统中找不到对应的资源文件,就采用这个默认的资源文件)。 -
<资源名>_<语言代码>.properties
命名的国际化资源文件是某一语言默认的资源文件,即某个本地化类型在系统中找不到精确匹配的资源文件,将采用相应语言默认的资源文件。 -
不同语言的同一资源文件,
value
(属性值)不同,但key
(属性名)相同,这样应用程序就可以通过Locale
和特定的key
来找到对应语言翻译后的value
。 -
文件内容 只能 包含
ASCII
字符,中文使用 unicode 编码(主流IDE
中一般自带 中文 - Unicode 转换)。
Java 常用工具类介绍
JDK 的 ResourceBundle
如果应用程序中拥有大量的本地化资源文件,直接通过传统的 File
操作资源文件显然太过笨拙。Java为我们提供了用于加载本地化资源文件的方便类 java.util.ResourceBoundle
。
-
从相对于类路径的目录中加载一个名为
resource
的本地化资源文件,并获取对应的属性值-
ResourceBundle rb = ResourceBundle.getBundle("org/shoulder/i18n/resource", locale)
-
rb.getString("greeting.common")
-
-
只允许指定类路径下的资源文件
-
如果指定的本地化资源文件不存在,它按以下顺序尝试加载其他的资源
-
系统默认本地化对象对应的资源
-
不带语言/地区标识的资源文件
-
Spring 中的 MessageSource
三个用法(详见其注释)
-
解析code对应的信息进行返回,如果对应的code不能被解析则返回默认信息defaultMessage。
-
解析code对应的信息进行返回,如果对应的code不能被解析则抛出异常NoSuchMessageException
-
通过传入的
MessageSourceResolvable
对应来解析对应的信息,对于传入的多个 code,只要有一个可以翻译,立即返回
它被 HierarchicalMessageSource
和 ApplicationContext
接口扩展,AbstractApplicationContext 中实现了
AbstractApplicationContext#initMessageSource()
做 MessageSource
的初始化工作
Spring 中提供的三个实现类
-
StaticMessageSource
-
主要用于程序测试,它允许通过编程的方式提供国际化信息。
-
-
ResourceBundleMessageSource
-
基于JDK ResourceBundle,会将访问过的ResourceBundle缓存起来,以便于下次直接从缓存中获取进行使用
-
cacheSeconds
-
-
ReloadableResourceBundleMessageSource
-
基于
PropertiesPersister
来加载对应的文件,使用java.util.Properties来保存对应的数据(即可包括properties
xml
文件)。 -
允许指定非类路径下的文件作为对应的资源文件,可以使用 Spring支持的资源文件的前缀,如
classpath:
、file:
、http:
、ftp:
等 footnote[Spring 支持的资源文件加载方式, https://blog.csdn.net/hulei19900322/article/details/75200356] -
cacheSeconds
提供了定时刷新功能,允许在不重启系统的情况下,更新资源的信息。
-
-
SpringSecurityMessageSource
-
The default MessageSource used by Spring Security.
-
若未成功翻译响应策略:
-
DoNothing:忽略
-
ReturnCode:返回特定错误码
-
TryLocale:使用HTTP Header 语言,或服务端本地语言重试