`
wdmcygah
  • 浏览: 62461 次
社区版块
存档分类
最新评论

网站国际化实现(1)—JDK的国际化支持

阅读更多

一、背景

很多网站的用户分布在世界各地,因此网站需要针对不同国家的用户展示不同语言的内容,因此就有了国际化实现的需求,大多数网站都会在网站的头部或尾部设置语言切换链接,这样就可以直接切换成相应的内容。其中有些网站是通过网站地址或参数进行区分,有些是通过设置cookie值进行进行区分。

这里先不讲网站具体的实现,先介绍下网站国际化需要的基础知识,即JDK本身对国际化的支持。这里说明下JDK本身的国际化只是网站国际化实现的基础,其本身还可以支持GUI程序或其它应用程序的国际化实现。

二、简介

JAVA官方国际化教程

国际化(Internationalization )用于便捷地支持不同语言或区域的处理,国际化有时简称为 i18n,取Internationalization单词的首字母和尾字母,中间因为还有18个字母,用18代替,故简写为i18n。

一般需要国际化处理的数据有时间、数字、金额、文本等。国际化一般有本地化的数据,而且通常都不是硬编码的,不需要每次修改都重新编译,而且还需要处理非常便捷。

国际化的整个过程可以大致分为三步:本地化、数据获取、格式化。下面再详细说明下。

三、本地化

既然要做到国际化,那么首先肯定得知道是哪个语言或区域,这个如何去获取或设置呢?JDK提供了Locale类去抽象本地化实现,Locale对象表示了特定的地理、政治和文化地区。

Locale有几个重要的编码这里先介绍下:

  1. 语言编码(Language Code): 两到三位符合ISO 639 标准的字母。这个编码比较好理解,主要用作不同语言的定义。语言编码参照表链接
  2. 脚本编码(Script Code):由一个大写首字母+三个小写字母组成,符合ISO 15924标准的编码。这个编码JDK7以后才引入,主要用于区分同一语言同一国家地区使用不同的书写系统的情形,例如uz-Cyrl-UZ表示使用西里尔字母的乌兹别克语。脚本编码参照表链接
  3. 区域编码(Region Code):由两个或者三个大写符合ISO 3166标准的字母组成。 这个编码主要用于表示国家或者地区。区域编码参照表链接
  4. 多样编码(Variant Code):这个编码在JDK7以前常用于定义语言或者区域之外的区别,比如计算平台Windows或UNIX。但是IETF BCP 47标准不建议这么使用。所以JDK7之后,多样编码(Variant Code)主要用来定义一门语言后者方言的多样性。多样编码参照表链接
    而前面说到的非语言的多样性,比如平台的区别(Windows, UNIX, Linux)或者发布信息(6u23 or JDK 7)等,JDK7引入Unicode Locale Extensions支持来符合IETF BCP 47标准。

JDK8支持的本地化一览链接(Supported Locales栏)

当然,在实际Locale使用中可能用不到所有的编码定义或拓展,大多数情况下语言编码和区域编码就足够区分定义,不过了解这些编码的含义与作用对使用上还是有好处的。实际上Locale对象的创建就是根据上述的编码和拓展定义出来的。

这里以JDK8为例,Locale的创建可以通过Locale.Builder类、Locale本身的构造方法、forLanguageTag方法、或者预先定义好的常量进行创建。当然getDefault方法也可以得到基于当前环境默认的Locale对象。这里方法上各有差异,本质还是设置前面说到的编码或拓展值。

四、数据获取

得到了本地化信息,那么下一步就是要获取对应的数据。前面提到过国际化需要信息不是硬编码的,这样就不要每次修改都重新编译,而且也易于维护。

在JDK中,数据隔离和获取一般使用ResourceBundle类配合properties文件使用,实际使用中,一般会定义一些properties文件,文件名前缀相同,后缀跟一些本地化的信息,这样不同的文件就可以存储不同本地化对应的数据。

这里说得太抽象,直接上结合官网示例修改的代码,为了便于阅读,下面列个大概,具体请看我上传的github项目代码

 

public class ResourceBundleDemo {

public static void main(String[] args) {
    // 这里用到的i18n下面的文件名都以下划线分隔,RBControl_语言编码_区域编码的形式
    String baseName = "i18n/RBControl";

    // 演示Locale常量解析RBControl_zh_cn.properties数据
    Locale l = Locale.CHINA;
    ResourceBundle rs = ResourceBundle.getBundle(baseName, l);
    String result = rs.getString("region");
    System.out.println("示例1结果:" + result);

    // 演示Locale.Builder解析RBControl_zh_hk.properties数据
    l = new Locale.Builder().setLanguage("zh").setRegion("hk").build();
    rs = ResourceBundle.getBundle(baseName, l);
    result = rs.getString("region");
    System.out.println("示例2结果:" + result);

    // 演示Locale构造函数解析RBControl_zh_tw.properties数据
    l = new Locale("zh", "tw");
    rs = ResourceBundle.getBundle(baseName, l);
    result = rs.getString("region");
    System.out.println("示例3结果:" + result);

    // 演示Locale构造函数解析RBControl_en_US.properties数据
    l = Locale.forLanguageTag("en-US");
    rs = ResourceBundle.getBundle(baseName, l);
    result = rs.getString("region");
    System.out.println("示例4结果:" + result);

    // 演示Locale解析RBControl_zh.properties数据,但是对应数据不存在时,会取默认RBControl.properties
    l = new Locale("zh");
    rs = ResourceBundle.getBundle(baseName, l);
    result = rs.getString("region");
    System.out.println("示例5结果:" + result);
}
}

 


Paste_Image.png

对于ResourceBundle,在指定的locale找不到的时候,getBundle方法会找最相近的
值。例如官网中举例ButtonLabel_fr_CA_UNIX是文件名,Locale默认是en_US,getBundle方法会按照如下的顺序查找ButtonLabel_fr_CA_UNIX、ButtonLabel_fr_CA、ButtonLabel_fr、ButtonLabel_en_US、ButtonLabel_en、ButtonLabel,如果getBundle在列表中找不到匹配,会抛出MissingResourceException异常,所以为了避免这个异常,最好每次都使用没有后缀的文件,在前面示例中就是ButtonLabel文件名。

五、格式化

上次已经可以获取到数据了,有些时候数据获取到之后可以直接展示,但是如果涉及到时间、数字、金额、动态文本等数据时,又需要额外做下处理了,因为本身这些数据就是本地化敏感的,那么这个时候怎么办呢?这时就需要对相应的数据进行格式化操作。下面详细做下说明。

5.1 数字与金额

数字与金额其实都是数值相关的处理,JDK提供了NumberFormat类进行处理,处理过程可以大致分为两步:(1)getInstance方法得到实例;(2)format方法格式化数据。

比如long、long可以使用NumberFormat.getNumberInstance(Locale inLocale)方法获得相应本地化的对象实例,比如int可以使用getIntegerInstance(Locale inLocale)方法获得对应实例,金额可以调用getCurrencyInstance(Locale inLocale)方法得到实例,还有百分比的情况可以调用getPercentInstance(Locale inLocale)得到实例;最后再调用format方法即可。

这里额外还说下DecimalFormat类,这个类主要做小数的格式化处理。比如有不少场景对于123456.789这样的数字要格式化成123,456.789 ;这个时候DecimalFormat就非常实用。简单示例如下:
NumberFormat nf = NumberFormat.getNumberInstance(locale);
DecimalFormat df = (DecimalFormat)nf;
df.applyPattern("###,###.###");
String output = df.format(value);

上面可以看到DecimalFormat格式化时会需要有个格式化的模式"###,###.###",而这个模式还可以支持更多灵活的语法。基本如下:

符号 含义
0 阿拉伯数字
# 阿拉伯数字,0如果无效的话就不显示
. 小数的分隔符
, 分组的分隔符
E 分隔科学计数法中的尾数和指数
; 格式化分隔符,分隔正数和负数子模式
- 默认的负数前缀
% 乘以100,百分数展示
? 乘以1000,千分数展示
¤ 货币记号,由货币符号替换。如果两个同时出现,则用国际货币符号替换。如果出现在某个模式中,则使用货币小数分隔符,而不使用小数分隔符
X 任意可以用在前缀或后缀的字符
' 用于在前缀或或后缀中为特殊字符加引号,例如 "'#'#" 将 123 格式化为 "#123";如果要创建单引号本身,就使用两个单引号"# 9''123"

这里有两个不太常用到的点做下说明:(1)格式里面有分号作分隔符,其实完整的模式应该是subpattern;subpattern,前一个subpattern是正数的格式化模式,后一个subpattern是负数的格式化模式,每一个subpattern的形式都可以用前面表格的去定义表示,不过负数的格式化模式是可选的,通常情况下不会用;(2)前面表格的分隔符还可以定制化,使用DecimalFormatSymbols类就可以自定义分隔符,具体使用时调用含DecimalFormatSymbols参数的DecimalFormat构造方法,再进行格式化处理即可。

5.2 日期与时间

日期与时间的处理,以前主要用到SimpleDateFormat这个实现类,JDK8新引进了java.time包下的DateTimeFormatter类也可以进行格式化处理。DateTimeFormatter可以看我前面写的JDK8新特性一览里面的介绍,下面以SimpleDateFormat举例,:
SimpleDateFormat formatter = new SimpleDateFormat(pattern, currentLocale);
Date today = new Date();
String output = formatter.format(today);
System.out.println(pattern + " " + output);

这里同样有个格式化语法

符号 含义 类型 示例
G 纪元 Text AD
y 年份 Number 2009
M 月(在一年中的月分) Text & Number July & 07
d 日(在一个月中的天数) Number 10
h 小时(12小时制,1-12) Number 12
H 小时(24小时制,0-23) Number 0
m Number 30
s Number 55
S 毫秒 Number 978
E 日(在一周中的天数) Text Tuesday
D 日(在一年中的天数) Number 189
F 第几周(这一天在这一个月的第几周) Number 2 (2nd Wed in July)
w 第几周(在一年的第几周) Number 27
W 第几周(这个月的第几周) Number 2
a 上午/下午(am/pm) Text PM
k 小时(24小时制,1-24) Number 24
K 小时(12小时制,0-11) Number 0
z 时区 Text Pacific Standard Time
' 文本分隔(格式化内容中插入文本时用到) Delimiter (none)
' 单引号 Literal '

5.3 文本

在网站应用里面,文本国际化应该是最常用到的了。而且复杂情况下,文本可能还是是固定不变的,可能是动态数据,还可能包含前面讲的金额或时间等信息。比如文本是“我在xxx时间,在xxx网站,花费了xxx钱,购买了xxx东西”,这个时候时间、站名、金额、东西都不一样。不过JDK的MessageFormat类提供了简便的实现。

主要的步骤可以分为三步:(1)定义文本模板;(2)初始化MessageFormat类;(3)根据模板和动态参数进行格式化处理。下面是简单示例:


定义模板.png

 

ResourceBundle messages =  ResourceBundle.getBundle("i18n/Message",currentLocale);
Object[] messageArguments = {new Date(), messages.getString("goods"),"taobao",65.00};
MessageFormat formatter = new MessageFormat(messages.getString("template"),currentLocale );  
String output = formatter.format(messageArguments);  
System.out.println(output);

详细代码示例可以看我上传的github项目代码

通过上面的示例可以看到,MessageFormat类会自动将传为的参数,按照ResourceBundle类获取的模板要求做相应的格式化处理,这样就可以满足动态数据的展示了。上面在定义文本模板时用到了类似{3,number,currency}这样的写法,表示第三个参数格式类型为数字,形式用金额形式。这里也可以用{3}或者{3,nmuber}这样就会相应的默认形式格式化。具体语法详细讲解链接

另外在有些语言环境下,复数的表现形式不同,比如英语环境下,one file、two files,这个时候的模板直接定义成{0}file这种形式就不太合适,这个时候就可以用到ChoiceFormat类进行处理。

通过上面的三个步骤(本地化—数据获取—格式化),整个国际化的过程就完成了。当然简单情况下本地化—数据获取两步也可能

最后还啰嗦一句,由于上面的每个点展开讲都可以写一篇甚至几篇博文,限于篇幅,笔者主要把概念和常用部分重点做了强调,有了清晰的概念介绍与示例,对于大家的理解应该还是很有帮助的。不过这里还是强烈建议大家仔细阅读下JAVA官方国际化教程,里面讲解得非常详细,而且有更多示例,笔者的一些示例也是在官方示例上面做的修改。

 

1
1
分享到:
评论

相关推荐

    JDK8 64位下载

    这对于类型安全的容器、序列化和国际化等方面非常有用。 ```java @NonNull String name; ``` #### 三、JDK8的下载与安装 对于64位操作系统,下载适合的JDK8版本尤为重要。官方推荐从Oracle官方网站下载最新的JDK8...

    JDK15_官网下载.zip

    10. **JEP 385:JDK源码的UTF-8编码**:JDK源码文件现在统一使用UTF-8编码,提高了跨平台兼容性和国际化支持。 在开发环境中配置好JDK15后,开发者可以利用这些新特性和改进来优化代码,提升应用的性能和用户体验。...

    JDK API 1.6.0 中文版

    8. **国际化(i18n)和本地化(l10n)**:1.6版本加强了对多语言环境的支持,开发者可以更容易地构建全球化应用。 9. **垃圾收集器优化**:Java 1.6中的垃圾收集器进行了优化,如Parallel Scavenge和Parallel Old...

    JDK 6(jdk1.6.0_45)

    10. **国际化和本地化改进:** 支持更多的字符集和语言环境,提供了更好的文本处理和日期/时间格式化功能。 ### JDK 6的安装与使用 1. **下载:** 你可以从Oracle官方网站上下载JDK 6的安装包,根据操作系统选择...

    JDK 1.6 64位 windows

    8. **国际化**:扩展了本地化支持,包括更多的语言和区域设置。 **二、64位Windows环境下的优势** 1. **更大内存支持**:64位Java可以利用更大的内存空间,理论上可以访问超过4GB的RAM,这对于处理大数据和内存...

    JDK API 1.6.0中文版

    JDK 1.6.0 API涵盖了Java语言的核心库,包括基础类库、集合框架、网络编程、I/O流、多线程、国际化、反射、安全管理等多个方面。 1. **基础类库**:如Object类,它是所有Java类的父类,提供了equals()、hashCode()...

    JDK1.6帮助文档--英文版

    国际化与本地化 JDK1.6支持多种语言和区域设置,通过ResourceBundle和Locale类实现全球化应用。 ### 12.附录 CHM文件通常包含详细的附录,如关键字列表、类库索引、错误代码解释等,便于快速查找和参考。 总的来说...

    JavaWeb项目,基于狂神的smbms的修改使用jdk1.8 mysql8.0

    - **main/resources**:存储配置文件、数据库连接字符串、国际化文件等。 2. **webapp**:Web应用目录,包含静态资源和Servlet相关文件。 - **WEB-INF**:存放web.xml(Web应用部署描述符)、Spring或Struts等...

    中软国际软件测试培训java课件

    Java提供了丰富的类库支持文件读写、网络通信等操作,这些知识对于测试用例的编写和自动化测试脚本的开发十分关键。 最后,课件可能还会涉及一些高级主题,如多线程、反射、设计模式等。多线程使得程序可以同时执行...

    jspsmartupload.rar

    7. **国际化支持**:由于描述中提到的乱码问题已被修正,我们可以推断`JSP Smart Upload`可能支持多种字符集,能够处理不同语言的文件名,符合国际化标准。 8. **兼容性**:作为一款成熟的Java Web组件,`JSP Smart...

    IReport技术手册中文版V1

    - **国际化支持**:报表支持多语言,便于全球化应用。 ### 实用技巧: - **模板重用**:通过模板库管理报表模板,提高开发效率。 - **样式预览**:在设计时实时预览样式变化,有助于快速调整。 - **调试和测试**:...

    JSP网上宠物商店系统的设计与实现.pdf

    MyEclipse是一种为Java EE开发者提供的集成开发环境,可以与Tomcat服务器和JDK环境无缝集成,支持JSP和Servlet的开发,非常适合开发基于Java的Web应用。SQL是用于管理和操作关系数据库的标准编程语言。通过SQL语言,...

    DLOG4J安装手册1

    - **国际化**: 引入了国际化框架的支持,尽管目前仅提供中文资源文件。 - **多管理员支持**: 同一个DLOG的不同管理员可以拥有自己的日记草稿信息。 - **密码找回**: 注册用户可以通过电子邮件找回登录密码。 - **...

    云应用开发 ——Google App Engine & Google Web Toolkit入门指南

    - **10.2.14 国际化**:如何为网站添加多语言支持。 - **10.2.15 欢迎界面和样式文件修改**:优化欢迎界面和样式文件。 - **10.2.16 总结**:回顾整个开发过程,总结经验教训。 ##### 10.3 高级例子——号码管家...

    基于JAVA的美食宣传网站.zip

    2. **资源文件**:如`src/main/resources`,用于存放配置文件(如`application.properties`)、静态资源(CSS、JavaScript、图片)和国际化文件。 3. **Web应用目录**:如`Web-INF`,其中`web.xml`是Web应用的部署...

    《Struts 2.1权威指南》PDF版本下载.txt

    - **博客管理系统**:实现文章发布、评论管理等功能,并通过Struts 2的国际化支持实现多语言界面。 ### 总结 《Struts 2.1权威指南》作为一本全面介绍Struts 2框架的专业书籍,涵盖了从基本概念到高级技巧的全方位...

    Ofbiz16.11.05运行及开发环境搭建等

    - **使用entity-auto服务实现国际化**:通过OFBiz的entity-auto服务,可以实现增加、修改和删除操作的国际化提示。这涉及到配置语言资源文件和定制服务逻辑。 - **电商新建用户逻辑**:OFBiz支持创建和管理电子商务...

    sqljdbc42 jdbc for java

    2. 支持Unicode:完全支持Unicode字符集,使得国际化和本地化应用开发更为便捷。 3. 批处理:允许一次性提交多个SQL语句,提高批量操作的性能。 4. 游标支持:提供多种游标类型,使开发者可以选择不同的数据读取模式...

    最新CMS程序CMS4J 2008-cms4j.zip

    7. **国际化支持**:支持多语言内容发布,适应全球化需求。 三、CMS4J 2008新特性 1. **性能优化**:针对2008版,CMS4J进行了代码优化,提高了系统响应速度和并发处理能力。 2. **增强的用户体验**:更新了用户界面...

Global site tag (gtag.js) - Google Analytics