格式化消息

-- Start

任何程序都需要向用户显示消息, 无论是成功的消息或者是失败的消息. 通常我们通过字符串拼接来构造消息, 下面是一个简单的例子.

public static void main(String[] args) throws Exception {
	Scanner in = new Scanner(System.in);

	System.out.print("What is your name? ");
	String name = in.nextLine();

	System.out.print("What's your birthday? ");
	Date birthday = DateFormat.getDateInstance().parse(in.nextLine());

	int age = getAge(birthday);

	// 通过字符串拼接构造消息
	System.out.println("Welcome " + name + ", your birthday is " + birthday + " and you are " + age + " years old.");
}

public static int getAge(Date birthday) {
	Calendar now = Calendar.getInstance();

	Calendar birthdayCal = Calendar.getInstance();
	birthdayCal.setTime(birthday);

	return now.get(Calendar.YEAR) - birthdayCal.get(Calendar.YEAR);
}
如果我们只面向一个国家或地区的用户, 上面的做法是可以的. 但是有好多大公司, 它们在全球各个国家和地区都有分公司, 这就要求我们的根据不同的语言环境来显示消息. MessageFormat 类提供的方法可以使我们很容易构造消息. ResourceBundle 存储了国际化的消息, 下面是一个简单的例子.

public static void main(String[] args) throws Exception {

	displayMessage(Locale.CHINA);
	System.out.println();
	displayMessage(Locale.US);
}

private static void displayMessage(Locale currentLocale) throws Exception {

	ResourceBundle messages = ResourceBundle.getBundle("MessageBundle", currentLocale);

	Scanner in = new Scanner(System.in);

	System.out.print(messages.getString("nameInfo"));
	String name = in.nextLine();

	System.out.print(messages.getString("birthdayInfo"));
	Date birthday = DateFormat.getDateInstance(DateFormat.DEFAULT, currentLocale).parse(in.nextLine());

	int age = getAge(birthday);

	MessageFormat formatter = new MessageFormat("");
	formatter.setLocale(currentLocale);
	formatter.applyPattern(messages.getString("helloMsg"));

	System.out.println(formatter.format(new Object[] { name, birthday, age }));
}

private static int getAge(Date birthday) {
	Calendar now = Calendar.getInstance();

	Calendar birthdayCal = Calendar.getInstance();
	birthdayCal.setTime(birthday);

	return now.get(Calendar.YEAR) - birthdayCal.get(Calendar.YEAR);
}

MessageBundle_en_US.properties

nameInfo = What is your name? 
birthdayInfo = What's your birthday? 
helloMsg = Welcome {0}, your birthday is {1,date,medium} and you are {2,number,integer} years old.

MessageBundle_zh_CN.properties

#你叫什么名字? 
nameInfo = \u4f60\u53eb\u4ec0\u4e48\u540d\u5b57\u003f\u0020

#你的出生年月日是? 
birthdayInfo = \u4f60\u7684\u51fa\u751f\u5e74\u6708\u65e5\u662f\u003f\u0020

#欢迎 你{0}, 你的生日是{1,date,medium}, 你今年 {2,number,integer} 岁了.
helloMsg = \u6b22\u8fce\u0020\u4f60{0}, \u4f60\u7684\u751f\u65e5\u662f{1,date,medium}, \u4f60\u4eca\u5e74 {2,number,integer} \u5c81\u4e86.

正如上面我们看到的那样, 我们需要把 Resouce 中的中文转换成 Unicode 编码, 下面的小程序可以帮助我们完成这样的工作, 或者你也可以使用 Java 安装目录下的  native2ascii 命令来完成同样的任务.

public static void main(String[] args) throws Exception {
	String s = "你叫什么名字? ";
	for (char c : s.toCharArray()) {
	    String codePoint = "0000" + Integer.toHexString(c);
	    System.out.print("\\u" + codePoint.substring(codePoint.length() - 4));
	}
}

上面例子的运行结果如下:

你叫什么名字? Shang Bo
你的出生年月日是? 2000-01-01
欢迎 你Shang Bo, 你的生日是2000-1-1, 你今年 12 岁了.

What is your name? Scott
What's your birthday? January 1, 2000
Welcome Scott, your birthday is Jan 1, 2000 and you are 12 years old.

通常我们要向消息中插入 日期, 时间, 字符串, 数字, 货币和百分比等类型的变量. 在消息中, 需要插入的变量处于大括号中, 可以有如下形式:

// 形式
{ ArgumentIndex }
{ ArgumentIndex , FormatType }
{ ArgumentIndex , FormatType , FormatStyle }

// 例子
Welcome {0}, your birthday is {1,date,medium} and you are {2,number,integer} years old.
formatter.format(new Object[] { name, birthday, age })

// 说明
ArgumentIndex 指的是 format 方法中 Object 对象数组中的索引.
FormatType 指的是数据类型, 可以是 number, date, time, choice 之一.
FormatStyle 指的是格式化样式, 可以是 short, medium, long, full, integer, currency, percent 或 自定义格式.

下面的表格总结了 FormatType 和 FormatStyle 和 Format 类的对应关系.

Format Type Format Style Subformat Created
(none) (none) null
number (none) NumberFormat.getInstance(getLocale())
 integer NumberFormat.getIntegerInstance(getLocale())
 currency NumberFormat.getCurrencyInstance(getLocale())
 percent NumberFormat.getPercentInstance(getLocale())
 SubformatPattern new DecimalFormat(subformatPattern, DecimalFormatSymbols.getInstance(getLocale()))
date (none) DateFormat.getDateInstance(DateFormat.DEFAULT, getLocale())
 short DateFormat.getDateInstance(DateFormat.SHORT, getLocale())
 medium DateFormat.getDateInstance(DateFormat.DEFAULT, getLocale())
 long DateFormat.getDateInstance(DateFormat.LONG, getLocale())
 full DateFormat.getDateInstance(DateFormat.FULL, getLocale())
 SubformatPattern new SimpleDateFormat(subformatPattern, getLocale())
time (none) DateFormat.getTimeInstance(DateFormat.DEFAULT, getLocale())
 short DateFormat.getTimeInstance(DateFormat.SHORT, getLocale())
 medium DateFormat.getTimeInstance(DateFormat.DEFAULT, getLocale())
 long DateFormat.getTimeInstance(DateFormat.LONG, getLocale())
 full DateFormat.getTimeInstance(DateFormat.FULL, getLocale())
 SubformatPattern new SimpleDateFormat(subformatPattern, getLocale())
choice SubformatPattern new ChoiceFormat(subformatPattern)


有时候看上去非常简单的消息, 构造起来却非常困难, 如: 我想构造如下消息.

这有n个人

如果n是0的话, 我们不能说 "这有0个人", 只能说 "这没有人", 英语更复杂.

there is no poeple.
there is one person.
there are n poeple.
如何构造这样的消息呢? 使用 ChoiceFormat 类可以很容易解决这个问题.

public static void main(String[] args) throws Exception {

	displayMessage(Locale.CHINA);
	System.out.println();
	displayMessage(Locale.US);
}

private static void displayMessage(Locale currentLocale) throws Exception {

	ResourceBundle messages = ResourceBundle.getBundle("MessageBundle", currentLocale);
	
	double[] limits = {0, 1, 2}; 
	String[] formats = {messages.getString("no"), messages.getString("one"), messages.getString("more")};
	ChoiceFormat choiceForm = new ChoiceFormat(limits, formats);

	MessageFormat formatter = new MessageFormat("");
	formatter.setLocale(currentLocale);
	formatter.applyPattern(messages.getString("pattern"));
	formatter.setFormats(new Format[] {choiceForm, NumberFormat.getInstance()});

	System.out.println(formatter.format(new Object[] {0, 0})); 
	System.out.println(formatter.format(new Object[] {1, 1}));
	System.out.println(formatter.format(new Object[] {2, 2}));
	System.out.println(formatter.format(new Object[] {2, 3}));
	System.out.println(formatter.format(new Object[] {2, 4}));
	System.out.println(formatter.format(new Object[] {2, 5}));
	
	// 1. 传入的第一个参数是 2
	// 2. 到limits数组中查找 2, 找到 2 在数组中的下标是 2
	// 3. 到formats数组中查找 下标是 2 的消息, 英语环境是 are {1} people
	// 4. 用第二个参数 6 替换 消息中的  {1} 得到 are 6 people
	System.out.println(formatter.format(new Object[] {2, 6})); 
}

MessageBundle_zh_CN.properties

# 这
pattern = \u8fd9{0}.

# 没有人
no = \u6ca1\u6709\u4eba

# 有1个人
one = \u6709\u0031\u4e2a\u4eba

#有{1}个人
more = \u6709{1}\u4e2a\u4eba

MessageBundle_en_US.properties

pattern = there {0}.
no = is no people
one = is one person
more = are {1} people

运行结果如下:

这没有人.
这有1个人.
这有2个人.
这有3个人.
这有4个人.
这有5个人.
这有6个人.

there is no people.
there is one person.
there are 2 people.
there are 3 people.
there are 4 people.
there are 5 people.
there are 6 people.

--- 更多参见: Java 精萃
-- 声 明:转载请注明出处
-- Last Updated on 2012-08-05
-- Written by ShangBo on 2012-05-15
-- End
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值