Если проблему можно решить за деньги, то это не проблема, это расходы

Переводим сайт на UTF-8

"Лучше день потерять, потом за час долететь" © Крылья, ноги, хвост

Не так давно мне "посчастливилось" перевести веб сайт средних размеров из одной кодировки в другую. Если быть точнее из windows-1251 на UTF-8. Потом еще один - побольше, на третьем я сломался, и следуя верному принципу вышесказанного мне пришлось потерять кучку времени на написание скрипта по автоматизации этого процесса, но зато потом, за час я все-таки долетел.

Прежде чем приступить к самому скрипту, хотелось бы описать мои подходы к данной проблеме и особенности решения. Естественно я использовал любимый командный интерпретатор bash в среде Linux.

Из особенностей работы скрипта можно отметить следующее:

  • Сохранение исходных данных
  • Копирование файлов и папок с символами пробела (whitespace)
  • Сохранение атрибутов файлов и директорий (владельцы, режим доступа)
  • Замена meta charset= на необходимую кодовую страницу в требуемых файлах по маске
  • Настраиваемые параметры скрипта

Что-бы понять работу, выполняемую скриптом, привожу алгоритм обработки файлов:

  • Вычищаем целевую директорию
  • Копируем в нее без изменения все содержимое исходной папки
  • Удаляем из целевой папки по маске все файлы, которые в дальнейшем будут перекодированы
  • Конвертируем требуемые файлы из исходной папки в целевую, восстанавливая при этом их атрибуты
  • Меняем в сконвертированных файлах строки содержащие charset=windows-1251 на charset=UTF-8 (как пример для моего случая, но параметры могут быть легко изменены)

Настраиваемые параметры скрипта следующие:

Исходные параметры:
SDIR="/usr/local/apache2/htdocs/site.ru/" - исходная директория сайта с символом слеша / в конце
SCP="CP1251" - исходная (from) кодовая страница для iconv
EXT=".*.(htm[l]*|php[3]*|js|css)$" - расширения файлов для перекодировки (здесь будут задействованы такие как .htm, .html, .php, .php3, .js, .css)
FCS="windows-1251" - название исходной кодовой страницы для замены мета charset= в файлах

Целевые параметры:
DROP_STRUCT=true - принимает значения false, true, регулируя условие: должна ли при старте вычищаться целевая директория

DDIR="/usr/local/apache2/htdocs/new.site.ru/" - целевая директория сайта с символом слеша / в конце (должна существовать)
DCP="UTF-8" - целевая (to) кодовая страница для iconv
TCS="UTF-8" - название целевой кодовой страницы для замены мета charset= в файлах

А вот собственно и сам скрипт:


#!/bin/bash

# --- CONFIG SECTION ---

# Source Dir's params

SDIR="/usr/local/apache2/htdocs/site.ru/"    # with slash '/' in the end
SCP="CP1251"        # codepage for 'iconv'
EXT=".*\.(htm[l]*|php[3]*|js|css)$"    #files extensions for coding
FCS="windows-1251"    # charset for replace

# Destination Dir's params

DROP_STRUCT=true    # false, true

DDIR="/usr/local/apache2/htdocs/new.site.ru/"    # with slash '/' in the end
DCP="UTF-8"        # codepage for 'iconv'
TCS="UTF-8"        # new charset

# --- END CONFIG SECTION ---

# Drop structure
#
if $DROP_STRUCT
then
    rm -dfr $DDIR*
fi

# Make new copy
#
cp -aR $SDIR* $DDIR

# Flush miscoded files
#
find $DDIR -type f | grep -E "$EXT" | xargs -i rm -f {}

# Convert From To
#
find $SDIR -type f | grep -E "$EXT" | sed "s#$SDIR##" | xargs -i echo {} | \
while read f
do
iconv -c -f $SCP -t $DCP -o "$DDIR$f" "$SDIR$f"

# Revert MODE & OWNER
chmod `find "$SDIR$f" -maxdepth 0 -printf "%m"` "$DDIR$f"
chown `find "$SDIR$f" -maxdepth 0 -printf "%u:%g"` "$DDIR$f"

# Replace strings
perl -pi -e "s#content\s*\=\s*[\"'].*?charset\s*=\s*$FCS.*?[\"']#content=\"text/html; charset=$TCS\"#g" "$DDIR$f"
done

А теперь, еще несколько полезных моментов.

1. Возможно даже после перекодировки в UTF-8 и замены meta content на charset=UTF-8 Вы все равно видите абракадабру или не то, что хотелось бы. Здесь все дело в том, что для нового сайта в UTF-8 необходимо заменить параметр default_charset для самого PHP, т.к. в глобальных переменных он явно установлен для другой кодовой страницы (windows-1251). Я делаю это в настройках виртуального хоста (httpd.conf) через:


php_admin_value default_charset UTF-8

2. Как правило, сейчас любой сайт хочет базы данных, которые Вам то-же надо будет перевести в UTF-8. Особого труда это не составляет, если под рукой есть phpMyAdmin или mysqldump, на крайний случай, для гиганских БД, наверняка придется писать скрипт конвертации и временно приостанавливать сервис. Простота идеи должна быть понятна: делаем дамп БД, перекодируем его с помощью того-же iconv и заменяем все, что связано с кодовыми страницами на желаемые данные, заливаем все в новую БД.

Еще более правильный вариант предложенный 4m@t!c сделать это на тестируемой БД с помощью ALTER TABLE tbl_name CONVERT TO CHARACTER SET charset_name;

С БД так-же может вылезти небольшая абракадабра, проявляющаяся в некорректном отображении русских 'ш' 'И'. Здесь также сыграет с нами шутку дефолтная кодовая страница для MySQL. Для устранения этой проблемы, после подключения к БД, Вам придется добавить в код Вашего сайта следующие строки:


mysql_query("SET NAMES 'utf8'");

Либо поменять default-character-set и default-collation для MySQL, если такое позволительно.

Помните!!! Подходите к таким переводам серьезно, сперва выполняя их на параллельной версии сайта и тестируйте, тестируйте, тестируйте.

Удачных переводов!

Добавлено:
Умные люди на Хабре и среди моих читателей дали очень дельные советы, за что им огромное спасибо. В связи с этим статья намного улучшилась.

12 комментариев на статью:
  • 4m@t!c:

    Достаточно одного запроса «SET NAMES utf-8», которая в том числе изменит CHARSET
    Не обязательно конвертировать дампы iconv. мжно сделать это с помощью ALTER TABLE tbl_name CONVERT TO CHARACTER SET charset_name;

  • admin:

    Огромное спасибо, я этого не знал (насчет ALTER CONVERT).
    Тогда алгоритм смены БД меняется:
    сперва делается копия существующей, а затем на ней делается ALTER CONVERT.

    Еще раз спасибо :)

  • Eltnic:

    Читал-читал и осознал собственное несовершенство. Хороший ресурс.

  • Миша:

    а если на сервере не поддерживается функция

    #php_value mbstring.func_overload 7

    #php_value default_charset UTF-8

    #php_value mbstring.language Russian

    #php_value mbstring.internal_encoding UTF-8

    #php_flag mbstring.encoding_translation on

    #php_value mbstring.http_input “UTF-8,KOI8-R,CP1251”

    #php_value mbstring.http_output UTF-8

    #php_value mbstring.detect_order “UTF-8,KOI8-R,CP1251”

    # end

    что делать?

  • Alexander:

    Хм… Все переходят на UTF-8, а я чуть было не начал переводить некоторые свои сайты с UTF-8 в 1251 :) И все ради экономии на длине текста, ведь UTF-8 занимает больше места. Но в итоге победил здравый смысл)

  • knigoboz:

    Есть один очень простой и эффективный способ перевода в кодировку utf-8.
    Первое: заменяем во всех файлах charset=windows-1251 на charset=uft-8.
    Вотрое: в папке, где хранятся ваши файлы, которые вы будете заливать на сервер, создаём папку с названием «utf-8».
    Далее копируем туда из основной папки все файлы.
    Теперь каждый скопированный в папку «utf-8» файл открываем в «блокноте» и сохраняем его в кодировке utf-8.
    Всё!
    Перекодированные файлы готовы к заливке на сервер!

  • knigoboz:

    Вот ещё, рекомендую: Здесь описывается текстовый редактор с перекодировкой в UTF-8 и даётся ссылка на загрузку.
    Всем удач!

  • Serg:

    А на хрена?
    В какой кодировке нравится в той и пишите.
    Преиущества utf-8 весьма спорны (несколько дополнительных символов), а гимор зачастую велик.
    Если Вас устраивает то, в чем вы сейчас пишете, в том и пишите.
    Не заморачивайтесь.
    Даже по коментам здесь — видно charset=windows-1251 рулит везде без проблемм, а с utf-8 вечные глюки и лишняя работа.

    • admin:

      на хрена:
      — мультиязычный сайт (или: попробуйте показать свой сайт-визитку на windows-1251 зарубежному партнеру)
      — активное использование ajax и фреймворков (без костыльков)
      — отход от архаизмов и переход на современные стандарты (если только вы не узколобая бабушка с кругозором в рамках местной деревни)

      — нахрен не надо, если вы не заморачиваетесь пунктами указанными выше

  • Sergey:

    Все споры о UTF-8 и с1251 не те и не о том.
    Самое главное отличие — это отображение одних и тех же шрифтов. То, что красиво в UTF-8 -в 1251 выглядит ужасно!!!
    В 1251 большинство шрифтов отображается криво.
    И почему-то про это не кто ни где не упоминает.

Ответить для admin
Обязательные поля помечены *