b2evolution 2.4.1 の日本語周りの改造案の説明
2008年4月8日追記: こちらの記事の改訂版「b2evolution 2.4.1 の日本語周りの改造」を掲載しましたので、今後はそちらをご覧ください。
「b2evolution 2.4.1 の日本語周りの改造案(テスト不十分)」で提示した改造差し替えファイルの内容説明です。
おそらく日本語メッセージファイルのv2.4.1対応の需要のほうが高いと思いますが、あれを作るのは時間がかかるので検討中です(今のところまったく手をつけていません)。自分でやった改造内容を忘れないうちに、備忘録を兼ねて説明を書いておくことを先にさせてもらいます。
はじめに: 改造差し替えファイルを使用する際の注意
私が言うまでも無いことかもしれませんが、適用する前にバックアップはちゃんと取っておいてください。ファイルとデータベースの両方を忘れずにバックアップしましょう。
インストールの際に、インストールページで最初に「Japanese (JP)」(日本語 (JP))のリンクを使って言語を日本語に指定しておくと、ブログやユーザのデフォルトの言語が日本語になり、後の設定が楽になりますので、「Japanese (JP)」(日本語 (JP))の選択を行ってください。
また、過去に管理(Admin)画面の「全体設定 › 地域」(Global settings › Regional)で設定を変更していた場合、「全体設定 › 地域」ページの下のほうに「デフォルトにリセット (データベース テーブルを削除)」(Reset to defaults (delete database table))リンクが表示されています。
この状態では blogs/conf/_locales.php
の更新が反映されませんので、「デフォルトにリセット (データベース テーブルを削除)」でリセットを行ってください。
なお、通常の使い方では「全体設定 › 地域」(Global settings › Regional)の設定を変更する必要はありませんので、リセット後はデフォルトそのままにしておいてください。
改造差し替えファイル
当サイトの「b2evolution 2.4.1 の日本語周りの改造案(テスト不十分)」から入手してください。
再掲すると混乱しそうなので、お手数をかけますが、よろしくお願いします。
前置き: 文字化けの原因
この前置きは長くてややこしい話ですし、実のところ私は完全には調査・理解できていない状態なので間違いもあるかと思います。あまり興味のない方は読まなくて構いません。
b2evolutionの文字化けにはいくつかの原因がありますが、根本的なものは、使用する文字セット(charset)の決定方法に問題があるという点です。
なお、「うちはb2evoを一切変更していないけど日本語も文字化けしないよ」という方は、ウェブブラウザの言語設定(HTTPリクエストの Accept-Language: ヘッダの指定内容)を英語(en や en-US など)のみにして、ログインしていない状態でご自分のブログにアクセスしてみてください。おそらく、文字化けするのが確認できるはずです。
(言語設定の変更は、IE6では「ツール」-「インターネット オプション」-「全般」タブ -「言語」、Firefox 2では「ツール」-「オプション」-「詳細 > 一般」タブ -「言語設定」で行います)
このように日本語設定以外のウェブブラウザでアクセスしたら、たとえ日本語表示可能な環境であっても文字化けしてしまうのでは、問題があると言えるでしょう。
閑話休題。
b2evoでは大雑把に言って、「ウェブブラウザの言語設定」→「ログインしている場合はユーザのロケール(言語・地域)設定」→「ブログのロケール設定」、の順番でロケールを判別します。最初の「ウェブブラウザの言語設定」のロケールから $evo_charset
(内部の文字セット設定)が決定され、これは特殊な場合を除き、出力処理が終わるまで基本的に変更されません。
この仕様により、英語設定のウェブブラウザでアクセスがあると、$evo_charset
(内部の文字セット設定)が ISO-8859-1 に設定されてしまいます。しかし、b2evoの日本語設定ブログのシステムメッセージや、日本語設定ユーザの書いた記事は、実際には UTF-8 で書かれていますので、UTF-8 の文字列を誤って ISO-8859-1 と見なしてしまう現象が発生します。
そして、UTF-8 で書かれた記事の ISO-8859-1 と見なされた文字列は ISO-8859-1 しか考慮されていないエスケープ処理が施されますし、UTF-8 のシステムメッセージは ISO-8859-1 と見なされた上で UTF-8 への文字コード変換が行われます。
2008年4月8日追記: 勘違いがありました。システムメッセージについては元の文字コードを誤認してはいないようです。
さらに、「ブログのロケール設定」はすでに決定された $evo_charset
(内部の文字セット設定)は変更できませんが、$io_charset
(出力時の変換文字セット設定。入力の文字セットも同一と見なされる)は「ブログのロケール設定」に合わせて変更が行われます。日本語ブログなら $io_charset
は UTF-8 になります。そして、出力時に $evo_charset
(ISO-8859-1)から $io_charset
(UTF-8)への文字コード変換が行われます。
このため、最初からの流れで言うと、実際には UTF-8 であるのに ISO-8859-1 と見なされた文字列は、ISO-8859-1 しか考慮されていないエスケープ処理や UTF-8 への文字コード変換が施された後、さらに ISO-8859-1 から UTF-8 に変換する処理が行われて出力されます。本当は、元々 UTF-8 なので UTF-8 出力するなら変換処理する必要も無いのですが。
こうして多重に誤った変換が施されるせいで、文字列が原形を留めず、単なる文字セット指定ミスとは違う、ウェブブラウザ側では修復不能な文字化けが発生することになります。
(一応 blogs/conf/_locales.php
であらかじめ $evo_charset
, $force_io_charset_if_accepted
などを強制指定することにより文字化けを発生しにくくすることができるようになっていますが、完全ではありません。わかっている問題では、$force_io_charset_if_accepted
の指定は、Accept-Charset: ヘッダを送信しない一部のウェブブラウザや検索エンジンのクローラが相手の時や、Accept-Charset: ヘッダの内容と大文字・小文字が食い違った時など、条件によって無視される場合があります)
改造の説明
ここから本題です。
変更部分のコードを書き写すのは、記事がやたら長くなってしまいますし面倒なので行いません。ご自身でオリジナルと私の改造版のファイルの差分を取るなどして、見比べてみてください。
ロケール(言語・地域)設定の変更
対象ファイル: blogs/conf/_locales.php
私の改造版では、blogs/conf/_locales.php
での各ロケール設定の文字セット指定をすべて UTF-8 に統一しています。
これによって、前述「前置き: 文字化けの原因」で書いた $evo_charset
(内部の文字セット設定)や $io_charset
(出力時の変換文字セット設定。入力の文字セットも同一と見なされる)の食い違いを無くし、不必要な文字コード変換や ISO-8859-1 しか考慮されていないエスケープ処理も行われないようになります。
根本的な解決ではありませんが、確実に問題を回避できるはずです。
(2008年4月8日追記: 少なくとも、b2evolutionの日本語ユーザの間で良く行われているらしい、blogs/conf/_locales.php
で $evo_charset
, $force_io_charset_if_accepted
, $db_config['connection_charset']
を utf-8 / utf8 に強制指定しておく方法よりは、ロケール設定からUTF-8以外の文字セット設定が混じり込む可能性が無いので、同じUTF-8統一指定でもこちらの方法のほうが信頼性が高くなります)
また、b2evolution の配布ファイルに付属してくる言語ファイル blogs/locales/de_DE/_global.php
と blogs/locales/fr_FR/_global.php
の2つは、どちらも ISO-8859-1 で書かれていますので、改造版ではこれを UTF-8 に変換してあります。
サブドメインが1文字のホスト名を不正とみなす問題の対策
対象ファイル: blogs/inc/_core/_url.funcs.php
はてなダイアリー(d.hatena.ne.jp)など、サブドメインが1文字のホスト名へのリンクを記事に書き込むなどした場合に、「Invalid URL format」エラーになってしまいます。
ざっと調べた範囲では、サブドメインが1文字でも、RFC2181の「11. Name syntax」あたりを見る限り問題は無いようですので、b2evolutionのバグの可能性が高いと思われます。
記事以外にもコメント、トラックバックの他、サブドメインが1文字のサイトからリンクをたどってきた場合のリファラ(Referer:)判定でも引っかかる(リファラspamと見なされる)ようです。
blogs/inc/_core/_url.funcs.php
の94~105行目で、ホスト名の最初の .
(ドット)まで2文字以上必要としている判定部分を、1文字でもエラーにならないようにしました。
UTF-8のメッセージファイル使用時に管理画面の一部で発生するエラー対策
対象ファイル: blogs/inc/_core/ui/results/_results.class.php
(_results.class.php
を編集する際は、特殊な文字が含まれているため、できれば西ヨーロッパ言語 (ISO-8859-1, Windows-1252 (CP1252)) を編集可能なエディタを使用することをお勧めします)
管理画面で機能アイコン画像等を表示するために(「全体設定 › ファイルタイプ」ページなど)、特殊な文字列置き換えとPHPコード実行処理が行われる際に、UTF-8のメッセージファイル(日本語等)で定義される文字列の一部が誤って置き換え対象となり、evalエラーを引き起こす問題の簡易的な回避策を施しています。
(これは完全な対処とは言えませんので、まだ条件が合うとエラーが発生する可能性も残っています。またUTF-8以外でも該当するものがあるかもしれません)
blogs/inc/_core/ui/results/_results.class.php
の1560行目で、¤ (0xA4)に囲まれた部分をPHPコードとして処理をするために置き換えていますが、例えば「除」のUTF-8コードは十六進表記で E9 99 A4 となり A4 が含まれているため、文字の並び方によっては誤判定が起こります。他にも 0xA4 が含まれる文字は「イ, 複, 検」など、いくつかあります。
簡易的な回避策として、¤ (0xA4)で囲まれた部分の始まりと終わりの文字がASCII文字であるかどうか判定するようにしました。置き換えるのはPHPコードであるはずなので、非ASCII文字が最初と最後に付くことはありえませんので。あくまで簡易処置ですが、これでも効果はあるようです。
また、日本語メッセージファイルのほうでも、該当部分で使われることが明らかな文字列は該当する文字を実体参照に置き換えています。例えば「除」→「除」など。
(もし根本的な修正を考えるなら、「¤」1文字だけで範囲指定するやり方を変えてしまったほうがいいのではないかと思います。例えば同じ文字でも3文字以上並べれば偶然の一致は起こりにくいでしょう)
トラックバック送信
対象ファイル: blogs/inc/comments/_trackback.funcs.php
b2evolutionのトラックバック機能は、相手が同じ文字セットを使用していることを前提に作られていますので、文字セットの違うブログとのトラックバックのやり取りで文字化けを起こす可能性があります。文字セットUTF-8、EUC-JP、Shift_JISが混在する日本語のブログ間では起こりやすい問題ですので、それを防ぐための対策を施します。
一点目は、トラックバックの仕様 TrackBack Technical Specification Version 1.2 で導入された、POSTリクエスト・Content-Type:ヘッダへの charset 記載を行うようにします。加えて、過去に日本の一部で独自に使用されていたパラメータ charset
も念のため使用します。
これにより、仕様に沿って作られたブログでは、受け取ったトラックバックの文字セットを認識して、適切な文字コード変換をしてくれるはずです。
二点目は、TrackBack Technical Specification のVersion 1.1以降ではトラックバックの送信にHTTP POSTリクエストを使わなければいけないことになっています。現在ではPOSTリクエストのみ受け付け、HTTP GETリクエストでのトラックバックは拒否するブログも、多くはありませんが存在するようになっています。
しかし、b2evolutionは5年以上昔の仕様との互換性を考慮して、相手のトラックバックURLに「?」が含まれるとHTTP GETリクエストを使うように作られています。ただし「?」を含むトラックバックURLは多く存在しており、結果として、かなり多くの場合に必要も無いのにGETリクエストでトラックバックを送っていました。
これを変更して、トラックバック送信にはGETは使用せず、POSTのみにしました。(よほど古いままバージョンアップされていないブログ相手でない限り、問題は起きないと思います)
三点目に、マルチバイト文字列関数を使用して(使用可能な場合)、マルチバイト文字の途中で文字を切ってしまって文字化けを起こすことの無いようにしました。
トラックバック受信
対象ファイル: blogs/htsrv/trackback.php
一点目は、トラックバックの仕様 TrackBack Technical Specification Version 1.2 で導入された、POSTリクエスト・Content-Type:ヘッダへの charset 記載を認識して、マルチバイト文字列関数が使用可能ならば、文字コード変換を行うようにしました。念のため、過去に日本の一部で独自に使用されていたパラメータ charset
も認識します。
二点目に、マルチバイト文字列関数を使用して(使用可能な場合)、マルチバイト文字の途中で文字を切ってしまって文字化けを起こすことの無いようにしました。
三点目に、Movable Typeなどからb2evolutionにトラックバックを送信した場合、成功したにもかかわらず、失敗したとMovable Type側で表示される問題の対処をしました。
トラックバックの仕様では、トラックバック受信時の相手へのレスポンスは、成功した場合はエラーコード 0 でメッセージ無しを返すことになっていますが、b2evolutionはエラーコード 0 とメッセージ「ok」を返していました。Movable Typeはメッセージがあるので失敗であるかのような表示をしてしまっているようです。
これを、成功した場合にはメッセージ無しになるように変更しました。ついでに、失敗した時に仕様には無いエラーコード 2 を返す場合があったのを、失敗のエラーコードは 1 に統一しました。
メール送信
対象ファイル: blogs/inc/_core/_misc.funcs.php
マルチバイト文字列関数が使用可能ならば、
- メール送信時のロケール(言語・地域設定)が日本語(ja-JP)で、かつメールの内容が完全にISO-2022-JP文字セットに変換可能な場合、ISO-2022-JPでメールを送信するようにしました。(その他の場合は、ロケールの文字セットで送信します)
- メールのFrom:とTo:ヘッダに非ASCII文字が含まれている場合、MIME Base64エンコード/ punycodeエンコードするようにしました。
2008年4月8日追記: メールのSubject:ヘッダは元々MIME Base64エンコードされるようになっていますが、MIMEヘッダエンコード関数 mb_encode_mimeheader()
を使用する際に設定が必要な、mb_internal_encoding()
が適切に設定されていない場合がありましたので、適切な設定を行うようにしています。
どんな条件でもISO-2022-JPではメール送信したくない場合は、改造版 blogs/inc/_core/_misc.funcs.php
の1567行目の、$want_to_use_iso2022jp = 1;
を $want_to_use_iso2022jp = 0;
に書き換えてください(1 を 0 に)。
ウェブブラウザの言語設定からのロケール判別
対象ファイル: blogs/inc/locales/_locale.funcs.php
ログイン画面やインストール・データベースアップデート画面で、ウェブブラウザの言語設定を元にロケール(言語・地域設定)を自動決定するようになっていますが、誤判定が多いです。
ウェブブラウザの言語設定が、例えば良くある設定で「ja(日本語), en-US(英語・アメリカ), en(英語)」という順番になっていると、本来は「ja(日本語)」が採用されなければならないところ、b2evolutionは2文字より5文字のロケールを優先してしまい「en-US(英語・アメリカ)」を採用してしまっていました。
これを、順序に従って「ja(日本語)」が採用されて、ロケール設定「ja-JP」になるようにしました。
なお、従来の動作通り、ウェブブラウザの言語設定が「en(英語), en-NZ(英語・ニュージーランド)」とか、同じ言語で2文字より5文字(地域付き)のほうが後に来てしまっている、あまり適切とは言えない設定でも、うまく「en-NZ」を採用するはずです。
さらに、b2evolution側のロケール設定で「pl-PL-utf-8」や「zh-CN-utf-8」など5文字を超えるものが、ウェブブラウザ側の言語設定「pl-PL」や「zh-CN」を完全一致と認識しない問題にも対処したつもりです。
経由検索エンジンのキーワード文字化け
対象ファイル: blogs/inc/sessions/model/_hitlog.funcs.php
統計の検索エンジン経由アクセスの使用キーワード表示がISO-8859-1にしか対応していませんので、その文字化け対策です。htmlentities()
に文字セット指定を行って、ISO-8859-1決め打ちにならないようにしました。ただし、検索エンジン側とb2evo側で使用している文字セットが違う場合の文字化けはこれでは直りません。
ロケール(言語・地域設定)が日本語になっていて、マルチバイト文字列関数が使える場合は、文字コードの自動判別も行います。日本語に関しては検索キーワード表示の文字化けが軽減されます。
年月日表示
対象ファイル:
blogs/inc/items/model/_itemlistlight.class.php
blogs/plugins/_archives.plugin.php
blogs/plugins/_calendar.plugin.php
日毎表示のタイトル部分、アーカイブ一覧の年月表示、カレンダーの年月表示では、日本語で一般的な語順でなく、「4月 2008, 8」や「4月 2008」のような表示になってしまいます。ロケール(言語・地域設定)が「ja-JP(日本語)」の時は、これを日本語らしい形式「2008年4月8日」や「2008年4月」にするよう変更しました。
なお、スキンで独自に設定が行われている場合もあるので、この改造ですべてが変更できるわけではありません。
ブックマークレット使用時の文字化け
対象ファイル: blogs/plugins/_bookmarklet.plugin.php
ブックマークレットでの文字化けは、ブックマークレットのJavaScriptで escape()
が用いられていることが原因です。encodeURIComponent()
を使用するように変更しました。
欧文のみの環境ですと、escape()
のほうが問題が少ないようです。しかし、UTF-8を使用する場合では encodeURIComponent()
のほうが無難です。