4.4.5 消息文本

这里要介绍的是对字符串进行格式化和解析的java.text.MessageFormat类。之所以到此才介绍,是因为MessageFormat类在本身的模式之中允许使用上面介绍的DateFormat、NumberFormat和ChoiceFormat类的对象作为其内部的子模式。当一个字符串中包含日期、时间和数字的时候,就可以利用MessageFormat类的这个特性。MessageFormat类的重点在于其使用的模式,可以在构造方法中提供,也可以通过applyPattern方法来改变。如果使用某个模式的MessageFormat类的对象只打算使用一次,可以用MessageFormat类中的静态方法format来快速根据某个模式进行格式化,不需要单独创建新的对象。在每个模式中除了直接显示的字符串之外,一般都包含在运行时刻进行替换的实际参数的占位符。在进行消息国际化时的一个基本原则是避免在代码中进行一段消息的连接操作,要把这整段消息都放在资源包中,由MessageFormat类来处理。例如,要生成类似“你好,张三,今天是星期五”这样的消息内容,其中的姓名和日期是可变的。不推荐用字符串拼接的做法把消息中的几个部分连接起来,而推荐把这一段消息都放在资源包中,写成“你好,{0},今天是{1}”这样的带参数占位符的形式。这其中的重要原因是不同语言的行文顺序是不同的。如果按照字符串相加的做法,某些语言就无法翻译成通顺的句子。

根据上面的示例,在MessageFormat类的模式中,参数占位符是通过在“{}”中包含参数的序号的方式来表示的。这些序号对应的是调用format方法进行格式化时提供的实际参数的数组中的位置,以及调用parse方法所得到的返回值的数组中的位置。对于每个参数,还可以进一步声明格式化的具体类型和模式。格式化类型的可选值有number、date、time、choice,而对应的模式则既可以是标准模式,又可以是自定义模式。例如,对number类型来说,既可以使用NumberFormat类支持的标准模式integer、currency和percent,又可以使用自定义的模式;对于date和time类型来说,可以使用的标准模式是DateFormat类支持的short、medium、long和full;而对于choice类型来说,则只支持自定义的模式。代码清单4-21给出了MessageFormat类的使用示例,一共定义了3个数字类型的参数,前两个使用的是标准模式integer和currency,而第三个使用的是自定义的模式。

代码清单4-21 MessageFormat类的使用示例


public void formatWithNumber(){

String pattern="购买了{0,number, integer}件商品,单价为{1,number, currency},合计:{2,number,\u00A4#,###.##}";

MessageFormat format=new MessageFormat(pattern);

int count=3;

double price=1599.3;

double total=price*count;

format.format(new Object[]{count, price, total});

}


除了通过MessageFormat类的模式字符串来声明内部所使用的子模式之外,也可以通过MessageFormat类的方法来进行设置。如果程序中提供了自己的Format类的子类实现,则可以通过这种方式来进行设置。与设置相关的一共有4个方法,区别在于是设置一个还是多个Format类的对象,以及是根据Format类的对象在格式化模式中的出现顺序还是参数顺序。考虑格式化模式“共有{1}人参加会议,其中{0}来自本部门”,其中定义了两个参数,不过第二个参数出现在第一个的前面。如果希望按照在模式中实际参数值的顺序来设置两个参数对应的Format类的对象,可以使用setFormatsByArgumentIndex方法;如果只希望设置单个参数的Format类的对象,可以使用setFormatByArgumentIndex方法;如果希望按照在模式中的出现顺序来设置,可以相应地使用setFormats和setFormat方法。由于表达同样语义的字符串在经过翻译之后,其中参数的出现位置可能发生变化,因此推荐的做法是基于参数值的顺序进行设置。

把程序中的文本都抽取出来放到资源文件中,这对于程序本身的可维护性也是很有帮助的。有些文本可能会在程序中出现多次,如果每次都是直接在源代码中使用,当文本发生变化的时候,需要在多个地方进行修改。如果文本存放在资源文件中,引用时使用的都是抽象的固定的键,对应的内容可以随意修改,而且只需要修改属性文件这一个地方即可。