[譯文] 如何開始一個真正的 GTK 項目(第五部分:配置 Gettext)

原文地址:Advanced GTK Techniques


這篇教程中你將學會:

  • 使用 Gettext 完成源代碼中的翻譯工作;
  • 使用 Intltool 完成其它文件的翻譯工作。

這篇文章是《如何開始一個真正的 GTK 項目》的一部分,如果你不想回看之前的章節(jié),可以直接下載教學示例程序 app-skeleton3。你也可以從頭開始


這是我們在開始正式的代碼工作前又一個基礎準備工作,這將實現程序在不同語言環(huán)境下的本地化。你可能覺得翻譯工作一般都是在應用成型后才展開,但是在編寫程序時同步開始翻譯往往更利于保持掌控翻譯的質量。

你是否需要為了這項工作學習 30 門外語呢?當然不用。在真實的場合中,人們用英語編寫程序(準確地說是 C locale),然后依賴一個名為 gettext 的工具將所有程序中顯示給用戶的詞句歸納到一個 翻譯模板 或一個 .pot 文件中。

之后要做的就是招募一些翻譯人員到你的項目中來(這才是最困難的部分),讓他們翻譯模板中的所有詞句,生成一個 .po 文件。通常,你需要給你的翻譯人員向源代碼倉庫 commit 的權限,這樣他們就能提交 .po 文件。之后,make install 命令會負責為用戶系統(tǒng)選擇正確的翻譯。

一個程序開始運行之后,它會查詢 LANG 系統(tǒng)變量。如果程序自身存在一個與 LANG 對應的翻譯文件就會調用它,如果不存在就顯示英語。另外,如果翻譯文件沒有實現完整的翻譯工作,程序會調用所有已經翻譯的內容,而未被翻譯的依舊使用英語。這使得翻譯工作可以逐步展開,只要翻譯者貢獻盡可能多的翻譯即可;并且,如果你在程序新的版本中添加了一些新詞句,而翻譯人員度假去了,你也不用擔心整個工程前功盡棄。

使用 gettext

首先,將我們現在的目錄拷貝一份到 app-skeleton4,或者直接重命名,然后修改版本號。為了將 gettext 添加到我們的編譯系統(tǒng)中,我們需要在 configure.ac 的“工具箱”部分添加如下內容:

app-skeleton4/configure.ac
AM_GNU_GETTEXT([external])
AM_GNU_GETTEXT_VERSION([0.18.1])

此外,在“輸出”部分的 AC_CONFIG_FILES 中添加一行:po/Makefile.in,在 Makefile.amSUBDIRS 中將 po 添加進去(po 目錄是 gettext 文件默認存儲的地方。

Makefile.in 而非 Makefile 放入 AC_CONFIG_FILES 中可能有些令人困惑。這是因為 gettext 生成的文件名為 Makefile.in.in,我們需要把它轉換為 Makefile.in。隨后,由 AM_GNU_GETTEXT 宏生成的代碼會自動將它轉換為 Makefile。當然,我們希望這種運行邏輯以后盡量少點出現,否則我們就可能會看到類似于 Makefile.in.in.in.in 的文件了。

我們現在需要運行 autoreconf -i 來安裝 gettext 相關文件。在執(zhí)行 configure 之前,我們還需要在 po 文件夾中添加一些文件。第一個是 POTFILES.in,這個文件列出了 gettext 需要翻譯的所有文件;第二個是 LINGUAS,它列出了所有可用的翻譯語言,元素之間用空格分隔。現在我們先創(chuàng)建這樣兩個空文件,隨后我們再添加內容。

最后一個需要添加的文件名為 Makevars。這個文件包含了一些可由用戶自定義的變量,這些變量將被應用于生成 po 目錄中的 Makefile。剛才運行了 autoreconf 之后,目錄中應該已經有了一個模板文件 Makevars.template。我們用它拷貝出一個 Makevars 文件并修改少量內容:我把 COPYRIGHT_HOLDER 改成了自己的名字,MSGID_BUGS_ADDRESS 改成了 $(PACKAGE_BUGREPORT),這樣翻譯人員就將會把 bug 匯報到我的郵箱中(再一個大型項目中,你可能需要設立不同的郵箱)。

AutoconfPACKAGE_BUGREPORT 變量定義為我們在 configure.acAC_INIT 中設定的郵箱。

接下來,我們需要標記 hello-world.c 中所有需要翻譯的界面字符串。首先在文件開頭加入一行:

// app-skeleton4/src/hello-world.c
#include <glib/gi18n.h>

這是 GLib 中專門定義 gettext 的庫(i18n 表示 “internationalization”,因為在首字母 “i” 和末字母 “n” 之間有 18 個字母)。這個頭文件中定義了函數 _() 用于處理翻譯字符串(如果 gettext 沒有啟用,_() 就不會做任何事;程序名設計成 _ 是為了盡可能減少對讀寫代碼造成的影響)。

隨后,我們在 main() 函數開頭添加下面這段代碼,在第 40 行輸入:

// app-skeleton4/src/hello-world.c
/* Set up internationalization */
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);

這段代碼使得程序能夠讀取翻譯文件的信息。最后,還要在 Makefile.am 中告訴程序去哪里尋找這些翻譯文件:

# app-skeleton4/src/Makefile.am
AM_CPPFLAGS = -DLOCALEDIR=\""$(localedir)"\"

目前我們有四個地方需要被標記成翻譯位置:一個在 print_hello() 中,一個在 on_delete_event() 中,一個在 main()gtk_window_set_title() 的參處中,還有一個在 main()gtk_button_new_with_label() 中。我們用 _() 把這些字符串括起來,比如 "Hello World" 就變成了 _("Hello World")

注意并不是所有的字符都需要翻譯!例如在函數 g_signal_connect() 中的信號名和 gtk_window_set_icon_name() 中的圖標名就不需要任何處理。程序內部調用的字符串均不需要翻譯,只有程序界面顯示的字符串才需要。如果你將 "destroy" 信號翻譯成德語,那程序就崩潰了,畢竟它可不知道 “zerst?ren” 信號是個什么鬼。

完成上述工作后,我們需要在 POTFILES.in 中添加需要被翻譯的文件:

# app-skeleton4/po/POTFILES.in
src/hello-world.c

如果我們現在在 po 目錄下運行 make update-po,就會產生一個名為 app-skeleton.pot 的文件。這是翻譯人員的模板文件。

翻譯程序

我們試著將程序翻譯成其它的語言來測試上面的工作。我將以荷蘭語(語言代碼 nl)做介紹。

首先要做的是創(chuàng)建一個翻譯語言的 .po 文件,在 po 目錄下運行:

msginit -l nl

這將會基于之前的翻譯模板生成一個名為 nl.po 文件。你可以用你喜歡的編輯器編輯它,在更大型的項目中,你可能會需要一個專業(yè)的翻譯文件編輯器,比如 VirtaalPoedit。你可以自己翻譯,也可以直接用我的:

#: src/hello-world.c:11
msgid "Hello World\n"
msgstr "Hallo Wereld\n"

#: src/hello-world.c:27
msgid "delete event occurred\n"
msgstr "delete event heeft plaatsgevonden\n"

#: src/hello-world.c:52
msgid "Hello"
msgstr "Hallo"

#: src/hello-world.c:76
msgid "Hello World"
msgstr "Hallo Wereld"

翻譯完成后,將語言代碼添加到 LINGUAS 中:

nl

隨后在 po 目錄下再一次運行 make update-po。這條命令很重要,每次有翻譯人員上傳了新的文件時,你都應該運行一次,否則文件就不會被應用。隨后我們可以運行 make install 來安裝。

你現在應該已經可以運行荷蘭語的程序了(或者其它你選擇的語言)。如果你的系統(tǒng)區(qū)域設定為荷蘭,直接運行程序就能看到效果。如果是其它的區(qū)域則需要在運行程序時單獨設定 LANG 環(huán)境變量(在終端中輸入 LANG=nl app-skeleton)。

譯者注:常用的語言代碼有:

  • zh_CN:中文(中國大陸)
    zh_HK:中文(中國香港)
    zh_TW:中文(中國臺灣)
    zh_SG:中文(新加坡)
  • en_US:英語(美國)
    en_UK:英語(英國)
    en_CA:英語(加拿大)
    en_HK:英語(中國香港)

更多語言代碼請參閱 Language CodesLocale Helper

翻譯非代碼文件

gettext 有一個缺陷:它只對代碼文件有效,可以通過調用 gettext() 讀取翻譯。而類似于 xml、桌面文件等數據文件則愛莫能助。這便是引入 intltool 的原因。它將這些文件中需要翻譯的字符串也添加到了翻譯模板中。在這些詞句被翻譯后,它還會將這些翻譯整合回數據文件中。我們現在將 intltool 添加到我們的程序中來翻譯桌面文件。

首先,intltool 有它自己的引導程序——intltoolize,它必須在 autoreconf 之后才能運行。使用了 intltool 之后我們的引導過程就不能再只用一句 autoreconf -i 敷衍了事了,我們需要制作一個引導腳本。在項目根目錄下,我們創(chuàng)建一個 autogen.sh 文件,賦予它可執(zhí)行權限,寫入:

# app-skeleton4/autogen.sh
#!/bin/bash

echo "Regenerating autotools files"
autoreconf --force --install || exit 1

echo "Setting up Intltool"
intltoolize --copy --force --automake || exit 1

我們需要用到 --force 選項,因為 autoreconfintltoolize 會覆蓋掉彼此的 po/Makefile.in.in 文件。寫好之后我們就可以運行一次引導腳本。

隨后,在 configure.ac 的 “工具箱” 部分添加如下內容:

# app-skeleton4/configure.ac
IT_PROG_INTLTOOL([0.40])

在 “庫” 部分,我們添加一個新的 “變量” 部分。為了能夠正常使用 intltool,我們需要定義一個名為 GETTEXT_PACKAGE 的變量,它指向程序在 gettext 中被定義的名字。

# app-skeleton4/configure.ac
# Needed by intltool
GETTEXT_PACKAGE=${PACKAGE_TARNAME}
AC_SUBST([GETTEXT_PACKAGE])

AC_SUBST 宏會確保所有 Makefile 中的 @GETTEXT_PACKAGE@ 變量都會被替換成變量指代的實際內容。現在再運行一次 make

接下來,我們需要告訴 intltool 哪些數據文件包含了需要被翻譯的語句。慶幸的是,有一個叫作 intltool-prepare 的腳本會自動幫我們完成這些工作。在項目根目錄下運行 intltool-prepare,你會注意到它自動將桌面文件添加到了 po/POTFILES 中并且修改了 Makefile.am 中桌面文件的安裝規(guī)則。

它還新建了一個文件,app-skeleton.desktop.in。如果你使用了代碼控制系統(tǒng)的話,最好遵循它給出的建議從倉庫中移除你的 app-skeleton.desktop 文件 —— 它現在可以由編譯系統(tǒng)根據 app-skeleton.desktop.in 自動創(chuàng)建出來。從現在起,如果你想編輯桌面文件,請在 app-skeleton.desktop.in 中做修改。

如果需要翻譯桌面文件,我們需要在 po 目錄下再運行一次 make update-po 命令。你將會看到 .pot.po 文件中新添加了兩句未翻譯的語句。你可以自行翻譯它們,也可以用我的翻譯:

#: ../app-skeleton.desktop.in.h:1
msgid "A sample application from the Advanced GTK+ Techniques tutorial"
msgstr "Een voorbeeldapplicatie uit de cursus Gevorderde GTK+ Technieken"

#: ../app-skeleton.desktop.in.h:2
msgid "App Skeleton"
msgstr "Skeletapplicatie"

接著,再運行一次 make update-po,然后是 make。你可以看到在生成的 app-skeleton.desktop 中你的翻譯已經被整合進去了。如果你現在運行 make install,翻譯文件就會被安裝。

以上就是全部編譯基礎構建的內容。現在我們可以正式開始寫代碼了。:-)


文章許可協議:Attribution-NonCommercial-ShareAlike 3.0 Unported

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,698評論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 99,202評論 3 426
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,742評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,580評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,297評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,688評論 1 327
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,693評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,875評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 49,438評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,183評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,384評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,931評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,612評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,022評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,297評論 1 292
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,093評論 3 397
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,330評論 2 377

推薦閱讀更多精彩內容