基本概念
国际化(Internationalization,I18N)
本地化(Localization,L10N)
应用程序国际化的目标
编写在任何受支持的区域设置中都同样能正常运行(且外观表现本地化)的代码
国际化中的Unicode编码
数字、日期和时间国际化
国际化/本地化工作的焦点
语言
数字格式
时间/日期格式
货币
身份证、社保号码和护照
电话号码、地址和邮政编码
度量衡
禁忌
名字和称谓
国际化程序中的数据显示
import java.util.*;
import java.text.DateFormat;
import java.text.NumberFormat;
public class TestDataFormat{
public static void main(String args[]){
Date now = new Date();
Locale locale = Locale.getDefault();
System.out.println("Locale: " + locale);
DateFormat df = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, locale);
System.out.println(df.format(now));
double d = 1234.56;
NumberFormat nf1 = NumberFormat.getInstance();
System.out.println(nf1.format(d));
NumberFormat nf2 = NumberFormat.getCurrencyInstance();
System.out.println(nf2.format(d));
}
}
资源包
在国际化的应用程序中,通常以资源包(Resource Bundle)的形式来保存与运行环境相关的资源,如消息文本、菜单及按钮标签等,每一个资源包对应一种用户Locale。
<包名>-<语言代码>-<国家/地区代码> 例:myRes-zh-Cn
<包名>-<语言代码> 例:MyLabels-en
资源包的两种表现形式:
属性文件(Property File)
资源绑定类(Resource Bundle Class)
属性文件
属性文件可用于保存要进行国际化的字符串信息,属纯文本文件,文件名前缀与资源包同名、后缀为".properties"。
属性文件的格式与先前的系统属性导出文件的格式相同。
使用java.util .ResourceBundle类可以加载属性文件并读取其中的资源。
import javax.swing.*;
import java.util.Date;
import java.util.Locale;
import java.util.ResourceBundle;
import java.text.DateFormat;
public class TestI18N{
private static ResourceBundle bundle;
private static Locale currentLocale;
static{
currentLocale = Locale.getDefault();
bundle = ResourceBundle.getBundle("MyLabels",currentLocale);
}
public static void main(String args[]){
JFrame jf = new JFrame(bundle.getString("title"));
JMenuBar jmb = new JMenuBar();
JMenu jm1 = new JMenu(bundle.getString("menu.file"));
JMenu jm2 = new JMenu(bundle.getString("menu.edit"));
JMenu jm3 = new JMenu(bundle.getString("menu.help"));
jmb.add(jm1);
jmb.add(jm2);
jmb.add(jm3);
jf.setJMenuBar(jmb);
JTextArea status = new JTextArea();
status.setText(bundle.getString("currentEnv") + currentLocale.getDisplayName());
JLabel time = new JLabel();
jf.add(status,"Center");
jf.add(time,"South");
jf.setSize(350,200);
jf.setLocation(400,300);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
MyTimer mt = new MyTimer(time);
mt.start();
}
}
class MyTimer extends Thread{
JLabel status;
public MyTimer(JLabel status){
this.status = status;
}
public void run(){
Locale locale = Locale.getDefault();
while(true){
Date now = new Date();
DateFormat df = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL,locale);
status.setText(df.format(now));
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
为避免因文本文件在不同平台上的编码方式不一而导致乱码问题,Java国际化程序的属性文件统一采用Unicode编码格式存储
JDK中提供了专门的工具native2ascii对属性文件进行Unicode编码转化。
命令格式
native2ascii [-<options>] <inputfile> [<outputfile>]
例:D:\>native2ascii D:\p1\a.properties D:\p2\a.properties
为避免文件混杂,也可将属性文件保存在单独的子目录中,访问时则使用"."分隔其路径层次。
资源绑定类
资源绑定类(Resource Bundle Class)是资源包的另一种表现形式,和属性文件相对应,每个资源绑定类对应一种不同的用户Locale。
资源绑定类应与资源包同名,其中保存的也是国际化的“键-值对”属性信息,但信息格式并不限于字符串类型。
继承抽象类java.util.ListResourceBundle是定义资源绑定类的最便捷方法,该类中采用二维对象数组(Object[][])的形式保存所有国际化资源、并提供了按照“键”查找“值”的功能。
资源绑定类的定义格式:
public class <bundle_name> extends ListResourceBundle{
//实现抽象方法getContents()
public Object[][] getContents(){
return contents;
}
//定义资源
private static final Object[][] contents = {
{<key1>,<value1>},
{<key2>,<value2>},
…
}
}
import java.awt.*;
import javax.swing.*;
import java.util.Date;
import java.util.Locale;
import java.util.ResourceBundle;
import java.text.DateFormat;
public class TestResourceBundle{
private static ResourceBundle bundle;
private static Locale currentLocale;
static{
currentLocale = Locale.getDefault();
bundle = ResourceBundle.getBundle("MyLabels",currentLocale);
}
public static void main(String args[]){
JFrame jf = new JFrame(bundle.getString("title"));
JMenuBar jmb = new JMenuBar();
JMenu jm1 = new JMenu(bundle.getString("menu.file"));
JMenu jm2 = new JMenu(bundle.getString("menu.edit"));
JMenu jm3 = new JMenu(bundle.getString("menu.help"));
jmb.add(jm1);
jmb.add(jm2);
jmb.add(jm3);
jf.setJMenuBar(jmb);
JTextArea status = new JTextArea();
status.setText(bundle.getString("currentEnv") + currentLocale.getDisplayName());
Color bg = (Color)bundle.getObject("bgColor");
status.setBackground(bg);
JLabel time = new JLabel();
jf.add(status,"Center");
jf.add(time,"South");
jf.setSize(350,200);
jf.setLocation(400,300);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
MyTimer mt = new MyTimer(time);
mt.start();
}
}
class MyTimer extends Thread{
JLabel status;
public MyTimer(JLabel status){
this.status = status;
}
public void run(){
Locale locale = Locale.getDefault();
while(true){
Date now = new Date();
DateFormat df = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL,locale);
status.setText(df.format(now));
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
消息格式化
和使用NumberFormat、DateFormat等工具类格式化数字和日期/时间信息的情况类似, JDK 中还提供了一个java.text.MessageFormat类专门用来格式化包含有可变参数的文本消息。
import java.util.Date;
import java.util.Locale;
import java.text.MessageFormat;
import java.util.Scanner;
public class TestMessageFormat{
private static int num = 10000;
public static void main(String args[]){
TestMessageFormat tmf = new TestMessageFormat();
System.out.print("请输入您的姓名:");
String userName = new Scanner(System.in).next();
System.out.println(tmf.formatMsg(userName));
}
public String formatMsg(String name){
String msg = "{0},欢迎您! 您是第{2}位访客,当前时间是{1}";
Locale locale = Locale.getDefault();
MessageFormat mf = new MessageFormat(msg,locale);
Object[] msgArgs = {name,new Date(),++num};
return mf.format(msgArgs);
}
}
相关术语:
消息模式(Message Pattern)
占位符(Placeholder)
占位符类型和样式
可以对消息模式串中的占位符进行可选的类型和样式设置,以获得更细致的格式化效果。
格式
{<index>,<type>,<style>}
设置占位符格式和样式
import java.util.Date;
import java.util.Locale;
import java.text.MessageFormat;
import java.util.Scanner;
public class TestPlaceholder{
private static int num = 10000;
public static void main(String args[]){
TestPlaceholder tmf = new TestPlaceholder();
System.out.print("请输入您的姓名:");
String userName = new Scanner(System.in).next();
System.out.println(tmf.formatMsg(userName));
}
public String formatMsg(String name){
String msg = "{0},您好! 您是第{1}位访客,今天是{2,date,medium},当前时间{2,time,short}";
Locale locale = Locale.getDefault();
MessageFormat mf = new MessageFormat(msg,locale);
Object[] msgArgs = {name,++num,new Date()};
return mf.format(msgArgs);
}
}
国际化程序中的消息格式化
国际化应用程序中的消息模式字符串本身也需要进行本地化处理,此时可以将之保存到资源包中,以提供对应于不同Locale的版本,其实现方式与保存普通文本资源的方式相同。
使用资源包保存消息模式字符串
import java.awt.Color;
import java.util.ListResourceBundle;
public class MyMsgs extends ListResourceBundle {
private final Object my_data[][] = {
{"note","Please input your name: "},
{"msg","Welcome,{0}! Your seriation no is {1},Date: {2,date,medium}, Time:{2,time,short}"}
};
public Object[][] getContents() {
return my_data;
}
}
import java.awt.Color;
import java.util.ListResourceBundle;
public class MyMsgs_zh_CN extends ListResourceBundle {
private final Object my_data[][] = {
{"note","请输入您的姓名: "},
{"msg","{0},您好! 您是第{1}位访客,今天是{2,date,medium},当前时间{2,time,short}"}
};
public Object[][] getContents() {
return my_data;
}
}
import java.util.*;
import java.text.*;
public class Test{
private static int num = 10000;
public static void main(String args[]){
Locale defaultLocale = Locale.getDefault();
ResourceBundle bundle = ResourceBundle.getBundle("MyMsgs",defaultLocale);
String note = bundle.getString("note");
String msg = bundle.getString("msg");
System.out.print(note);
String userName = new Scanner(System.in).next();
MessageFormat mf = new MessageFormat(msg,defaultLocale);
Object[] msgArgs = {userName,++num,new Date()};
System.out.println(mf.format(msgArgs));
}
}