То, что я пытаюсь сделать, довольно просто: я хочу напечатать дату (отметку времени) на китайском (или русском) языке.
Для всех языков я использую
setlocale(LC_TIME, 'hu_HU.utf8', 'hu_HU.UTF-8', 'hu_HU', 'hr');
$date = strftime('%a %e %b %Y, %H:%M');
$date = utf8_encode($date);
Это возвращает строку UTF-8 даже без utf8_encode()
, Все отлично. Теперь, когда я делаю то же самое с 'zh_CN.utf8'
локаль (или 'zh_CN.UTF-8'
, 'zh_CN'
или же 'zh'
) это не возвращает правильную дату. С или без utf8_encode()
это возвращается
'2018å¹?mæ?#dæ?'
Я не говорю по-китайски, но это явно неправильно. Я узнал, что он должен вернуть что-то вроде '年'
, Этот символ имеет шестнадцатеричное кодирование UTF-8 E5 B9 B4
но когда я смотрю на возвращенную строку, появляются неправильные шестнадцатеричные значения. Есть (после 2018 года) C3 A5 C2 B9 3F 6D C3 A6 ...
,
Когда я проверяю кодировку возвращаемой строки с mb_detect_encoding()
это всегда возвращает UTF-8. Я ожидал этого, потому что я использую 'zh_CN.utf8'
языковой стандарт, устанавливающий кодировку UTF-8.
Осмотревшись довольно долго, я наткнулся этот ответ Петра. Он предлагает использовать формат '%Y年%m月%e日'
в strftime()
функция. Когда я использую это, я получаю тот же результат, что и раньше.
Это приводит меня к мысли, что кодировка неверна. Но так ли это? Кодировка неправильная? Как мне преобразовать результат в правильную кодировку?
У меня более менее та же проблема для русского языка.
Я провел несколько часов, и я нашел правильные кодировки. strftime()
является не поставляя UTF-8
Строка. Для получения подробной информации смотрите в нижней части этого ответа. Я закончил с formatTime()
функция, которая доставляет мне правильное время в правильной кодировке (UTF-8
для меня).
function formatTime($format, $language = null, $timestamp = null){
switch($language){
case 'chinese':
$locale = setlocale(LC_TIME, 'zh_CN.utf8', 'zh_CN.UTF-8', 'zh_CN', 'zh');
break;
case 'hungarian':
$locale = setlocale(LC_TIME, 'hu_HU.utf8', 'hu_HU.UTF-8', 'hu_HU', 'hr');
break;
case 'russian':
$locale = setlocale(LC_TIME, 'ru_RU.utf8', 'ru_RU.UTF-8', 'ru_RU', 'ru');
break;
case 'german':
$locale = setlocale(LC_TIME, 'de_DE.utf8', 'de_DE.UTF-8', 'de_DE', 'de');
break;
case 'french':
$locale = setlocale(LC_TIME, 'fr_FR.utf8', 'fr_FR.UTF-8', 'fr_FR', 'fr');
break;
case 'polish':
$locale = setlocale(LC_TIME, 'pl_PL.utf8', 'pl_PL.UTF-8', 'pl_PL', 'pl');
break;
case 'turkish':
$locale = setlocale(LC_TIME, 'tr_TR.utf8', 'tr_TR.UTF-8', 'tr_TR', 'tr');
break;
case 'english':
$locale = setlocale(LC_TIME, 'en_GB.utf8', 'en_GB.UTF-8', 'en_GB', 'en');
break;
// ...
default: break;
}
if(!is_numeric($timestamp)){
$datetime = strftime($format);
}
else{
$datetime = strftime($format, $timestamp);
}
$current_locale = strtolower(setlocale(LC_TIME, 0));
if(($pos = strpos("utf", $current_locale)) === false || strpos("8", $current_locale, $pos) === false){
// UTF-8 locale is not used, the encodings are found out with the code shown below
$locale_default_encodings = array(
"german" => "ISO-8859-1",
"french" => "ISO-8859-1",
"polish" => "ISO-8859-2",
"turkish" => "ISO-8859-9",
// Testing hungarian results in "Windows-1252", but php.net recommends to
// use ISO-8859-2, in fact Windows-1252 is based on ISO-8859-2 so it should
// (hopefully) work with both (*)
"hungarian" => "ISO-8859-2",
"chinese" => "CP936",
"russian" => "KOI8-R");
$target_encoding = mb_internal_encoding(); // or "UTF-8" or whatever
if(isset($locale_default_encodings[$language])){
$datetime = mb_convert_encoding(
$datetime,
$target_encoding,
$locale_default_encodings[$language]
);
}
else{
// try to avoid this case
$datetime = mb_convert_encoding($datetime, $target_encoding);
}
}
setlocale(LC_TIME, $locale);
return $datetime;
}
(*): http://php.net/manual/de/function.strftime.php#94399
Я проверил strftime("%B")
результат для конкретного языка. Это полное название месяца. Я проверил перевод для своих языков, затем я посмотрел шестнадцатеричные значения для UTF-8
для разных букв перевода.
Сейчас я перебираю все кодировки, которые поддерживает php. Я конвертирую результат, заданный strftime()
от текущей итерированной кодировки к UTF-8
, Теперь я могу сравнить результат strftime()
конвертировано в UTF-8
к шестнадцатеричным значениям ручных переводов, которые также являются шестнадцатеричными значениями для UTF-8
, Если они соответствуют результату strftime()
имеет кодировку текущей интегрированной кодировки.
Я выбираю шестнадцатеричные значения, потому что они определенно одинаковы и не зависят от внутренней кодировки, потому что они являются ASCII-строками (или даже числами в php).
Это дает мне следующий вывод, код размещен ниже:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<h1>Detecting the font encoding of <code>strftime()</code>
</h1>
<h2>hungarian</h2>
<p>
<code>strftime()</code> for March for language hungarian. Expected hex: <code>6fc5be756a616b</code>, converted expected hex to string: <code>ožujak</code>
</p>
<table>
<tr>
<td>initial return value</td>
<td>oߵjak</td>
<td>6f9e756a616b</td>
</tr>
<tr>
<td colspan='3'>Encodings that deliver the correct result:</td>
</tr>
<tr style='background: green;'>
<td>Windows-1252</td>
<td>ožujak</td>
<td>6fc5be756a616b</td>
</tr>
</table>
<h2>chinese</h2>
<p>
<code>strftime()</code> for December for language chinese. Expected hex: <code>e58d81e4ba8ce69c88</code>, converted expected hex to string: <code>十二月</code>
</p>
<table>
<tr>
<td>initial return value</td>
<td>ʮՂ</td>
<td>caaeb6fed4c2</td>
</tr>
<tr>
<td colspan='3'>Encodings that deliver the correct result:</td>
</tr>
<tr style='background: green;'>
<td>EUC-CN</td>
<td>十二月</td>
<td>e58d81e4ba8ce69c88</td>
</tr>
<tr style='background: green;'>
<td>CP936</td>
<td>十二月</td>
<td>e58d81e4ba8ce69c88</td>
</tr>
<tr style='background: green;'>
<td>GB18030</td>
<td>十二月</td>
<td>e58d81e4ba8ce69c88</td>
</tr>
</table>
<h2>russian</h2>
<p>
<code>strftime()</code> for December for language russian. Expected hex: <code>d0b4d095d099d0aed090d09fd0ad</code>, converted expected hex to string: <code>дЕЙЮАПЭ</code>
</p>
<table>
<tr>
<td>initial return value</td>
<td>ť롡td>
<td>c4e5eae0e1f0fc</td>
</tr>
<tr>
<td colspan='3'>Encodings that deliver the correct result:</td>
</tr>
<tr style='background: green;'>
<td>KOI8-R</td>
<td>дЕЙЮАПЭ</td>
<td>d0b4d095d099d0aed090d09fd0ad</td>
</tr>
<tr style='background: green;'>
<td>KOI8-U</td>
<td>дЕЙЮАПЭ</td>
<td>d0b4d095d099d0aed090d09fd0ad</td>
</tr>
</table>
</body>
</html>
Других решений пока нет …