开发者社区> guitar_zp> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

[Java 进阶]Java中的国际化

简介: 背景知识 现代软件开发,往往做出的应用程序不止给一个国家的人去使用。不同国家的人往往存在语言文字不通的问题。由此产生了国际化(internationalization)、多语言(multi-language)、本地化(locale)这些词,它们其实都是一个意思,支持多种语言,提供给不同国家的用户使用。
+关注继续查看

背景知识

现代软件开发,往往做出的应用程序不止给一个国家的人去使用。不同国家的人往往存在语言文字不通的问题。由此产生了国际化(internationalization)、多语言(multi-language)、本地化(locale)这些词,它们其实都是一个意思,支持多种语言,提供给不同国家的用户使用。

语言编码、国家/地区编码

web 开发的朋友可能多多少少接触过类似 zh-cn, en-us 这样的编码字样。

这些编码是用来表示指定的国家地区的语言类型的。那么,这些含有特殊含义的编码是如何产生的呢?

ISO-639标准使用编码定义了国际上常见的语言,每一种语言由两个小写字母表示。

ISO-3166标准使用编码定义了国家/地区,每个国家/地区由两个大写字母表示。

为什么要用语言+国家/地区来标示语言呢?

道理很简单:拿咱们中国来说,同样是中文,全国各地的方言就多如牛毛,更不要说世界上有那么多种语言,得有多少方言?

下表列举了一些常见国家、地区的语言编码:

国家/地区

语言编码

国家/地区

语言编码

简体中文(中国)

zh-cn

繁体中文(台湾地区)

zh-tw

繁体中文(香港)

zh-hk

英语(香港)

en-hk

英语(美国)

en-us

英语(英国)

en-gb

英语(全球)

en-ww

英语(加拿大)

en-ca

英语(澳大利亚)

en-au

英语(爱尔兰)

en-ie

英语(芬兰)

en-fi

芬兰语(芬兰)

fi-fi

英语(丹麦)

en-dk

丹麦语(丹麦)

da-dk

英语(以色列)

en-il

希伯来语(以色列)

he-il

英语(南非)

en-za

英语(印度)

en-in

英语(挪威)

en-no

英语(新加坡)

en-sg

英语(新西兰)

en-nz

英语(印度尼西亚)

en-id

英语(菲律宾)

en-ph

英语(泰国)

en-th

英语(马来西亚)

en-my

英语(阿拉伯)

en-xa

韩文(韩国)

ko-kr

日语(日本)

ja-jp

荷兰语(荷兰)

nl-nl

荷兰语(比利时)

nl-be

葡萄牙语(葡萄牙)

pt-pt

葡萄牙语(巴西)

pt-br

法语(法国)

fr-fr

法语(卢森堡)

fr-lu

法语(瑞士)

fr-ch

法语(比利时)

fr-be

法语(加拿大)

fr-ca

西班牙语(拉丁美洲)

es-la

西班牙语(西班牙)

es-es

西班牙语(阿根廷)

es-ar

西班牙语(美国)

es-us

西班牙语(墨西哥)

es-mx

西班牙语(哥伦比亚)

es-co

西班牙语(波多黎各)

es-pr

德语(德国)

de-de

德语(奥地利)

de-at

德语(瑞士)

de-ch

俄语(俄罗斯)

ru-ru

意大利语(意大利)

it-it

希腊语(希腊)

el-gr

挪威语(挪威)

no-no

匈牙利语(匈牙利)

hu-hu

土耳其语(土耳其)

tr-tr

捷克语(捷克共和国)

cs-cz

斯洛文尼亚语

sl-sl

波兰语(波兰)

pl-pl

瑞典语(瑞典)

sv-se

 

 

注:由表中可以看出语言、国家/地区 编码一般都是英文单词的缩写。

 

字符编码

在此处,引申一下字符编码的概念。

是不是有了语言、国家/地区编码,计算机就可以识别各种语言了?

答案是否。作为程序员,相信每个人都会遇到过这样的情况:,期望打印中文,结果输出的却是乱码。

这种情况,往往是因为字符编码的问题。

计算机在设计之初,并没有考虑多个国家,多种不同语言的应用场景。当时定义一种ASCII码,将字母、数字和其他符号编号用7比特的二进制数来表示。后来,计算机在世界开始普及,为了适应多种文字,出现了多种编码格式,例如中文汉字一般使用的编码格式为GB2312GBK

由此,又产生了一个问题,不同字符编码之间互相无法识别。于是,为了一统江湖,出现了 unicode编码。它为每种语言的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台的文本转换需求。

有人不禁要问,既然 Unicode 可以支持所有语言的字符,那还要其他字符编码做什么

Unicode 有一个缺点:为了支持所有语言的字符,所以它需要用更多位数去表示,比如ASCII表示一个英文字符只需要一个字节,而 Unicode 则需要两个字节。很明显,如果字符数多,这样的效率会很低。

为了解决这个问题,有出现了一些中间格式的字符编码:如UTF-8UTF-16UTF-32等。中国程序员一般使用UTF-8编码。

 

国际化的 Java 实现

所有的语言文字在计算机中都是字符串。所以,实现国际化,归根结底就是根据语言类型去定义好字符串模板而已

那么,如何定义不同语言的字符串模板呢?

对于所有编程语言都常见的做法是定义 XML:为每种语言添加一个 XML 文件,在 XML 文件中定义需要的字符串模板,并指定一个唯一的标识ID。只要熟悉对应编程语言的 XML 库方法,实现非常简单。因为本文目的是介绍 Java 特色的国际化实现,这里不再详述 XML 实现方式。

 

Java中实现国际化的方法

定义 properties

实现国际化,归根结底就是根据语言类型去定义好字符串模板而已。

Java 中的多语言字符串模板一般保存在properties资源文件中。

它必须遵照以下的命名规范:

<资源名>_<语言代码>_<国家/地区代码>.properties

其中,语言代码和国家/地区代码都是可选的。

<资源名>.properties命名的国际化资源文件是默认的资源文件,即某个本地化类型在系统中找不到对应的资源文件,就采用这个默认的资源文件。

例:

定义中英文两种多语言资源文件,将其置于com.notes.locale.resources 路径下。

content_en_US.properties

helloWorld = HelloWorld!

time = Thecurrenttimeis%s.

content_zh_CN.properties

helloWorld = \u4e16\u754c\uff0c\u4f60\u597d\uff01

time = \u5f53\u524d\u65f6\u95f4\u662f\u0025\u0073\u3002

可以看到,两种语言的Key 完全一致,只是 Value 是对应语言的字符串。

本地化不同的同一资源文件,虽然属性值各不相同,但属性名却是相同的,这样应用程序就可以通过Locale对象和属性名精确调用到某个具体的属性值了。

本文的字符编码中提到了,为了达到跨编码也正常显示的目的,有必要将非ASCII 字符转为 Unicode 编码。上面的中文资源文件就是中文转为 Unicode 的结果。

Unicode 转换工具

JDKbin目录下为我们提供了一个Unicode 转换工具:native2ascii

它可以将中文字符的资源文件转换为Unicode代码格式的文件,命令格式如下:

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

例:

假设content_zh_CN.properties d:\ 目录。执行以下命令可以新建 content_zh_CN_new.properties ,其中的内容就所有中文字符转为 UTF-8 编码格式的结果。

native2ascii -encoding utf-8 d:\content_zh_CN.properties d:\content_zh_CN_new.properties

 

加载资源文件

定义了多语言资源文件,下一步就是加载资源文件了。

Java为我们提供了用于加载本地化资源文件的工具类:java.util.ResourceBoundle

使用方式见下例:

Locale localeEn = new Locale("en", "US");

Locale localeZh = new Locale("zh", "CN");

ResourceBundle rbEn = ResourceBundle.getBundle("com.notes.locale.resources.content", localeEn);

ResourceBundle rbZh = ResourceBundle.getBundle("com.notes.locale.resources.content", localeZh);

System.out.println("us-US:" + rbEn.getString("helloWorld"));

System.out.println("us-US:" + String.format(rbEn.getString("time"), "08:00"));

System.out.println("zh-CN" + rbZh.getString("helloWorld"));

System.out.println("zh-CN" + String.format(rbZh.getString("time"), "08:00"));

需要说明的是:ResourceBundle类的 getBundle方法第一个参数 baseName需要输入的是资源文件的package路径 + 文件前缀。

以本文例子来说,content_zh_CN.properties content_en_US.properties baseName 都是:com.notes.locale.resources.content

在加载资源时,如果指定的本地化资源文件不存在,它会尝试按下面的顺序加载其他的资源:本地系统默认本地化对象对应的资源-> 默认的资源。如果指定错误,Java 会提示找不到资源文件。

输出:

us-US:Hello World!

us-US:The current time is 08:00.

zh-CN:世界,你好!

zh-CN:当前时间是08:00

 

支持国际化的国际化工具类

Java 中也提供了几个支持国际化的格式化工具类。例如:NumberFormatDateFormatMessageFormat

NumberFormat

NumberFormat 是所有数字格式类的基类。它提供格式化和解析数字的接口。它也提供了决定数字所属语言类型的方法。

Locale locale = new Locale("en", "US");

NumberFormat format = NumberFormat.getCurrencyInstance(locale);

doublenum = 123456.78;

System.out.println("51423.45 Format:");

System.out.println(String.format("%s : %s", locale.toString(), format.format(num)));

 

DateFormat

DateFormat 是日期、时间格式化类的抽象类。它支持基于语言习惯的日期、时间格式。

Date date = new Date();

DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM, new Locale("zh", "CN"));

DateFormat df2 = DateFormat.getDateInstance(DateFormat.MEDIUM, new Locale("en", "US"));

System.out.println(df.format(date));

System.out.println(df2.format(date));

 

MessageFormat

Messageformat 提供一种与语言无关的拼接消息的方式。通过这种拼接方式,将最终呈现返回给使用者。

String pattern1 = "{0},你好!你于  {1} 消费  {2} 元。";

String pattern2 = "At {1,time,short} On {1,date,long}{0} paid {2,number, currency}.";

Object[] params = { "Jack", new GregorianCalendar().getTime(), 8888 };

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);

 

 

 

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
java中的引用
## 介绍 JAVA 中有 4 种类型的引用: – **强引用** – **软引用** – **弱引用** – **幻像引用** 这些引用的区别仅在于**垃圾回收器**管理它们的方式。如果你从未听说过它们,这意味着你只使用强大的。了解差异可以帮助您,特别是如果您需要存储临时对象并且无法使用真正的缓存库(如eHcache或Guava)。 由于这些类型与 JVM 垃圾回收器密切相关,因此我将简要回顾一下有关 JAVA 中垃圾回收的一些信息,然后介绍不同的类型。 [TOC] ## 垃圾回收器 Java和C++之间的主要区别在于**内存管理**。在Java中,开发人员不需要知道内存是
26 0
Java 序列化
读完这篇文章你将会收获到 • Serializable 和 Externalizable 的使用 • 序列化 ID 问题 • 静态变量序列化 • 父类的序列化 • ArrayList 序列化:为啥 size 被序列化两次? • 序列化对单例的破坏 序列化就是将对象的状态信息转为可以存储或者传输的形式的过程 比如说将对象序列化之后存储在硬盘上 比如说将对象序列化之后返回给调用方 反序列化则是序列化的反过程
25 0
Java:多态
Java多态的介绍
54 0
Java 序列化
序列化: 将对象变为可传输内容的过程就是序列化.方式: 1、Java原生: 简介 Java原生序列化实体类必须实现Serializable接口。 原理是通过Java原生流(InputStream和OutputStream)的方式进行转化。
7164 0
Java 序列化
Java 序列化 简介 定义 序列化:序列化是将对象转换为字节流。 反序列化:反序列化是将字节流转换为对象。 用途 序列化的用途有: 序列化可以将对象的字节序列持久化——保存在内存、文件、数据库中。
1069 0
Java 序列化
由于java内存中的对象的状态和值是不被记录和保存的。 要想在java内存中记录并保存内存中对象的状态及其值,称作“序列化”。 对象序列化是java自身记录对象状态的一种机制。 被序列化后的对象,可以保存到一个文件中或者数据库中,也可以用套接字在网络上传送。
733 0
java输出一万之内的质数,质数个数及求和
1 public class Hello { 2 3 public static void main(String[] args) { 4 int sum = 0; 5 int prinum = 0; 6 ...
1598 0
+关注
186
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载