Недавно пришлось реализовывать на движке Smarty проект мультиязычного сайта – когда пользователь в любой момент можешь выбрать другой язык и контент адекватно на это отреагирует соответствующим изменением. Самый большой топик по проблеме находится на официальном форуме Smarty: «best way to build a multi-language site with smarty». Там я нашел ссылку на нечто фундаментальное, но громоздкое, что так и не решился испробовать: smarty.incutio.com/?page=SmartyMultilanguageSupport. А также маленькое и изящное решение предложенное Тобиасом Шиттковски (Tobias Schittkowski) в своем блоге: www.schittkowski.de/index.php?q=node/20.

Решение Тобиаса заключается в создании внутри шаблонов страниц переводных якорьков вида @@СЛОВО@@, которые при обработке шаблонизатором заменяются на текст в нужном языке. Сами языки находятся в отдельных файлах, содержащих ассоциативные массива вида: $language['СЛОВО'] = ‘ПЕРЕВОД’. К smarty это подключается через фильтр вывода.

Для себя нашел следующие минусы решения:

1. Сложность синхронизации языковых файлов. Мне кажется, что даже на среднем сайте при вялом стремлении к изменениям языковые файлы через год будут представлять из себя винегрет с двойными переменными, неполными переводами и низким структурированием.

2. Изменение содержимого файлов. Просто так скопировать любой текст в ассоциативный массив не получится. Необходимо экранировать кавычки, следить за спец. символами, думать о кодировке – а это дополнительная работа и опять же риск ошибки.

3. Низкая эффективность использования. Каждый раз прося сервер отобразить нужную нам страницу, мы фактически заставляем беднягу перелопачивать весь контент сайта.

Самый простой способ уйти от перечисленных недостатков – хранить контент в базе данных. Именно в этом направлении я и решил дорабатывать метод Тобиаса.

В работе задействованы следующие таблицы базы данных:

image

--
-- Структура таблицы `lng_pages`
--

CREATE TABLE IF NOT EXISTS `lng_pages` (
`lp_id` int(10) unsigned NOT NULL auto_increment,
`lp_name` varchar(255) NOT NULL default '',
PRIMARY KEY (`lp_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;

--
-- Структура таблицы `lng_words`
--

CREATE TABLE IF NOT EXISTS `lng_words` (
`lw_id` int(10) unsigned NOT NULL auto_increment,
`lw_word` varchar(255) NOT NULL,
`lw_html` tinyint(3) unsigned NOT NULL default '0',
`lw_page` int(10) unsigned NOT NULL default '0',
`lw_en` text NOT NULL,
`lw_de` text NOT NULL,
`lw_esp` text NOT NULL,
`lw_ru` text NOT NULL,
PRIMARY KEY (`lw_id`),
KEY `lw_word` (`lw_word`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;


Подключение к smarty реализовано так же как у Тобиаса, но количество функций сократил:

$smarty->register_outputfilter("translate_template");

function translate_template($tpl_output, &$smarty)
{
//Находим все переменные задействованные в шаблоне
preg_match_all('/@@(.+?)@@/', $tpl_output, $lng_word);
//Создаем фильтр для SQL запроса
while( list($key, $value) = each($lng_word[1]) )
{
$lng_words[] = "lw_word='".$value."'";
}
$filter = implode(" OR ", $lng_words);
//Ищем перевод всех переменных в шаблоне
$TranslateWordsQ = "SELECT lw_word, ".get_language()." AS lw_translate ".
"FROM lng_words ".
"WHERE ".$filter;
$TranslateWordsR = mysql_query($TranslateWordsQ);
while( $TranslateWords=mysql_fetch_array($TranslateWordsR) )
{
$tpl_output = preg_replace("/@@".$TranslateWords['lw_word']."@@/", var_out($TranslateWords['lw_translate']), $tpl_output);
}
return $tpl_output;
}

function get_language()
{
$lang_variable = ( isset($_SESSION['language']) )?$_SESSION['language']:"en";
switch ($lang_variable)
{
case "en":
return "lw_en";
break;
case "ru":
return "lw_ru";
break;
case "esp":
return "lw_esp";
break;
case "de":
return "lw_de";
break;
}
}


В итоге получилось простое решение по реализации мультиязычности на сайте. Администраторский интерфейс уже публиковать не стал, но ленивые всегда могут остановиться на phpMyAdmin.