Dittoの日本語(マルチバイト文字)文字化け対応について、
まともに
Dittoを改造するとなるとどうなるか?
(
「substr」を「mb_substr」に変える等の程度で対応させることが可能なのか?)
ちょうど、
RSS出力を使う機会があったので、その部分で試してみました。
(MODx0.9.6.1p2で試していますが、現時点では0.9.6.2も同様のはずです)
ちなみに、PHxを使うのではなく、Dittoのデフォルト動作の改造ですので、
こちらの「DittoでRSSが上手く吐き出せない」
http://modxcms.com/forums/index.php/topic,20855.0.html
での対策とは、ちょっと違うアプローチになっているかと思います。
今回、改造の対象としたのは、上記の記事でも指摘されていますが、
DittoのRSS出力では、
要約(introtext)が入力されていないドキュメントでは、
内容(content)が[+summary+]として「description」タグに使われます。
その際、デフォルトでは半角300文字(=300バイト)でカットされますが、
日本語等のマルチバイト文字に対応していないため、
300バイト目が日本語等のマルチバイト文字だった場合、
1文字の途中でぶちきってしまうので、
ありえない文字コードになってしまいます。
たいていのRSSリーダーでは、文末がちょっと文字化けする程度で大きな問題にはならないでしょうが、
IE7では、これがエラーになって使えなくなってしまいます。
(ちなみに、パラメータにより、300文字の制限は変えられます)
これに該当する箇所は、
assets/snippets/ditto/extenders/summary.extender.inc.php
の213行目~225行目の「truncate::textTrunc」メソッドです。
function textTrunc($string, $limit, $break=". ") {
// Original PHP code from The Art of Web: www.the-art-of-web.com
// return with no change if string is shorter than $limit
if(strlen($string) <= $limit) return $string;
$string = substr($string, 0, $limit);
if(false !== ($breakpoint = strrpos($string, $break))) {
$string = substr($string, 0, $breakpoint+1);
}
return $string;
}
日本語等のマルチバイト文字(ここではUTF-8)に対応させるには、以下のような改造になるかと思います。
213行目)「$break=". "」を「$break="。"」に変更。(*1)
215行目)「mb_internal_encoding("UTF-8");」を追加。(*2)
217行目)「strlen」を「mb_strwidth」に変更。
219行目)「substr」を「mb_strimwidth」に変更。
220行目)「strrpos」を「mb_strrpos」に変更。
221行目)「substr」を「mb_substr」に変更。
こうみてくると、
単純に「substr」を「mb_substr」に変えるだけでは、
必ずしも動かない可能性があることがわかります。
さらに、この上位にあたる「truncate::html_substr」にも手を加えてみましたが、
200行目のカウントアップの処理[$c++;」は、
「$c+=mb_strwidth($current_char);」のように置き換えないと、
正しく文字の幅がカウントされないなど、
文字列関数だけに着目してもダメだということがあります。
ちなみに、必ずしも「全角=2バイト」ではありませんので、
表示上の文字数の計算と、文字中の文字の位置の計算は別になり、
「mb_strlen」と「mb_strwidth」、「mb_substr」と「mb_strimwidth」等を使い分けることが必要になります。
★「mbstring拡張モジュール」を前提にしているので、
この改造を行なってしまうと、当然ながら、
このモジュールが有効になっていない環境では動かなくなります。
(欧米のサーバ等では使えないんだろうなぁ…)
(*1) この変更は、今回の目的とは直接関係ないですが、
元が最後のピリオドで止めるという処理だったので、これを句点に変更しました。
(*2) 「mb_internal_encoding("UTF-8");」は、
本来から言うと、下記の言語ファイルに入れたほうが良いかもしれません。
assets/snippets/ditto/lang/japanese-utf8.inc.php
前述の箇所の改造後のソースは以下になります。
function textTrunc($string, $limit, $break="。") {
// Original PHP code from The Art of Web: www.the-art-of-web.com
mb_internal_encoding("UTF-8");
// return with no change if string is shorter than $limit
if(mb_strwidth($string) <= $limit) return $string;
$string = mb_strimwidth($string, 0, $limit);
if(false !== ($breakpoint = mb_strrpos($string, $break))) {
$string = mb_substr($string, 0, $breakpoint+1);
}
return $string;
}