LC_MESSSAGESを適用するためにmacOSホームブレークbashからエクスポートする必要があるのはなぜですか?

LC_MESSSAGESを適用するためにmacOSホームブレークbashからエクスポートする必要があるのはなぜですか?

macOSでは、homebrewに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。しかし、合理的な値に設定しても効果はありません。

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 マニュアルセクションバッシュ変数LC_MESSAGESエクスポートまたはエクスポートする必要はありませんLANG(そしてここにリストされている他のほとんどの変数はエクスポートする必要はありません)。

なぜこれですか?

答え1

あなたは正しいです。シェル変数を割り当てると、エクスポートするかどうかに関係なく、そのカテゴリのPOSIXが変数値として呼び出さLC_*れます。の場合は、すべての変数を再度呼び出します。の場合には効果がありません。bashsetlocale()LANGsetlocale(LC_ALL, thevalue)setlocale(LC_*)LC_*LANGUAGE

これでbashGNUプロジェクトのシェルです。テキストローカライゼーションのgettext場合libintlbashconfigure--with-included-gettext

gettext各言語のデータベースでメッセージ翻訳を見つけます。どの言語であるかはカテゴリ値によって決まりますが、環境変数で上書きLC_MESSAGESできます。$LANGUAGE

gettextドキュメントによると、以前の呼び出しはsetlocale()カテゴリ値を決定する呼び出しでなければなりませんが、いくつかの複雑な問題があります。

マルチスレッドアプリケーションの場合、現在、この値を取得するために使用できる標準API gettextはありません。bashマルチスレッドアプリケーションではありませんが、それが何であってもsetlocale(category, NULL)返品は実装定義実際には常に使用できるわけではありません。

したがって、実際にはgettextは、setlocale()GNU libcの一部として構築されたシステム、またはlibcがGNU libcのシステム(たとえば、GNU libcで構築されたシステム)から言語名を取得するためbashにのみ使用されます。--with-included-gettext

getenv()他のシステムでは、以前に呼び出された方法に関係なくロケールを決定するために使用されるため、これらのsetlocale()動作が表示されます。

これらの変数をエクスポートするのは簡単な回避策です。輸出されていない場合は、環境の一部ではないと言うこともできます。 POSIXはこれについては明確ではありません。別の観点では、翻訳がによって行われるのではなく、サードパーティのbashメカニズムによって実行されるということです。実装する他のコマンドでは、環境変数を使用して2つのソフトウェア(ここbashgettext)の間でロケール情報を渡す必要があります。

GNUシステムでは、状況は実際には悪化しています。

上記のように、gettextはGNU libcに含まれています。$LANGUAGEPOSIXロケールAPIよりも先行します$LC_MESSAGEが、$LANGUAGE一部ではなくその上にある拡張です。

setlocale(LC_MESSAGES, NULL)したがって、GNUシステムでは、LC_MESSAGESカテゴリの名前を取得するためにgettextが使用されます。なぜなら、LC_MESSAGESLANGUAGEカテゴリは常にロケールカテゴリではなくgetenv()使用されるからです。LANGUAGE

問題は、bash変数処理の一部としてenviron[]libc配列との接続が失われた独自の環境を管理することです。独自のものがあり、getenv()独自のバージョンの環境を照会しますが、gettextlibcの一部として構築され、bash動的にリンクされるとdgettext()libcから呼び出されます。これはlibcではなくlibc内の内部呼び出しであるため、単に開始時刻から値を取得します。getenv()bash$LANGUAGEbash

したがって、GNUシステムでは、静的にリンクされているかビルドを使用しない限り、生成されたメッセージに対するすべての変更は、変数のエクスポートのbash有無にかかわらず無視されます。--with-included-gettext他のシステムでは、gettextはlibcの一部ではないので(エクスポートする限り)大丈夫です。したがって、sを呼び出します。$LANGUAGEbash$LANGUAGEbashgetenv()

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'

$LANGUAGEbashスペイン語ではなくフランス語のメッセージ、当時呼び出された値)。

実際、他の殻に比べてあまり良くありません。

zsh他の言語に翻訳されていませんが、GNUシステムで内部的に使用されていますstrerror()gettext

$ 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ときに無効にする必要がありましたが、そうではありませんでした)。

答え2

見てこの回答シェル変数と環境変数の違いを説明します。本質的に:

シェル変数を設定します。

LANG=en_US.UTF-8

環境変数を設定します。

export LANG=en_US.UTF-8

シェル変数はシェル専用で、子プロセスに渡されないため、ロケールの環境変数を設定する必要があります。

関連情報