基本概念
国际化(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)); } }