為什麼 LC_MESSSAGES 需要在 macOS homebrew bash 上導出才能生效?
在 macOS 上,從自製軟體安裝了 bash,我注意到該設置
LC_MESSAGES
似乎對目前 shell 的語言環境設置有一些影響,但LC_MESSAGES
在導出之前消息實際上並沒有改變:取消設置
LANG
andLC_MESSAGES
,我按預期收到一條英文錯誤消息:bash-4.4$ unset LANG LC_MESSAGES bash-4.4$ if :; fi bash: syntax error near unexpected token `fi'
設置
LC_MESSAGES
為不正確的值會導致錯誤setlocale
:bash-4.4$ LC_MESSAGES=foo bash: warning: setlocale: LC_MESSAGES: cannot change locale (foo): No such file or directory
因此*,*當我設置
LC_MESSAGES
. 但是將其設置為合理的值沒有效果:bash-4.4$ LC_MESSAGES=ja_JP.UTF-8 bash-4.4$ if :; fi bash: syntax error near unexpected token `fi'
直到我導出它:
bash-4.4$ export LC_MESSAGES bash-4.4$ if :; fi bash: 予期しないトークン `fi' 周辺に構文エラーがあります
(所有這
LANG
一切似乎都適用。)Bash 手冊中關於Bash 變數的部分沒有說
LC_MESSAGES
或LANG
必須導出(並且那裡列出的大多數其他變數不必導出)。為什麼是這樣?
你是對的,分配
LC_*
shell變數確實會導致使用變數值bash
呼叫setlocale()
相應類別的POSIX,無論它們是否被導出。對於,它再次LANG
呼叫所有變數。因為,它什麼也沒做。setlocale(LC_ALL, thevalue)``setlocale(LC_*)``LC_*``LANGUAGE
現在,
bash
是 GNU 項目的外殼。對於文本的本地化,它使用 GNUgettext
,也稱為libintl
. 它甚至帶有自己的版本,與原始碼捆綁在一起,bash
如果您configure
使用--with-included-gettext
.
gettext
在每種語言的數據庫中查找消息翻譯。它是哪種語言由LC_MESSAGES
類別的值決定,但可以被$LANGUAGE
環境變數覆蓋。根據 gettext 文件,之前的呼叫
setlocale()
應該是確定類別值的呼叫,但有一些複雜性:對於多執行緒應用程序,目前沒有標準 API 可用於 gettext 檢索該值。
bash
不是多執行緒應用程序,但即使setlocale(category, NULL)
返回的是實現定義的,實際上並不總是可用的。因此,在實踐中,gettext 僅用於
setlocale()
在作為 GNU libc 的一部分建構或在 libc 是 GNU libc 的系統上檢索語言名稱(就像在 GNU 系統上建構的那樣bash
)--with-included-gettext
,因為它知道它可以依賴它。在其他系統上,它用於
getenv()
確定語言環境,而不管setlocale()
之前是如何呼叫的,這就是您看到這種行為的原因。導出這些變數很容易解決。有人可能會爭辯說,如果它們不出口,它們無論如何都不是環境的一部分。POSIX 對此不是很清楚。另一種看待它的方式是,翻譯不是由
bash
,而是由第三方機製完成的,所以就像在執行其他命令時一樣,我們需要使用環境變數在兩個軟體之間傳遞語言環境資訊(這里bash
和gettext
)。現在,在 GNU 系統上,它實際上變得更糟了。
如上所示,gettext 包含在 GNU libc 中。
$LANGUAGE
優先於$LC_MESSAGE
但$LANGUAGE
不是 POSIX 語言環境 API 的一部分,這是它之上的擴展。因此,在 GNU 系統上,gettext 將用於
setlocale(LC_MESSAGES, NULL)
獲取 LC_MESSAGES 類別的名稱,因為LANGUAGE
它始終使用getenv()
,LANGUAGE
不是語言環境類別。問題是它自己
bash
管理環境作為其變數處理的一部分,與 libc 的environ[]
數組斷開連接。它確實有自己的getenv()
,它確實查詢自己的環境版本,但是當gettext
它作為 libc 的一部分建構並且bash
動態連結時dgettext()
,它會從 libc 呼叫,getenv()
因為這是 libc 內的內部呼叫,而不是bash
’’,所以將僅從開始$LANGUAGE
時獲取值bash
。因此,在 GNU 系統上,除非
bash
是靜態連結或使用 建構的,否則無論變數是否被導出,對 生成的消息的--with-included-gettext
任何更改都$LANGUAGE
將被忽略。bash
在其他系統上,這很好(只要$LANGUAGE
被導出),因為 gettext 不是 libc 的一部分,所以它確實呼叫bash
’sgetenv()
。在 Debian 上:
$ LANGUAGE=fr bash -c 'LANGUAGE=es; eval fi' bash: eval: ligne 0: erreur de syntaxe près du symbole inattendu « fi » bash: eval: ligne 0: `fi'
(法語消息,呼叫
$LANGUAGE
了當時的值,而不是西班牙語)。bash
實際上,使用其他外殼並沒有好多少。
zsh
沒有翻譯成其他語言,但確實使用strerror()
了gettext
在 GNU 系統內部使用的語言:$ LANGUAGE=fr zsh -c 'LANGUAGE=es; true</x; LANGUAGE=en; true</a; true < /etc/shadow' zsh:1: no existe el archivo o el directorio: /x zsh:1: no existe el archivo o el directorio: /a zsh:1: permission denied: /etc/shadow
很榮幸,
LANGUAGE=es
但看到 ENOENT 的第二條消息沒有以英語顯示(大概是由 gettext 以某種方式記憶體;該記憶體在$LANGUAGE
更改時應該已失效,但事實並非如此)。