国际化不仅包含多语言翻译,还包含各地的使用习惯和习俗等。软件语言的切换选项在代码实现中的唯一标识符参照:

每种语言代码由两个小写字母表示(由 ISO-639 标准定义)每个国家/地区代码由两个大写字母表示(由 ISO-3166 标准定义)。

Table 1. 国际标准语言和地区标识表
序号 语言_国家 语言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、前端、后端、小程序都需要遵循这些约束

资源文件存放

翻译文件,必须放置在对应语种缩写的文件夹下,如中文翻译必须放置于 zh_CH 文件夹下 翻译文件格式为:.properties

统一字符编码 UTF-8

  • 所有文本统一采用 UTF-8 编码后进行网络传输。

  • 特殊字符必须进行转译后存入数据库(如 \)。

  • 对于会导出给用户编辑,而后再导入的文本文件,如 csv 等,统一使用 UTF-8 编码。

因为语言不同,当存在多种编码的时候,应用展现层由于编码解析问题,容易造成乱码问题或者字符截断问题,故需要统一使用一种国际化的字符编码。

验证规则国际化

  • Email 除 @ 不对特殊字符限制

  • 手机号、证件号等校验规则可配置(长度、字符)

  • 假日跟随国家地区变化或允许自定义

国际化访问

公布的资源需要全球可访问

  • 手机号带国际区号,如中国地区 +86

  • 附加的网站在全球范围内可访问

可视化页面相关约束

APP、小程序、Web 页面开发都需要遵循的约束。

可视化界面

  • 软件安装或完成时,要有语言切换的选项(可重启后生效)。

  • 预加载界面避免使用文字:未获取到语言设置前(如:Web 页面在首次加载时),预加载页面、浏览器标题等信息尽量避免使用文字,推荐使用表示加载中的动图;必须使用文字的选用 Loading

  • 长文案缩略与鼠标悬停提示:客户端界面所有文案在非中文环境下布局合理、样式正常,对于文案较长的情况,需要对文案进行缩略 + …​ 处理,同时增加鼠标悬停提示(Html 的 Title 属性),确保用户通过 Title 可以查看到完整文案。

    • 某些国家的语言(如德语、俄语等)翻译后的字符特别长,平均长度是英语的 1.5 到 2 倍,界面在显示文案时会因为超长字符出现破坏页面布局、样式或超长字符无法显示全的情况,需要定制兼容。

  • 保存用户语言偏好:在用户切换语言后,重启浏览器或重启服务器的操作后,用户再次进入时仍要使用上次选择的语言。

  • 尽量不直接使用带有文字的图片(用户上传图片除外),即使需要,也要保证图片中的文字可翻译,建议文字是在无文字的图片上进行二次叠加的。

可视化文案信息处理

  • 软件界面展示的文本字段需要以唯一的字符Key来标识文本字段。

  • 字符 Key 与其对应的语言只允许增加,不允许修改与删除。

  • 在软件通信传输过程中,不允许直接的文本传输(用户输入文本并写入数据库的除外),应将文本转换成翻译标识 Key 后再进行传输,由表示层进行翻译展示。

Key 规则如下:
  • Key中只能包含ASCII字符,包含数字、英文字母、“_”、“-”、“.”,不能包含中文等非ASCII字符

    1. 字符 key 必须采用 ASCII 码编写,仅可包含【数字、英文字母、下划线(_)、中划线(-)、英文句号(.)】,尽量避免使 用容易产生混淆的组合,如 0 和 1,两者与小写英文字母 o 和 l 。

    2. Key 最大长度限制为 128 位。

    3. 代码中只能使用字符 Key,展示层负责翻译显示。

    4. 在代码、脚本等文件中除注释外,不应存在任何的特定语言文本信息(如:中文),涉及的文件类型包含但不仅限于:.java、.js、.json、.properties、.sql、.vue、.html、.xml、.ui、.h、.cpp 等。

    5. 不允许用多个 Key 来拼接组成词或句子,语法不一定正确。

    6. 用于界面显示的多语言 Key 包含三部分组成,格式定义为 <应用标识>.<i18nKey>.<类型标识>

Table 2. 类型标识
类型 标识 说明

名称

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

  • 传输、存储时采用默认格式,终端展示时允许修改格式。

标准时间转为本地时间:

demoCode.java
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
Date date = format.parse("2020-08-08T00:00:00+08:30");

包含英文版

  • 简介、更新日志、用户手册、安装界面

  • 默认支持中、英文语言,允许以语言包形式扩展语言(多语言安装包要带版本号)。

不使用本地化信息

  • 身份证号、民族、籍贯、省份、城市、所在区县、小区、楼幢、单元

  • 三方应用等需要使用国际版本名称(如:微信应显示为 WeChat ),不使用没有国际化版本的第三方应用。

  • 特殊地区敏感信息(不区分大小写):Blacklist、Whitelist、Master Slave、Master Slave Tracking 等。

针对使用习惯兼容性测试

  • PC 浏览器

    • Windows:ChromeFireFoxIE/Edge;

    • MAC OS: SafariChrome

  • 移动端

    • Android

    • IOS

💻 编码和使用 [1]

  • 合理利用请求上下文、线程上下文保存语言标识,避免在业务方法入参中包含业务无关的参数。

Java 中创建语言标识方式

demoCode.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 的使用

demoCode.java
    // 信息格式化串
    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")

  • 只允许指定类路径下的资源文件

  • 如果指定的本地化资源文件不存在,它按以下顺序尝试加载其他的资源

    1. 系统默认本地化对象对应的资源

    2. 不带语言/地区标识的资源文件

JDK 的 MessageFormat

占位符

greeting.common=How are you!{0},today is {1}

Spring 中的 MessageSource

三个用法(详见其注释)

  • 解析code对应的信息进行返回,如果对应的code不能被解析则返回默认信息defaultMessage。

  • 解析code对应的信息进行返回,如果对应的code不能被解析则抛出异常NoSuchMessageException

  • 通过传入的 MessageSourceResolvable 对应来解析对应的信息,对于传入的多个 code,只要有一个可以翻译,立即返回

它被 HierarchicalMessageSourceApplicationContext 接口扩展,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.

messageSource

若未成功翻译响应策略:

  • DoNothing:忽略

  • ReturnCode:返回特定错误码

  • TryLocale:使用HTTP Header 语言,或服务端本地语言重试

其他编码注意项

前端

界面,错误码,动态表单,请求服务器 + 缓存的方式

后端

后端的国际化需要注意的点有两部分:api 接口的 msg 字段(全局自定义异常拦截)、系统数据层面翻译。

JDK 提供的相关类:

  • Locale 地区、语言

  • TimeZone 时区

  • NumberFormat 数字格式化

  • Currency 货币格式化

  • DateTimeFormatter 时间格式化

  • Collator 字符比较,排序

  • CollationKey 字符比较,排序

  • Normalizer 返回str的范化形式

  • MessageFormat

  • Format

  • ResourceBundle