1. 程式人生 > >筆記:國際化

筆記:國際化

mar 依賴 one 小寫字母 就會 結果 com 公司 number

國際化英文單詞為:Internationalization,又稱I18N,I為因為單詞的第一個字母,18為這個單詞的長度,而N代表這個單詞的最後一個字母。國際化又稱本地化(Localization,L10N)。

Java國際化主要通過如下3個類完成

  • java.util.ResourceBundle:用於加載一個資源包
  • java.util.Locale:對應一個特定的國家/區域、語言環境。
  • java.text.MessageFormat:用於將消息格式化

為實現程序的國際化,必須提供程序所需要的資源文件。資源文件的內容由key-value對組成,資源文件的命名可以有3種格式:

  • basename_language_country.properties
  • basename_language.properties
  • basename_properties

若資源文件包含非西方字符,則需要用JDK自帶的工具來處理:native2ascii,這個工具的語法格式如下:

native2ascii 資源文件名 目標資源文件名

如:

native2ascii mess_zh_XXX.proerties mess_zh_CN.proerties

Locale類可獲取各國區域環境(如:Locale.ENGLISH、Locale.CHINESE,這些常量返回一個Locale實例),也可以獲取當前系統所使用的區域語言環境,也可以使用語言和區域來創建Locale對象,Java的本地語言使用的時國際化標準組織(ISO)所定義的編碼,本地語言由小寫的兩個字母的代碼表示,遵循ISO-639-1;國家代碼由大寫的兩個字母的代碼組成,遵循ISO-3166-1,下表為常用的代碼:

語言

代碼

國家

代碼

Chinese

zh

China

CN

English

en

United States

US

Japanese

ja

Japan

JP

可以使用Locale的靜態方法 getAvailableLocales 來獲取所支持的語言和國家,該方法返回一個Locale數組,該數組裏包含了java所支持的語言和國家,代碼如下:

Locale[] availableLocales = Locale.getAvailableLocales();

????????for (Locale l : availableLocales) {

??????????????System.out.println("Locale DisplayName=" + l.getDisplayName() + " Country=" + l.getCountry() + " Language="

+ l.getLanguage());

}

  1. 數字格式

    數字和貨幣的格式時高度依賴與Locale的,Java類庫提供了一個格式器(formatter)對象的集合,可以對java.text包中的數字值進行格式化和解析,可以通過下面的步驟來對特定的Locale的數字進行格式化:

  • 獲取Locale對象
  • 使用工廠方法獲取格式器對象,工廠方法時 NumberFormat 類的靜態方法,接受一個Locale 類型的參數,有三個工廠方法:getNumberInstance(數字)、getCurrencyInstance(貨幣) getPercentInstance(百分比)
  • 使用這個格式器對象來完成格式化和解析工作

格式化示例代碼:

Locale deLocale = new Locale("de", "DE");

NumberFormat currFmt = NumberFormat.getCurrencyInstance(deLocale);

double amt = 123878.34;

String formatResult = currFmt.format(amt);

System.out.println("amt=" + amt + " Format=" + formatResult);

如果想要讀取一個按照某個Locale的慣用法而輸入或存儲的數字,那邊就需要使用 parse 方法。

解析示例代碼:

Localede Locale=newLocale("de","DE");

NumberFormat currFmt=NumberFormat.getCurrencyInstance(deLocale);

Number input=currFmt.parse(formatResult);

double parseAmt=input.doubleValue();

System.out.println("FormatAmt="+formatResult+"ParseAmt="+parseAmt);

  1. 日期和時間

    當格式化日期和時間時,需要考慮4個與Locale相關的問題,例如:月份和星期應該用本地語言來表示;年月日的順序要符合本地習慣;公歷可能不是本地首選的日期表示方法;必須要考慮本地時區。Java 使用 DateFormat 類來處理這些問題,和 NumberFormat 類很類似,調用 DateFormat 類的靜態方法,並傳入 Locale 來實例化,還需要設置日期或時間的格式化值,DateFormat 有如下三個工廠方法:

    DateFormat.getDateInstance(dateStyle,loc);

    DateFormat.getTimeInstance(timeStyle,loc);

    DateFormat.getDateTimeInstance(dateStyle,timeStyle,loc);

    其日期和時間的風格使用 DateFormat 的靜態常量來表示,常用的靜態常量如下:

    DateFormat.DEFAULT

    DateFormat.FULL

    DateFormat.LONG

    DateFormat.MEDIUM

    DateFormat.SHORT

    示例代碼如下:

    String fmt = "";

    Date nowDate = new Date();

    DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, zhLocale);

    fmt = dateFormat.format(nowDate);

    System.out.println("Short style date string " + fmt);

    ? ?

    dateFormat = DateFormat.getTimeInstance(DateFormat.MEDIUM, zhLocale);

    fmt = dateFormat.format(nowDate);

    System.out.println("MEDIUM style time string " + fmt);

    ? ?

    dateFormat = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, zhLocale);

    fmt = dateFormat.format(nowDate);

    System.out.println("FULL style date FULL style time string " + fmt);

    如果要解析一個用戶輸入的日期,可以使用 DateFormat 類的 parse 方法,但其輸入的格式必須和創建 DateFormat 對象設置的風格一致,如果不一致則會拋出 IllegalArgumentException 異常,默認支持寬松的轉換,比如日期 2017年2月30日,會被解析為 2017年3月2日,如果希望關閉寬松的轉換,需要設置 lenient 標識,示例代碼如下:

    dateFormat.setLenient(false);

    Date convertDate = dateFormat.parse(fmt);

    System.out.println("parse string " + fmt + " convertDate " + convertDate);

  2. 字符串排序

    Java 中的排序時用Unicode字符來決定順序的,比如小寫字母的Unicode值比大寫的大,有重音符的字母的值甚至更大,這樣將使結果失去意義,如果需要定義排序的強度,可以使用Locale對象來創建 Collator 類,該類繼承了 Comparator接口,因此可以將該對象傳遞給 Collections.soft 方法來進行排序,示例代碼如下:

    List<String> list = new LinkedList<>();

    list.add("America");

    list.add("Zulu");

    list.add("able");

    list.add("zebra");

    ? ?

    Collator collator = Collator.getInstance(zhLocale);

    // 設置排序強度

    collator.setStrength(Collator.PRIMARY);

    Collections.sort(list, collator);

    可以設置排序強度來選擇不同的排序行為,字符間的差別可以被分為首要的(primary)、其次的(secndary)和再次的(tertiary)。比如,在英語中,A 和 Z 之間的差異被歸類為首要的;A 和 ? (重音符)之間的差異是其次的;A 和 a 之間是再次的,可以使用方法 setStrength 來設置排序強度,Collator.PRIMARY 表示首要的、Collator.SECONDARY 表示其次的、 Collator.TERTIARY 表示再次的,而Collator.IDENTICAL 則表示不允許有任何差異。

    偶爾我們會碰到一個字符或字符序列在描述成Unicode時,可以有多種方式,例如,字母序列"ffi"可以用代碼U+FB03描述成單個字符"拉丁小連字ffi",Unicode 標準對字符串定義了四種範式形式:D、KD、C和KC,其中 D 和 KD 時用於排序的,在範化形式 D 重,重音字符被分解為基字符和組合重音符;範化形式 KD 更進一步將兼容性字符也進行了分解,例如 ffi 連字符或商標符號TM,我們可以選擇排序器所使用的範化程度:Collator.NO_DECOMPOSITION表示不對字符串做任何範化;Collator.CANONICAL_DECOMPOSITION 使用範化形式 D;Collator.FULL_DECOMPOSITION 使用範化形式 KD。可以使用方法 setDecomposition 來設置分解模式

  3. 消息格式化

    Java 類庫中有一個 MessageFormat 類,用來格式化帶變量的文本,就像這樣:"今天 {0} 是個好日子,{1} 公司給我發了工資 {2} 元",括號中的數字是一個占位符,可以用實際的名字和值來替換他,使用靜態的 MessageFormat.format 方法,該方法使用當前系統的 Locale 對值進行格式化,如果需要用指定的 Locale 來進行格式化,需要使用 MessageFormat 實例的 format 方法,示例代碼如下:

    String mfString = "今天 {0} 是個好日子,{1} 公司給我發了工資 {2} 元";

    String formatString = MessageFormat.format(mfString, new Date(), "匯元1", 50000);

    System.out.println(formatString);

    ? ?

    MessageFormat mf = new MessageFormat(mfString, zhLocale);

    formatString = mf.format(new Object[]{new Date(), "匯元2", 50000});

    System.out.println(formatString);

    如果還想在指定占位符的同時設置類型和樣式,可以按照如下格式:

    String mfString = "今天 {0,date,long} 是個好日子,{1} 公司給我發了工資 {2,number,currency} 元";

    mf = new MessageFormat(mfString, zhLocale);

    formatString = mf.format(new Object[]{new Date(), "匯元3", 50000});

    System.out.println(formatString);

    輸出內容

    今天 2017年5月26日 是個好日子,匯元3 公司給我發了工資 ¥50,000.00 元

    占位符索引後面可以跟一個類型和樣式,之間用逗號隔開,類型可以是number、time、date、choice,如果類型是 number 則樣式有 integer、currency、percent;如果類型是 time 或 date,那麽樣式有 short、medium、long、full 或者是一個日期模式(yyyy-MM-dd);choice 表示希望消息跟隨占位符的值而變化,選項格式是由一個序列對構成的,每個序列對包含一個下限和一個格式化字符串,下限和字符串使用#號分隔,對於對之間用 | 分隔,示例如下:

    mfString = "今天 {0,date,yyyy-MM-dd} 是個好日子,{1,choice,0#匯元|1#匯元1|2#匯元2} 公司給我發了工資 {2,number,currency} 元";

    mf.applyPattern(mfString);

    formatString = mf.format(new Object[]{new Date(), 1, 50000});

    System.out.println(formatString);

    可以使用 < 符號或 符號 來替換 # ,則表示值小於或者小於等於下限值,示例代碼如下:

    // choice說明: 這個表示 小於 1000 並且 1001-5000 的使用"可憐" ,5001-50000 的使用"不夠",50001 以上為"正好"

    mfString = "今天 {0,date,yyyy-MM-dd} 是個好日子,{1} 公司給我發了工資 {2,number,currency} 元,{2,choice,1000<可憐|5000<不夠|50000<正好}";

    mf.applyPattern(mfString);

    formatString = mf.format(new Object[]{new Date(), "匯元4", 60000});

    System.out.println(formatString

    輸入內容

    今天 2017-05-26 是個好日子,匯元4 公司給我發了工資 ¥60,000.00 元,正好

  4. 資源包

    當本地化一個應用時,可能會有大量的消息字符串、按鈕標簽和其他的東西需要被翻譯,為了能靈活的完成這項任務,你會希望外部定義消息字符串,通常稱之為資源(resource),翻譯人員不需要接觸程序源代碼就可以很容易的編輯資源文件,在Java 中使用屬性文件來設定字符串資源,並未其他類型的資源實現相應的類。

    當本地化一個應用時,會制造出很多資源包(resource bundle),每一個包都是一個屬性文件或者一個描述了玉locale相關的項的類,對於每一個包,都要為所有你想要支持的locale提供相應的版本,並需要對這些包使用一種統一的命名規則,例如,為中國定義的資源放在一個名為"包名_zh_CN"的文件中,而為所有使用中文簡體的國家所共享的資源則放在名為"包名_zh"的文件中,一般來說,使用

    包名_語言_國家

    來命名所有和國家相關的資源,使用

    包名_語言

    來命名所有和語言相關的資源,最後,作為後備,可以把默認資源放到一個沒有後綴的文件中,可以使用下面的代碼加載一個包:

    ResourceBundle resourceBundle = ResourceBundle.getBundle(bundleName, locale);

    getBundle 方法加載包的順序如下:

    1. 包名_當前locale的語言_當前locale的國家_當前locale的變量
    2. 包名_當前locale的語言_當前locale的國家
    3. 包名_當前locale的語言
    4. 包名_默認locale的語言_默認locale的國家_默認locale的變量
    5. 包名_默認locale的語言_默認locale的國家
    6. 包名_默認locale的語言
    7. 包名
    8. 拋出 MissingResourceException 異常

    一旦getBundle 方法定位了一個包,比如,"包名_zh_CN" ,他還會繼續查找"包名_zh""包名"這二個包,如果這些包也存在,他們在資源層次中就稱為了"包名_zh_CN"的父包,以後查找資源的時候,如果在當前包中沒有找到,就會去查找其父包。

    1. 屬性文件

      屬性文件是為了提供字符串資源常用的文件,每行存放一個鍵-值對的文本文件,比如,MyProgramStrings.properties 就是一個屬性文件,存儲屬性文件都是ASCII文件,然後可以使用 native2ascii 工具來產生MyProgramStrings_語言_國家.properties 文件,示例如下:

      native2ascii MyProgramStrings.properties MyProgramStrings_zh_CN.properties

      要查找一個具體的字符串,可以調用:

      String resourceText= resourceBundle.getString("show.text");

      示例資源文件內容:

      show.text=\u8fd9\u4e2a\u662f\u8d44\u6e90\u6587\u4ef6\u7684\u5185\u5bb9

    2. 包類

      為了提供字符串以外的資源,需要定義類,必須繼承 ResourceBundle 類(簡單的方法是繼承 ListResourceBundle類),應該使用標準的命名規則來命名類,比如:

      MyProgramResource.java

      MyProgramResource_zh.java

      MyProgramResource_zh_CN.java

      可以使用與加載屬性文件相同的 getBundle 方法來加載這個類,其 bundleName 資源包類的完整命名(包名和類名):

      ResourceBundle resourceBundle1Class =

      ????????????????????????????????ResourceBundle.getBundle("locale.MyProgramResource", Locale.SIMPLIFIED_CHINESE);

      要查找一個字符串或其他類型資源可以調用:

      resourceText = resourceBundle1Class.getString("resourceClass.show.text");

      Object resourceObj = resourceBundle1Class.getObject("resourceClass.show.obj");

      如果一個Key同時存在屬性文件和包類,則包類的優先。

      ? ?

筆記:國際化