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

原文地址:Advanced GTK Techniques


這篇教程中你將學(xué)會:

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

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


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

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

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

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

使用 gettext

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

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

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

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

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

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

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

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

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

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

隨后,我們在 main() 函數(shù)開頭添加下面這段代碼,在第 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)"\"

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

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

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

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

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

翻譯程序

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

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

msginit -l nl

這將會基于之前的翻譯模板生成一個名為 nl.po 文件。你可以用你喜歡的編輯器編輯它,在更大型的項(xiàng)目中,你可能會需要一個專業(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 目錄下再一次運(yùn)行 make update-po。這條命令很重要,每次有翻譯人員上傳了新的文件時,你都應(yīng)該運(yùn)行一次,否則文件就不會被應(yīng)用。隨后我們可以運(yùn)行 make install 來安裝。

你現(xiàn)在應(yīng)該已經(jīng)可以運(yùn)行荷蘭語的程序了(或者其它你選擇的語言)。如果你的系統(tǒng)區(qū)域設(shè)定為荷蘭,直接運(yùn)行程序就能看到效果。如果是其它的區(qū)域則需要在運(yùn)行程序時單獨(dú)設(shè)定 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 有一個缺陷:它只對代碼文件有效,可以通過調(diào)用 gettext() 讀取翻譯。而類似于 xml、桌面文件等數(shù)據(jù)文件則愛莫能助。這便是引入 intltool 的原因。它將這些文件中需要翻譯的字符串也添加到了翻譯模板中。在這些詞句被翻譯后,它還會將這些翻譯整合回?cái)?shù)據(jù)文件中。我們現(xiàn)在將 intltool 添加到我們的程序中來翻譯桌面文件。

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

# 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 選項(xiàng),因?yàn)?autoreconfintltoolize 會覆蓋掉彼此的 po/Makefile.in.in 文件。寫好之后我們就可以運(yùn)行一次引導(dǎo)腳本。

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

# 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@ 變量都會被替換成變量指代的實(shí)際內(nèi)容。現(xiàn)在再運(yùn)行一次 make

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

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

如果需要翻譯桌面文件,我們需要在 po 目錄下再運(yùn)行一次 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"

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

以上就是全部編譯基礎(chǔ)構(gòu)建的內(nèi)容。現(xiàn)在我們可以正式開始寫代碼了。:-)


文章許可協(xié)議:Attribution-NonCommercial-ShareAlike 3.0 Unported

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容