為了讓 Linux® 應用程序在全世界范圍都可以使用,而不會在西方語言與世界上其他語言之間產生任何區別,我們應該發行一些本地化后的版本,它們可以輸入、存儲、提取或呈現任何語言,而不管這些語言是多么復雜。多語言庫,或稱為 m17n,為類 UNIX® 平臺上的所有語言提供了一個國際化解決方案。
在很短的時間之內 —— 總共還不到 20 年 —— 個人計算機已經成為我們工作和生活中的一種必需設備。受到半導體和處理器快速發展的推動,大量的供應商使得計算機的價格一落千丈,Internet 也已經在全球廣為分布,個人計算機現在已經不再是一種奢侈品,而是一種常見的家用電器了。
實際上,在很多富裕的國家(例如美國、日本、英國),每兩個家庭就會擁有一臺計算機,并且會使用寬帶服務。就全世界來看,雖然家庭收入可能會有很大的不同,但是個人計算機都很容易購買了,即使在馬爾代夫,我們也很容易購買到筆記本。另外,如果我們碰巧說的是 Dhivehi 方言(馬爾代夫的一種方言),微軟也為我們提供了一個這種版本的 Microsoft® Windows® XP 操作系統。
就全球廣泛接受的個人計算機來說,大部分現代操作系統都提供了一些編程庫來促進 國際化 的發展,或者將軟件調整為支持多種語言的。國際化(通常簡寫為 i18n,節選自 i-nternationalizatio-n)庫通常都會將應用程序的文本資源(按鈕標簽、用戶界面[UI] 提示和菜單選項)保存成多種語言的。在啟動國際化后的應用程序后顯示哪種語言,這要取決于用戶的區域設置 —— 通常,這是一個可配置的系統或個人帳號首選項。
理想來說 —— 至少對于獨立軟件供應商來說 —— 相同的可執行程序以日語或希臘語運行時都能運行得一樣好。然而,構建 “本地方言” 版本的應用程序的情況遠遠沒有這么理想。包括被廣泛認可的 ISO(International Standards Organization)/IEC(International Engineering Consortium)10646 和 Unicode,沒有哪種字符編碼可以解決如何實現任意語言的輸入和呈現問題。ISO/IEC 10646 和 Unicode 只指定了如何存儲、檢索和排序字符以及字符的特殊組合。例如,這些標準并沒有規定統一的格式、嵌入式數據或標識來讓使用泰國語書寫的文檔怎樣才能按照泰國語的規范規則正確地呈現出它們的樣子來。是的,Unicode 可以維護使用泰國語書寫的文檔的內容,也可以保證這種文件在所有使用 Unicode 的平臺上都可以很好地進行移植,但是它并不能保證我們可以正確查看文件,也不能保證文檔所呈現出來的樣子與作者的意圖一致。
我們來考慮一下這種情況:盡管 Linux GNU C
庫(glibc)提供了一些函數來處理 ISO 10646 兼容的 31 位字符,但是它并不能保證這些字符可以在顯示設備上正確進行顯示。有些 glibc 字符串函數,例如 strcat()
和 strlen()
,都可以正確地處理多字節的問題,但是要正確顯示阿拉伯語,需要雙向(bidi)顯示的功能,這種功能只有在圖形用戶界面(GUI)工具包和專用字符串顯示庫中才能找到。
例如,GNOME 需要 GTK+ 工具包和 Pango(一個文本顯示庫)來實現對 i18n 的完整支持(然而,Pango 在解決自己用途不夠廣泛方面有一些限制。請參看側欄 pango 的問題)。其他 GUI 工具包提供了對 i18n 的支持,但卻并不總是能兼容這些標準。當然,Linux 上的圖形應用程序也需要 X Window System 的基本顯示庫 Xlib,它提供了兩種繪圖(形狀和線條)和字符顯示原語。不幸的是,Xlib 只能顯示西歐語言。
![]() |
|
要讓應用程序在全球都可以使用 —— 而不會在西方國家和世界上其他很多語言之間產生不公平的現象 —— 我們必須要能夠 輸入、存儲、提取并 顯示 任何語言,而不管這究竟會多么復雜。正如上面介紹的一樣,有一些廣為認可的標準為多字節存儲和可移植性提供了一些便利;然而,現在還沒有為輸入和顯示制定標準。更加糟糕的是,即使是最好的多語言文本編輯器也會被迫混合使用簡單的國際化庫和私有 GUI 工具包。添加一種語言可能會需要另外一種(很可能是新的)定制庫。
Multilingualization Library 或 m17n 庫會盡力為類 UNIX 平臺上多種語言書寫的文本的輸入、處理和顯示提供一種單一的解決方案。另外,m17n 的目標是充分利用現有且大家都可以很好地理解的典型 UNIX 應用程序框架,而不是利用軟件開發人員的其他模型。
最后,m17n 會努力使國際化的內容更加豐富,而不僅僅是簡單地從英語移植到另外一種語言上。使用 m17n,同一個二進制文件可以在一個系統上顯示法語,在另外一個系統上顯示蒙古語,甚至在同一個屏幕上就可以顯示多種語言的文本。更好的是,m17n 可以(令人信服地)實現諸如文本數據庫之類的功能,這使它可以存儲并處理大量的國際化內容。
m17n 庫是在日本 Tsukuba 的 National Institute for Advanced Science and Technology 工作的 4 個日本程序員編寫的。很多年以來,日本都一直走在了國際化的前端,部分原因是日語學者一直在試圖為人文學科探索一種百科全書式的方法 —— 尤其對世界上各種語言更為關注(有關歷史上的一些內容,請參看側欄 亞洲語言的起源。)
![]() |
|
m17n 庫是由 3 個庫和一個存儲單一腳本以及正確顯示腳本所需要的元數據的數據庫構成的:
C
庫可以類似地實現 glibc (以及各種風格的 libc )的一些基本的文本處理功能。 圖 1 給出了 m17n 的 4 個部分,以及這些庫是如何與現有的系統組件對應的。m17n 組件和傳統 UNIX 庫之間存在驚人的類似之處并不意外:m17n 的創建者希望能夠讓多語言的應用程序的編寫盡量簡單。我們只要使用一個等效的多語言庫來替換相同語義的函數即可。
(從側面來看,m17n C
和 X 庫就預示著 X 服務器可以提供國際化功能。不過,m17n 對底層操作系統和呈現機制的假設較少,因此我們可以將 m17n 移植到其他窗口系統上。實際上,將 m17n 集成到跨平臺的 GUI 工具包(例如類 UNIX 系統上使用的 Qt)正是當前的工作重點,m17n 團隊正在將自己的代碼加入 GTK 的修正版本中。)
添加新拼字法也非常簡單:不需要改編 m17n 庫來顯示新腳本。相反,只需要創建一個新的 m17n M-text,并將 M-text 添加到 m17n 數據庫上即可。
可以將 M-text 當作一個泛化的 C
字符串,因為這就可以將任意屬性添加到通常與 C
字符串有關的字符代碼中。一個屬性可以指定語言要顯示的字符,而另外一個屬性則可以指定特定的字體。Bidi 信息也是使用 M-text 表示來進行編碼的,并且基本的字形信息都可以出現。
例如,圖 2(已經得到 m17n 開發人員的許可進行復制)展示了這些屬性如何用來修改文本字符串的外觀。這個字符串非常簡單,內容是 “This is sample text to show the property”。然而,每個字符串都有一個 face
屬性 —— 或多個 face
屬性 —— 它決定了要使用哪些字體來顯示字符。該圖中所顯示的 face
屬性都故意進行了簡化,但是我們可以看到這種特性所提供的靈活性,這對于世界上很多手寫語言來說都是必要的。
有很多腳本都需要復雜的過程來重新進行排序,或重新放置各個要顯示的復雜合成圖形。諸如泰米爾語、緬甸語和泰國語之類的腳本在進行顯示之前都需要這種重新排序過程。作為更為具體的一個例子,圖 3(也已經獲得作者許可進行復制)展示了單詞 Hindi 是如何進行處理來使用 Devanagri 腳本正確顯示的。這需要兩個階段。第一個階段是將字符序列從字節順序(字符在內存中如何存儲)轉換成正確的手寫順序(字符如何在紙面上顯示)。第二個階段負責掃描特有的字形和讀音序列(如果存在)并將這個序列替換成 “復合” 字形(英語有很多這種轉換來增強文本的可讀性。根據所使用的字體的不同, f 和 i 序列通常都會使用一個 fi 字形代替,這要取決于我們選擇的字體)。
這個重新排序過程的通用名字是 Complex Font Layout(CFL)。通常,CFL 信息都包含在字體中,在某些情況中,已經寫死到顯示庫中了。在 m17n 中,CFL 信息可以在 FLT(Font Layout Table)中找到。有些拼字法需要少量的 FLT 數據;另外一些字符則需要很多信息來捕獲復雜的規則。
例如, Sino-Japanese orthography 就沒有前后規則可以影響單個字形組合的復合。然而,泰國語的確有一些有趣的規則可以影響 orthography 的變化,但是它對于泰國口語不會產生任何影響。泰國語的拼字法對于周圍的文本來說非常敏感,但是對于口語來說則并非如此。印度腳本中特定的組合規則也相當復雜,必須使用 FLT 來顯示。
最后,諸如字體、雙向顯示、Unicode 和語言之類的數據都會將文本的顯示呈現在屏幕上。下一個棘手的問題 —— 也可能是現在正出現在您腦海中的問題 —— 是我們如何以非 ASCII 字體的形式來輸入文本。
對于英語和很多歐洲語言來說,一個字符映射為一個鍵(或兩個鍵)就足夠了。大寫鍵可以直接打印,鍵盤驅動程序可以對很多更為特殊的情況進行編碼,但是其模型是相同的:按下一個鍵就代表輸入某個特定的字符。
那么,如果一種拼字法中有數百個字符,或者更特殊一點,它們之間有很多組合,情況如何呢?那么我們就不能簡單地使用一次擊鍵來實現了,而是需要使用 擊鍵序列 ,或者快速連續輸入多個鍵。有一種特別的軟件叫 輸入法,它可以將每個鍵盤序列轉換成一個字符或一系列字符。
當然,有些擊鍵序列可能就是一次擊鍵。另外,我們可以創建一種輸入法將標準的拉丁字母鍵盤 轉換 成其他語系的拼字法。例如,老式的日語鍵盤就將拉丁字母轉換成平假名和片假名。然而,試圖使用 26 個字母(A 到 Z 的拉丁字母)表示大約 46 個平假名有些困難。
鍵盤映射、擊鍵序列以及音譯輸入法都可以使用 m17n 數據庫來表示。這種方法很大的一個優點是可以將拼字法 的規則與應用程序代碼清楚地區分開來。應用程序代碼最適合由程序員來開發;如何顯示正確的文本則是語言學家的工作。
正如上面介紹的一樣,m17n 包括 3 個庫和 m17n 數據庫?,F在,我們可以使用一個 m17n libc,另外還可以使用一個 Xlib 的 m17n 版本進行編碼。開發團隊正在努力編寫第 3 層的庫即 m17n X 工具包,它將成為 GTK+ 的一部分。m17n 開發人員也從事語言綁定的工作,這樣諸如 Perl 和 Ruby 之類的編程語言就都可以使用 m17n 了(這個工具包和綁定何時可用,尚沒有進度表)。m17n 庫也已經被接納為 Linux Standard Base(LSB)的一個部分,它可能會成為 Linux 國際化標準實現的一個很好的部分。
m17n 庫的最新版本是 1.3.3,這是在 2006 年 2 月 22 日發布的。我們可以按照下面的方式來獲取 m17n 庫:
$ cvs -d :pserver:anonymous@cvs.m17n.org:/cvs/m17n login $ cvs -d :pserver:anonymous@cvs.m17n.org:/cvs/m17n co m17n-lib $ cvs -d :pserver:anonymous@cvs.m17n.org:/cvs/m17n co m17n-db |
從源代碼開始編譯程序也非常簡單:m17n 庫使用了典型的配置腳本來配置系統,并為編譯和安裝創建合適的 Makefile(詳細內容請參看 m17n 軟件包中的 README 文件)。
apt-cache search m17n
。
根據 APT 所指向的 Debian 儲存庫的不同,可能會看到如清單 1 所示的輸出內容。
libm17n-0 - a multilingual text processing library - runtime libm17n-dev - a multilingual text processing library - development m17n-db - a multilingual text processing library - database m17n-docs - a multilingual text processing library - documents m17n-env - set up multilingual X environment m17n-lib-bin - a multilingual text processing library - utilities mlterm-im-m17nlib - MultiLingual TERMinal, m17nlib input method plugin |
在找到包名之后,就可以運行 apt-get install
來自動下載并安裝 m17n 包了。根據 m17n 開發人員的說法,為 Fedora Core、Mandrake、SUSE Linux 和 Gentoo Linux 提供的包也都可以使用。
m17n 庫依賴于幾個其他庫,這幾個庫在您的系統上可能有,也可能沒有。請閱讀前提條件中最新的列表。
從內部來說,m17n 庫會被組織成幾個應用程序接口(API):
m17n 庫的使用與其他 Linux 或 UNIX 的庫的使用相同。如果要使用這個庫的所有特性,就需要在程序中包含 m17n.h 頭文件,然后在鏈接選項中加上 -lm17n
選項,這可以在 Makefile 中實現。 如果只想使用 m17n 的一部分功能,Core、Shell、GUI 以及 Miscellaneous API 每個都有單獨的包含文件。不幸的是,m17n 并沒有很多樣例代碼,很多明顯引用它們的程序,例如可以識別 m17n 的應用程序,也只有兩年的時間。然而,m17n 的軟件開發包(SDK)確實包含了一個簡單的程序,它可以使用各種編碼來顯示文件。我們可以查看一下所下載的 m17n 工具包中的 example 目錄。在這個目錄中,打開 mview.c 文件。這個文件的一部分如清單 2 所示。
... 325 M17N_INIT (); 326 if (merror_code != MERROR_NONE) 327 FATAL_ERROR ("%s\n", "Fail to initialize the m17n library."); 328 329 /* Decide how to decode the input stream. */ 330 if (coding_name) 331 { 332 coding = mconv_resolve_coding (msymbol (coding_name)); 333 if (coding == Mnil) 334 FATAL_ERROR ("Invalid coding: %s\n", coding_name); 335 } 336 else 337 coding = Mcoding_utf_8; 338 339 mt = mconv_decode_stream (coding, fp); 340 fclose (fp); 341 if (! mt) 342 FATAL_ERROR ("%s\n", "Fail to decode the input file or stream!"); 343 344 { 345 MPlist *param = mplist (); 346 MFace *face = mface (); 347 348 if (fontsize) 349 mface_put_prop (face, Msize, (void *) fontsize); 350 mplist_put (param, Mwidget, shell); 351 mplist_put (param, Mface, face); 352 frame = mframe (param); 353 m17n_object_unref (param); 354 m17n_object_unref (face); 355 } 356 357 /* Create this widget hierarchy. 358 Shell - form -+- quit 359 | 360 +- viewport - text */ 361 362 form = XtCreateManagedWidget ("form", formWidgetClass, shell, NULL, 0); 363 XtSetArg (arg[0], XtNleft, XawChainLeft); 364 XtSetArg (arg[1], XtNright, XawChainLeft); 365 XtSetArg (arg[2], XtNtop, XawChainTop); 366 XtSetArg (arg[3], XtNbottom, XawChainTop); 367 XtSetArg (arg[4], XtNaclearcase/" target="_blank" >ccelerators, XtParseAcceleratorTable (quit_action)); 368 quit = XtCreateManagedWidget ("quit", commandWidgetClass, form, arg, 5); 369 XtAddCallback (quit, XtNcallback, QuitProc, NULL); 370 371 viewport_width = (int) mframe_get_prop (frame, Mfont_width) * 80; 372 viewport_height 373 = ((int) mframe_get_prop (frame, Mfont_ascent) 374 + (int) mframe_get_prop (frame, Mfont_descent)) * 24; 375 XtSetArg (arg[0], XtNallowVert, True); 376 XtSetArg (arg[1], XtNforceBars, False); 377 XtSetArg (arg[2], XtNfromVert, quit); 378 XtSetArg (arg[3], XtNtop, XawChainTop); 379 XtSetArg (arg[4], XtNbottom, XawChainBottom); 380 XtSetArg (arg[5], XtNright, XawChainRight); 381 XtSetArg (arg[6], XtNwidth, viewport_width); 382 XtSetArg (arg[7], XtNheight, viewport_height); 383 viewport = XtCreateManagedWidget ("viewport", viewportWidgetClass, form, 384 arg, 8); 385 386 /* Before creating the text widget, we must calculate the height of 387 the M-text to draw. */ 388 control.two_dimensional = 1; 389 control.enable_bidi = 1; 390 control.disable_caching = 1; 391 control.max_line_width = viewport_width; 392 mdraw_text_extents (frame, mt, 0, mtext_len (mt), &control, 393 NULL, NULL, &metric); ... |
下面對這些代碼詳細介紹一下:
coding_name
變量源自于一個指定輸入文件編碼的命令行參數;如果沒有提供這種命令行,就使用 UTF-8 編碼。 coding
中。 shell = XtOpenApplication (&context, "M17NView", NULL, 0, &argc, argv, NULL, sessionShellWidgetClass, NULL, 0)
和最后的類型定義。 總而言之,上面對這段代碼片段的簡短分析就說明了在標準的 X 應用程序中通常要執行哪些操作。在很多情況中,創建一個多語言的應用程序只需要很少的額外代碼就可以實現,這需要采用 m17n 的函數,而不是傳統的 X 調用。
如果沒有可以構建 m17n 代碼的系統,也不要煩惱。您仍然可以通過在線 m17n 呈現演示來體驗這個庫的作用(參看 參考資料 中的鏈接)。
據開發人員說,他們正在繼續在 GTK+ 中集成 m17n —— 這是擴寬 m17n 的認可程度以及影響力的下一個必不可少的步驟?,F在,m17n 項目缺少樣例代碼供參考和擴展。建立更好的文檔也是另外一個需要做的工作,這與為主流平臺提供二進制文件一樣重要。然而,m17n 確實承諾會對各個省的方言也能夠實現 WYSIEYG 的編輯。這對任何語言來說都是個好消息。
個人計算機已經不再是什么新奇的東西了。實際上,在不到 20 年的時間內,計算機已經成為了家庭的日常用品 —— 只不過它不是什么衣服之類的東西,而是用來管理信息的工具。然而,有些國家計算機的獲得和使用還并不普遍。為了平衡這種不平等,需要讓這些國家能夠獲得負擔得起的各種計算機硬件和軟件。另外,還要保證本土居民能夠以本地方言來使用計算機。
m17n 庫構建在 Unicode 和其他標準之上,用來根據手寫語言的規則畫出任意復雜的拼字法。它將代碼與字符的格式區分開來,因此相同的代碼可以反復使用,甚至是在相同的應用程序中呈現不同的拼字法都可以。隨著這些工作的不斷進展,m17n 正在逐漸讓計算機語言變成一種全球的方言。