和田@NTTデータエンジニアリングシステムズです。
随分、間が開いてしまいましたが、
前回報告させていただいた回避策では、
別の問題を発生させる事を確認しましたので、
改めて回避策を報告させていただきます。
具体的には、
Windows の Moodle 1.6(ja_utf8使用) でカレンダーの日付文字化け回避のために
lib/moodlelib.php ファイルの951行目から956行目をコメントアウトしたり、
ja_utf8 の langconfig.php の localewincharset の設定値を空にしますと、
日付の曜日部分等に英語表記もしくは文字化けが発生する事になります。
(1) 現象
・カレンダーの日付(年と月)が文字化けする
・日付の曜日の部分だけが英語表記か文字化けする
・月をドロップダウンで選択する箇所で英語表記か文字化けする
(2) 実行環境
・Windows XP, 2000
・PHP 5.1.6
・Moodle 1.6.2+(ja_utf8 言語パッケージ使用)
(3) 原因
根本的な原因は、lib/moodlelib.php ファイル内 userdate 関数の
実装不備と考えています。
以下の処理(lib/moodlelib.php 951行目から956行目)は
Windows の PHP で strftime 関数で取得した曜日等の文字エンコードが
SJIS(CP932)になる事への回避措置として用意されているものと考えられます。
if (!empty($CFG->unicodedb) && $CFG->ostype == 'WINDOWS') {
if ($localewincharset = get_string('localewincharset')) {
$textlib = textlib_get_instance();
$datestring = $textlib->convert($datestring, $localewincharset, current_charset());
}
}
しかし、この処理だけでは、
日付フォーマットに ja_utf8 パッケージ langconfig.php の
'%%Y年 %%m月 %%d日(%%A)' 等が使われた場合のように、
strftime 関数の結果が格納される $datestring に
UTF-8(年、月、日) と SJIS(曜日)が混在するケースに対応できず、
一部文字の文字化けの発生原因となります。
そのため、この問題を回避する適切な方法として、
上記処理に加えて、strftime や gmstrftime 関数に渡される
日付フォーマットの文字エンコードを事前に変換(UTF-8 から SJIS)する
処理の追加が考えられます。
(4) 回避策
回避策は、
lib/moodlelib.php の userdate 関数内の
strftime 関数実行前に $format の文字エンコードを
Windows 用の文字エンコードに変換する処理を加える事です。
一応、後述の [変更例] のように lib/moodlelib.php を編集して、
日付の文字化け・英語表記の問題が解決する事を確認しました。
ただし、この場合、
ja_utf8 の langconfig.php の localewincharset の設定値は
デフォルト値の 'CP932' を使用する必要があります。
ただ、元の moodlelib.php がそうなっているので従いましたが、
個人的に、Windows 固有の処理をこのような関数内に直接挿入してしまうのは
好ましい実装だとは思っていません。
[変更例]
function userdate($date, $format='', $timezone=99, $fixday = true) {
・・・
if ($format == '') {
if (empty($strftimedaydatetime)) {
$strftimedaydatetime = get_string('strftimedaydatetime');
}
$format = $strftimedaydatetime;
}
//新規追加する処理
//カレントの文字エンコードから Windows 用の文字エンコードに変換
$localewincharset = null;
$textlib = null;
if (!empty($CFG->unicodedb) && $CFG->ostype == 'WINDOWS') {
if ($localewincharset = get_string('localewincharset')) {
$textlib = textlib_get_instance();
$format = $textlib->convert($format, current_charset(), $localewincharset);
}
}
・・・
} else {
$date += (int)($timezone * 3600);
if ($fixday) {
$datestring = gmstrftime($formatnoday, $date);
$daystring = str_replace(' 0', '', gmstrftime(' %d', $date));
$datestring = str_replace('DD', $daystring, $datestring);
} else {
$datestring = gmstrftime($format, $date);
}
}
/// If we are running under Windows and unicode is enabled, try to convert the datestring
/// to current_charset() (because it's impossible to specify UTF-8 to fetch locale info in Win32)
//Windows 用の文字エンコードからカレントの文字エンコードに変換
//この処理は元の実装のままでも良いが、
//新規追加した処理と重複する部分を削除した
if ($localewincharset) {
$datestring = $textlib->convert($datestring, $localewincharset, current_charset());
}
return $datestring;
}