在Web系统的国际化问题中我们常提到本地化(Locale)显示,这里的本地化通常包括数字(NumberFormat),货币(NumberFormat.getCurrencyInstance()),时间(DateFormat)及任意文本(MessageFormat)本地化。下面,就对这几个类的大致用法做一个概括性的描述,需要更加详细的用法可以参考一个API,基本上都还是比较好理解的。
[size=large]【Locale】[/size]
[b]构造参数:[/b]
[b]1.Language.[/b]常用的有zh, en, de...
[b]2.Country.[/b]常用的有CN, TW, US, GB(Great Britain)...
[b]3.Variant.[/b]这个似乎只是起一个标识(供应商或浏览器)的作用,这里贴上API上的说明:vendor and browser specific code
Locale在VM加载的时候就已经初始化了默认值,所以我们可以通过Locale.getDefault()方法获取默认的Locale实例。当然也可以根据上面的三个参数手动创造新的Locale实例,如new Locale(Language, Country, Variant),示例如下:
在Java.text包下面的格式化类中,基本上每一个格式化都会与具体的Locale相关,也就是进行特定语境下的Format。所以,通常在构造一个Format实体的时候,我们通常会传递一个Locale实体作为参数。
[size=large]【NumberFormat】[/size]
NumberFormat可以对所有的Number进行Format,而针对特殊的Decimal,Java专门创建了一个DecimalFormat予以处理。在处理Decimal的时候需要考虑小数位的取舍位数,数字分隔符等问题,而使用类似“#,##0.0#”的格式化字符串可以一次解决多个问题,是比较推荐的使用方法。示例如下:
[size=large]【DateFormat】[/size]
DateFormat是我们比较常用的一种格式化方法,除了使用DateFormat之外,对于页面显示,我个人更加喜欢SimpleDateFormat,这个类可以更加人性化地显示日常所需。示例如下:
[size=large]【MessageFormat】[/size]
MessageFormat在国际化显示Message时经常用到,其最大的特点就是对于消息的定制显示,所谓定制也就是模版化,即每个消息在显示之前都需要定制其显示模版,在显示的时候只需要填充需要的变量便可以达到动态显示的目的了;另外,除了动态显示之外,这个类还可以根据字符串与字符串模版解析出之前传递进来的参数数组,但其解析存在很大的不确定性,目前,个人还没找到其卓越贡献之处。示例如下:
很明显,我们不会在代码里显示的调用判断语句去决定调用什么格式化显示,这样的代码显得非常不雅观,而且没有扩展性、可维护性,所以我们通常都根据Locale类自动决定页面的显示,这里涉及到另一个概念:Resource Bundle。
[size=large]【Resource Bundle】[/size]
要构成Bundle,自然需要一堆Properties文件。那么VM又是如何让这些Bundle文件有序工作的呢,根据Core Java的说明可知,getBundle方法会加载文件会涉及到以下几个Properties文件:
[quote]1.bundleName_currentLocaleLanguage_currentLocaleCountry_currentLocaleVariant
2.bundleName_currentLocaleLanguage_currentLocaleCountry
3.bundleName_currentLocaleLanguage
4.bundleName_defaultLocaleLanguage_defaultLocaleCountry_defaultLocaleVariant
5.bundleName_defaultLocaleLanguage_defaultLocaleCountry
6.bundleName_defaultLocaleLanguage
7.bundleName
[/quote]
每次,getBundle方法加载Properties文件时,首先会根据Locale加载符合BundleName_Language_Country_Variant命名规范的文件,然后还会依次加载符合BundleName_Language_Country,BundleName_Language,BundleName命名规范的文件,如果在加载过程中没有加载到相关文件,则会将Locale转换成Default Locale再加载一次。并最终将这些Properties文件组合成一个Bundle,形成一定的继承关系。其中,BundleName_Language_Country_Variant为最子一级,BundleName为最父一级,在getString或getObject查询相关内容时,则从最子级开始,往最父级查询,直到找到相关内容。
在使用Bundle进行国际化处理时,需要注意Properties文件需要转成通用的UTF-8格式,以达到通用的转码目的。这样,如果使用eclipse编辑国际化资源文件,则可以直接使用PropertiesEditor插件。
示例代码如下:
[b]资源文件命名:[/b]
[quote]
test_en_US.properties
test_zh_CN.properties
[/quote]
[size=large]【Locale】[/size]
[b]构造参数:[/b]
[b]1.Language.[/b]常用的有zh, en, de...
[b]2.Country.[/b]常用的有CN, TW, US, GB(Great Britain)...
[b]3.Variant.[/b]这个似乎只是起一个标识(供应商或浏览器)的作用,这里贴上API上的说明:vendor and browser specific code
Locale在VM加载的时候就已经初始化了默认值,所以我们可以通过Locale.getDefault()方法获取默认的Locale实例。当然也可以根据上面的三个参数手动创造新的Locale实例,如new Locale(Language, Country, Variant),示例如下:
Locale locale1 = Locale.getDefault();
System.out.println("locale default display name: " + locale1.getDisplayName());
System.out.println("locale default display language: " + locale1.getDisplayLanguage());
System.out.println("locale default display country: " + locale1.getDisplayCountry());
System.out.println("locale default display variant: " + locale1.getDisplayVariant());
Locale locale2 = Locale.getDefault(Locale.Category.FORMAT);
System.out.println("locale default display name: " + locale2.getDisplayName());
System.out.println("locale default display language: " + locale2.getDisplayLanguage());
System.out.println("locale default display country: " + locale2.getDisplayCountry());
System.out.println("locale default display variant: " + locale2.getDisplayVariant());
Locale locale3 = Locale.getDefault(Locale.Category.FORMAT);
System.out.println("locale default display name: " + locale3.getDisplayName());
System.out.println("locale default display language: " + locale3.getDisplayLanguage());
System.out.println("locale default display country: " + locale3.getDisplayCountry());
System.out.println("locale default display variant: " + locale3.getDisplayVariant());
Locale locale4 = new Locale("en", "GB");
System.out.println("locale default display name: " + locale4.getDisplayName());
System.out.println("locale default display language: " + locale4.getDisplayLanguage());
System.out.println("locale default display country: " + locale4.getDisplayCountry());
System.out.println("locale default display variant: " + locale4.getDisplayVariant());
在Java.text包下面的格式化类中,基本上每一个格式化都会与具体的Locale相关,也就是进行特定语境下的Format。所以,通常在构造一个Format实体的时候,我们通常会传递一个Locale实体作为参数。
[size=large]【NumberFormat】[/size]
NumberFormat可以对所有的Number进行Format,而针对特殊的Decimal,Java专门创建了一个DecimalFormat予以处理。在处理Decimal的时候需要考虑小数位的取舍位数,数字分隔符等问题,而使用类似“#,##0.0#”的格式化字符串可以一次解决多个问题,是比较推荐的使用方法。示例如下:
class NumberFormatTest {
private Locale loc;
public NumberFormatTest(Locale loc){
this.loc = loc;
}
public void numberTest(){
NumberFormat mft = NumberFormat.getNumberInstance(loc);
double amt = 123123123.12312;
System.out.println(mft.format(amt));
System.out.println(mft.format(amt, new StringBuffer("&&"), new FieldPosition(13213123)).toString());
}
public void currencyTest(){
NumberFormat mft = NumberFormat.getCurrencyInstance(loc);
double amt = 123123123.12312;
System.out.println(mft.format(amt));
System.out.println(mft.format(amt, new StringBuffer("&&"), new FieldPosition(-1)).toString());
}
public void percentTest(){
NumberFormat mft = NumberFormat.getPercentInstance(loc);
double amt = 123123123.12312;
System.out.println(mft.format(amt));
System.out.println(mft.format(amt, new StringBuffer("&&"), new FieldPosition(-1)).toString());
}
public void decimalTest(){
/*
* If you supply a pattern with multiple grouping characters,
* the interval between the last one and the end of the integer is the one that is used
* 分隔间距的大小依据最后一组分组的长度确定,在有小数点时是指在小数点前的一组
*/
DecimalFormat dft1 = new DecimalFormat("#,##");
System.out.println(dft1.format(111111.11));
System.out.println(dft1.format(.11));
System.out.println(dft1.format(111111));
/*
* #和0都指代数字,只不过0在该位置上没有数据的时候以0代替,而#则直接忽略不显示
*/
DecimalFormat dft2 = new DecimalFormat("#,##0.0#");
System.out.println(dft2.format(111111.11));
System.out.println(dft2.format(.11));
System.out.println(dft2.format(111111));
}
public static void main(String[] args){
Locale loc = new Locale("en", "US");
NumberFormatTest test = new NumberFormatTest(loc);
test.decimalTest();
}
}
[size=large]【DateFormat】[/size]
DateFormat是我们比较常用的一种格式化方法,除了使用DateFormat之外,对于页面显示,我个人更加喜欢SimpleDateFormat,这个类可以更加人性化地显示日常所需。示例如下:
public class DateTimeTest {
private Locale loc;
public DateTimeTest(Locale loc){
this.loc = loc;
}
public void dateTest(){
DateFormat dft = DateFormat.getDateInstance(DateFormat.FULL, loc);
System.out.println(dft.format(new Date()));
SimpleDateFormat smft = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.S", loc);
System.out.println(smft.format(new Date()));
}
/**
* @param args
*/
public static void main(String[] args) {
DateTimeTest test = new DateTimeTest(Locale.getDefault());
test.dateTest();
}
}
[size=large]【MessageFormat】[/size]
MessageFormat在国际化显示Message时经常用到,其最大的特点就是对于消息的定制显示,所谓定制也就是模版化,即每个消息在显示之前都需要定制其显示模版,在显示的时候只需要填充需要的变量便可以达到动态显示的目的了;另外,除了动态显示之外,这个类还可以根据字符串与字符串模版解析出之前传递进来的参数数组,但其解析存在很大的不确定性,目前,个人还没找到其卓越贡献之处。示例如下:
public class MessageTest {
/**
* @param args
*/
public static void main(String[] args) {
String pattern = "On {2,date,long}, a {0} destroyed {1} houses and caused {3,number,currency} of damage.";
MessageFormat mft = new MessageFormat(pattern, Locale.getDefault());
String msg = mft.format(new Object[]{
"hurricane",
99,
new Date(),
100000000
});
System.out.println(msg);
/*
* parse with its type
*/
Object[] objs;
try {
objs = mft.parse(msg);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return;
}
if(objs != null){
for(int i = 0; i < objs.length; i++){
System.out.print(objs[i].getClass() + " : ");
System.out.println(objs[i]);
}
}
/*
* When a single argument is parsed more than once in the string,
* the last match will be the final result of the parsing
*/
MessageFormat mf = new MessageFormat("{0,number,#.##}, {0,number,#.#}");
Object[] objs1 = {new Double(3.1415)};
String result = mf.format( objs1 );
// result now equals "3.14, 3.1"
System.out.println(result);
objs1 = null;
objs1 = mf.parse(result, new ParsePosition(0));
// objs now equals {new Double(3.1)}
for(int i = 0; i < objs1.length; i++){
System.out.println(objs1[i]);
}
MessageFormat mf2 = new MessageFormat("{0}, {0}, {0}");
String forParsing = "x, y, z";
Object[] objs2 = mf2.parse(forParsing, new ParsePosition(0));
// result now equals {new String("z")}
for(int i = 0; i < objs2.length; i++){
System.out.println(objs2[i]);
}
}
}
很明显,我们不会在代码里显示的调用判断语句去决定调用什么格式化显示,这样的代码显得非常不雅观,而且没有扩展性、可维护性,所以我们通常都根据Locale类自动决定页面的显示,这里涉及到另一个概念:Resource Bundle。
[size=large]【Resource Bundle】[/size]
要构成Bundle,自然需要一堆Properties文件。那么VM又是如何让这些Bundle文件有序工作的呢,根据Core Java的说明可知,getBundle方法会加载文件会涉及到以下几个Properties文件:
[quote]1.bundleName_currentLocaleLanguage_currentLocaleCountry_currentLocaleVariant
2.bundleName_currentLocaleLanguage_currentLocaleCountry
3.bundleName_currentLocaleLanguage
4.bundleName_defaultLocaleLanguage_defaultLocaleCountry_defaultLocaleVariant
5.bundleName_defaultLocaleLanguage_defaultLocaleCountry
6.bundleName_defaultLocaleLanguage
7.bundleName
[/quote]
每次,getBundle方法加载Properties文件时,首先会根据Locale加载符合BundleName_Language_Country_Variant命名规范的文件,然后还会依次加载符合BundleName_Language_Country,BundleName_Language,BundleName命名规范的文件,如果在加载过程中没有加载到相关文件,则会将Locale转换成Default Locale再加载一次。并最终将这些Properties文件组合成一个Bundle,形成一定的继承关系。其中,BundleName_Language_Country_Variant为最子一级,BundleName为最父一级,在getString或getObject查询相关内容时,则从最子级开始,往最父级查询,直到找到相关内容。
在使用Bundle进行国际化处理时,需要注意Properties文件需要转成通用的UTF-8格式,以达到通用的转码目的。这样,如果使用eclipse编辑国际化资源文件,则可以直接使用PropertiesEditor插件。
示例代码如下:
public class ResourceTest {
/**
* @param args
* @throws UnsupportedEncodingException
*/
public static void main(String[] args) throws UnsupportedEncodingException {
//注意中文的资源文件需要先进行native2ascii的转码,使其成为ascii码才能识别
Locale loc = new Locale("zh", "CN");
//getBundle方法默认使用ResourceTest.ClassLoader加载资源,默认路径为classes根目录
ResourceBundle bundle = ResourceBundle.getBundle("text/internationalization/resource/test", loc);
String name = bundle.getString("name");
System.out.println(name);
//常用的转码方式,以ISO_8859_1为中介转为需要的编码,也就是都需要先变成二进制的编码,因为所有编码都是对二进制进行组合识别
//System.out.println(new String(name.getBytes("ISO_8859_1"), "UTF-8"));
}
}
[b]资源文件命名:[/b]
[quote]
test_en_US.properties
test_zh_CN.properties
[/quote]