Тема: Заготовка FAQ по UTF-8
Благодаря hcs вы можете не заморачиваться, а взять готовую сборку для UTF-8 здесь: http://punbb.ru/viewtopic.php?id=791 hcs регулярно обновляет ее, когда обнаруживается что-то новое.
Тем не менее, для любознательных пишу этот FAQ. Это "заготовка", потому что пока нет уверенности, что учтены все нюансы. Чем больше мы знаем, тем больше убеждаемся что не знаем ничего. Я буду постоянно править и дополнять FAQ, а лишние комментарии чистить, чтобы легче читалось.
Итак:
Что нужно сделать, чтобы перевести действующий форум на кодировку страниц UTF-8
0. Вы должны перекодировать все русские сообщения в языковых файлах из window-1251 или koi-8 в UTF-8. Для этого вам понадобятся умные редакторы вроде akelpad или PHP Exper Editor.
1. В языковом файле lang/Russian/comon.php надо указать новую кодировку страниц
// Determine what locale to use
switch (PHP_OS)
{
case 'WINNT':
case 'WIN32':
$locale = 'russian';
break;
case 'FreeBSD':
case 'NetBSD':
case 'OpenBSD':
$locale = 'ru_RU.utf-8';
break;
default:
$locale = 'ru_RU';
break;
}
// Attempt to set the locale
setlocale(LC_CTYPE, $locale);
setlocale(LC_TIME, $locale);
// Language definitions for frequently used strings
$lang_common = array(
// Text orientation and encoding
'lang_direction' => 'ltr', // ltr (Left-To-Right) or rtl (Right-To-Left)
'lang_encoding' => 'utf-8',
'lang_multibyte' => true,я рекомендую вставить такие lang_encoding и lang_multibyte во все языки! Тогда независимо от языка интерфейса пользователи будут видеть неискаженный текст на других языках.
2. в файле include/dblayer/mysql.php сразу за if ($this->link_id) надо указать что мы работаем с данными в уникоде:
if ($this->link_id)
{
@mysql_query("SET NAMES 'utf8'");Обратите внимание: здесь utf8 пишется без дефиса!
Когда вы укажете такое, сервер MySQL перестанет конвертировать данные из своего внутреннего unicode-представления в windows-1251
3. Функции strlen, strtolower, strtoupper не умеют работать с мультибайтной кодировкой. Выход такой:
3a. в include/common.php после подключения языкового файла укажем, что будет использоваться UTF-8.
@include PUN_ROOT.'lang/'.$pun_user['language'].'/common.php';
if (!isset($lang_common))
exit('There is no valid language pack \''.pun_htmlspecialchars($pun_user['language']).'\' installed. Please reinstall a language of that name.');
mb_internal_encoding('UTF-8');3b. Во всех *.php находим и заменяем strlen на mb_strlen, strtolower на mb_strtolower, strtoupper на mb_strtoupper.
Особый случай - функция pun_strlen() в include/functions.php Она измеряет длинну строки с учетом символов в виде &#nnn.
Она должны выглядеть так:
function pun_strlen($str)
{
// FIX FOR UTF-ENCODING
// Original:
//return strlen(preg_replace('/&#([0-9]+);/', '!', $str));
// Tested, but not valid:
//return mb_strlen(preg_replace('/&#([0-9]+);/', '!', $str), 'UTF-8');
return strlen(utf8_decode($str));
}4. По непонятным причинам в файле seach.php предусмотрен альтернативный поиск для мультибайтных кодировок. Работает он не совсем корректно. Мы избавимся от этого
находим строчку "$multibyte =", делаем так:
// UTF FIX BEGIN
// ORIGINAL:
//$multibyte = (isset($lang_common['lang_multibyte']) && $lang_common['lang_multibyte']) ? true : false;
$multibyte = false;
// UTF FIX ENDПосле этих изменений форум должен нормально отображать и принимать русский текст, кодировка страниц при этом будет UTF-8. Мы ничего не изменили в бызе MySQL, но работать уже можем!
Я хочу использовать кроме русского и другие языки. Например, французский. При сохранении сообщения символы с "умляутами" портятся. Как исправить?
Для полной "уникодизации" необходимо изменить collation таблиц в БД на utf8_general_ci или иной utf8_ порядок сравнения.
Исключение - поле search_words.word. Она должна иметь collation binary. Сменить collation можно в phpMyAdmin.
Если форум ставится заново, откройте install.php и добавьте модификатор utf8 рядом со всеми полями типа varchar, типа такого:
`message` text character set utf8ошибка "Unable to insert search index words"
Ошибка происходит из-за попытки записать в таблицу неуникальное слово. Дело в том, что при записи в базу стрОки обрезаются до длины поля, поэтому длинные слова, различающиеся последними символами станут одинаковыми. Поле search_words.word изначально расчитано на слова длиной до 20 (однобайтовых) символов. Кирилические символы в UTF-8 занимают два байта. Другие алфавиты могут потребовать три байта на символ.
Необходимо убедиться, что
- поле search_words.word имеет collation BINARY либо само поле типа VARBINARY (зависит от версии MySQL)
- длина поля не менее 40 (20 символов * 2 байта или 13 символов * 3 байта)
т.е. нормальный вид поля word VARBINARY(40)
В файле include/search_idx.php найти функцию split_words и в ней заменить условие на максимальную длину слова. Вот как должен выглядеть этот фрагмент:
$num_chars = strlen($word);
if ($num_chars < 3 || $num_chars > 40 || in_array($word, $stopwords))
unset($words[$i]);Обратите внимание на немультибайтную strlen - здесь нам надо вычислить длину строки в байтах, а не символах!
Если ошибка уже случилась, надо очистить таблицу search_words и полечить ее в myPHPadmin, затем провести переиндексацию в админке форума (раздел Maintenance)