четверг, 8 ноября 2012 г.

Kohana 3: транслитерация кириллицы

Kohana уже доросла до версии 3.3, а проблема с транслитерацией кириллицы тянется уже довольно давно.

Использую следующие костыли/надстройки для решения этой проблемы.

В 'APPPATH/classes' создаём скрипт 'utf8.php' со следующим содержимым:

<?php defined('SYSPATH') OR die('No direct script access.');
class UTF8 extends Kohana_UTF8 {

 /**
  * @var  boolean  Does the server support UTF-8 natively?
  */
 public static $server_utf8 = NULL;

 /**
  * @var  array  List of called methods that have had their required file included.
  */
 public static $called = array();


 /**
  * Replaces special/accented UTF-8 characters by ASCII-7 "equivalents".
  *
  *     $ascii = UTF8::transliterate_to_ascii($utf8);
  *
  * @author  Andreas Gohr <andi@splitbrain.org>
  * @param   string   $str
  * @param   integer  $case
  * @return  string
  */
 public static function transliterate_to_ascii($str, $case = 0)
 {
  if ( ! isset(self::$called[__FUNCTION__]))
  {
   require APPPATH.'utf8'.DIRECTORY_SEPARATOR.__FUNCTION__.EXT;

   // Function has been called
   self::$called[__FUNCTION__] = TRUE;
  }

  return _transliterate_to_ascii($str, $case);
 }
}

В каталоге 'APPPATH' создадим каталог 'utf8' и поместим в него скрипт 'transliterate_to_ascii.php' со следующим содержимым:

<?php defined('SYSPATH') or die('No direct script access.');
/**
 * UTF8::transliterate_to_ascii
 *
 * @package    Kohana
 * @author     Kohana Team
 * @copyright  (c) 2007-2011 Kohana Team
 * @copyright  (c) 2005 Harry Fuecks
 * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt
 */
function _transliterate_to_ascii($str, $case = 0)
{
 static $utf8_lower_accents = NULL;
 static $utf8_upper_accents = NULL;

 if ($case <= 0)
 {
  if ($utf8_lower_accents === NULL)
  {
   $utf8_lower_accents = array(
    'a' => 'a',  'o' => 'o',  'd' => 'd',  '?' => 'f',  'e' => 'e',  's' => 's',  'o' => 'o',
    '?' => 'ss', 'a' => 'a',  'r' => 'r',  '?' => 't',  'n' => 'n',  'a' => 'a',  'k' => 'k',
    's' => 's',  '?' => 'y',  'n' => 'n',  'l' => 'l',  'h' => 'h',  '?' => 'p',  'o' => 'o',
    'u' => 'u',  'e' => 'e',  'e' => 'e',  'c' => 'c',  '?' => 'w',  'c' => 'c',  'o' => 'o',
    '?' => 's',  'o' => 'o',  'g' => 'g',  't' => 't',  '?' => 's',  'e' => 'e',  'c' => 'c',
    's' => 's',  'i' => 'i',  'u' => 'u',  'c' => 'c',  'e' => 'e',  'w' => 'w',  '?' => 't',
    'u' => 'u',  'c' => 'c',  'o' => 'o',  'e' => 'e',  'y' => 'y',  'a' => 'a',  'l' => 'l',
    'u' => 'u',  'u' => 'u',  's' => 's',  'g' => 'g',  'l' => 'l',  '?' => 'f',  'z' => 'z',
    '?' => 'w',  '?' => 'b',  'a' => 'a',  'i' => 'i',  'i' => 'i',  '?' => 'd',  't' => 't',
    'r' => 'r',  'a' => 'a',  'i' => 'i',  'r' => 'r',  'e' => 'e',  'u' => 'u',  'o' => 'o',
    'e' => 'e',  'n' => 'n',  'n' => 'n',  'h' => 'h',  'g' => 'g',  'd' => 'd',  'j' => 'j',
    'y' => 'y',  'u' => 'u',  'u' => 'u',  'u' => 'u',  't' => 't',  'y' => 'y',  'o' => 'o',
    'a' => 'a',  'l' => 'l',  '?' => 'w',  'z' => 'z',  'i' => 'i',  'a' => 'a',  'g' => 'g',
    '?' => 'm',  'o' => 'o',  'i' => 'i',  'u' => 'u',  'i' => 'i',  'z' => 'z',  'a' => 'a',
    'u' => 'u',  '?' => 'th', '?' => 'dh', '?' => 'ae', 'µ' => 'u',  'e' => 'e',  '?' => 'i',
    'а' => 'a', 'б' => 'b', 'в' => 'v', 'г' => 'g', 'д' => 'd', 'е' => 'e', 'ё' => 'yo', 'ж' => 'zh',
    'з' => 'z', 'и' => 'i', 'й' => 'j', 'к' => 'k', 'л' => 'l', 'м' => 'm', 'н' => 'n', 'о' => 'o',
    'п' => 'p', 'р' => 'r', 'с' => 's', 'т' => 't', 'у' => 'u', 'ф' => 'f', 'х' => 'h', 'ц' => 'c',
    'ч' => 'ch', 'ш' => 'sh', 'щ' => 'shh', 'ъ' => '?', 'ы' => 'y', 'ь' => '?', 'э' => 'e', 'ю' => 'ju',
    'я' => 'ja',
   );
  }

  $str = str_replace(
   array_keys($utf8_lower_accents),
   array_values($utf8_lower_accents),
   $str
  );
 }

 if ($case >= 0)
 {
  if ($utf8_upper_accents === NULL)
  {
   $utf8_upper_accents = array(
    'A' => 'A',  'O' => 'O',  'D' => 'D',  '?' => 'F',  'E' => 'E',  'S' => 'S',  'O' => 'O',
    'A' => 'A',  'R' => 'R',  '?' => 'T',  'N' => 'N',  'A' => 'A',  'K' => 'K',  'E' => 'E',
    'S' => 'S',  '?' => 'Y',  'N' => 'N',  'L' => 'L',  'H' => 'H',  '?' => 'P',  'O' => 'O',
    'U' => 'U',  'E' => 'E',  'E' => 'E',  'C' => 'C',  '?' => 'W',  'C' => 'C',  'O' => 'O',
    '?' => 'S',  'O' => 'O',  'G' => 'G',  'T' => 'T',  '?' => 'S',  'E' => 'E',  'C' => 'C',
    'S' => 'S',  'I' => 'I',  'U' => 'U',  'C' => 'C',  'E' => 'E',  'W' => 'W',  '?' => 'T',
    'U' => 'U',  'C' => 'C',  'O' => 'O',  'E' => 'E',  'Y' => 'Y',  'A' => 'A',  'L' => 'L',
    'U' => 'U',  'U' => 'U',  'S' => 'S',  'G' => 'G',  'L' => 'L',  '?' => 'F',  'Z' => 'Z',
    '?' => 'W',  '?' => 'B',  'A' => 'A',  'I' => 'I',  'I' => 'I',  '?' => 'D',  'T' => 'T',
    'R' => 'R',  'A' => 'A',  'I' => 'I',  'R' => 'R',  'E' => 'E',  'U' => 'U',  'O' => 'O',
    'E' => 'E',  'N' => 'N',  'N' => 'N',  'H' => 'H',  'G' => 'G',  'D' => 'D',  'J' => 'J',
    'Y' => 'Y',  'U' => 'U',  'U' => 'U',  'U' => 'U',  'T' => 'T',  'Y' => 'Y',  'O' => 'O',
    'A' => 'A',  'L' => 'L',  '?' => 'W',  'Z' => 'Z',  'I' => 'I',  'A' => 'A',  'G' => 'G',
    '?' => 'M',  'O' => 'O',  'I' => 'I',  'U' => 'U',  'I' => 'I',  'Z' => 'Z',  'A' => 'A',
    'U' => 'U',  '?' => 'Th', '?' => 'Dh', '?' => 'Ae', 'I' => 'I',
    'А' => 'A', 'Б' => 'B', 'В' => 'V', 'Г' => 'G', 'Д' => 'D', 'Е' => 'E', 'Ё' => 'Yo', 'Ж' => 'Zh',
    'З' => 'Z', 'И' => 'I', 'Й' => 'J', 'К' => 'K', 'Л' => 'L', 'М' => 'M', 'Н' => 'N', 'О' => 'O',
    'П' => 'P', 'Р' => 'R', 'С' => 'S', 'Т' => 'T', 'У' => 'U', 'Ф' => 'F', 'Х' => 'Kh', 'Ц' => 'Tc',
    'Ч' => 'Ch', 'Ш' => 'Sh', 'Щ' => 'Sch', 'Ъ' => '\'', 'Ы' => 'Y', 'Ь' => '', 'Э' => 'E', 'Ю' => 'Ju',
    'Я' => 'Ja'
   );
  }

  $str = str_replace(
   array_keys($utf8_upper_accents),
   array_values($utf8_upper_accents),
   $str
  );
 }

 return $str;
}

P.S. Под 'APPPATH' подразумеваю константу APPPATH определяемую в index.php:

define('APPPATH', realpath($application).DIRECTORY_SEPARATOR);

Похожие по тематике посты:

3 комментария:

Вячеслав комментирует...

Да, согласен, поддержки кириллицы не хватает. Сам пользуюсь такими хаками.
Отдельное спасибо за статьи по kohana!
Все-таки, несмотря на недостатки и довольную низкую популярность, этот фреймворк сразу завоевал мою симпатию и, думаю, так и останется самым любимым за свою прозрачную структуру и стандарт кодирования.

alex комментирует...

Мне Kohana не хватает Zend'овских ассертов в unittest...
assertResponseCode(), assertModule(), assertController(), assertAction(), assertQueryContentContains()
:(

Andy комментирует...

На счёт низкой популярности - не согласен. Kohana - развивающийся фреймворк и её популярность растёт, она уже наступает на пятки тройке монстров Zend, Symfony и Yii.
Появляются вакансии с требованием - опыт разработки на Kohana, что говорит о популярности фреймворка и использовании его в продакшене.