概述
- 软件的本地化:一个软件在某个国家或地区使用时,采用该国家或地区的语言,数字,货币,日期等习惯.
- 软件的国际化:软件开发时,让它能支持多个国家和地区的本地化应用,使得应用软件能够适应多个地区的语言和文化风俗习惯.
- 随用户区域信息而变化的数据称为本地信息敏感数据,例如数字,货币等数据.
- 应用程序的国际化就是在应用软件的设计阶段,使软件能够支持多个国家和地区的用户的使用习惯.
- 国际化又称为i18n:internationalization.
特征
-对于程序中的本地信息敏感数据(日期、货币等)能根据当前所在的国家或地区的文化习惯进行显示
- 对于文本元素(错误提示信息、状态信息等)不是直接写在应用程序中,而是存储在应用程序外部的资源文件中,在应用程序中通过- 程序代码来动态获得这些数据
- 无需修改和重新编译程序就能支持新的国家或地区的用户使用
java 国际化解决方案
- 文本信息不能硬编码在程序代码中,而是需要将它们从应用程序中分离出来,在软件运行时根据本地信息读取相应的文本内容进行显示
- 数值、货币、时间、日期等本地敏感数据可能在程序运行时动态产生,所以无法像文字一样简单地将它们从应用程序中分离出来,而是需要特殊处理。java 中提供了解决这些问题的API类(位于java.util包和java.text包中)
Locale 类
- Locale 实例对象代表一个特定的地理,政治或文化上的一个区域
- 一个Locale 对象本身不会验证它代表的语言和国家地区信息是否正确,只是向本地敏感的类提供本地信息,与国际化相关的格式化和解析任务由本地敏感的类(若JDK中某个类在运行时需要根据Locale对象来调整其功能,这个类就称为本地敏感类)去完成
一、Locale 的三种创建方式
- 获取默认的Locale
Locale locale = Locale.getDefault()
- 使用Locale 的静态对象
public static final Locale CANADA
public static final Locale CANADA_FRENCH
public static final Locale CHINA
public static final Locale CHINESE
public static final Locale ENGLISH
public static final Locale FRANCE
public static final Locale FRENCH
public static final Locale GERMAN
public static final Locale GERMANY
public static final Locale ITALIAN
public static final Locale ITALY
public static final Locale JAPAN
public static final Locale JAPANESE
public static final Locale KOREA
public static final Locale KOREAN
public static final Locale PRC
public static final Locale ROOT
public static final Locale SIMPLIFIED_CHINESE
public static final Locale TAIWAN
public static final Locale TRADITIONAL_CHINESE
public static final Locale UK
public static final Locale US
以上是Locale 提供的静态对象,每个返回一个Locale.
Locale locale = Locale.CHINESE
- 使用Locale 的构造函数(Locale提供了三种构造函数)
Locale(String language)
Locale(String language, String country)
Locale(String language, String country, String variant)
在WEB应用中可以通过request.getLocale()方法来获取.
稍等我们可以看到Locale 怎么使用.
DateFormat类
- DateFormat 类可以将一个日期/时间对象格式化为表示某个国家地区的日期/时间字符串,也可以将表示某个本地的日期/时间的字符串解析为相应的日期/时间对象
- DateFormat 类定义了一些用于描述日期/时间的显示模式的int 型的常量,包括FULL,LONG,MEDIUM,DEFAULT,SHORT,这些常量用于描述表示日期/时间字符串的长度。这些常量说明表示的日期/时间的确切格式取决于具体的国家和地区.
下面这是格式:
y 年 Year 1996; 96
M 年中的月份 Month July; Jul; 07
w 年中的周数 Number 27
W 月份中的周数 Number 2
D 年中的天数 Number 189
d 月份中的天数 Number 10
F 月份中的星期 Number 2
E 星期中的天数 Text Tuesday; Tue
a Am/pm 标记 Text PM
H 一天中的小时数(0-23) Number 0
k 一天中的小时数(1-24) Number 24
K am/pm 中的小时数(0-11) Number 0
h am/pm 中的小时数(1-12) Number 12
m 小时中的分钟数 Number 30
s 分钟中的秒数 Number 55
S 毫秒数 Number 978
z 时区 General time zone Pacific Standard Time; PST; GMT-08:00
Z 时区 RFC 822 time zone -0800
DateFormat 本身是一个抽象类.
- 若只希望通过DateFormat把一个Date对象转为一个字符串,可以通过DateFormat 的工厂方法来获取DateFormat 对象
- 可以获取值格式化Date 的DateFormat 对象getDateInstacnce(int Style,Locale locale)
- 可以获取值格式化Time 的DateFormat 对象getTimeInstacnce(int style,Locale locale)
- 可以既获取格式化Date,也格式化Time 的DateFormat 对象:getDateTimeInstance(int dateStyle, int timeDtyle.MEDIUM, Locale locale);
- 其中style 可以取值为:DateFormat 的常量:SHORT,MEDIUM,LONG,FULL,Locale 则为代表国家地区的Locale
- 通过对DateFormat 的format 方法来格式化Date对象到字符串.
@Test
public void testDateFormat(){
Date date = new Date();
System.out.println(date);
Locale locale = Locale.CHINA;
//创建 DateFormat 对象
DateFormat format =
DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.MEDIUM, locale);
String str = format.format(date);
System.out.println(str);
}
若有一个字符串,如何解析为一个Date 对象呢?
先创建DateFormat 对象:创建DateFomat 的子类SimpleDateFormat 对象,SimpleDateFormat(String pattern).
其中pattern 为日期,时间的格式,例如:yyyy-MM-dd hh:mm:ss
调用DateFormat 的parse方法来解析字符串到Date 对象.
@Test
public void testDateFormat2() throws ParseException {
Date date = new Date();
System.out.println(date);
Locale locale = Locale.CHINA;
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd E a HH:mm:ss");
//date ------>String
String format = dateFormat.format(date);
System.out.println(format);
//String ------>date
Date date1 = dateFormat.parse("2020-04-28 星期二 下午 16:43:40");
System.out.println(date1);
}
-
format(Date date)
将日期类转化为给定格式的字符串 -
parse(String date)
将字符串转化为日期类,如果给定了格式则必须按照给定格式的字符串输入.
NumberFormat 类
格式化数字到NumberFormat:格式化数字到字符串,或货币字符串的工具类.
- 通过工厂方法获取 NumberFormat 对象
NumberFormat.getNumberInstance(locale);//仅格式化为数字的字符串
NumberFormat.getCurrencyInstance(locale);//格式为货币的字符串 - 通过 format 方法来进行格式化
- 通过 parse 方法把一个字符串解析为一个Number 类型.
- 格式化数字字符串
@Test
public void testNumberFormat() throws ParseException {
double d = 123456789.123d;
Locale locale2 = Locale.CHINA;
//格式化为数字
NumberFormat format = NumberFormat.getNumberInstance(locale2);
String s = format.format(d);
System.out.println("格式化为数字:"+s);
s = "123,456,789.123";
d = (double) format.parse(s);
System.out.println(d);
}
- 格式化为货币
@Test
public void testNumberFormat() throws ParseException {
double d = 123456789.123d;
Locale locale2 = Locale.CHINA;
//格式化为数字
NumberFormat format = NumberFormat.getNumberInstance(locale2);
String s = format.format(d);
System.out.println("格式化为数字:"+s);
s = "123,456,789.123";
d = (double) format.parse(s);
System.out.println(d);
}
MessageFormat 类
MessageFormat:可以格式化模式字符串
模式字符串:带占位符的字符串:“Date:{0},Salary:{1}”;
可以通过 format 方法对模式字符串进行格式化
@Test
public void testMessageFormat(){
String str = "Date:{0},Salary:{1}";
Locale locale = Locale.CHINA;
Date date = new Date();
double sal = 12345.12;
MessageFormat messageFormat = new MessageFormat(str);
// String result = messageFormat.format(str, date, sal);
DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
String dateStr = dateFormat.format(date);
NumberFormat numberFormat = NumberFormat.getCurrencyInstance(locale);
String salStr = numberFormat.format(sal);
String result = messageFormat.format(str,dateStr,salStr);
System.out.println(result);
}
format方法的第一个参数为模式字符串,后面的参数分别填充了模式字符串的展位符.
ResourceBundle:资源包类
文本信息不能硬编码在程序代码中,而是需要将它们从应用程序中分离出来,在软件运行时根据本地信息读取相应的文本内容进行显示
基于以上原因:我们需要将文本信息单独配置一个文件出来.
- 这个类的作用就是读取资源属性文件(properties),然后根据.properties文件的名称信息(本地化信息),匹配当前系统的国别语言信息(也可以程序指定),然后获取相应的properties文件的内容。
- 使用这个类,properties需要遵循一定的命名规范,一般的命名规范是: 自定义名_语言代码_国别代码.properties,如果是默认的,直接写为:自定义名.properties。比如:i18n_en_US.properties
- 没有提供语言和地区的资源文件是系统默认的资源文件(如:i18n.properties),资源文件都必须是ISO-8859-1编码,因此,对于所有非西方语系的处理,都必须先将之转换为Java Unicode Escape格式。转换方法是通过JDK自带的工具native2ascii.
- 在类路径下需要有对应的资源文件:baseName.properties.
其中baseName是基名 - 可以使用baseName_语言代码_国家代码.properties来添加不同国家或地区的资源文件.
i18n_zh_CN.properties - 要求所有基名相同的资源文件的key必须完全一致.
- 可以使用natice2ascii命令来得到汉字对应的ascii码.
- 可以调用ResourceBundle的getBundle(基名,Locale实例)获取ResourceBundle
对象. - 可以调用ResourceBundle的getString(key)来获取资源文件的value字符串的值
- 结合DateFormat,NumberFormat,MessageFormat 即可实现国际化.
还是上面那个例子,我们将date和salary 来配置在文件中.
@Test
public void testResourceBundle(){
Locale locale = Locale.CHINA;
/**
* 下面这行代码程序首先会在classpath下寻找i18n_zh_CN.properties(因为是Locale.CHINA)
* 如果找不到则找i18n_zh.properties,还是找不到则找i18n.properties
* 还找不到就抛出异常
*/
ResourceBundle resourceBundle = ResourceBundle.getBundle("i18n", locale);
//从资源文件中读取参数.
String dateLabel = resourceBundle.getString("date");
String salaryLabel = resourceBundle.getString("salary");
String str = "{0}:{1},{2}:{3}";
Date date = new Date();
double sal = 12345.12;
DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
String dateStr = dateFormat.format(date);
NumberFormat numberFormat = NumberFormat.getCurrencyInstance(locale);
String salStr = numberFormat.format(sal);
String result = MessageFormat.format(str,dateLabel,dateStr,salaryLabel,salStr);
System.out.println(result);
}
这里有两个资源文件:
- i18n_en_US.properties
date=Date
salary=Salary
使用这个资源文件(Locale locale = Locale.US;)结果为:
Date:Apr 28, 2020,Salary:$12,345.12
- i18n_zh_CN.properties
date=\u65e5\u671f
salary=\u5de5\u8d44
使用这个资源文件(Locale locale = Locale.CHINA;)结果为:
日期:2020-4-28,工资:¥12,345.12
由此实现国际化