使用 Gettext 方案製做多國語系程式 - 總覽

有時候程式寫著寫著一不小心生意愈做愈大,常常就會需要面對不同語言文化客戶的問題, 也就是「國際化與地區化」的議題(Internationalisation and Localisation), 而這些問題中的第一個需要解決的問題常常就是:如何讓我的程式可以切換顯示各國語言的文字? 對於這個問題,我要介紹使用的是 gettext 方案, 這是在自由軟體以及 UNIX 平臺上廣泛被使用的方案。

方案比較

讓應用程式支援多國語系的解決方案可能有很多不同的方式,那麼為什麼我推薦 gettext 方案呢? 這裡就要來比較 gettext 在一些常見的方案中有哪些突出的優點? 對於桌面三大平臺: 在 Linux 和其他 UNIX like 平臺上使用比例最高的就是 gettext 方案,好像也就沒什麼好比較的; 在 OS X 平臺上,我對 Apple 的東西涉略不多,難下論斷, 但既然 OS X 繼承自 Free BSD,那麼理論上應該也可以使用 gettext 無縫接軌; 因此最後會發現,似乎只有在 Windows 平臺上才有不同方案比較的意義, 所以接下來就來就來看看在 Windows 上面可能常見到的多語系開發方案。

不像在 Linux 下被 gettext 統一天下, Windows 平臺下的多語系支援方案比較凌亂,各種解決方案百花齊放。 這可能是因為微軟主推的方案太過繁複,然後其他的方案又都有一些窒礙難行的地方, 沒有誰能夠大大的優於另外一個,最終導致各種稀奇古怪的方案充斥的結果。 在這些方案當中,比較常見的一些如下:

  • INI 類文字檔

    做法最簡單也很常見的一個方案是把程式中使用到的各種文字編為 ID, 然後把這些 ID 和對應的實際文字寫在一個 INI 檔案 (或其他格式文字檔案、這裡僅以 INI 為代表), 實務上有人為每一個支援的語言各建立一個 INI 檔, 也有人把所有支援語言的文字編在同一個 INI 檔(這時就要注意編碼問題 ^_^)。 當程式執行時,首先依據自身設定載入對應的 INI 檔案, 然後在後面程式運作的途中需要輸出文字時,就查詢並取用 INI 檔內的字串即可, 例如「INI 多國語言介面」。

    這個方案的優點是簡單容易實現,而且似乎不管什麼樣的程式、什麼樣的平臺都可適用; 然而缺點是缺乏現成的相關處理工具。

    要知道一個在具有一定規模的實際專案中,所使用的字串數量成千上萬, 並且隨著程式的開發過程可能還會不斷變化, 那麼翻譯人員難道要手工搜尋處理這樣龐大且隨時變化的文字檔? 通常我們會希望透過一些處理工具來輔助翻譯人員, 至少能夠執行合併新舊翻譯檔、區分已翻譯和未翻譯條目等等工作; 可是因為整個文字檔的格式內容是自訂的、載入以及使用方式也是自己設計的, 因此可能沒有現成的工具可以處理這些工作, 那麼要不就是需要自己開發相關工具,要不就是讓翻譯人員全手動處理翻譯檔!

    這些因素使得 INI 方案只適用於不那麼複雜的小型程式、或者缺乏開發資源的專案 (不過就苦了翻譯人員了,雖然在缺乏開發資源的情況下通常程式開發和翻譯會是同一票人)。

  • Windows 資源檔

    對於熟悉 Windows 程式開發的人可能多半知道, Windows GUI 程式有一個特別的「Windows 資源檔」這種東西, 還要用資源檔編譯器編譯後再連結到最終的程式。 微軟對於多國語系開發的部份主打使用資源檔, 就是將所有程式中使用的字串都編到資源檔裡面去,執行時期再透過 ID 來找對應的字串, 概念上與使用 INI 檔類似,只不過把 INI 檔換成資源檔。

    這麼做的好處除了微軟在 官方文件上對於多語系程式推薦使用這種方式、 以及可以把程式全包成一個執行檔而不用附帶一大堆檔案以外,似乎就沒再有什麼太大的優點。 使用 Windows 資源檔做為多國語系解決方案倒是存在不少缺點:

    • 當人們需要增加一些翻譯字串、或想要擴充支援原來沒有被支援的語言時, 需要修改資源檔並重新編譯程式。
    • 除了使用 Visual Studio 以外,有沒有什麼好的資源檔編輯器? 要知道一般的翻譯人員可能通常不會寫程式,難道要先教會他們使用 Visual Studio?
    • 如果開發的程式不是 MFC 或 WTL 專案,能夠簡單順暢的使用資源檔嗎?
    • 如果不使用 Visual C++ 編譯器的話,能夠簡單順暢的使用資源檔嗎?
    • 既然 Windows 資源檔是 Windows 下特有的產物,那如何面對跨平臺程式開發的需求?

    此外,若專案沒有建基在 Unicode 環境的話,還會面臨一些嚴重的問題。 我在剛畢業的時候負責開發維護一個建立在 Borland C++ Builder 6 下的專案, 就是使用 Windows 資源檔以及一些小腳本處理多國語言的問題, 我們還做了一些工具用來掃描 DFM 檔案並生成翻譯用的 Excel 檔, 再把 Excel 檔內容取出並覆蓋回 DFM 檔和 RC 檔。 那時我們的程式主要需要中文、簡中、和英文版本,為此我們需要編出三套執行檔。 英文的部份在任何語系下都能被支援, 但為了編譯 GBK 編碼的資源,我們需要重新開機到簡中環境下編譯這部份的程式碼, 然後再重開機回來進行程式打包工作,你看那如果我們要支援更多一點語言的話不就忙死了! 我後來寫了 batch 腳本處理各種工序,使得一個按鍵可以完成絕大部分的編譯打包工作; 只是碰到需要切換語系並重新開機的部份就沒輒了,整個程序最終仍然無法一鍵完成。

    有人會說那如果把整個專案改成 Unicode 環境呢?這個我嘗試過了,最終仍以失敗告終。 為了這件事我還寫了一份關於 「字元編碼與程式設計」的心得。

  • 其他 GUI 框架自己的方案

    其實除了微軟的 MFC、WTL 等等之外,Windows 平臺上還有一些其他的 GUI 框架, 這些框架可能也覺得現有多語系程式的開發存在許多困難的問題, 因此索性也各自提出了自己的多語系方案,比如 Qt 早早就推出了多語系方案, 較新版本的 Borland VCL framework 也推出了自己的 ITE 多語系方案, 這樣,Windows 上的多語系方案就更凌亂了!

    使用這些 GUI 框架多語系方案的好處是這些方案與框架本身最匹配, 並且可以無需理會平臺的差異,在不同平臺下都使用同一套做法(如果該框架本身有跨平臺的話)。 而缺點就是該方案只適用於該框架,當轉換框架時就要重新來過; 或者比如說我今天想要做個 console 工具程式, 難道為了多語系的緣故我還要大費周張把 GUI 框架給拉進來嗎?

  • GNU gettext

    最後介紹重頭戲,就是廣泛被用在跨平臺程式的 gettext 方案, 這個方案有一些優點:

    • 由於使用廣泛且歷史悠久,存在許多現成的工具程式。
    • 可以將開發者和翻譯者的工作流程分離,翻譯者無需涉及程式碼和編譯工作。
    • 該方案是一個僅僅用於處理多語系文字相關的程式庫和工具集,無涉任何框架, 可被使用於任何專案。
    • 除了 C/C++ 以外,gettext 還有如 Java、Python、C# 等等多種語言的 bundle, 適用範圍挺大。
    • 該方案本身無平臺上的強相依性,可被移植使用於任何平臺。
    • 該方案本身結構單純,即便在執行時庫的移植或授權上遇到問題,自行實做也很容易。
    • 該方案下的語系檔案本身獨立於應用程式之外, 可在不動到應用程式的情況下對翻譯資料進行更新、擴充,或加入原本所沒有的語系。

    由於 gettext 的執行時庫已被納入 Glibc, 也就是說基本上在 Linux 平臺下是原生支援 gettext 環境, 連特別連結什麼程式庫的動作都不需要,只要直接使用它就好了,可說是非常方便。 由於眾多優點,特別是在跨平臺特性上面的優點, 使得大量跨平臺的自由軟體在 Windows 上都還是依循使用 gettext 方案, 比如說著名的 LibreOffice

    缺點就是 Windows 並沒有原生支援 gettext,至少需要執行時庫的移植工作; Windows 的 locale name 和 Linux 也不太一樣,需要轉換一下, 這些不方便的地方使得 gettext 方案並沒有大量流行在非跨平臺的應用程式上。

以上就是個人對一些多語系方案的見解和觀察,也許有些偏頗或不週全的地方還請多包涵。 我本人因為一些緣故需要在 Windows 使用 gettext 方案,目前在移植工作上進行的不太順利, 後來一看 MO 檔案的格式 超級簡單,索性就自己做了一個 簡單的(而且限制比較多的)程式庫 解決了我的問題。

gettext 使用指引

雖然主打著介紹和教學的標題, 但在本篇裡,我沒有打算描述太多有關於 gettext 的使用細節, 只打算做一些比較簡單的介紹,對於較多的操作細節則留待後篇描述。

對於一個使用 gettext 方案處理多語系文字的程式而言,在開發、建置、和發佈等整個過程中, 大約會需要面對幾個部份的工作: