每個面向國際的語言都需要有自己的國際化解決方案囤屹,Java 從一開始就是用 Unicode
來處理所有的字符串晦鞋,這讓 Java 具備了國際化的一個重要特性肌似。除了提供 Unicode
之外菩帝,Java 還需要解決不同區(qū)域的表示形式过蹂。
Locale
java.util
包中提供了 Locale
類用來處理不同的語言和區(qū)域上的國際化問題纠吴。使用 Locale
類可以定制一個區(qū)域的信息硬鞍,而 Locale
類提供了如下構(gòu)造方法來構(gòu)造 Locale
對象。
public final class Locale implements Cloneable, Serializable {
// 從語言戴已,國家和變體構(gòu)造語言環(huán)境
public Locale(String language, String country, String variant) {
···
}
// 從語言和國家構(gòu)造語言環(huán)境
public Locale(String language, String country) {
···
}
// 從語言代碼構(gòu)造語言環(huán)境
public Locale(String language) {
···
}
}
由構(gòu)造方法可知膳凝,構(gòu)造一個 Locale
,可以設(shè)置語言恭陡、國家或地區(qū)蹬音、和變體。語言是由 2 個或 3 個小寫字母表示休玩,例如 zh
著淆、en
或 de
劫狠。這里可以查看 ISO-639-1
語言代碼標識。
Locale
中預定義了大量的 Locale
語言永部,他們只設(shè)定了語言而沒有設(shè)定國家独泞,如下所示。
// 英語
public static final Locale ENGLISH = createConstant("en", "");
// 法語
public static final Locale FRENCH = createConstant("fr", "");
// 德語
public static final Locale GERMAN = createConstant("de", "");
// 意大利語
public static final Locale ITALIAN = createConstant("it", "");
// 日語
public static final Locale JAPANESE = createConstant("ja", "");
// 韓語
public static final Locale KOREAN = createConstant("ko", "");
// 漢語
public static final Locale CHINESE = createConstant("zh", "");
// 簡體中文
public static final Locale SIMPLIFIED_CHINESE = createConstant("zh", "CN");
// 繁體中文
public static final Locale TRADITIONAL_CHINESE = createConstant("zh", "TW");
國家和地區(qū)也是由 2 個大小字母或 3 個數(shù)字表示苔埋,例如 CN
懦砂、US
或 DE
。這里可以查看 ISO-3166-1
中的國家或地區(qū)標識组橄。
Locale
中也為各個國家預定義了 Locale
對象荞膘,如下所示。
// 法國
public static final Locale FRANCE = createConstant("fr", "FR");
// 德國
public static final Locale GERMANY = createConstant("de", "DE");
// 意大利
public static final Locale ITALY = createConstant("it", "IT");
// 日本
public static final Locale JAPAN = createConstant("ja", "JP");
// 韓國
public static final Locale KOREA = createConstant("ko", "KR");
// 中國
public static final Locale CHINA = SIMPLIFIED_CHINESE;
// 中華人名共和國
public static final Locale PRC = SIMPLIFIED_CHINESE;
// 臺灣
public static final Locale TAIWAN = TRADITIONAL_CHINESE;
// 英國
public static final Locale UK = createConstant("en", "GB");
// 美國
public static final Locale US = createConstant("en", "US");
// 加拿大
public static final Locale CANADA = createConstant("en", "CA");
// 法國裔加拿大
public static final Locale CANADA_FRENCH = createConstant("fr", "CA");
// 表示根語言環(huán)境的常量
public static final Locale ROOT = createConstant("", "");
變體玉工,用于指定各種雜項特性羽资,如果有多個變量值,每個變量值都指示其自己的語義遵班,則這些值應按重要性排序屠升,并用下劃線分隔。變體字段區(qū)分大小寫狭郑。
Locale
除了構(gòu)造方法可以構(gòu)造 Locale
對象腹暖,Java SE 7 還提供了 forLanguageTag
靜態(tài)方法來構(gòu)建 Locale
對象,如下所示:
Locale zhChinese = Locale.forLanguageTag("zh-CN");
Locale twChinese = Locale.forLanguageTag("zh-TW");
System.out.println(zhChinese.getDisplayName());
System.out.println(twChinese.getDisplayName());
// 中文(中國)
// 中文(臺灣)
Locale
中 setDefault
方法可以將更改默認地區(qū)翰萨,并使用 getDefault
方法獲取當前的 Locale
對象微服,如下所示:
System.out.println(Locale.getDefault());
Locale.setDefault(Locale.US);
System.out.println(Locale.getDefault());
// zh_CN
// en-US
Locale
中提供的 getAvailableLocale
靜態(tài)方法會返回由 JVM 所能夠識別的所有 Locale
構(gòu)成的數(shù)組。
ResourceBundle
使用 Locale
設(shè)置具體的語言和區(qū)域后缨历,就可以使用 ResourceBundle
根據(jù)不同的 Locale
對資源進行加載。資源需要放在 resources
資源包中糙麦,并根據(jù)不同 Locale
設(shè)置對應的資源文件辛孵。如下所示,通過 ResourceBundle
類提供的 getBundle
靜態(tài)方法赡磅,來根據(jù) locale
自動綁定對應的資源文件魄缚。
Locale locale = Locale.CHINA;
ResourceBundle bundle = ResourceBundle.getBundle("i18n", locale);
查找一個具體的字符串,可以調(diào)用
String language = bundle.getString("language");
這里的資源文件的名稱使用統(tǒng)一的命名規(guī)則焚廊,然后根據(jù)不同的區(qū)域冶匹,要標識其本地信息的附加部分。例如咆瘟,一個資源包的命名是 i18n
嚼隘,則與中文、英文環(huán)境相對應的資源如下圖所示袒餐。
這里可以把默認資源放在一個沒有后綴 i18n
文件中飞蛹,然后 getBundle
方法定位 i18n_zh_CN
時谤狡,還會繼續(xù)查找文件 i18n_zh
和默認文件 i18n
這兩個文件,如果這些文件存在卧檐,它們在資源層次中會成為 i18n_zh_CN
的父文件墓懂。以后,當查找一個資源時霉囚,如果當前資源文件中沒有找到捕仔,就會去查找其父資源文件。
后綴名為 .properties
的文件中盈罐,屬性是采用 =
分隔的鍵值對的形式榜跌,如下所示:
language=中文
color=紅色
ResourceBundle
是一個抽象類,如下所示暖呕。
public abstract class ResourceBundle {
...
}
繼承了 ResourceBundle
的類有具體的實現(xiàn)類 PropertyResourceBundle
和抽象類 ListResourceBundle
斜做。而 PropertyResourceBundle
是用來支持 ResourceBundle
讀取 properties
資源文件的具體子類,但不需要直接使用 PropertyResourceBundle
湾揽,ResourceBundle.getBundle
會自動查找相應的屬性文件瓤逼,并創(chuàng)建一個引用它的 PropertyResourceBundle
。但是 properties
資源文件提供的值只能是字符串库物。
如果想提供以外的資源霸旗,需要繼承 ListResourceBundle
抽象類并把所有資源放到一個對象數(shù)組中并提供查找功能,如下所示戚揭。
public class I18N extends ListResourceBundle {
@Override
protected Object[][] getContents() {
return new Object[][]{
{"color", "yellow" }
};
}
}
public class I18N_zh extends ListResourceBundle {
@Override
protected Object[][] getContents() {
return new Object[][]{
{"language", new String[]{"簡體中文", "繁體中文"}}
};
}
}
public class I18N_zh_CN extends ListResourceBundle {
@Override
protected Object[][] getContents() {
return new Object[][]{
{"language", "簡體中文"},
{"color", new String[]{"紅色", "黃色", "黑色"} }
};
}
}
這里的類的命名規(guī)則也使用標準命名規(guī)則來命名诱告。然后使用 getBundle
方法來加載對應的類:
ResourceBundle bundle = ResourceBundle.getBundle("I18N", Locale.forLanguageTag("zh-CN"));
System.out.println(bundle.getString("language"));
System.out.println(Arrays.toString(bundle.getStringArray("color")));
除此之外,資源包類也可以繼承 ResourceBundle
類進行擴展民晒,但是需要實現(xiàn)兩個方法精居,一是枚舉所有鍵,二是用給定的鍵查找相應的值:
Enumeration<String> getKeys();
Object handleGetObject(String key);
ResourceBundle
類的 getObject
方法會調(diào)用你提供的 handleGetObject
方法潜必。
NumberFormat
java.text
包中的 NumberFormat
可以根據(jù)不同的 Locale
對數(shù)值進行格式化和解析靴姿。
使用 getNumberInstance
靜態(tài)方法獲取對數(shù)字進行格式化和解析的實例,然后將對應的數(shù)字進行格式化磁滚,看相應的 Locale
實例對應的格式化如何不同佛吓。
NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.SIMPLIFIED_CHINESE);
String result = numberFormat.format(2021.0731);
System.out.println(result);
結(jié)果是 2,021.073
,更換成美國垂攘、德國等的結(jié)果分別是 2,021.073
和 2.021,073
维雇。當數(shù)字值為貨幣的時候,使用 getCurrencyInstance
靜態(tài)方法晒他,如下所示吱型。
NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(Locale.SIMPLIFIED_CHINESE);
String result = currencyFormat.format(2021.0731);
System.out.println(result);
結(jié)果是 ¥2,021.07
,更換成美國陨仅、德國等的結(jié)果分別是 $2,021.07
和 2.021,07 €
唁影。
處理貨幣時耕陷,可以使用 Currency
類來控制貨幣,可以通過 Currency.getInstance
靜態(tài)方法傳入一個貨幣標識并返回 Currency
對象据沈,然后調(diào)用 NumberFormat
中的 setCurrency
方法哟沫。下面的例子是給德國用戶設(shè)置人民幣的格式,如下所示锌介。
NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(Locale.SIMPLIFIED_CHINESE);
currencyFormat.setCurrency(Currency.getInstance("CNY"));
String result = currencyFormat.format(2021.0731);
System.out.println(result); // 2.021,07 CN¥
貨幣標識符由 ISO-4217
定義嗜诀,下面就簡單的介紹幾種貨幣標識符。
貨幣值 | 標識符 | 貨幣值 | 標識符 |
---|---|---|---|
U.S.Dollar | USD | Chinese Renminbi(Yuan) | CNY |
Euro | EUR | Indian Rupee | INR |
British Pound | GBP | Russian Ruble | RUB |
Japanese Yen | JPY |
DateTimeFormatter
每個國家和地區(qū)對日期和時間的顯示都不可能相同孔祸,Java SE 8 提供了 java.time.DateTimeFormatter
類來處理與 Locale
相關(guān)的日期和時間隆敢,格式化和解析出符合本地日期和時間的表示方法。如下所示崔慧,使用 DateTimeFormatter
中的 withLocale
設(shè)置不同的 Locale
拂蝎。
DateTimeFormatter dateFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL).withLocale(Locale.SIMPLIFIED_CHINESE);
之后,就可以格式化 LocaleDate
惶室、LocaleDateTime
温自、LocaleTime
和 ZonedDateTime
了。
ZonedDateTime appointment = ZonedDateTime.now();
String formatted = formatter.format(appointment);
輸出的結(jié)果是 2021年7月31日星期六
皇钞,更換成美國悼泌、德國的結(jié)果分別是 Saturday, July 31, 2021
和 Samstag, 31. Juli 2021
。輸出一個地區(qū)的第一天時夹界,可以通過 Locale
設(shè)置馆里,如下所示。
DayOfWeek first = WeekFields.of(locale).getFirstDayOfWeek();
MessageFormat
一段消息中也會根據(jù)不同的 Locale
進行變更的可變數(shù)據(jù)可柿,Java 提供了 java.text.MessageFormat
來對消息進行格式化鸠踪。如下一段話:
String msg = "On {2}, {0} destroyed {1} houses and caused {3} of damage."
括號中的數(shù)字是占位符,可以通過 MessageFormat.format
靜態(tài)方法使用實際的值來替換占位符复斥。它是一個 varargs
方法营密,所以可以通過下面的方法提供參數(shù):
String result = MessageFormat.format(msg, "a hurricane", 99, new GregorianCalendar(1999, 0, 1).getTime(), 10.0E8);
輸出結(jié)果是:
On 1/1/99 12:00 AM, a hurricane destroyed 99 houses and caused 100,000,000 of damage.
上面的例子還可以為占位符提供可選的格式,將打印的內(nèi)容變得更為精細永票。
On {2, date, long}, {0} destroyed {1} houses and caused {3, number, currency} of damage.
輸出結(jié)果是:
On January 1, 1999, a hurricane destroyed 99 houses and caused $100,000,000 of damage.
一般來說,占位符索引后面可以跟一個 type
和一個 style
滥沫,它們之間用逗號隔開侣集。
MessageFormat.format
靜態(tài)方法使用當前的 Locale
對值進行格式化。要想用任意的 Locale
進行格式化兰绣,需要為這個類提供可以使用的 varargs
方法世分。你需要把將要格式化的值置于 Object[]
數(shù)組中,如下所示缀辩。
MessageFormat mf = new MessageFormat(pattern, locale);
String msg = mf.format(new Object[] {values});
歡迎關(guān)注公眾號「海人為記」臭埋,期待與你共同進步踪央!