2008年6月18日 星期三

GLib 就是懶.為一些 G stuffs 加上多國語言支援

Standard
一般來說,實作一個多國語言的應用程式,會在需要被翻譯的字串上做特殊標注或處理。為了減少標注所浪費的字元數,在 『gi18n.h』中更近一步定義了 _(String) 和 N_(String) ,用以取代原本的 gettext(String)。

事實上,多國語言支援的內部實作,就是呼叫 gettext() 去處理並依情況而填入不同語言的字串,當找不到符合系統語言的翻譯檔時,便會使用開發時所標注的原始語言。使用 #define 去定義成 _(String) 這種形式,也只是圖個方便使用罷了。

然而,N_(String) 又是什麼?和 _(String) 使用的時機不一樣,是用來針對一些 static variable 所提供的多國語言支援方法。之前說到,其實 _(String) 是使用 gettext() 這 function 做字串處理,這在多數狀況下都是可行的,但是在某種情況下,直接使用 gettext() 去對字串做翻譯替換確是有很大的問題。

用一些例子會更為清楚,宣告一個靜態的陣列,裡面事先填好一些字串〔這裡使用 GtkItemFactoryEntry 的資料結構當範例〕:
static GtkItemFactoryEntry menu_items[] =
{
{ N_("/_File"), NULL, NULL, 0, "<branch>" },
{ N_("/_File/_New Window"), NEW_WINDOW_ACCEL, newwindow, 1, "<stockitem>", GTK_STOCK_ADD }
};

其中,『/_File』和『/_File/_New Window』分別使用 N_(String) 被標示成需要被翻譯。為什麼在這不使用 _(String) 而是使用 N_(String) ?因為這是一個靜態的變數陣列,無法要求這陣列被存取時,使用 function 去動態取得字串資料然後再填入陣列,這本身並不符合陣列的行為和定義。可是,不能使用 _(String) 就意味著靜態陣列裡的字串,沒辦法支援多國語言。因此,N_(String) 就是在這樣的問題發生之下,被創造出來的方法,在這特殊的情況中被用來代替 _(String)。

N_(String) 是 gettext_noop() 的簡化表示法,與 _(String) 不同,他提供的是一個固定的指標位置標注字串,而不是像 gettext() 是動態處理字串翻譯。所以, N_(String) 實際上並不處理任何翻譯動作,單純只是標注和提出字串,並給予一個指標位置。

也因為 N_(String) 僅僅只是標注字串,真正要處理 N_(String) 所標注的翻譯,還得借助一些方法。如果是使用之前的範例,要建立 Menu 之前就必需使用其提供的 translate function ,告訴 GtkWidget 陣列中有字串需要被翻譯。
gtk_item_factory_set_translate_func(GtkItemFactory *ifactory,
GtkTranslateFunc func,
gpointer data,
GtkDestroyNotify notify);

最簡單的使用可以像這樣:
gtk_item_factory_set_translate_func(item_factory, my_translate_func, NULL, NULL);

並且要自行定義一個翻譯函數:
gchar *my_translate_func(const gchar *path, gpointer data)
{
return _(path);
}

很多 G 系列的東西,因為都會使用到陣列形式當做參數,所以都會有 g*_set_translate_func 的 function 可使用,我們可使用相同的方法去對陣列中的字串做翻譯。這些函式都可從 G 系列的官方文件中找到:

http://library.gnome.org/devel/references