Google Prettify

2014-10-17

寫程式要講求國際觀─如何對程式進行國際化

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.Systemui.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。 再一次強調,這必需依狀況而調整,不是絕對的做法

沒有留言 :

您或許對這些有興趣

Related Posts with Thumbnails

最後

謝謝您的閱讀,希望您可以有豐富的收獲。