4.6. locale 的設定

# setenv LC_CTYPE zh_TW.Big5
# setenv LANG zh_TW.Big5

Note: 5.x 的簡體使用者請用:

# setenv LC_CTYPE zh_CN.GBK
# setenv LANG zh_CN.GBK

4.x 的簡體使用者請用:

# setenv LC_CTYPE zh_CN.encCN
# setenv LANG zh_CN.eucCN

locale 是一組 C 程式語言處理自然語言(文字)的程式介面, 也可以簡單的說,locale 就是一組地區性語言的資訊。 由國家語言和各地習俗影響所決定的慣例,或代表一個地理區域的定義所組成, 這些慣例包含文字、日期、數字、貨幣格式和排序等等。這代表著 locale 可讓程式的輸出可以直接反應地方區域性的文化。C 語言的 locale 定義,分為下列各大類:

其中與一般使用者息息相關的,是字元定義 (LC_CTYPE) 與語言顯示 (LANG)。LC_CTYPE 直接關係到某些字元或內碼在目前的 locale 下是否可列印?要如何轉換字碼?對應到哪一個字?.... 等等。LANG 則關係到軟體的訊息輸出是不是符合地域性,例如 :我們需要的是中文。而一個真正完整支援 locale 系統, 是當使用者在 shell prompt 下,直接設好環境變數後, 則馬上就能切換到那個語言了。當 LC_MESSAGES、LC_TIME、LC_NUMERIC、 LC_MONETARY 等沒有設定的時候,會直接取用 LANG 的環境設定值。

設定 Locale 的字元定義為台灣地區的 Big5 繁體中文碼定義, 有了正確的 locale 的定義後,使得任何地區的語文,只要在加入適當的 locale data 之後,C Library 就能正確地處理軟體顯示訊息, 而我們使用的中文當然也不例外,而目前常用的中文 locale data 就是 zh_TW.Big5,代表的就是中文語系(zh)台灣地區(TW) 使用Big5編碼系統(Big5)。

Note: locale 命名規則:語言_地區名.字元編碼名稱

當一個程式啟動時,系統會預設給它一個初始 locale,稱為 POSIX 或 C locale。在此 locale 下,程式的表現會與傳統的 C 語言中一樣, 使用英文做訊息輸出,只能處理英文等 ASCII 碼等等。 如果該程式有支援 I18N,也就是說它有按照 I18N 的標準來寫, 則它在啟動後就會馬上呼叫系統函式來改變它的 locale, 如此它就搖身一變,變成可以處理該 locale 所代表的地區語文了。

zh_TW.Big5 是目前台灣內廣泛使用的 locale, zh 是華語(Chinese),1998 年 ISO639 裡面以兩個英文字母來代表語言編碼, 這個縮寫據筆者所知沒有任何含義,而 TW 代表的就是台灣(Taiwan) 地區的縮寫,最後的 Big5 則是編碼方式。

locale 設定檔在編譯後, 則是儲存在 /usr/share/locale/ 目錄下, 以 zh_TW.Big5 locale 為例,該目錄中就包含了 LC_COLLATELC_CTYPELC_TIME

而 LC_MESSAGES 則是儲存在 /usr/local/share/locale/zh_TW/LC_MESSAGES/ 或是 /usr/X11R6/share/locale/zh_TW.Big5/ 底下。由於 LC_MESSAGES 類別掌管的是程式訊息輸出所用的語言, 而且不同程式間的訊息都不會一樣,因此它不能像其他類別一樣, 只提供單一一個資料檔即可。相反的, 在這堜珣蘑的方式是由各應用程式自行提供它們的訊息資料檔, 並統一放在各 locale 的 LC_MESSAGES 的目錄下。例如 mutt 程式, 其訊息的部分除了英文以外,可能還同時提供了繁體中文、簡體中文、 日文、法文 等翻譯,因此,在以上這些語文所代表的 locale 中, 其底下的 LC_MESSAGES 目錄中都會有一份屬於 mutt 程式的訊息資料檔。 換句話說,在 I18N 架構下,程式訊息部分是與程式分離的, 如此才能分別對各 locale 做 ``區域化'' (即翻譯成各地區的語言)。 如此,當 mutt 在執行時,系統會根據目前它的 LC_MESSAGES locale 設定去找找看有沒有它的訊息資料檔存在,有的話就以該語言做訊息輸出, 否則的話則以 C locale 的方式 (即英文) 來輸出訊息。

以上所有的 locale 類別中,除了 LANG 之外,最重要的就是 LC_CTYPE 了。此類別掌管的是該 locale 中所有字元的處理方式。 一個應用程式若要能被 ``區域化'' 成某地區的語文, 首要工作就是要能處理該地區的文字。例如每個字的內碼如何編碼? 是單一 byte 還是由多個 bytes 組成的?怎樣的編碼才是合法可用的? 這個字是不是可以印?是不是數字?若給定任意的文字字串, 要如何能區分出一個個字等等。因此,此類別牽涉的層面相當廣, 除了程式本身的文字處理能力以外,甚至到 X Window 中的文字顯示 (即 XOM: X Output Method)、文字輸入 (即 XIM: X Input Method) 等等, 都與它有關。因此,當要開始使用一個支援 I18N 的程式之前, 一定要先設好 LC_CTYPE 這個 locale 類別。

必須指定 zh_TW.Big5 這個 locale 環境變數給 Shell,Shell 才能正確的處理中文訊息,一般而言只要指定 locale 的字元定義 LC_CTYPE 為 zh_TW.Big5 即可讓 Shell 正確的處理中文,若要讓 Shell 的輸出訊息也顯示中文,則可將 locale 訊息顯示 LANG 也設定為中文的 locale data。

至於要設定 stty pass8 的原因是,台灣地區所通行的 Big5 編碼, 及大陸地區所使用的 GB 編碼,其開頭的位元幾乎都是大於 128 的數值, 也就是所謂 non-ASCII 碼的範圍(ASCII 是指小於 128 的編碼)。 中文問題就在這裡,許多程式由於各式各樣的原因, 並未考慮到輸入的資料可能是 non-ASCII 碼的問題, 程式往往假設了她所要處理的資料都是 ASCII 碼 (因為大部分軟體為外國人發展的),更糟糕的是, 當程式遇到 non-ASCII 碼時,常常假設她不存在, 而將它的第八個位元截去,這是所謂的 8-bit 輸入中文時, 每每將第八位元砍掉,所以中文都變成亂碼。因此必須指定 stty pass8, 警告 Shell 不要將輸入的第八個位元截掉,這樣才能正確顯示中文。

ENABLE_STARTUP_LOCALE 則是 a.out 遺物,他會強制 ld.so 載入程式前, 先呼叫 setlocale(),在 3.x 前就把這個 ugly hack 拿掉了。

關於字元的分類與編碼,一個 locale 所包含的合法字元與其編碼方式, 稱之為字集(character set)。以 zh_TW.Big5 locale 為例, 其實它內部包含了兩個 sub-character set,一個是 ASCII 用來表示一編的英文、數字、電腦慣用符號 等等,另一個就是以 Big5 編碼方式的,俗稱的全形字,包括了中文字、 全形英文、數字、以及符號 等。前者每個字元的長度是一個 byte,而後者每個字元的長度則是兩個 byte。

在 locale 的設定中,以 LC_CTYPE 最為重要, LC_CTYPE 包含了字元內碼資訊, 直接影響部份 C 函式隊字元的處理結果,包括:

zh_TW.Big5 LC_CTYPE locale 的設定檔在 /usr/src/share/mklocale/zh_TW.Big5.src, 在 LC_CTYPE 的設定檔中,它將所有的字元分類成以下幾種:

UPPER: 拼音字的大寫字。
LOWER: 拼音字的小寫字。
ALPHA: 所有的拼音字母。
DIGIT: 阿拉伯數字。
SPACE: 空隔字元,如空白 (space)、換行、tab ...字元等。
XDIGIT: 代表十六進位數字的字元。
BLANK: 空白字元,通常只包括空白 (space) 與 tab 兩個。
CNTRL: 電腦的控制字元。
PUNCT: 標點符號。
GRAPH: 所有有筆畫的字元,不包括空白與空格。
PRINT: 所有可以印出的字元,包括空白 (blank) 字元。
TOUPPER: 小寫拼音字母轉大寫拼音字母的對應表。
TOLOWER: 大寫拼音字母轉小寫拼音字母的對應表。