現在是個國際化的時代,軟體程式如果能夠支援多國語言,想必可以有效地加快傳播速度。讓軟體程式擁有多國語言的能力有很多種方式,像是使用相依於作業系統環境本身所提供的Locale機制的工具,再搭配語言設定檔,例如GNU的gettext工具搭配PO和MO檔。或是使用能跨作業系統的獨立函式庫和語言設定檔,例如fluentd搭配其定義好的特殊語法的語言設定檔。當然也可以很單純地在程式內將所有的文字使用某種key-value的資料結構來儲存,在程式執行階段輸出文字的時候再決定要使用哪個語系實體的值。



筆者在以前使用PHP做開發的時候都是使用PO和MO檔,但存取它們實在是太麻煩了。所以後來使用Java做開發時,就直接改用key-value的資料結構來儲存多國語言用的文字,直接把文字以hardcode的形式寫在一個獨立的原始碼檔案內,雖然很方便,但不太好修改,因為一旦修改就要進行重新編譯的動作。再後來用Node.js做開發時,決定改用JSON格式作為語言設定檔,在程式初始化的時候就將JSON資料轉成key-value的資料結構來儲存,經過一段時間的嘗試,發現這樣的方式是最簡單、最容易維護、且又可以輕鬆跨作業系統的,甚至還能夠在不同程式語言間或是網頁瀏覽器上無痛共享相同的JSON語言設定檔,也不用專門為了搞多國語言又要去學習新的工具和語法!至於開發Android時,多國語言文字的部份筆者還是只靠那難用的XML檔案。

那麼,在Rust上要如何實現以JSON格式作為語言設定檔的多國語言功能呢?

JSON Get Text

「JSON Get Text」是筆者開發的套件,可以利用JSON格式作為語言設定檔,讓Rust程式輕鬆支援多國語言。

Crates.io

https://crates.io/crates/json-gettext

Cargo.toml

json-gettext = "*"

JSON語言設定檔

JSON語言設定檔是一個JSON物件的純文字檔案,鍵值對應的值通常是一個字串,但是也可以是其他任意的JSON值。檔案名稱隨意,方便辨識即可,一般會使用Locale格式的字串,例如「en_US」,再搭配JSON檔案的副檔名「.json」。以下舉例美國英文和繁體中文的語言設定檔撰寫的方式:

JSON Get Text擁有預設語言的功能,所有非預設語言的語言設定檔的鍵值均必須包含在預設語言的語言設定檔中。以這個例子來說,我們設定「en_US.json」為預設語言,所以「en_US.json」可以擁有「zh_TW.json」所沒有的「rust」鍵值。

建立語庫(Context)與搜尋文字

「json-gettext」這個crate底下的「JSONGetText」結構體的每個實體即為一個多國語庫,在同一支Rust程式中,可以建立多個「JSONGetText」結構實體來創造不同的多國語庫。如果要建立「JSONGetText」結構實體,除了可以使用「json-gettext」這個crate底下的「JSONGetTextBuilder」結構實體外,更建議直接使用「static_json_gettext_build」巨集。

「static_json_gettext_build」巨集的第一個參數為預設的語言名稱,接下來的參數兩兩一組,至少要有一組。每一組的第一個參數表示一個語言的名稱,可以隨意取,能夠辨識即可。第二個參數表示該語言名稱的語言設定檔路徑,路徑相對於「CARGO_MANIFEST_DIR」也就是「Cargo.toml」檔案的所在目錄的路徑。

假設「Cargo.toml」檔案位於「/path/to」目錄中,我們將「en_US.json」和「zh_TW.json」放置在「/path/to/langs」下,範例程式如下:

透過「get_text」巨集,第一個參數傳入要使用的語庫(「JSONGetText」結構實體),第二個參數傳入要使用的語言名稱,如果不傳的話則表示使用預設的語言名稱。第三個參數(若不指定語言名稱,則為第二個參數)則是傳入要尋找的文字或其它JSON值的鍵值。如果有找到值,就會回傳「Option」列舉的「Some」變體,其包裹的值的型別為「json-gettext」這個crate底下的「Value」列舉。假如無法在指定的語言名稱,找到指定的鍵值時,則會嘗試去預設的語言名稱找同樣的鍵值。如果還是找不到,則會回傳「Option」列舉的「None」變體。

如果想要一次取得多個鍵值對應的值,可以直接替「get_text」巨集加上更多參數,傳入更多的鍵值。此時「get_text」巨集回傳的「Option」列舉的「Some」變體就不會只單純包裹「Value」列舉的實體,而會是「HashMap<&str, Value>」。範例程式如下:

將語言設定檔編譯至執行檔中

JSON Get Text的JSON語言設定檔,在非「release」模式下編譯時,會透過Lazy Load的方式,在程式執行階段才會去讀取檔案,然後儲存到記憶體中。而在「release」模式下編譯時,JSON語言設定檔會自動被一起編譯至程式執行檔,如此一來在交付程式給客戶或是部署程式時,就不再需要提供另外的JSON語言設定檔了!另外,這樣做的話,不但可以讓程式看起來更乾淨,還可以保護JSON語言設定檔不會被任意竄改。