国际化开发规范

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

为了保证用户体验和便于维护,特作出以下要求。

文件存放

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

字符编码

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

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

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

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

可视化界

  • 软件安装或完成时,存在语言切换的选项(允许重启后生效)。

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

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

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

  • 软件语言在切换后,重启浏览器或重启服务器的操作后仍要保留上次选择的语言。

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

可视化文案信息处理

  • 软件界面展示的文本字段需要以唯一的字符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 1. 类型标识
类型 标识 说明

名称

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

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

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

输入校验国际化

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

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

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

国际化访问

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

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

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

包含英文版

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

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

不使用本地化信息

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

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

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

针对使用习惯兼容性测试

  • PC 浏览器

    • Windows:ChromeFireFoxIE/Edge;

    • MAC OS: SafariChrome

  • 移动端

    • Android

    • IOS

编码和使用 [1]

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

[[Java 中创建语言标识方式]]

app.rb
    // 带有语言和国家/地区信息的本地化对象
    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 的使用]]

app.rb
    // 信息格式化串
    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代码编辑资源文件是很不方便的,所以,通常我们直接使用正常的方式编写资源文件,在测试或部署时再采用工具进行转换。 JDK 在 bin 目录下为我们提供了一个完成此项功能的 native2ascii 工具,它可以将中文等资源文件转换为 Unicode 代码格式的文件,命令格式如下:

native2ascii [-reverse] [-encoding 编码] [输入文件 [输出文件]]

  • UTF-8 格式的多语言资源文件 resource_zh_CN.properties 转为 ASCII 资源文件

    • native2ascii -encoding utf-8 d:\resource_zh_CN.properties 即可得到 d:\resource_zh_CN_1.properties

该命令还是不方便,主流 IDE 中一般自带转换。

常用多语言工具类介绍

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

其他国际化注意点

前端

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

后端

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

JDK 提供的相关类:

  • Locale 地区、语言

  • TimeZone 时区

  • NumberFormat 数字格式化

  • Currency 货币格式化

  • DateTimeFormatter 时间格式化

  • Collator 字符比较,排序

  • CollationKey 字符比较,排序

  • Normalizer 返回str的范化形式

  • MessageFormat

  • Format

  • ResourceBundle


1. Spring 源码分析 MessageSource, https://blog.csdn.net/sid1109217623/article/details/84065725