Java 基本上是一個講究國際化的語言,本身在如何進行國際化有很大的使用彈性。一般所使用的framework,像JSF、Struts,不乏提供預設的國際化操作。不過當你如果沒有使用這些framework的時候,或是你的系統很小,小到不需要使用任何framework,卻又想要國際化的時候,該怎麼使用Java原生的國際化框架呢?
寫在前頭
properties
首先,你必需要先知道所謂的properties檔。
Properties
是一種key-value的純文字語言檔。 所有的定義,不如給你一個範例。 這就是一個範例:
example.properties
deftUser=Super User
ui.menu.System=系統
ui.menu.Login=登入
=
前面的就是key,後面的就是value。
properties檔有幾個需要注意的事:
- 他的副檔名是.properties。
- 編碼格式建議(不是必需)是使用UTF-8。
- 慣例上會使用像package的方式來為key進行分類。
(Java裡面也有一個class 叫Properties
跟我們目前要討論的主題有點關係,但不是那麼有關係。 有關係的地方在於,他也是叫Properties,也同樣是key-value;就這樣而已。 大家有空的話也是可以看看。)
Locale
Locale
在Java 裡面,在在與國際化有關。當你看到一個class或是一個method有這個字樣,就表示他跟國際化或多或少有些關係。 另外,英文不是我們的第一語言,所以你可能會覺得他跟本機有什麼關係。 請仔細看清楚,本機的英文是local,本地化的英文是locale ,不一樣。 順帶一提,如果你看到class或method有local字樣,也表示他跟本機有關,但是跟畚箕無關 XD…。
有時候這兩者的關係並不是那麼高
談到本地化,Locale
有二件事需要知道。一、本地化的語言,二、與本地化的國家。 以語言來說,最有名的是zh─ zhong wen(中文)。 XD。 以國家來說,當然要提到TW XDD。 這就是偶爾會看到所謂的zh_TW。 當然,世界不是那麼小,ISO 還定義了其他的國家與語言縮寫。 以西方語系來說,可能就會是en_US。
你可以參考:
在Java 裡面,也有一個Locale
class,可以取得本預設本地化,還有其相關資訊。當然,想要更了解Locale
,還是需要參考API。
最快速的本地化
ResourceBundle
這是我們主要用來進行國際化的class。
簡單來說,國際化的程式碼就是這樣的…
package taichitech.blog;
import java.util.Locale;
import java.util.ResourceBundle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Localization {
private static Logger logger = LoggerFactory.getLogger(Localization.class);
public static void main(String[] args) {
ResourceBundle deftResourceBundle = ResourceBundle.getBundle("example", Locale.getDefault());
printMessage("預設國際化-我是台灣人", deftResourceBundle);
ResourceBundle engResourceBundle = ResourceBundle.getBundle("example", Locale.US);
printMessage("美國化", engResourceBundle);
Locale.setDefault(Locale.CHINA); //修改執行環境預設Locale
ResourceBundle twResourceBundle = ResourceBundle.getBundle("example");
printMessage("大陸化", twResourceBundle);
}
private static void printMessage(String preMessage, ResourceBundle resourceBundle) {
logger.debug(preMessage);
logger.debug("\t" + resourceBundle.getString("deftUser"));
logger.debug("\t" + resourceBundle.getString("ui.menu.System"));
logger.debug("\t" + resourceBundle.getString("ui.menu.Login"));
}
}
資源檔,也就是前面所提到的.properties
檔;除了example.properties外,還要再產生2個檔:example_zh_TW.properties檔與example_en_US.properties:
example_zh_TW.properties
deftUser=\u7ba1\u7406\u4eba\u54e1
ui.menu.System=\u7cfb\u7d71
ui.menu.Login=\u767b\u5165
這一段一般人看不懂,不過電腦看得懂。 在電腦的眼中,長得會是像這樣子:
deftUser=管理人員
ui.menu.System=系統
ui.menu.Login=登入
example_en_US.properties
deftUser=Manager
ui.menu.System=System
ui.menu.Login=Sign in
執行結果:
188 [DEBUG] 預設國際化-我是台灣人
191 [DEBUG] 管理人員
191 [DEBUG] 系統
191 [DEBUG] 登入
192 [DEBUG] 美國化
192 [DEBUG] Manager
192 [DEBUG] System
192 [DEBUG] Sign in
193 [DEBUG] 大陸化
193 [DEBUG] Super User
193 [DEBUG] 系統
193 [DEBUG] ç»å
¥
發生了什麼事?
這段程式碼,一共做了3個範例,使用了3個.properties檔。每個.properties檔除了key值之外,value的值其實都不相同,有些還甚至不太相關。不過這是案例設計,可以讓大家作交叉比對。其實在實務上,這種情形也是很有可能發生。每一段的範例使用不同的Locale
來設法取得不同的.properties檔。
在正體(繁體)作業系統/Java 執行環境上的本地化參數,預設是 Locale.TWIWAN
,也就是之前提到的zh_TW。 在預設的情況下,ResourceBundle.getBundle
會吃 zh_TW 的資源檔。如果在根目錄下,找不到example_zh_TW.properties
檔,就會使用example.properties
作為resource檔案。 你可能已經猜到,「大陸化」那一段,就是吃到example.properties。
因為我們並沒有 example_zh_CN.properties
,而且是Taiwna版、繁體中文的執行環境,所以一來getBundle()
會吃到example.properties
,二來會導致ui.menu.System
與ui.menu.Login
兩個值出不來。 如果執行環境是簡體環境,應該是可以正確顯示。
前面也提到ResourceBundle
是主要處理國際化的class。 不過ResourceBundle是抽像類別,你無法直接使用new
來產生instance,所以必需透過static method來取得instance。 取得的static method 是ResourceBundle.getBundle
。 當然,getBundle
還有許多用法與overloading
,當然是詳參Java doc API。 我這裡先講解一下範例中所使用的API。
public static final ResourceBundle getBundle(String baseName, Locale locale)
getBundle
,第一個參數,丟的是baseName,顧名思義,就是.properties
的檔名但不加副檔名。 locale 是打算使用本地化資訊。
美國化那一段的範例,就是直接使用預設的美國Locale
來取得資源檔。
ResourceBundle engResourceBundle = ResourceBundle.getBundle("example", Locale.US);
有注意到Locale.US
了嗎?是的,Locale
中已經預設了好幾個常用的國家,如美國、中國…,基本上一次世界大戰與二次世界大戰有參戰的國家都有。如果真的有要用而預設值沒有的話Locale
,可以參照上面的ISO規範連結與API。
最後一段的範例示範,修改了預設的Locale,改為偉大的祖國 ─ China。
Locale.setDefault(Locale.CHINA); //修改執行環境預設Locale
範例中打算利用Locale.CHINA
來產生RresourceBundle
物件,來取得 example_zh_CN.properties
資源,不過因為我們並沒有這個檔案,所以取到了example.properties
。
進階變化
如果不在root下?
之前我們的範例,資源檔(.properties)放在根package下,如果不想放到 root package下,直接在base name前帶上 package名稱就可以了。
ResourceBundle.getBundle("not.root.package.example")
Native2ascii
example_zh_TW.properties
一般人看不懂,這是中文轉成原生ASCII碼的結果。 一般人看不懂,可是Java國際化的時候卻需要他。 在下載的JDK版本中,有附帶一個native2ascii.exe
可以將人類看得懂的東西,例如「管理人員」,轉成看不懂的東西,例如「\u7ba1\u7406\u4eba\u54e1」:
管理人員→\u7ba1\u7406\u4eba\u54e1
Native2ascii需要在命令列模式下執行,格式如下:
C:\>native2ascii [options] [inputfile [outputfile]]
其中options
有這些:
-reverse 將看不懂的東西轉成看的懂的東西
-encoding encoding_name 指定編碼格式,通常是utf-8
球不是這樣踢的,範例不是這樣寫的
有些書或範例,英文版寫在example.properties檔,會將中文寫在其他的檔案,例如example.txt,再用工具轉成example_zh_TW.properties。我這裡特別提出來,是因為最近的開發經驗與之前的開發經驗不同。 該怎麼做,端視忽團隊的規劃。 我個人會偏向像範例這樣的用法,將開發的語言,也就是中文,寫在example.properties中,而在最後,產生example_en_US.properties,而不是產生example_zh_TW.properties。 再一次強調,這必需依狀況而調整,不是絕對的做法。
沒有留言 :
張貼留言