気の向くままに辿るIT/ICT/IoT
UNIX/Linux

Cygwin 日本語対応と方法

ホーム前へ次へ
Cygwinの日本語対応って具体的にはどうやるの?

Cygwin 日本語対応と方法

Cygwinの具体的な日本語対応の方法

 Windows環境でUNIX/Linux/BSD風のコンパイル環境を利用できるフリーソフトウェアCygwinで日本語を利用するに当たり、CygwinのセットアップCygwin日本語パッケージのインストールと日本語環境設定でCygwinをインストールし、lscatで表示される日本語は正しく表示できるようになったと思います。

 これらの設定を行った上でshellコマンド等が出力するメッセージが文字化けする場合には、ソフトウェアの国際化に関連したメッセージカタログ(po/mo形式)とのミスマッチがその要因として濃厚で参照しているのはバイナリ形式の拡張子が.moのファイルであることがわかり、それを編集すればよさそうだという概要まで見てきましたので、ここでは、その具体的な方法を書きます。

 尚、メッセージカタログの編集をしたのに反映されないとか、日本語用ファイルをインストールした等の場合で日本語表示されるはずのメッセージが英語であるという場合には、環境変数NLSPATHを追加登録します。

 その上で文字化けが起こる場合には、やはりメッセージカタログに起因するものと思われ、その場合も以降の編集が必要です。

LC_MESSAGESディレクトリ

 まず、メッセージカタログのバイナリであるmo形式のファイルは、複数まとまって1つのディレクトリに格納されている場合と国際化を行うソフトウェア個々に専用のディレクトリを持っている場合がありますが、何れにしてもmo形式があるそのディレクトリはLC_MESSAGES、1つ上のディレクトリはロケール正式名やロケールの言語名と決められています。

 mo形式のファイルのあるLC_MESSAGESディレクトリには、普通、それぞれの国ごとに多くのサブディレクトリがあり、日本語関連のディレクトリ名には、ja、またはjaで始まるロケール正式名やロケールの言語名が付けられています。

localeコマンド/言語圏設定確認

 文字化けを解消する為には、文字コードに注目する必要があり、文字コードに注目すると言語圏による違いを吸収する設定(国際化)に着目することになり、国際化に着目するとそれらしい環境変数に着目する必要があるらしいことがわかり、そのためには、localeコマンドを使うとよさそうだという話になります。

 尚、localコマンドとlocaleコマンドは似て非なるコマンドでbashの場合、localは変数の局所化の為のbashシェル組み込みコマンド、localeは、UNIX/Linuxシステムコマンド(shell共通であるはず)の言語圏固有の設定表示コマンドで、英語localeの一般的な日本語では「場所」「背景」等、IT用語では「国・地域」・「言語圏」を意味します。

 というわけでlocaleで現時点で各種システム環境変数に設定されている文字コードを調べてみるとデフォルトまたは、設定ファイルなどで意図的に環境変数LANGに設定した値が表示されます。

$ locale 

 LANG=ja_JP.SJIS 
 LC_CTYPE="ja_JP.SJIS" 
 LC_NUMERIC="ja_JP.SJIS" 
 LC_TIME="ja_JP.SJIS" 
 LC_COLLATE="ja_JP.SJIS" 
 LC_MONETARY="ja_JP.SJIS" 
 LC_MESSAGES="ja_JP.SJIS" 
 LC_ALL= 

環境変数LC_MESSAGES

 そこには前述のディレクトリと同名の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の設定値)が異なることに起因してエラーメッセージなどの文字化けが発生します。

 一方、以降のメッセージカタログを編集した後、後述の環境変数へのパスの登録を行わないと日本語表示されるべきメッセージが英語表記のままになります。

 また、何れの設定を行った場合でも、設定するファイルが不適切だと文字化け、英語表記、日本語表記が混在します。

LC_MESSAGESディレクトリ

 原因を確認したところで次にそのメッセージカタログのあるパスですが、例えば、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がついたものです。

文字コード設定に応じたLC_MESSAGESディレクトリを作成

$ cd /usr/share/locale 
$ ls -F 
./ 
../ 
... 
it/ 
ja/ 
ko/ 
... 

 例えば、/usr/share/localeディレクトリに限定してみると文字化けの事象がある場合、jaとつくディレクトリは、おそらくロケール言語名ja以外にないかもしれません。

 このjaとして唯一あるjaディレクトリ内のサブディレクトリLC_MESSAGESにあるメッセージカタログ(.mo)のファイル自体の文字コード(ファイルエンコーディング)とその中でhtmlファイルのようにファイルエンコーディングされている文字コードが設定されているはずのcharsetの値が、使用したい文字コードと異なることが原因で文字化けが発生することがあるわけですが、ベースとしてロケール言語名jaディレクトリは残しておきたいのでディレクトリごとコピー先の名前を使用したい文字コードのロケール正式名としてコピーします。

 つまり、以後そのコピーした使用したいロケール正式名のディレクトリ内のサブディレクトリLC_MESSAGESにあるメッセージカタログ(.mo)を編集することになります。

$ pwd 
/usr/share/locale 
$ cp -r ja ja_JP.SJIS 
$ ls -F 
./ 
../ 
... 
it/ 
ja/ 
ja_JP.SJIS/ 
ko/ 
... 

 使用したい文字コード(=環境変数LANGの設定値)のロケール正式名がja_JP.SJISであれば、コマンドラインからコピーする場合、再帰的にコピーする-rオプション付きcpコマンドでこのようにja_JP.SJISディレクトリを新たに作成します。

 よってディレクトリja_JP.SJISにはLC_MESSAGESというサブディレクトリがあり、そこには、既に.moファイルがコピーされて存在していることになります。

.moファイル charset設定値の編集

 その後のメッセージカタログにおける文字化け解消対策の具体的な流れとしては、.moファイルは、バイナリ形式なので、それぞれのja_JP.SJIS/LC_MESSAGESにある.moファイルの内容を編集する為に一時的にpo形式ファイル(テキスト形式であればよく、.poという拡張子を付ける必要もありません)に変換、charasetに大文字や小文字で設定されている可能性のあるeuc-jp(charaset=euc-jp)やutf-8(charaset=utf-8)を正規表現・パターンマッチを使って利用する文字コードに置換した後、ファイル自体を使用する文字コード指定で保存してバイナリ(.mo)に戻します。

メッセージカタログのバイナリ(mo)/テキスト(po)変換

 参考までに/usr/share/locale/ja_JP.SJIS/LC_MESSAGESにあるメッセージカタログのcharsetにセットされている値を確認する簡易スクリプトchk_charsetをホームディレクトリの任意のedit_moサブディレクトリで作って確認してみるとEUC-JP、euc-jp、UTF-8、utf-8、shift_jisなど同じ文字コードでも大文字・小文字が混在していることがわかります。

$ cd ~/edit_mo 
$ cat chk_charset 
#!/usr/bin/sh 
 FILES=`find /usr/share/locale/ja_JP.SJIS/LC_MESSAGES -name "*.mo"` 
 for infile in $FILES 
 do 
   msgunfmt $infile | grep "charset=" | awk '{ print $0 }' 
 done 
$ chmod u+x chk_charset 
$ ./chk_charset 
... 
"Content-Type: text/plain; charset=EUC-JP\n" 
"Content-Type: text/plain; charset=euc-jp\n" 
"Content-Type: text/plain; charset=UTF-8\n" 
"Content-Type: text/plain; charset=utf-8\n" 
"Content-Type: text/plain; charset=shift_jis\n" 
... 

 若干処理は遅いですが、ファイルもそれほど多いわけでもないのでとりあえず、よしとします(尚、この場合xargsで受け取ると期待した結果になりません)。

 この為、scriptを書く場合には、大文字・小文字を考慮して正規表現、パターンマッチを利用し、使用する文字コードに置換する必要があることがわかります。

 次に任意のバイナリの.moファイル(例ではabook.mo)を選んでmsgunfmtコマンドで読めるように変換してみてmsgstrの行で文字化けしている個所があれば、そこが日本語で書かれているはずの部分です(msgidは原文、msgstrはmsgidの訳語となる現地語、またmsgunfmtコマンドはデフォルトでは標準出力に出力されます)。

$ cd /usr/share/locale/ja_JP.SJIS/LC_MESSAGES 
$ pwd 
/usr/share/locale/ja_JP.SJIS/LC_MESSAGES 
$ msgunfmt abook.mo 
... 
msgid %s is not a directory\n" 
msgstr..."%s 縺ォ繧ケ繝ャ繝シ繝..." 
... 
$ msgunfmt abook.mo | grep "charset=" 
"Content-Type: text/plain; charset=euc-jp\n" 

 この例のabook.moの場合、charsetはeuc-jpなので他の文字コード(sjis等)を使用している(環境変数LANGやLC_MESSAGESがja_JP.SJIS等の)場合に文字化けするわけですが、ここでabook.moのcharset設定とファイル自体のファイルエンコーディングを変更してみて読めるようになれば成功ということになるので今度はchange_charsetという簡易スクリプトを先の任意のedit_moサブディレクトリで作って確認してみます。

$ cd ~/edit_mo 
$ cat change_charset 
#!/usr/bin/sh 
 T_DIR=/usr/share/locale/ja_JP.SJIS/LC_MESSAGES 
 msgunfmt $T_DIR/abook.mo > tmp 
 sed s/charset=euc-jp/charset=shift_jis/ tmp > tmp1 
 iconv -f euc-jp tmp1 > tmp 
 rm tmp1 
$ chmod u+x change_charset 
$ ./change_charset 
$ msgunfmt tmp | grep "charset=" 
"Content-Type: text/plain; charset=shift-jis\n" 
$ msgunfmt tmp 
... 
msgid %s is not a directory\n" 
msgstr "%s はディレクトリではありません\n" 
... 

 この簡易スクリプトchange_charsetで.moをmsgunfmtで読めるように変換してtmpに出力、sedでcharsetの値を置換して一時ファイルtmp1にリダイレクト、iconvでファイルエンコーディングを変換して一時ファイルtmpにリダイレクトすることにより、期待通り、msgstr行が文字化けせずに読めるようになっているはずです。

 (尚、ここで挙げた例にあるmsgstrの文字化けしたフレーズと文字化けが解消されたフレーズは、何も考えずに適当に抽出したので偶然が偶然を呼ばない限り一致していません。)

iconv -f from_char_code -t to_char_code foo 

 尚、iconvで一般的な使用法と思われるのは、変換前/変換後のファイルを-f/-tオプションで指定する方法だと思いますが、この例の場合には、それではエラーになり、期待した結果にならない場合があります。

iconv -c -f from_char_code -t to_char_code foo 
iconv -s -f from_char_code -t to_char_code foo 

 また、無効な文字を無視する-cオプションや無視した上でエラーメッセージも出力しない-sオプションを付加すればエラー自体は回避できますが、エスケープシーケンスも無効と判断されるようでバックスラッシュ(\)が削除されるなど期待通りに正しく処理されず、これを放置するとメッセージが出力されたときにツラツラと改行なしで文字が並ぶことになったり、プロンプトが改行されずにメッセージの後ろに続いてしまったりと、文字化けの方が、まだましな気がしなくもありません。

iconv -f euc-jp tmp1 > tmp 

 もし、そういう状況に遭遇した際、これを回避する為には、先の例のように-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

環境変数NLSPATH/メッセージカタログパスの登録

 ここでは、元々のメッセージカタログが置かれていた/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も処理できています。

lessコマンドの文字コード設定

 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/参照。

LINK

ホーム前へ次へ