Windows環境でUNIX/Linux/BSD風のコンパイル環境を利用できるフリーソフトウェアCygwinで日本語を利用するに当たり、CygwinのセットアップとCygwin日本語パッケージのインストールと日本語環境設定でCygwinをインストールし、lsやcatで表示される日本語は正しく表示できるようになったと思います。
これらの設定を行った上でshellやコマンド等が出力するメッセージが文字化けする場合には、ソフトウェアの国際化に関連したメッセージカタログ(po/mo形式)とのミスマッチがその要因として濃厚で参照しているのはバイナリ形式の拡張子が.moのファイルであることがわかり、それを編集すればよさそうだという概要まで見てきましたので、ここでは、その具体的な方法を書きます。
尚、メッセージカタログの編集をしたのに反映されないとか、日本語用ファイルをインストールした等の場合で日本語表示されるはずのメッセージが英語であるという場合には、環境変数NLSPATHを追加登録します。
その上で文字化けが起こる場合には、やはりメッセージカタログに起因するものと思われ、その場合も以降の編集が必要です。
まず、メッセージカタログのバイナリであるmo形式のファイルは、複数まとまって1つのディレクトリに格納されている場合と国際化を行うソフトウェア個々に専用のディレクトリを持っている場合がありますが、何れにしてもmo形式があるそのディレクトリはLC_MESSAGES、1つ上のディレクトリはロケール正式名やロケールの言語名と決められています。
mo形式のファイルのあるLC_MESSAGESディレクトリには、普通、それぞれの国ごとに多くのサブディレクトリがあり、日本語関連のディレクトリ名には、ja、またはjaで始まるロケール正式名やロケールの言語名が付けられています。
文字化けを解消する為には、文字コードに注目する必要があり、文字コードに注目すると言語圏による違いを吸収する設定(国際化)に着目することになり、国際化に着目するとそれらしい環境変数に着目する必要があるらしいことがわかり、そのためには、localeコマンドを使うとよさそうだという話になります。
尚、localコマンドとlocaleコマンドは似て非なるコマンドでbashの場合、localは変数の局所化の為のbashシェル組み込みコマンド、localeは、UNIX/Linuxシステムコマンド(shell共通であるはず)の言語圏固有の設定表示コマンドで、英語localeの一般的な日本語では「場所」「背景」等、IT用語では「国・地域」・「言語圏」を意味します。
というわけでlocaleで現時点で各種システム環境変数に設定されている文字コードを調べてみるとデフォルトまたは、設定ファイルなどで意図的に環境変数LANGに設定した値が表示されます。
$ locale
そこには前述のディレクトリと同名のLC_MESSAGESという環境変数があることがわかります。
環境によって変数の種類は一律ではありませんが、接頭辞LC_はlocaleであり、言語圏固有の設定を行う環境変数で、LANGや各LC_値が未設定の場合の既定値は'C'(C locale/POSIX locale)です。
この例は環境変数LANGの値をja_JP.SJISに設定したことにより、LC_で始まる変数に自動的にLANGの値が設定され、「LC_ALL=」の値は常に非表示ですが、LC_ALLに値を設定するとLANG以外の「LC_」で始まる他の変数の設定は全てLC_ALLで設定されたものになり、例えば、設定値として既定のロケールCをLC_ALLにLC_ALL='C'のように設定するとPOSIX準拠のPOSIX互換システムの既定システムロケール、未設定またはそれ以外のシステムでは処理系定義となります。
環境変数 | 言語圏固有の様式 |
---|---|
LANG | システム環境で利用する言語 |
LC_CTYPE | 文字種・大文字/小文字 |
LC_NUMERIC | 数値表現・書式 |
LC_TIME | 日付時刻の書式 |
LC_COLLATE | 文字列の並び換え順 |
LC_MONETARY | 通貨の書式 |
LC_MESSAGES | メッセージに利用する言語 |
LC_ALL | 接頭辞LC_の変数全てに反映させる為の変数 |
*接頭辞LC_の変数の種類は環境による *LANG/各接頭辞LC_の変数が未設定の場合、既定値Cが設定される(LC_ALL='C') *LANGが設定される場合、各接頭辞LC_の変数の既定はLANGの値(個々の設定変更可、LC_ALL設定値は常に非表示) |
Cygwinの日本語化にあたって今ここで文字コードの違いによる文字化け要因として特に注目すべきは、ここまでの流れでいかにもメッセージの文字化けに関わりが深そうなLANGとLC_MESSAGESであり、localeで表示されるこの値とメッセージカタログ自体のファイルエンコーディングとそれらファイル内でファイルエンコーディングに沿って適切な値が設定されているはずの文字コード設定(charasetの設定値)が異なることに起因してエラーメッセージなどの文字化けが発生します。
一方、以降のメッセージカタログを編集した後、後述の環境変数へのパスの登録を行わないと日本語表示されるべきメッセージが英語表記のままになります。
また、何れの設定を行った場合でも、設定するファイルが不適切だと文字化け、英語表記、日本語表記が混在します。
原因を確認したところで次にそのメッセージカタログのあるパスですが、例えば、vi/vimについては/usr/share/vimディレクトリの更にサブディレクトリとして該当するvim+vimバージョンから成るディレクトリ内のlang/ja/LC_MESSAGESディレクトリに.moファイルが1つ、それ以外のメッセージカタログとしては、例えば、/usr/share/locale/ja/LC_MESSAGESというディレクトリ内に複数の各種.moファイルがあることがわかります。
vi/vimバージョンが55だとしたら...
$ /usr/share/vim/vim55/lang/ja/LC_MESSAGES
他にここには結構たくさんの.moがあったり...
$ /usr/share/locale/ja/LC_MESSAGES
ちなみに/usr/share/localeのサブディレクトリ、vi/vimの当該サブディレクトリだけでなく、pythonのサブディレクトリ、Cygwinとは別にWindows環境でJavaやGIMPなど世界中で使われているソフトをインストールしていたりする場合なども、findで検索してみたりすると、それぞれかなりの数の国や地域ごとの文字コード名ディレクトリのサブディレクトリLC_MESSAGESが大量に見つかります。
それらを眺めてみると、例えば英語(English)圏の文字コードenを1つとっても複数の種類のenディレクトリが存在することがわかりますが、日本語用のディレクトリは、ディレクトリ名にjaがついたものです。
例えば、/usr/share/localeディレクトリに限定してみると文字化けの事象がある場合、jaとつくディレクトリは、おそらくロケール言語名ja以外にないかもしれません。
このjaとして唯一あるjaディレクトリ内のサブディレクトリLC_MESSAGESにあるメッセージカタログ(.mo)のファイル自体の文字コード(ファイルエンコーディング)とその中でhtmlファイルのようにファイルエンコーディングされている文字コードが設定されているはずのcharsetの値が、使用したい文字コードと異なることが原因で文字化けが発生することがあるわけですが、ベースとしてロケール言語名jaディレクトリは残しておきたいのでディレクトリごとコピー先の名前を使用したい文字コードのロケール正式名としてコピーします。
つまり、以後そのコピーした使用したいロケール正式名のディレクトリ内のサブディレクトリLC_MESSAGESにあるメッセージカタログ(.mo)を編集することになります。
使用したい文字コード(=環境変数LANGの設定値)のロケール正式名がja_JP.SJISであれば、コマンドラインからコピーする場合、再帰的にコピーする-rオプション付きcpコマンドでこのようにja_JP.SJISディレクトリを新たに作成します。
よってディレクトリja_JP.SJISにはLC_MESSAGESというサブディレクトリがあり、そこには、既に.moファイルがコピーされて存在していることになります。
その後のメッセージカタログにおける文字化け解消対策の具体的な流れとしては、.moファイルは、バイナリ形式なので、それぞれのja_JP.SJIS/LC_MESSAGESにある.moファイルの内容を編集する為に一時的にpo形式ファイル(テキスト形式であればよく、.poという拡張子を付ける必要もありません)に変換、charasetに大文字や小文字で設定されている可能性のあるeuc-jp(charaset=euc-jp)やutf-8(charaset=utf-8)を正規表現・パターンマッチを使って利用する文字コードに置換した後、ファイル自体を使用する文字コード指定で保存してバイナリ(.mo)に戻します。
参考までに/usr/share/locale/ja_JP.SJIS/LC_MESSAGESにあるメッセージカタログのcharsetにセットされている値を確認する簡易スクリプトchk_charsetをホームディレクトリの任意のedit_moサブディレクトリで作って確認してみるとEUC-JP、euc-jp、UTF-8、utf-8、shift_jisなど同じ文字コードでも大文字・小文字が混在していることがわかります。
若干処理は遅いですが、ファイルもそれほど多いわけでもないのでとりあえず、よしとします(尚、この場合xargsで受け取ると期待した結果になりません)。
この為、scriptを書く場合には、大文字・小文字を考慮して正規表現、パターンマッチを利用し、使用する文字コードに置換する必要があることがわかります。
次に任意のバイナリの.moファイル(例ではabook.mo)を選んでmsgunfmtコマンドで読めるように変換してみてmsgstrの行で文字化けしている個所があれば、そこが日本語で書かれているはずの部分です(msgidは原文、msgstrはmsgidの訳語となる現地語、またmsgunfmtコマンドはデフォルトでは標準出力に出力されます)。
この例のabook.moの場合、charsetはeuc-jpなので他の文字コード(sjis等)を使用している(環境変数LANGやLC_MESSAGESがja_JP.SJIS等の)場合に文字化けするわけですが、ここでabook.moのcharset設定とファイル自体のファイルエンコーディングを変更してみて読めるようになれば成功ということになるので今度はchange_charsetという簡易スクリプトを先の任意のedit_moサブディレクトリで作って確認してみます。
この簡易スクリプトchange_charsetで.moをmsgunfmtで読めるように変換してtmpに出力、sedでcharsetの値を置換して一時ファイルtmp1にリダイレクト、iconvでファイルエンコーディングを変換して一時ファイルtmpにリダイレクトすることにより、期待通り、msgstr行が文字化けせずに読めるようになっているはずです。
(尚、ここで挙げた例にあるmsgstrの文字化けしたフレーズと文字化けが解消されたフレーズは、何も考えずに適当に抽出したので偶然が偶然を呼ばない限り一致していません。)
尚、iconvで一般的な使用法と思われるのは、変換前/変換後のファイルを-f/-tオプションで指定する方法だと思いますが、この例の場合には、それではエラーになり、期待した結果にならない場合があります。
また、無効な文字を無視する-cオプションや無視した上でエラーメッセージも出力しない-sオプションを付加すればエラー自体は回避できますが、エスケープシーケンスも無効と判断されるようでバックスラッシュ(\)が削除されるなど期待通りに正しく処理されず、これを放置するとメッセージが出力されたときにツラツラと改行なしで文字が並ぶことになったり、プロンプトが改行されずにメッセージの後ろに続いてしまったりと、文字化けの方が、まだましな気がしなくもありません。
もし、そういう状況に遭遇した際、これを回避する為には、先の例のように-tを指定せず、-f オプションのみ指定します。
この時、-f指定の前後何れかに念のため-cオプションを付加して(-f/-t共に指定する場合はiconvの直後で-f/-tの前に、-f/-t何れか一方を指定する際に付加する場合は後ろが本来の仕様である模様)も同じ結果を得ることができますが、変換元ファイルのファイルエンコーディングを自動認識できないので-tオプションのみでは期待した結果にはなりません。
ちなみにnkfコマンドは変換元ファイルのファイルエンコーディングを自動認識できるようなのでnkfコマンドを利用すれば、この点に関しては数行分入力が少なくて済むであろう一方、nkfよりiconvの方が普及しており、GNU iconv含め標準インストールされている可能性が高いものと思われ、特にここはCygwinの話なので尚更かもしれません。
このように-f指定のみで-t指定しない場合のsh/bashのiconvの挙動は、実装で定義された文字に変換されることになっていて、おそらく環境変数LANGや同じく環境変数LC_MESSAGES(、LC_ALL、LC_CTYPE、NLSPATH)に設定されている文字コードに変換されるはずなので(、やや心許なく消極的で受動的な感はありますが、)先の結果は正しく出力されるはずです。
というわけで該当する.moファイルをこうした方法で変換・置換して元のファイル名で保存すればよいことがわかります。
該当するファイルを入力としてバイナリ形式フォーマットのメッセージカタログをテキスト形式フォーマットに変換する(戻す)msgunfmtコマンドで変換
sedコマンド等でcharasetに大文字や小文字で設定されている可能性のあるeuc-jpとutf-8などの設定値を正規表現・パターンマッチを使って利用する文字コードに置換
nkfやiconvなどを使ってファイルを使用する文字コード指定で保存
メッセージカタログをテキストからバイナリに変換するmsgfmtコマンドを利用しバイナリ(.mo)に変換(して元に戻す)
入力ファイルが複数ある場合には、ファイル数の分だけループ
nkf/network kanji filter
iconv/international codeset conversion library
msgfmt/message format
msgunfmt/message unformat
ここでは、元々のメッセージカタログが置かれていた/usr/share/localeの
ja/LC_MESSAGES
(ja)とは別に
ja_JP.SJIS/LC_MESSAGES
(ja_JP.SJIS)を追加し、そこにあるmoファイルのファイルエンコーディングとそのcharset値の変更を想定しました。
残る作業としては、メッセージカタログの参照先として、この新たに作成したパスを探索するように設定して、これらの結果が反映されるようにします。
この設定には、NLSPATHという環境変数が利用され、今回追加したja_JP.SJISディレクトリの場合、スタートアップファイルとなる設定ファイルに以下のように記述します。
export NLSPATH=/usr/share/locale/%L/%N:$NLSPATH
パーセント記号に続く文字は環境変数設定時にシステムで認識される変数で以下のようになっています。
設定値 | locale/ロケール | 例 |
---|---|---|
%L | 正式名 | ja_JP.SJIS |
%l | 言語名 | ja |
%t | 国名 | JP |
%c | コードセット名 | SJIS |
%N | メッセージカタログファイル名 | bash(.mo) |
尚、NLSPATHを設定するスタートアップファイルが悩ましく、というよりスムースに使うことは難しいかもしれません。
なぜなら、.bash_login含めbashで最も先に読み込まれる.bashrcに設定しても起動したシェルのエラー表示などのメッセージが英語表記か文字化けし、サブシェルやexitで抜けて元のシェルに戻った時には日本語表示されるというような状況になるからです。
不思議なのは同じ.bashrcを起動する都度、ドット.コマンドやsourceコマンドで取り込むと(稀にうまくいかないこともありますが)日本語表記され、メッセージカタログパスの環境設定及び参照にタイムラグがあるように見受けられることです。
私がどこか間違っているのかもしれませんし、環境によるのかもしれませんが、全く機能しないわけでもないので新たな発見に遭遇するまで気にしないことにします。
また、この一連の作業を行うscriptを作って試してみましたが、70近くあったファイルの内、なぜか、gettext-tools.moだけcharsetの値を変更できませんでした。
未確認のまますぐにファイルエンコーディング含め手動設定してしまったこともあり変更した場合としない場合でどこにどう影響するはずなのかについては調べていません。
ただ、その後のテストでは、当現象が再現できず、同じscriptでgettext-tools.moも処理できています。
export LESSCHARSET=dos
ページャとしてlessコマンドを利用する場合、後にUTF-8に対応したものの、そのままでは日本語が文字化けするものと思われますが、Windowsで利用するCygwinにおいては環境変数LESSCHARSETに設定値dosを設定すると(Windowsの先祖であり、Windowsのベースとなっている)MS-DOSに適した文字コードが適用されることになっていて、その一環として特にShift_JISの場合、文字化けも解消するケースがあり、bashの場合、このように設定します。
Unicodeの1つであるUTF-8は、多バイト文字を含むより多くの文字を解釈できるようになっているのでEUC-JPやShift_JISよりも文字化けの頻度は限りなく少ないはずですが、様々な文字コードが混在する中では完全な解決には至りません。
また、UNIX/Linuxにおいてこれまで標準となってきている文字コードEUC/Extended Unix Codeは、AT&T社(米国)視点で開発された多バイト文字を扱う為の文字コード体系で、日本語視点で考案されたJISの弱点を補う形で誕生したShift_JISとの利用における優位性や利便性は、ラテン語圏の米英語や日本語が混在する環境では互いに微妙です。
こうした中、Windowsで解釈されるShift_JISは、基本的にCP932というMicrosoft独自の拡張文字集合を加えたShift_JISとして扱われ、他方、あらゆる面で日本語仕様となっているWindows上においてもCygwinがWindows環境で動作する事が大前提であり、環境変数などの設定も相まって、中でも特にShift_JISにおいては、MS-DOSに適した文字コードが設定されるLESSCHARSETにおけるdosという値の設定だけで文字化けが解消するものと思われます。
ascii/latin1/latin9/iso8859/ebcdic/IBM-1047/
koi8-r/next/utf-8/dos/ujis(≒euc-jp)/sjis ...etc.
ちなみにLESSCHARSETに設定可能な文字コードには、このようなものがあり、前述のlocaleコマンドで表示される環境変数の設定値においてロケールの既定値Cがセットされた場合の文字コードはlatin1です。
Cygwinではなく、UNIXやLinuxにおけるlessコマンドの日本語対応は、日本語などの文字コードには未対応だった為、日本では別途jlessコマンドが作られ、環境変数にもJLESSCHARSETを設定できるように工夫がなされた経緯があります。
vi/vimは個別に扱いましたが、/usr/share/locale/ja_JP.SJIS/LC_MESSAGES以下にある.moファイルはshellそのものやコマンドごとのものであり、この対応を行えば、そこにあるshellやコマンドが出力するメッセージについても対応が完了することになります。
環境変数LANGと、この環境変数NLSPATHに設定するディレクトリ名は、一致させる必要があり、前述の通り、LANGの設定がLC_MESSAGESをはじめとする接頭辞LC_の変数に設定され、NLSPATHで指定されたパスを探索した際に一致するLANG、各種LC_で始まる変数がある場合に、そのディレクトリにメッセージカタログを読みに行く仕組みになっています。
よってここまでで文字化けがなくなると共に、NLSPATH未設定によるメッセージカタログとLANGなどの文字コードの不一致により、新たに設定したディレクトリが探索されず、英語で表示されていたかもしれないコマンドヘルプ(-help/--help/-h/--h)やshellのエラーメッセージなど含め、日本語化が完了するはずです。
ちなみに環境変数LC_ALLにja_JP.SJISをセットしたとしても、この一連の作業は行われず期待する結果にはなりません。
もちろん、コンソール/ターミナル/ターミナルエミュレータにおける設定も必要です。
例えばターミナルエミュレータにTTSSH(SSH対応Tera Term Pro付属の)cygtermを利用する場合には、cygtermを立ち上げ、メニューの[設定]>>[その他の設定]から[Cygwin]タブを選んで「TERM」項目のKT=/KR=の2項目に希望の文字コードを設定します。
【KT】 SJIS/EUC/JIS/UTF8
【KR】 SJIS/EUC/JIS/UTF8/UTF8m
KTは送信漢字コード、KRは受信漢字コードでKT/KRには、それぞれこのような文字コード値が設定可能です。
ちなみに漢字コード、改行コード等の端末基本設定はメニューの[設定]>>[端末]から設定可能です。
TTSSH詳細はマニュアルhttp://ttssh2.sourceforge.jp/manual/ja/参照。