首页 > 编程语言 >第十一章《Java实战常用类》第13节:ResourceBundle类和MessageFormat类

第十一章《Java实战常用类》第13节:ResourceBundle类和MessageFormat类

时间:2023-01-02 14:34:28浏览次数:68  
标签:11 文件 13 Java 读取 MessageFormat info Message 资源

​很多大型软件都要实现国际化。所谓国际化是指同一套代码能够根据语言环境在软件界面上显示出不同的语言文字,此外,还应该根据不同的语言环境显示出对各种数据不同的表达风格,例如以当地人习惯的形式书写日期以及货币金额等。使用很多if...else这样的判断语句来确定界面显示哪种语言文字虽然能够实现国际化,但这种做法显然非常的麻烦,并且后期的维护也非常困难。目前最合理的解决方案是把界面上的文字保存在多个资源文件中,然后根据语言环境的不同,找到对应的资源文件,再把资源文件中的文字显示到界面上。这样做的最大好处就是减少了代码量,并且后期维护软件的成本也会降低很多,同时也提高了软件的可扩展性。

在Java语言中,资源文件一般都是后缀为properties的文件。这种资源文件以“键值对”的方式来保存信息,例如:​

name=Mike​

以上这一行数据就是以键值对的形式表示的,=左边的name称为键,=右边的Mike称为值,键和值共同形成数据的整体,在查找数据时通过指定键来找到对应的值。资源文件通常要与Java类的名称保持相同的命名风格,因此资源文件名称的首字母要大写。此外,资源文件要放到CLASSPATH下才可以被访问到。如果读者不明白什么是CLASSPATH,可以不用太在意这个概念,只要知道把资源文件和Java源文件放在一起就可以了,这样编译器在编译源文件时就会自动把资源文件复制到CLASSPATH下。​

资源文件中的字符都以Unicode形式完成编码,这就要求必须为文件设置正确的编码形式。设置步骤为:从File菜单中单击settings这个菜单项,在菜单项弹出的对话框中选择Editor选项,在这个选项下找到FileEncodings子选项并单击,之后勾选“Transparent native-to-ascii conversion”,最后单击“OK”按钮完成设置,如图11-32所示。​

第十一章《Java实战常用类》第13节:ResourceBundle类和MessageFormat类_Java

图11-32设置字符编码​

为演示资源文件的读取操作,需要先在源程序文件夹src中建立一个资源文件。读者可以先选中src文件夹,然后从菜单中按照File->new->Resource Bundle的顺序新建一个名为Message的资源文件,如图11-33所示。​

第十一章《Java实战常用类》第13节:ResourceBundle类和MessageFormat类_MessageFormat_02

图11-33新建资源文件​

在图11-32所示的对话框中,只需要填写资源文件的名称就可以,不需要填写后缀properties,填写完文件名称后,单击“OK”按钮即可完成资源文件的创建。创建好资源文件后,在资源文件中写入一组键值对“info=默认”,如图11-34所示。​

第十一章《Java实战常用类》第13节:ResourceBundle类和MessageFormat类_国际化_03

图11-34在资源文件中添加键值对​

建立好这个资源文件后,再按相同方式建立两个资源文件,一个名为Message_en_US,并且在这个资源文件中写入一组键值对“info=welcome”,另一个名为Message_zh_CN,这个文件中写入的键值对是“info=欢迎”。这样的话,工程中就有了三个资源文件,它们分别用来保存两种语言的文字(中文和英文)。当建立好这两个资源文件后,就可以编写程序读取这些资源文件。读取资源文件需要用到ResourceBundle类,这个类在java.uitl包下,它是一个抽象类,程序员需要通过getBundle()静态方法创建这个类的子类对象。下面的【例11_33】展示了如何创建ResourceBundle类的子类对象以及使用这个对象读取资源文件。​

【例11_33读取资源文件】

Exam11_33.java​

import java.util.Locale;
import java.util.ResourceBundle;
public class Exam11_33 {
public static void main(String[] args) {
Locale lcUS = new Locale("en","US");//美国英语
Locale lcFR = new Locale("fr","FR");//法国法语
//按默认语言环境读取资源文件
ResourceBundle rb1 = ResourceBundle.getBundle("Message");//①
//获取资源文件中info键对应的值
String str1 = rb1.getString("info");
//读取美国英语对应的资源文件
ResourceBundle rb2 = ResourceBundle.getBundle("Message",lcUS);//②
//获取资源文件中info键对应的值
String str2 = rb2.getString("info");
//读取法国法语对应的资源文件
ResourceBundle rb3 = ResourceBundle.getBundle("Message",lcFR);//③
//获取资源文件中info键对应的值
String str3 = rb3.getString("info");
System.out.println("默认语言环境下资源文件中info键对应的值:"+str1);
System.out.println("美国英语环境下资源文件中info键对应的值:"+str2);
System.out.println("法国法语环境下资源文件中info键对应的值:"+str3);
}
}

【例11_33】的运行结果如图11-35所示。​

第十一章《Java实战常用类》第13节:ResourceBundle类和MessageFormat类_MessageFormat_04

图11-35【例11_33】运行结果​

【例11_33】虽然只是展示了如何读取资源文件,但它实质上展示了国际化的实现原理。【例11_33】的代码中,调用getBundle()方法创建对象时给方法传递的字符串参数是“Message”,这表示要读取以Message开头的资源文件。语句①中的getBundle()方法只有一个表示文件名称的参数,并没有表示语言环境的参数,但从图11-35可以看出:语句①所创建的ResourceBundle类对象读取资源文件时,读取的是Message_zh_CN中的数据,是这是因为当前操作系统默认的语言环境代码是“zh_CN”。这个运行结果可以说明:getBundle()方法在没有指定语言环境的情况下,能够根据当前操作系统的语言环境自动找到对应的资源文件并获取其中的信息。由此可以得知:同一套软件系统安装到不同语言环境的操作系统中,无需程序员人为设定软件应该读取哪一个资源文件,它完全可以自己找到对应的资源文件,也就是说,在英文操作系统中,软件会自动读取英文对应的资源文件,在中文操作系统中,软件又会自动读取中文对应的资源文件。而程序员要做的只是正确的命名所有的资源文件,并且在读取资源文件后把其中的信息显示到软件界面上。​

语句②的getBundle()方法有两个参数,第一个参数指定要读取的资源文件名称,第二个参数指定了一种语言环境,语句②所指定的语言环境是美国英语。从图11-35可以看出:当指定了语言环境是美国英语后,ResourceBundle类对象读取Message_en_US这个资源文件中的数据,这是因为美国英语的语言代码是“en_US”,它与Message_en_US这个资源文件名称的后半部分吻合。getBundle()方法在指定了语言环境的情况下,就会根据所指定的语言环境来决定读取哪一个资源文件。由此可见,程序员可以不受操作系统语言环境的限制自由的指定读取哪一个资源文件,这样的机制能够让软件具有切换界面语言文字的功能,程序员只要按用户选择的语言把对应的Locale类对象传递给getBundle()方法,在方法读取到资源文件中的数据后再把这些数据显示到软件界面上即可。​

语句③的getBundle()方法指定的语言环境是法国法语,法国法语的代码是“fr_FR”,但本例中并没有创建与之对应的资源文件。从图11-35可以看出,语句③所创建出的ResourceBundle类对象最终读取的是Message_zh_CN这个资源文件中的数据,这是因为如果一旦找不到与某种语言环境对应的资源文件,那么就会选择读取操作系统默认语言环境所对应的资源文件。​

本例中还有一个Message.properties资源文件,这个文件在本例中一直没有被读取,那么什么情况下会读取Message.properties资源文件中的数据呢?它会在其他资源文件找不到指定键的情况下被读取。例如:​

String str1 = rb1.getString("info");​

这条语句是要读取Message_zh_CN.properties这个资源文件中info键所对应的值,假设这个资源文件中没有以info作为键的数据,那么在根据键名称找不到数据的情况下会读取Message.properties这个资源文件中的数据,也就是说:Message.properties是其他所有以Message开头的资源文件的“替补”。如果替补文件中也没有以info作为键的数据,那么读取数据的操作将会抛出异常。读者可尝试把Message_zh_CN.properties中的数据删除后重新读取资源文件,看是否能读取到Message.properties中的数据。​

从【例11_33】可以看出:在为软件创建资源文件时,通常都是创建一组名称前半部分相同的文件,名称的后半部分以语言代码命名。专业上把文件名前半部分不包含语言代码的部分称为“基础名称”或“基本名称”,例如本例中这些资源文件的前半部分都是“Message”,这个“Message”就是基础名称。而getBundle()方法在指定资源文件时只需要指出基础名称即可。​

【例11_33】虽然能够读取资源文件中的数据,但这些数据都是静态的,不包含动态信息。所谓动态信息就是指不固定的信息,这部分信息在程序运行时确定,例如:登录界面上有欢迎信息,信息的前半部分是“你好”,如果登录名是张三,则欢迎信息是“你好张三”,而如果登录名是李四,则显示“你好李四”。实际上,在读取资源文件中的数据时可以添加动态信息,动态信息使用一对大括号充当占位符,程序实际运行时,会用相应的数据代替占位符。此外,大括号中间还要添加一个整数,它用于表示动态信息的编号,需要注意:动态信息的编号是从0开始的。为演示动态信息的显示效果,读者需把资源文件中info所对应的值后面加上{0},这样Message.properties中的数据变为:​

info=默认{0}​
Message_zh_CN.properties中的数据变为:​
info=欢迎{0}​
而Message_en_US.properties中的数据变为:​
info=welcome{0}​

想用动态信息代替占位符,需要用MessageFormat的类对象完成,这个类位于java. text包下,它有一个format()静态方法能够用动态信息替换占位符。下面的【例11_34】展示了显示动态信息的过程。​

【例11_34显示动态信息】

Exam11_34.java​

import java.util.*;
import java.text.MessageFormat;
public class Exam11_34 {
public static void main(String[] args) {
Locale lcUS = new Locale("en","US");//美国英语
Scanner sc = new Scanner(System.in);
System.out.println("请输入登录名:");
String name = sc.nextLine();
//按默认语言环境读取资源文件
ResourceBundle rb1 = ResourceBundle.getBundle("Message");
//获取资源文件中info键对应的值
String info1 = rb1.getString("info");
//以name替换占位符
String str1 = MessageFormat.format(info1,name);//①
//读取美国英语对应的资源文件
ResourceBundle rb2 = ResourceBundle.getBundle("Message",lcUS);
//获取资源文件中info键对应的值
String info2 = rb2.getString("info");
//以name替换占位符
String str2 = MessageFormat.format(info2,name);
System.out.println("中文问候语:"+str1);
System.out.println("英语问候语:"+str2);
}
}

【例11_34】中,语句①中format()方法的作用是用name字符串代替info键所对应信息中的占位符,这样的代替操作会形成一个新的字符串,format()方法就会返回这个新的字符串。如果info键所对应的信息中包含多个占位符,那么可以继续给format()方法添加更多的字符串参数来按顺序依次代替这些占位符。【例11_34】的运行结果如图11-36所示。​

第十一章《Java实战常用类》第13节:ResourceBundle类和MessageFormat类_Java_05

图11-36【例11_34】运行结果​

从图11-36可以看出:由用户动态输入的信息可以被插入到资源文件中数据的指定位置,这样使得软件的国际化操作变得更加灵活。

本文字版教程还配有更详细的视频讲解,小伙伴们可以点击这里观看。

标签:11,文件,13,Java,读取,MessageFormat,info,Message,资源
From: https://blog.51cto.com/mugexuetang/5983687

相关文章