|
|
《Java程序设计语言 基础》第 2 部分 越来越多大大小小的公司都为了在世界各地开展自己的业务而使用不同语言。 有效的交流会带来良好的商机,于是对应用程序实施本地化就会给人们带来利益, 使他们能够更好地交流、增加客户对他们的满意程度。 JavaTM 2 平台提供有国际化功能,这些 功能使您能够从应用程序中提取出依赖于本地文化背景的信息(国际化),并使 之适应于任何您所需要的文化地区(本地化)。 本课采用了 第 2 部分、第 5 课:集合中的两 个客户程序,然后对它们实施国际化处理、使其文字适用于法国、德国和美国。 识别依赖于文化背景的数据
第一件要做的事情是识别您应用程序中依赖于文化背景的数据。依赖于文化背 景的数据是指一种文化或国家与另一种文化或国家不同的数据。文字是依赖于文 化背景的数据的最明显和最普遍的示例,当然诸如数字格式、声音、时间和日期 等其它方面也是必须考虑的。 RMIClient1.java 和 RMIClient2.java 类有以下可见于终端用户的依赖于文化背景的数据:
尽管应用程序有一个服务器程序,但该服务器程序并没有国际化和本地化。服 务器程序中唯一可见的依赖于文化背景的数据是错误信息。 服务器程序仅在一个地方运行,假设除能理解错误信息所用语言的系统管理员 之外的其它人都不能看见它。本例中,错误信息所采用语言为英语。
public void print(){
if(s!=null){
Iterator it = s.iterator();
while(it.hasNext()){
try{
String customer = (String)it.next();
System.out.println(customer);
}catch (java.util.NoSuchElementException e){
System.out.println("No data available");
}
}
}else{
System.out.println("No customer IDs available");
}
}
这个 在此情况下,无论应用程序在何处使用该出错信息,系统为该出错信息所提供 的文字都会被发送到命令行。此时的问题在于,最好始终在任何可行位置使用
public void print()
throws java.util.NoSuchElementException{
if(s!=null){
Iterator it = s.iterator();
while(it.hasNext()){
String customer = (String)it.next();
System.out.println(customer);
}
}else{
System.out.println("No customer IDs available");
}
}
以下列出了用户可见的进而需要国际化和本地化的标题、标签、按钮、数字和 错误文本。这些都摘自RMIClient1.java 和 RMIClient2.java。
创建关键字/值对文件
由于所有对用户具有可视性的文字信息都将从应用程序中提取出来并进行翻译, 所以您的应用程序需要提供一种在执行期间访问已翻译文字的方式。这一点可通过 关键字/值对文件来完成,该文件具有各种语言版本。关键字是从应用程序而不是 从硬编码文本中引用的,用于从所用语言的对应文件中调入相应的文字信息。 例如,您可以把关键字 purchase 映射到德语文件中的 Kaufen、法语文件中 的 Achetex 和美国英语文件中的 Purchase。在您的应用程序中,您可以引用关 键字 purchase 并指明其所采用的语言。 关键字/值对存放在属性文件中,它保存着程序属性和特征信息。属性文件是 纯文本格式的文件,每一种需要使用的语言都需要有一个属性文件。 本例中有三个属性文件,分别对应于英语、法语和德语译文。由于本应用程序 当前使用的是硬编码文本,所以对它进行国际化最简单的方法是使用硬编码文字 来设定英语属性文件的关键字/值对。 属性文件都遵循命名约定,这样应用程序才能够在运行时定位并调入正确的文 件。命名约定中规定语言和国家代码应成为文件名一部分。之所以要同时包含语 言和国家,是因为相同语言会因国家的不同而不同。例如,美国英语和澳大利亚 英语的差别很小,瑞士德语和澳大利亚德语不仅相互不同而且都不同于德国本土 的德语。 德语、法语、美国英语译文的各自属性文件的文件名分别为:
以下为英语属性文件。关键字出现在等号(=)的左边,而文字值(英文文字) 则出现在右边。 MessagesBundle_en_US.properties apples=Apples:
peaches=Peaches:
pears=Pears:
items=Total Items:
cost=Total Cost:
card=Credit Card:
customer=Customer ID:
title=Fruit 1.25 Each
1col=Select Items
2col=Specify Quantity
reset=Reset
view=View
purchase=Purchase
invalid=Invalid Value
send=Cannot send data to server
nolookup=Cannot look up remote server object
nodata=No data available
noID=No customer IDs available
noserver=Cannot access data in server
以上属性文件完成后,您就可以把它交给您的法语和德语翻译人员,让他们给 出与等号(=)右边的文字向对等的法语和德语。请为自己保留该文件的备份,因 为您也需要对您的应用程序文本中的关键字进行国际化。 带有 德语 译文的属性文件将为水果订购客户创建以下的用户界面:
带有 法语 带有 译文的属性文件则会为水果订购客户创建以下的用户界面:
国际化应用程序文本本节讲述如何国际化RMIClient1.java 程序。RMIClient2.java 程序的代码与前者几乎相同,因此您可以把相同步骤应用到您自己的程序中。 实例变量除了对包含国际化类的 //Initialized in main method
static String language, country;
Locale currentLocale;
static ResourceBundle messages;
//Initialized in actionPerformed method
NumberFormat numFormat;
main 方法本程序的设计是为了让用户指定命令行上使用的语言。所以,对
其它两个参数分别代表语言和国家代码。如果程序被调用时带有 1 个命令行 参数(仅指计算机名),国家和语言参数就分别假设为美国和英语。 以下示例将说明,带有用于指明计算机名称和德语(de DE)命令行参数的程 序是如何启动并运行的。其中的所有操作都是在同一命令行上进行的。 java -Djava.rmi.server.codebase=
http://kq6py/~zelda/classes/
-Djava.security.policy=java.policy
RMIClient1 kq6py.eng.sun.com de DE
以下是 对象
如果调用应用程序时带有命令行参数 public static void main(String[] args){
//Check for language and country codes
if(args.length != 3) {
language = new String("en");
country = new String ("US");
System.out.println("English");
}else{
language = new String(args[1]);
country = new String(args[2]);
System.out.println(language + country);
}
//Create locale and resource bundle
currentLocale = new Locale(language, country);
messages = ResourceBundle.getBundle("MessagesBundle",
currentLocale);
WindowListener l = new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
};
//Create the RMIClient1 object
RMIClient1 frame = new RMIClient1();
frame.addWindowListener(l);
frame.pack();
frame.setVisible(true);
if(System.getSecurityManager() == null) {
System.setSecurityManager(
new RMISecurityManager());
}
try {
String name = "//" + args[0] + "/Send";
send = ((Send) Naming.lookup(name));
} catch (java.rmi.NotBoundException e) {
System.out.println(messages.getString(
"nolookup"));
} catch(java.rmi.RemoteException e){
System.out.println(messages.getString(
"nolookup"));
} catch(java.net.MalformedURLException e) {
System.out.println(messages.getString(
"nolookup"));
}
}
对可用错误文本信息的访问,可通过调用 ResourceBundle
上的 getString 方法并向其传递映射到可用文本信息的关键字来实现。
try {
String name = "//" + args[0] + "/Send";
send = ((Send) Naming.lookup(name));
} catch (java.rmi.NotBoundException e) {
System.out.println(messages.getString(
"nolookup"));
} catch(java.rmi.RemoteException e){
System.out.println(messages.getString(
"nolookup"));
} catch(java.net.MalformedURLException e) {
System.out.println(messages.getString(
"nolookup"));
}
构造函数
窗口标题的设定,可通过调用 RMIClient1(){
//Set window title
setTitle(messages.getString("title"));
构造函数下面所要做的是,使用参数 args 查找远程服务器对象。
如果这一过程发生任何错误,catch 语句就会从 ResourceBundle
中获取可用错误文本信息,并把信息显示到命令行上。JLabel
和 JButton 等显示文本信息的用户界面对象可用相同的方法创建:
//Create left and right column labels
col1 = new JLabel(messages.getString("1col"));
col2 = new JLabel(messages.getString("2col"));
...
//Create buttons and make action listeners
purchase = new JButton(messages.getString(
"purchase"));
purchase.addActionListener(this);
reset = new JButton(messages.getString("reset"));
reset.addActionListener(this);
actionPerformed 方法
if(order.apples.length() > 0){
//Catch invalid number error
try{
applesNo = Integer.valueOf(order.apples);
order.itotal += applesNo.intValue();
}catch(java.lang.NumberFormatException e){
appleqnt.setText(messages.getString("invalid"));
}
} else {
order.itotal += 0;
}
方法 actionPerformed 计算订购总数和总费用,然后把它们转换
成适用于当前所用语言的正确格式并显示在用户界面上。
国际化数字
//Create number formatter
numFormat = NumberFormat.getNumberInstance(
currentLocale);
//Display running total
text = numFormat.format(order.itotal);
this.items.setText(text);
//Calculate and display running cost
order.icost = (order.itotal * 1.25);
text2 = numFormat.format(order.icost);
this.cost.setText(text2);
try{
send.sendOrder(order);
} catch (java.rmi.RemoteException e) {
System.out.println(messages.getString("send"));
}
编译并运行应用程序以下是示例程序编译和运行的概括说明。值得注意的是,当您启动客户程序时, 若想使用除美式英语之外的语言,就需要包含语言和国家代码。 编译以下这些命令假设开发工作是在 Unix:
cd /home/zelda/classes
javac Send.java
javac RemoteServer.java
javac RMIClient2.java
javac RMIClient1.java
rmic -d . RemoteServer
cp RemoteServer*.class /home/zelda/public_html/classes
cp Send.class /home/zelda/public_html/classes
cp DataOrder.class /home/zelda/public_html/classes
Win32:
cd \home\zelda\classes
javac Send.java
javac RemoteServer.java
javac RMIClient2.java
javac RMIClient1.java
rmic -d . RemoteServer
copy RemoteServer*.class
\home\zelda\public_html\classes
copy Send.class \home\zelda\public_html\classes
copy DataOrder.class \home\zelda\public_html\classes
启动 rmi 注册程序
Unix: cd /home/zelda/public_html/classes
unsetenv CLASSPATH
rmiregistry &
Win32: cd \home\zelda\public_html\classes
set CLASSPATH=
start rmiregistry
启动服务器
Unix: cd /home/zelda/public_html/classes
java -Djava.rmi.server.codebase=
http://kq6py/~zelda/classes
-Dtava.rmi.server.hostname=kq6py.eng.sun.com
-Djava.security.policy=java.policy RemoteServer
Win32:
cd \home\zelda\public_html\classes
java -Djava.rmi.server.codebase=
file:c:\home\zelda\public_html\classes
-Djava.rmi.server.hostname=kq6py.eng.sun.com
-Djava.security.policy=java.policy RemoteServer
启动德语版 RMIClient1请注意在命令行的末尾添加表示德语语言和国家的 Unix:
cd /home/zelda/classes
java -Djava.rmi.server.codebase=
http://kq6py/~zelda/classes/
-Djava.security.policy=java.policy
RMIClient1 kq6py.eng.sun.com de DE
Win32:
cd \home\zelda\classes
java -Djava.rmi.server.codebase=
file:c:\home\zelda\classes\
-Djava.security.policy=java.policy RMIClient1
kq6py.eng.sun.com de DE
启动法语版的 RMIClient2请注意在命令行的末尾添加表示法语语言和国家的 Unix:
cd /home/zelda/classes
java -Djava.rmi.server.codebase=
http://kq6py/~zelda/classes
-Djava.rmi.server.hostname=kq6py.eng.sun.com
-Djava.security.policy=java.policy
RMIClient2 kq6py.eng.sun.com fr FR
Win32:
cd \home\zelda\classes
java -Djava.rmi.server.codebase=
file:c:\home\zelda\public_html\classes
-Djava.rmi.server.hostname=kq6py.eng.sun.com
-Djava.security.policy=java.policy RMIClient2
kq6py.eng.sun.com/home/zelda/public_html fr FR
程序的改进诸如本应用程序的订货应用程序的现实情况可能是:RMIClient1 是一个嵌入在网页中的 applet 程序。当提交订单时,订单处理人员就从它们的 本地计算机上把 RMIClient2 当作应用程序运行。 这样我们就可以做一个有趣的练习,把 过点击执行相应 applet 程序的链接来选择语言。以下分别是 英语、 法语 和 德语 以下是在网页上调用法语版 applet 程序的 <HTML>
<BODY>
<APPLET CODE=RMIFrenchApp.class WIDTH=300 HEIGHT=300>
</APPLET>
</BODY>
</HTML>
若使用 applet 程序浏览器,则应输入以下程序代码,使 rmiFrench.html
作为法语版 applet 程序的 HTML 文件。
appletviewer rmiFrench.html
对程序当前状态的另一项改进是加强出错信息文本。您可以在 Java API 文档 中查找错误并使用其中的信息,通过提供更多特殊信息而使出 错信息对用户更加友好。 也许您还需要采用客户程序来捕获和处理使用不正确关键字时产生的错误。以 下是此类错误发生时系统提供的错误和栈跟踪: Exception in thread "main"
java.util.MissingResourceException:
Can't find resource
at java.util.ResourceBundle.getObject(Compiled Code)
at java.util.ResourceBundle.getString(Compiled Code)
at RMIClient1.<init>(Compiled Code)
at RMIClient1.main(Compiled Code)
更多信息有关国际化的详细说明,请参阅Java教程中的国际化部分。 有关 applet 程序的更多信息,请参阅 Java 教程 中的 编写 Applet 程序 部分。 1当术语“Java virtual machine(Java 虚拟机 )”或“JVM”在本网站中使用时是指适用于 Java 平台的虚拟机。 [TOP]
|
|
||||||||||||||||||||||||||||||||||||