JAVA 国际化基础知识(二)
2011-11-16 20:59
351 查看
输入法
实际上,以上所有讨论都涉及操作或显示数据。然而,必须以某种方式输入数据。对于最终用户,最常用的是键盘。但是,如果键盘不支持某种语言输入所需的字符,您该怎么办呢?输入法(Inputmethod)是允许数据输入的软件组件的一个技术术语。Java平台既允许使用主机OS输入法也允许使用基于Java语言的输入法。如果您需要实现输入法,您可以使用输入法框架(InputMethodFramework)。您可以在JDK文档中Internationalization一节中的InputMethodFramework(可以从
API(InputMethodClientAPI)及输入法引擎SPI(InputMethodEngineSPI)的规范、参考和教程
Unicode与Java字符
Java字符与char数据类型
Java程序员的一个最知名的抱怨是“我只看到程序输出是问号(或方块),我的数据是怎么被破坏的呢?”通常,作为Java开发人员,您应该理解实际发生了什么以及这一表面问题后面的原因,而这种知识在处理国际化问题时尤为重要。Java语言规范(JavaLanguageSpecification)将char定义为原始的、数值型和整型的类型。此外,char是唯一的无符号(unsigned)数字类型,它允许一些有趣的(或讨厌的,这取决于您的观点)窍门。char在另一方面也十分特殊,因为将它们送到诸如显示器或打印机的输出设备时,会将其值从字符映射或字体映射成字形。然而,从根本上来说,char是数值类型,支持所有整数运算。因此
Unicode转义符设置char。因为char是数值型,所以您也可以使用八进制、十进制或十六进制表示法甚至反转位来赋值。
假设出现上述情况并假定没有程序错误,上面问题的答案是:字符映射或字体不支持该字符,显示问号或方块来作为替代。该char本身的值仍然有效。但是,这样您就不能可视地验证数据;您不得不核对数值。下面的示例显示了这一行为。
这幅图像显示了日本象形文字中的“Go”或5,以Unicode表示为‘\u4E94’。该字符导致在下面的charExample程序中显示成问号和方块:
importjavax.swing.*;
publicclasscharExample
{
publicstaticvoidmain(String[]args)
{
booleanbFirst=true;
charaChar[]={
'A',//character
65,//decimal
0x41,//hex
0101,//octal
'\u0041'//Unicodeescape
};
charmyChar=256;
for(inti=0;i<aChar.length;i++)
{
System.out.print(aChar[i]+++"");
if(i==(aChar.length-1))
{
System.out.println("\n---------");
if(bFirst)
{
i=-1;
bFirst=!bFirst;
}
}
}//endfor
//theresultofaddingtwocharsisanint
System.out.println("aChar[0]+aChar[1]equals:"+
(aChar[0]+aChar[1]));
System.out.println("myCharat256:"+myChar);
System.out.println("myCharat20116or\\u4E94:"+
(myChar=20116));
//showintegervalueofthechar
System.out.println("myCharnumericvalue:"+
(int)myChar);
JFramejf=newJFrame();
JOptionPane.showMessageDialog(jf,
"myCharat20116or\\u4E94:"+
(myChar=20116)+
"\nmyCharnumericvalue:"+
(int)myChar,
"charExample",JOptionPane.ERROR_MESSAGE);
jf.dispose();
System.exit(0);
}//endmain
}//EndclasscharExample
首先,程序用字母“A”的各种表示法初始化一个char数组,并将一个char变量设置成256(‘\u0100’)。程序在一个循环中打印两次数组的值。打印之后递增每个元素的值(char是数值型,记得吗?)。接下来,将头两个元素加到一起,然后打印其结果(int)。然后,打印char变量,首先用其初始值,然后用值20116或‘\u4E94’,它是5的日本象形文字“Go”。如预期的那样在使用代码页cp1252的WindowsNT上,会在显示器上将这两个值打印成问号。根据您的系统使用的代码页,显示可能略微有些不同。要核查其值,接下来将变量作为int打印。最后,JOptionPane显示其值,对于不受支持的char‘\u4E94’,它显示一个方块。
下面是charExample的输出:
AAAAA
---------
BBBBB
---------
aChar[0]+aChar[1]equals:134
myCharat256:?
myCharat20116or\u4E94:?
myCharnumericvalue:20116
JOptionPane显示:
字体、字体特性及Lucida字体
Java平台既识别逻辑字体也识别物理字体。逻辑字体是那些被映射到主机系统字体的字体。比如人们熟悉的Serif、Sans-serif、Monospaced、Dialog以及DialogInput字体。还有四种逻辑字体样式:普通、粗体、斜体及粗斜体。使用一个位于JRE/lib目录下的font.properties文件来实现主机字体到逻辑字体的映射。尽管细节因系统而异,但是缺省font.properties文件通常设置成英语环境,虽然JDK也有一个本地化日语版本可用。还提供了另外的font.properties文件;JDK1.3.1
Windows版包含阿拉伯语、希伯来语、日语、韩国语、俄语、泰国语字体文件以及中文的几种版本的字体文件。就象命名约定一样,搜索适当的font.properties同用于ResourceBundle的方法类似(但不全相同)。如果特定于语言的font.properties文件同您的系统的语言环境相匹配,并且安装了期望的字体(通常随OS的那一版本一起提供),那么就会对该语言进行自动映射。否则,就使用缺省的(通常是英语)文件映射。
如果您安装了适当的字体并且在调用Java应用程序时传入了相应的语言及国家或地区代码,那么也会进行自动映射。如果期望的font.properties文件存在,那么这种行为对开发非常有用。也可以将最初的缺省font.properties文件拷贝到别的地方,然后将特定文件重命名为“font.properties”,通过这样来将该语言/字体有效地设置成缺省情况。虽然对于开发人员来说很容易,但这显然不是最终用户非得做的事情。
如果您必须亲自定制或创建一个新的font.properties文件,那就完全是另一回事,而且更难。可以在JDK文档Internationalization一节中的FontProperties中找到处理font.properties文件的指示信息。
物理字体是我们始终在使用的正常字体。基于ASCII和ISO8859-1的字体不会有问题。然而,一旦超出这一范围,主机平台显然必须理解它们,并且必须对它们进行Unicode编码以使之能够用于您的Java程序。找到这些字体不象以前那么难了。例如,WindowsMSMinchoTrueType字体(主要是日语)是Unicode编码的,可以以标准方式立即投入使用。当在系统上装入了适当的物理字体时,您可以让用户选择他们想要的字体并保存他们的首选项,或者将该字体设置成整个软件包的标准字体,而不用进入
font.properties文件。
Java2SDK还提供三种物理字体系列:LucidaSans、LucidaBright和LucidaSansTypewriter。每一系列都含有四种字体―分别用于普通、斜体、粗体以及粗斜体样式―总计12种字体。虽然关于这些字体的确切功能的信息极为稀少,但LucidaSans字体处理大多数欧洲和中东语言。不包括亚洲语言。由于该字体随JDK一起提供,因此教程中的所有图形应用程序示例都使用LucidaSans字体。更多信息,请参阅JDK文档Internationalization一节中的Physical
Fonts(可以从
提供本地化的资源
创建语言环境
提供任何类型的本地化资源时,您应该做的第一件事情是创建一个适当的语言环境(参阅Localel=newLocale(Stringlanguage,Stringcountry); |
下面是用于特定于德国的德语语言环境:
Localel=newLocale("de","DE"); |
使用资源束
ResourceBundle包含键/值结合。键总是String,而值总是PropertyResourceBundle中的String,但可以是ListResourceBundle中的任何对象或定制子类。如果没有找到请求的资源,那么ResourceBundle访问方法会抛出一个MissingResourceException。参阅
ResourceBundle.getBundle(StringbaseName)和ResourceBundle.getBundle(StringbaseName,Localelocale)提供一种内置的搜索机制,当这些束的结构恰当时,这种机制工作得非常好。正常的搜索从base_language_country_variant到base_language_country,再到base_language,最后到base。注:如果请求一个特定的、非缺省的语言环境,并且同资源一起存在一个缺省的语言环境束,那么搜索将会停在那儿而不是继续搜索到基础束。我们的示例程序(参阅
.properties文件被命名为:
·ByTheNumbersrb.properties
·ByTheNumbersrb_de.properties
·ByTheNumbersrb_en.properties
·ByTheNumbersrb_fr.properties
·ByTheNumbersrb_ru.properties
所有文件都含有全部所需资源。英语用作缺省值,ByTheNumbersrb.properties和ByTheNumbersrb_en.properties是相同的。这种做法略微有些偏离传统认知,即:对于基础缺省语言,不需要专门命名的.properties文件,因此我们不需要ByTheNumbersrb_en.properties。然而,当一段特定信息使用非缺省语言环境时,这种设置却是必需的,我们的示例程序就是这种情形。假定一个英语语言环境将被用来在一台法语为缺省语言环境机器上显示某项。如果_fr
束中存在相同的键,_en搜索失败时,将会选择该值。这完全不是所请求的或所期待的那样。如果在程序的任意一次给定运行中只使用一种语言环境,那么专门命名的副本就不是必需的。但无论如何,在任何情况下这种做法都不需要新的代码并且起作用。
如果我们需要更特定的语言环境支持,例如奥地利语、瑞士语和德语(分别是_de_AT、_de_CH和_de_DE),那么只将国家或地区细节置于以适当的国家或地区命名的特性文件(例如,myprops_de_CH.properties)中,而将更一般的元素置于_de束级别,这样做会很有意义。在那种情形下,需要其它元素时,将总能找到_de束。
您也应该为束实现几种命名约定。我们的示例使用这种通用格式:Object.getClass().getName()+"rb"。主要规则是:对于.properties文件,不要只使用类名称作为其基础名称。忽视该规则在有些平台上照样能行,但在其它一些平台上您会大吃一惊。记入文档的准则是:如果类和具有相同名称的.properties文件同时存在,那么被选中和装入的将是类。就是这样。这一行为的一个好结果是:使用适当命名的束,您可以在ListResourceBundle和PropertyResourceBundle之间转换,而不用更改代码;只要将期望的类型移到类路径即可。
您可能会发现让不同的信息类型具有多个ResourceBundle更加合适。它们可以为许多不同的程序提供资源。特定的前缀或后缀约定对于避免类名冲突仍然有用。
使用PropertyResourceBundle
PropertyResourceBundle的语义同其父束ResourceBundle的语义相同。不同之处在于数据存储的位置。PropertyResourceBundle由符合Properties约定的.properties文件支持。下面是创建文件所要知道的内容:·文件被格式化为ISO8859-1编码的基本文本,因此您可以使用任何编辑器来创建和编辑文件。
·以#开头的行是注释。
·每个资源以key=value的形式被设置成键/值对。
·文件扩展名必须是.properties。名称必须遵守下列格式,其中language由ISO-639定义,country由ISO-3166定义(参阅
1.baseName.properties
2.baseName_language.properties
3.baseName_language_country.properties
4.baseName_language_country_variant.properties
下面是ByTheNumbersrb_en.properties的一个示例项:
1=One: |
1=\u041E\u0434\u0438\u043D: |
PropertyResourceBundle代码示例
显示在右侧的ByTheNumbers示例使用俄语语言环境―ru_RU。ByTheNumbers.java(参阅
DisplayName),并将其装入JComboBox。用户可以键入适当名称的号码然后按OK。程序验证这些输入项并显示祝贺消息或重试消息。我们提供一个按钮以随机顺序显示号码名称。用户可以从JComboBox选择任何语言,并且字段初始将以数值顺序显示选中的语言。程序使用LucidaSans字体,因此可以正确地显示所有受支持的语言。遗憾的是,我们的翻译还没有返回我们对标题翻译的请求,因此“title=Keyinnumberstomatchthewords:”键/值对仅仅出现在基础名称文件中,它给了我们一个机会,让我们明白不位于层次结构较低位置的键可以在祖先文件中找到。
要运行该程序,使用下列任意一条命令:
ljavaByTheNumbers//如果支持缺省语言环境,就使用它,否则,就使用英语。
ljava-Duser.language=de-Duser.region=DEByTheNumbers//德语
ljava-Duser.language=en-Duser.region=USByTheNumbers//英语
ljava-Duser.language=fr-Duser.region=FRByTheNumbers//法语
ljava-Duser.language=ru-Duser.region=RUByTheNumbers//俄语
下面显示了五个.properties文件中的两个:
ByTheNumbersrb.properties(与ByTheNumbersrb_en.properties相同)
#DefaultpropertiesinEnglish 0=Zero: 1=One: 2=Two: 3=Three: 4=Four: 5=Five: 6=Six: 7=Seven: 8=Eight: 9=Nine: 10=Ten: random=Random title=Keyinnumberstomatchthewords: |
#DefaultpropertiesinRussian 0=\u041D\u0443\u043B\u044C: 1=\u041E\u0434\u0438\u043D: 2=\u0414\u0432\u0430: 3=\u0422\u0440\u0438: 4=\u0427\u0435\u0442\u044B\u0440\u0435: 5=\u041F\u044F\u0442\u044C: 6=\u0428\u0435\u0441\u0442\u044C: 7=\u0441\u0435\u043C\u044C: 8=\u0412\u043E\u0441\u0435\u043C\u044C: 9=\u0414\u0435\u0432\u044F\u0442\u044C: 10=\u0414\u0435\u0441\u044F\u0442\u044C: random=\u041D\u0430\u0443\u0433\u0430\u0434 |
PropertyResourceBundle代码示例:I18N详细信息
让我们看一看同I18N有关的代码部分。首先,建立支持的语言环境和ResourceBundle基础名称。Locale[]alSupported={ Locale.US, Locale.FRANCE, Locale.GERMANY, newLocale("ru","RU") }; ... StringsRBName=getClass().getName()+"rb"; |
FontfJB=jbOK.getFont(); fLucida=newFont("LucidaSans", fJB.getStyle(), fJB.getSize()); ... asDNames=newString[alSupported.length]; LocalelDefault=Locale.getDefault(); for(i=0;i<alSupported.length;i++) { asDNames[i]= alSupported[i].getDisplayName(); if(iSelIndex==0&& lDefault.equals(alSupported[i])) {iSelIndex=i;} }//endfor |
jlTemp.setFont(fLucida); jlTemp.setName(i+"");//setName ... loadFromResourceBundle();//getlocalizedlabels ... jbRandom.setFont(fLucida); jbRandom.setText(rb.getString("random")); ... jlTemp=newJLabel(rb.getString("title")); jlTemp.setFont(fLucida); |
publicvoidloadFromResourceBundle() { try {//getthePropertyResourceBundle rb=ResourceBundle.getBundle( sRBName, alSupported[iSelIndex]); //getdataassociatedwithkeys for(inti=0;i<sfiSIZE;i++) { aiOrder[i]=i; ajl[i].setText(rb.getString(ajl[i].getName())); } bRandomize=false; }//endtry catch(MissingResourceExceptionmre) { JOptionPane.showMessageDialog(this, "ResourceBundleproblem;\n"+ "Specificerror:"+mre.getMessage(), "",JOptionPane.ERROR_MESSAGE); } }//endloadFromResourceBundle |
使用日期、数字和货币
日期、数字和货币
对于任何那些从未出过国,或从未接触过日期、数字和货币的“外国”用法的人来说,格式化和解析日期、数字和货币显得很简单。毕竟,所有人都能理解lundi1avril2002或至少4.1.02的月和日部分,对吗?虽然我们极少有人能实际以150,75购买321500,7项东西,但我们能够很容易地理解以欧元表示的价格有多少项。或者可能不是这样。这些示例看起来可能不典型,但他们确实发生了,并且表示了为什么非本国人在理解本国的日期、数字和货币格式方面经常会有问题。结果是,全世界使用的日期有各种各样的顺序和符号。数字和货币也是如此。此外,货币符号可能不止一个字符,它可能出现在值的前面或后面,和值之间有或没有空格。在大多数编程语言中,您几乎总是靠自己来处理这些情形。但JavaAPI却能够处理每个受支持语言环境的所有的不同格式。而且,通过使用DateFormatSymbols和DecimalFormatSymbols类,您可以获得诸如这样的信息:本地化的长短月日名称、十进制与货币分隔符以及货币与百分比符号。
API文档鼓励您为I18N应用程序使用抽象父类DateFormat和NumberFormat的getInstance()和getXXXInstance()方法。从1.3(和1.4)参考实现起,分别返回SimpleDateFormat和DecimalFormat的实例。两个类都有缺省的模式与符号用于格式化和解析,并且还允许定制。
下面几页中的示例程序都使用缺省模式来帮助您理解它们是怎样工作的。您将看到:由于API设计的缘故,三个示例中的代码都非常相似。从最终用户的观点,它们也非常相似:以本机语言环境提供一个输入域。当用户按下OK按钮时,就在表示用户选择的语言环境和标准解析的“原始”值的单独域中显示值。这三个示例都将处理由JDKAPI支持的所有语言环境。LucidaSans字体用于所有显示。“ToggleDisplayNames”按钮将语言环境名称的显示从用户的本机语言切换为特定语言环境的本机语言。当字体中没有用于本地化显示名称的第一个字符的字形时,“
-fontcan'tdisplay.”就被附加到下拉框中的语言环境名称上。程序仍然会工作,但在那种情况下,您可能会看到输出的某些部分是您熟悉的方框或问号。
使用下面的命令调用程序:
javaAppName |
java-Duser.language=lc-Duser.region=ccAppName |
注:由于要访问整个语言环境显示名称集,这些应用程序将比正常情况要花费更长的时间来启动。
日期格式化示例
这个JIBDateGUI示例使用德语作为缺省语言环境―de_DE。JIBDateGUI(参阅
程序通过定义缺省和选中的DateFormats和locales开始。java.sql.Date被初始化成显示标准ISO日期值的当前日期(注:没有为该示例对日期进行标准化),然后定义Lucida字体、缺省语言环境、支持的语言环境的数组以及本机和本地化的语言环境显示名称。
DateFormatdfLocal, dfSelected; java.sql.DatejsqlDate=newjava.sql.Date( System.currentTimeMillis()); FontfLucida; ... LocalelDefault=Locale.getDefault(); Locale[]alSupported; String[]asDNames, asLDNames; |
FontfJCB=jbToggle.getFont(); fLucida=newFont("LucidaSans", fJCB.getStyle(), fJCB.getSize()); iFormat=argiFormat; dfLocal=DateFormat.getDateInstance( iFormat); alSupported=Locale.getAvailableLocales(); asDNames=newString[alSupported.length]; asLDNames=newString[alSupported.length]; for(inti=0;i<alSupported.length;i++) { asDNames[i]= alSupported[i].getDisplayName(); s1= alSupported[i].getDisplayName(alSupported[i]); if(fLucida.canDisplay(s1.charAt(0))) {asLDNames[i]=s1;} else {asLDNames[i]=s1+"-fontcan'tdisplay.";} if(iSelIndex==0&& lDefault.equals(alSupported[i])) {iSelIndex=i;} }//endfor ... jtI.setText(dfLocal.format(jsqlDate)); ... dfLocal.setLenient(false); ... JLabeljlTemp=newJLabel("Default="+ lDefault.getDisplayName()); jlTemp.setFont(fLucida); |
ISO值。
if(oSource==jcb) { dfSelected=DateFormat.getDateInstance( iFormat, alSupported[jcb.getSelectedIndex()]); }//endifjcb,continueon jtD.setText(""); jtP.setText(""); try { java.util.Dated=dfLocal.parse( jtI.getText()); jtI.setText(dfLocal.format(d)); jtI.setCaretPosition(0); jtD.setText(dfSelected.format(d)); jtD.setCaretPosition(0); d=dfSelected.parse(jtD.getText()); //getnewjava.sql.Date jsqlDate=newjava.sql.Date(d.getTime()); jtP.setText(jsqlDate.toString()); } catch(ParseExceptionpe) { JOptionPane.showMessageDialog(this, pe.getMessage(),"",JOptionPane.ERROR_MESSAGE); } |
相关文章推荐
- Java 国际化基础知识(一)
- 学习Spring必学的Java基础知识(8)----国际化信息
- 主题:学习Spring必学的Java基础知识(8)----国际化信息
- Java 基础小知识一: 使用ResourceBundle 和 MessageFormat 实现国际化信息输出
- java 框架基础知识(8)----国际化信息-->MVC
- 学习Spring必学的Java基础知识(8)----国际化信息
- Java基础知识强化之IO流笔记72:NIO之 NIO核心组件(NIO使用代码示例)
- java基础知识汇总4
- Java Web基础知识之文件下载:当你下载文件的时候到底发生了什么?
- Java基础知识
- java基础知识:循环语句
- Java基础知识
- 由环境配置引发的java基础知识讨论
- java基础知识5-二进制
- Java基础知识之static关键字、静态代码块
- Java基础知识:面向对象
- 黑马程序员学习日记 Java基础知识精华
- javaweb基础知识
- Java基础知识——super关键字
- Java核心技术卷I:基础知识(原书第8版):12.6 约束与局限性