「文字化け対策」(MT導入と改造) | ◇ ◀ ▲ ▶ |
エントリーの編集画面でエントリーのリストのタイトルが文字化けしてしまう。 このバグを修正した。
このバグはtitle_short作成時にUTF8の文字列を文字境界を無視して切ってしまうところに原因がある。 問題の箇所はlib\MT\App\Cms.pmのlist_entries関数の次のtitle_shortを作成している部分である。
1564 unless ($row->{title_short}) { 1565 my $title = remove_html($obj->text); 1566 $row->{title_short} = substr(($title, 0, 22) . '...'; 1567 } 1568 $row->{title_short} = substr(($row->{title_short}, 0, 22) . '...' 1569 if length($row->{title_short}) > 25; 1570 $row->{title_short} = encode_html($row->{title_short}, 1);
このsubstr関数が悪者。
これを解決するのはそんなに難しくなく、utf8を理解するsubstr関数を用意すればいい。 ちょっと引用が長いが次のような関数を用意した。
1432 my $utf8charpatternx = q { 1433 [\x00-\x7F] # UCS-2 U+0000..U+007F 1434 |[\xC2-\xDF][\x80-\xBF] # UCS-2 U+0080..U+07FF 1435 |\xE0[\xA0-\xBF][\x80-\xBF] # UCS-2 U+0800..U+7FFF 1436 |[\xE1-\xEF][\x80-\xBF][\x80-\xBF] # UCS-2 U+1000..U+D7FF, U+E000..U+FFFF 1437 # |\xF0[\x90-\xBF][\x80-\xBF][\x80-\xBF] 1438 # |[\xF1-\xF3][\x80-\xBF][\x80-\xBF][\x80-\xBF] 1439 # |\xF4[\x80-\x8F][\x80-\xBF][\x80-\xBF] 1440 # |[\xF5-\xF7][\x80-\xBF][\x80-\xBF][\x80-\xBF][\x80-\xBF] 1441 # |[\xF8-\xFD][\x80-\xBF][\x80-\xBF][\x80-\xBF][\x80-\xBF][\x80-\xBF] 1442 }; 1443 sub substrutf8 { 1444 my ($str, $pos, $len) = @_; 1445 my @chars = $str =~ /$utf8charpatternx/gox; 1446 return "" if $#chars < $pos; 1447 my $endpos = $pos + $len - 1; 1448 $endpos = $#chars if $endpos > $#chars; 1449 join "", @chars[$pos .. $endpos]; 1450 } 1451 sub lengthutf8 { 1452 my ($str) = @_; 1453 my @chars = $str =~ /$utf8charpatternx/gox; 1454 return $#chars + 1; 1455 } 1456 sub charwidthutf8 { 1457 my ($str) = @_; 1458 my @chars = $str =~ /$utf8charpatternx/gox; 1459 my $w = 0; 1460 foreach my $ch (@chars) { 1461 $w += (length($ch) < 2 ? 1 : 2); # assume U+0800..U+FFFF as wide!!! 1462 } 1463 return $w; 1464 }
substrutf8関数は、まず、与えられた文字列を$utf8charpatternx正規表現で分解する。 その後に必要な範囲の配列を要素を一つの文字列につなげて返す。
lengthutf8は同様のことをしてその配列の長さを返す。
charwidthutf8はlengthutf8と似ているが全角半角をおおざっぱに見分けて文字列の幅を返す。 UnicodeのU+0800以降は全角であると強引に決めうちしてしまっている。 ただし、UTF-8では原理的に全角半角を単純なレンジで判断できないので、 この方法はあまり良い方法とはいえないだろうが、とりあえず簡単に実装してしまった。
上記のバグの該当箇所のsubstrとlengthをそれぞれsubstrutf8とcharwidthutf8で置き換えた。
1564 my $collimit = 15; 1565 unless ($row->{title_short}) { 1566 my $title = remove_html($obj->text); 1567 $row->{title_short} = substrutf8($title, 0, $collimit - 3) . '...'; 1568 } 1569 $row->{title_short} = substrutf8($row->{title_short}, 0, $collimit - 3) . '...' 1570 if charwidthutf8($row->{title_short}) > $collimit; 1571 $row->{title_short} = encode_html($row->{title_short}, 1);
それでもまだ25文字で区切っている部分で、タイトルを省略しても表示が折り返してしまう。 25の半分である12~13とlengthutf8を比較するようにしても良かったのだが、 実際には試行錯誤の末にcharwidthutf8と15を比較するようにした。
とりあえずこれで文字化けはなくなった。
「文字化け対策」へのコメント コメントを書く
「文字化け対策」へのトラックバック