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);
 }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章