JavaSE(十二)国际化


  Java提供国际化(i18n,取internationalization首尾字母,中间省略18个字母)的支持,它可以让同一个数据(如日期、货币、文字、资源等)根据本地化信息(主要包含国家和语言)的不同表现出不同的形式

Locale

  Locale代表了一个本地化信息(主要包含国家和语言),许多类型的数据都可以根据本地化信息以相应的形式表现出来。标准化组织定义了所有国家的编码与所有语言的编码,它们用两个大写字母表示国家,如中国CN、日本JP、加拿大CA等,用两个小写字母表示语言,如汉语zh、英语en等。

public final class Locale implements Cloneable, Serializable {
    public static final Locale CHINESE = createConstant("zh", "");
    ...预定义了一些列默认的Locale,可以直接使用...
    
	public static String[] getISOCountries(); // 返回ISO所有的国家编码
    public static String[] getISOLanguages(); // 返回ISO所有的语言编码
    
    public Locale(String language, String country, String variant); // 参数分别为语言编码、国家编码、额外变量
    public Locale(String language, String country);
    public Locale(String language);
    
    public static synchronized void setDefault(Locale newLocale); // 默认设置了根据操作系统语言和国家构建的Locale,也可以再次调用该方法更改默认设置
    public static Locale getDefault(); // 在很多基于Locale的类和方法中,如果没有指定依赖的Locale,通常就通过该方法获取默认的Locale
    public static Locale[] getAvailableLocales(); // 返回所有支持的Locale(必须包含Locale.US)
    
    public String getLanguage(); // 获取语言编码,如zh
    public String getDisplayLanguage(Locale inLocale); // 以inLocale信息显示当前Locale的语言,如Chinese、中文等
    public final String getDisplayLanguage(); // getDisplayLanguage(getDefault())
    public String getCountry(); // 获取国家编码,如CN
    public String getDisplayCountry(Locale inLocale); // 以inLocale信息显示当前Locale的国家,如China、中国等
    public final String getDisplayCountry(); // getDisplayCountry(getDefault())
    public String toLanguageTag(); // 获取语言和国家编码,如zh-CN
    public String getDisplayName(Locale inLocale); // 以inLocale信息显示当前Locale的语言和国家,如Chinese(China)、中文 (中国)等
    public final String getDisplayName(); // getDisplayName(getDefault());   
}

数据本地化

日期与时间的格式化

  格式化日期与时间时,与Locale相关的问题主要体现在以下几点:

  • 月份与星期应该用本地语言来表示
  • 年月日顺序要符合本地习惯
  • 公历可能不是本地首选的日期表示方法
  • 时区

  DataFormat类可用来对日期和时间进行本地化展示及其反过程,也就是说可以将Date对象用本地化字符串展现出来,也可以根据本地化字符串生成Date对象。对于同一个本地化信息,DateFormat还支持风格设置,也就是说根据风格的不同,同一个数据在相同本地化下也可能有不同的展示形式,风格有DateFormat.DEFAULT、DateFormat.FULL、DateFormat.LONG、DateFormat.MEDIUM、DateFormat.SHORT。
  DateFormat有三种,一种针对时间,一种针对日期,一种既有日期又有时间,每一种DateFormat在解析和格式化时都只处理相应的部分,可以通过静态方法getXxxInstance(…)来获取相应的DateFormat。这种示例的处理方式通常有两种方案,一是多态方案,可以让getXxxInstance(…)获取不同的DateFormat子类对象来控制不同的行为;第二种方案可以让getXxxInstance(…)获取相同DateFormat子类的不同实例,只是在获取后用一个标志位来进行标识,然后通过标志位来控制不同的行为,实际上DateFormat采用的是第二种方案,getXxxInstance(…)获取的SimpleDateFormat类(SimpleDateFormat并非线程安全的),后面的NumberFormat也采用类似的方案。

public abstract class DateFormat extends Format {
    public static Locale[] getAvailableLocales(); // Date格式支持的所有Locale
    
    public final static DateFormat getTimeInstance(int style, Locale aLocale);    
    public final static DateFormat getTimeInstance(int style); 
    public final static DateFormat getTimeInstance();
    public final static DateFormat getDateInstance(int style, Locale aLocale);
    public final static DateFormat getDateInstance(int style);
    public final static DateFormat getDateInstance();
    public final static DateFormat getDateTimeInstance(int dateStyle, int timeStyle, Locale aLocale); // dateStyle日期风格,timeStyle时间风格,aLocale本地化信息
    public final static DateFormat getDateTimeInstance(int dateStyle, int timeStyle);
    public final static DateFormat getDateTimeInstance();
    public final static DateFormat getInstance(); // getDateTimeInstance(SHORT, SHORT);
    
    public final String format(Date date); // 将Date格式化为字符串
    public Date parse(String source) throws ParseException; // 将字符串解析为Date
    
    public void setLenient(boolean lenient); // 日期/时间解析是否不严格,默认true表示不严格,如字符串为1月33日时,不严格解析为2月2日,严格解析抛异常
    public boolean isLenient();
    public void setCalendar(Calendar newCalendar); // 设置日历(默认使用公历的日历),该日历的方法被很多底层接口回调
    public Calendar getCalendar();
    public void setTimeZone(TimeZone tz); // 当希望所使用的时区不是Locale对应的时区时可设置指定的时区
    public TimeZone getTimeZone(); 
}

数值格式化

  格式化数值时,数值可以以数字、百分比、货币三种形式表现出来,而每一种形式针对不同的Locale都有不同的显示格式,这些显示格式主要体现在一下几点:

  • 货币符号的样式、位置(如有些货币符号在数字前,有些在数字后)
  • 小数点和十进制分隔符(如通常用“,”来进行十进制分割,用’.'来表示小数点,而德国恰恰相反)
  • 百分比样式

  NumberFormat类可用来对数值进行本地化展示及其反过程,通过它的getXxxInstance(…)可以获得处理不同形式数值的NumberFormat实例。
  数值在进行货币格式化时,不同Locale下显示的文本不仅格式不一样,而且代表的意义也会发生变化,如某一个数字500,用Locale.US格式化后显示$500,用Locale.CHINA格式化后显示¥500,前者代表500美元,后者代表500人民币,通过设置NumberFormat的Currency可以解决此问题。Currency代表着币种,设置Currency后,货币的格式依然会根据Locale表现出不同样式,但币种不会变,如币种设置为EUR,那么500在不同Locale下展示成任何样式,所代表的都是500欧元。与Locale的国家和语言一样,标准化组织用三个大写字母对常用的币种进行了编码,如CNY代表人民币,USD代表美元,EUR代表欧元,GBP代表英镑等。

public abstract class NumberFormat extends Format  {
    public static Locale[] getAvailableLocales(); // 数值格式化支持的所有Locale
    
    // 获取处理各种数值形式的NumberFormat实例
    public final static NumberFormat getNumberInstance(); // 以数字形式处理数值的NumberFormat
    public static NumberFormat getNumberInstance(Locale inLocale);
    public final static NumberFormat getInstance(); // getNumberInstance()
    public static NumberFormat getInstance(Locale inLocale);
    public final static NumberFormat getIntegerInstance(); // 以整数形式处理数值的NumberFormat
    public static NumberFormat getIntegerInstance(Locale inLocale);
    public final static NumberFormat getCurrencyInstance(); // 以货币形式处理数值的NumberFormat
    public static NumberFormat getCurrencyInstance(Locale inLocale);
    public final static NumberFormat getPercentInstance(); // 以百分比形式处理数值的NumberFormat
    public static NumberFormat getPercentInstance(Locale inLocale);
        
    public final String format(double/long number); // 将数值转换为相应形式的字符串
    public Number parse(String source); // 将相应形式的字符串转换为数值
    
    public boolean isGroupingUsed(); // 是否将数值分组,如12345是否表现为124,456,分组便于肉眼识别
    public void setGroupingUsed(boolean newValue);

	// 格式化输出时对整数与小数位数的控制,截断都是保留靠近小数点的位,补齐都是在远离小数点的位
    public int getMaximumIntegerDigits(); // 当整数部分位数大于该值时,只保留该值个整数位数
    public void setMaximumIntegerDigits(int newValue);
    public int getMinimumIntegerDigits(); // 当整数部分位数小于该值时,保留该值个整数位数(不够的位数补0)
    public void setMinimumIntegerDigits(int newValue);
    public int getMaximumFractionDigits(); // 当小数部分位数大于该值时,只保留该值个小数位数
    public void setMaximumFractionDigits(int newValue);
    public int getMinimumFractionDigits(); // 当小数部分位数小于该值时,保留该值个小数位数(不够的位数补0),如果需要保留N位小数,可将小数部分位数最大最小值都设为N
    public void setMinimumFractionDigits(int newValue);
    public RoundingMode getRoundingMode(); // 小数截断时的模式,默认RoundingMode.HALF_UP表示四舍五入
    public void setRoundingMode(RoundingMode roundingMode); 
    
    public void setCurrency(Currency currency); // 设置币种
    public Currency getCurrency(); 
}

// 币种
public final class Currency implements Serializable {
    public static Set<Currency> getAvailableCurrencies(); // 所有支持的币种,如CNY
    
	public static Currency getInstance(String currencyCode); // 通过币种的编码获取币种对象
    public static Currency getInstance(Locale locale); // locale对应的币种,如Locale.CHINA对应人民币
    
    public String getCurrencyCode(); // 币种的编码
    public int getDefaultFractionDigits(); // 该币种默认的小数保留位数
    public String getSymbol(Locale locale); // 以指定的locale显示币种符号,如美元符号在Locale.CHINA下显示为USD,在Locale.US下显示为$
    public String getSymbol(); // getSymbol(Locale.getDefault())
    public String getDisplayName(Locale locale); // 以指定的locale显示币种的名字,如美元在Locale.CHINA下显示为美元,在Locale.US下显示为US Dollar
    public String getDisplayName(); // getDisplayName(Locale.getDefault())
}

资源包

  Class类的getResource方法可以获取资源,但不能支持Locale化,而ResourceBundle可以获取与Locale相关的资源
  ResourceBundle只支持两种资源,一种为properties文件,另一种是继承自ResourceBundle的类,这两种资源本质上都是提供key-value集合
  ResourceBundle可以根据资源包名与locale信息来定位资源。资源定位优先级顺序如下,在同一优先级下,类的优先级比properties文件的优先级高。

  1. 包名_当前Locale的语言_当前Locale的国家_当前Locale的变量
  2. 包名_当前Locale的语言_当前Locale的国家
  3. 包名_当前Locale的语言(所有使用该语言的国家都支持,如果某个国家也是使用该语言,但有特殊要求,可以再提供1或2的资源
  4. 包名_默认Locale的语言_默认Locale的国家_默认Locale的变量
  5. 包名_默认Locale的语言_默认Locale的国家
  6. 包名_默认Locale的语言
  7. 包名(一般会提供该资源来作为最终兜底的资源

  Properties文件资源的包名应该是相对于classpath的路径,用“/”符号分割如dir/…/baseName(最后一级是文件名,但不能包含properties后缀,也不包含Locale相关的信息,但资源文件本身命名应该包含locale信息与properties后缀)。Java类资源的包名就是类全名但不应该包含Locale相关信息,如com.test.MyResourceBundle的实际类名可能是com.test.MyResourceBundle_zh_CN。我们可以对同一资源提供多种Locale对应的资源包,这样就可以根据各地的本地化信息得到不一样的显示效果,如按钮、提示等可以根据不同Locale显示中文还是英文等。
  类资源必须继承ResourceBundle并至少重写其getKeys()与handleGetObject(String key)方法,getKeys()为资源包含的所有键,handleGetObject(String key)用来获取键的值,属性文件资源的值为字符串,而类资源可以是任何类型的值

public abstract class ResourceBundle {
    public static final ResourceBundle getBundle(String baseName, Locale locale); // 根据包名与locale信息获取资源包
    public static final ResourceBundle getBundle(String baseName); // getBundle(baseName, Locale.getDefault())
    
    public String getBaseBundleName(); // 获取包名
    public Locale getLocale();
    
    public abstract Enumeration<String> getKeys(); // 获取资源包下的所有key
    public boolean containsKey(String key); // 资源包下是否包含指定key
    public final Object getObject(String key); // 类资源文件可以返回任何类型的值,一般调用该方法
    public final String getString(String key); // (String) getObject(key),属性文件资源都是返回字符串类型的值,一般调用该方法
    public final String[] getStringArray(String key); // (String[]) getObject(key)
}

字符串排序

  对于相同的字符串集合进行排序,针对不同的Locale信息可能需要遵循不同的排序规则,如美国希望通过ASCII来进行排序,而中国更希望根据拼音先后来进行排序,有些地方排序不区分大小写等。
  对字符串的排序,无论顺序还是逆序,最终都体现在字符串的比较上。Collections.sort(List<T> list)可用来对列表进行排序,这要求T实现了Comparable接口以实现元素间的比较,字符串实现了Comparable接口,但其比较方案是基于各个字节的值大小来进行的,与Locale无关,这时可以通过Collections.sort(List<T> list, Comparator<? super T> c)自定义比较方案进行排序,Java的国际化提供了针对不同Locale的比较器Collator

public abstract class Collator implements java.util.Comparator<Object>, Cloneable {
    public static synchronized Locale[] getAvailableLocales(); // 排序支持的所有Locale
    
    public static Collator getInstance(Locale desiredLocale); // 获取desiredLocale习惯的比较方式的比较器
    public static synchronized Collator getInstance(); // getInstance(Locale.getDefault())
    
    public abstract int compare(String source, String target); // 对两个字符串进行比较
    
    // 比较强度,可取Collator.PRIMARY、Collator.SECONDARY、Collator.TERTIARY,越往后差别越小,比较强度就越强
    // 当进行比较时,不会关注比小于当前强度值的差别,如'A'与'Z'的差别为PRIMARY、'A'与'a'的差别为TERTIARY
    public synchronized int getStrength(); 
    public synchronized void setStrength(int newStrength);
 }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章