MacOSで、自作からbashをインストールすると、LC_MESSAGES
の設定が現在のシェルのロケール設定に何らかの影響を与えるように見えることに気付きましたが、メッセージはLC_MESSAGES
がエクスポートされるまで実際には変更されません。
LANG
とLC_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
を設定すると、somethingが変化します。ただし、適切な値に設定しても効果はありません。
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_*
_シェル変数を割り当てると、エクスポートされているかどうかに関係なく、bash
が対応するカテゴリのPOSIXsetlocale()
を変数の値で呼び出すようになります。 LANG
の場合、すべての_LC_*
_変数に対してsetlocale(LC_ALL, thevalue)
に続いてsetlocale(LC_*)
を再度呼び出します。 LANGUAGE
の場合、何もしません。
現在、bash
はGNUプロジェクトのシェルです。テキストのローカリゼーションには、GNU gettext
、別名libintl
を使用します。独自のものも付属しています。 _--with-included-gettext
_でbash
スクリプトを呼び出すと、configure
でコンパイルできるソースにバンドルされているバージョン。
gettext
は、言語ごとのデータベースでメッセージの翻訳を検索します。どの言語であるかは、_LC_MESSAGES
_カテゴリの値によって決まりますが、_$LANGUAGE
_環境変数で上書きできます。
Gettextのドキュメントによると、前回のsetlocale()
の呼び出しは、カテゴリの値を決定するものである必要がありますが、いくつかの問題があります。
マルチスレッドアプリケーションの場合、 現在、gettextがその値を取得するために使用できる標準APIはありません 。 bash
はマルチスレッドアプリケーションではありませんが、 setlocale(category, NULL)
が返すものは実装定義であり、実際には常に使用できるとは限りません 。
したがって、実際には、gettextはsetlocale()
を使用して、GNU libcの一部として、またはlibcがGNU libc(GNUシステム)でbash
と_--with-included-gettext
_を使用して構築されたもののように、信頼できることがわかっているため)。
他のシステムでは、以前にgetenv()
がどのように呼び出されたかに関係なく、setlocale()
を使用してロケールを決定します。そのため、この動作が表示されます。
これらの変数のエクスポートは簡単な回避策です。それらがエクスポートされない場合、それらはとにかく環境の一部ではないと主張することができます。 POSIXはそれについてあまり明確ではありません。別の見方をすれば、変換はbash
ではなく、サードパーティのメカニズムによって行われるため、executing他のコマンドの場合と同様に、環境変数を使用してロケール情報を渡す必要があります。 2つのソフトウェア(ここでは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()
はgetenv()
をから呼び出します。 libcは、bash
の呼び出しではなく、libc内の内部呼び出しであるため、bash
が開始された時点からのみ_$LANGUAGE
_値を取得します。
したがって、GNUシステムでは、bash
が静的にリンクされているか、_--with-included-gettext
_で構築されていない限り、_$LANGUAGE
_への変更は、変数がbash
によって生成されたメッセージに対して無視されます。 gettextはlibcの一部ではないため、他のシステムでは(_$LANGUAGE
_がエクスポートされている限り)問題ありません。そのため、bash
のgetenv()
が呼び出されます。
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'
_
(フランス語のメッセージ、スペイン語ではなく、bash
が呼び出されたときの_$LANGUAGE
_の値)。
実際、他のシェルではそれほど良くありません。
zsh
は他の言語に翻訳されていませんが、GNUシステムで内部的にgettext
を使用するstrerror()
を使用します:
_$ 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の2番目のメッセージが英語で表示されていないことを確認してください(おそらくgettextによってキャッシュされています。_$LANGUAGE
_が変更されたときにそのキャッシュは無効になっているはずですが、そうではありませんでした)。
シェル変数と環境変数の違いの説明については、 この回答 をご覧ください。本質的に:
シェル変数の設定:
LANG=en_US.UTF-8
環境変数の設定:
export LANG=en_US.UTF-8
シェル変数はシェル専用であり、子プロセスには渡されないため、ロケールの環境変数を設定する必要があります。