Одним из последних проектов над которым я работал около месяца — разработка социальной сети. Сайт не такой уж сложный. Но был (и есть один момент) который мне не дает покоя — это поддержка языков. В WordPress, данный вопрос давно и хорошо проработан. И если ты имеешь навыки работы за компьютером, писать и набирать текст. То перевести тему или плагин большого труда не составит. Но для самописной CMS, которую я использую практически во всех разработках — это проблема. Если изначально, поддержка языков не была проработано на должном уровне.По наивности, я решил использовать тот функционал локализации, который предоставляет фреймворк Kohana (собственно на котором и написан мой движок). По умолчанию, фразы и слова с переводом хранятся в обычном php файле в виде массива. Пример перевода пагинации ниже:
return array( 'First' => 'Первая', 'Last' => 'Последняя', 'Previous' => '<', 'Next' => '>', );
Преимущество данного подхода одно — производительность. Какое еще? Да кажется все. Но в высоко нагруженных проектах это важно!Если конечно дорабатывать, ранее созданный сайт с такой локализацией не планируется. То подобный способ вполне пригоден. Или если существует (или пишется под заказ) специально ПО, позволяющее сформировать вышеуказанный массив из функции «__(...)», то данный подход так же сгодится (об этой идее я кстати прочел на хабаре).Так вот. Я как представил себе, сколько это правок придется вносить в код и в текст во время доработок. Сколько нервов я потрачу с этими языками (а их на данный момент пять). Что «сразу» же решил отказаться от данного подхода.Потратив некоторое время, я пришел к следующему выводу.
- Можно использовать расширение PHP «gettext»
- Использовать наработки существующих CMS (WP, Drupal, Joomla)
Начал с первого. Честно сказать оказалось не так все просто. Всякие заморочки с установкой текущей локализации, непонятные пути к файлам и установкой кодировки. В общем, ничего у меня не вышло. К тому же! У клиента, на сервере данное расширение отсутствовало. В следствии чего, от данного подхода пришлось отказаться.Хотя с gettext разобраться все же нужно.Пункт второй. К счастью, на тот момент у меня был небольшой опыт работы с WP. И я решил в нем покопаться. Изучив как в нем реализован данный механизм. Было принято решение использовать именно его.Проанализировав последовательность вызовов функций «load_theme_textdomain()» и «__()» был разработан простенький класс работы с языками. Который был успешно внедрен (но окончательно не протестирован).
Как подключить POMO WordPress к своему движку
От самого WordPress нам понадобится пять файлов из каталога «/wp-includes/pomo/» (а именно — entry.php, mo.php, po.php, streams.php, translations.php). Возможно все файлы и не нужны, дело в том, что логику работы классов я не изучал, поэтому взял все и не стал «париться».Ниже, пример класса, выполняющего те же задачи что и функции «load_theme_textdomain()» и «__()» в WP.
<?php
class Lng
{
/**
* Массив языковых файлов
* @var array
*/
private static $_lang_ar = array();
/**
* Возвращает перевод строки
* @param string $s переводимая строка. Пример "Hello from {site}"
* @param strings $l языковый файл
* @param array $a массив замены подстрок. Пример - array('{site}' => 'wp2fl.com')
* @return strings
*/
public static function _($s, $l = 'default', $a = NULL)
{
$l = Lng::l().'-'.$l.'.mo';
if(isset(Lng::$_lang_ar[$l]) == FALSE)
{
Lng::$_lang_ar[$l] = new NOOP_Translations;
}
$s = Lng::$_lang_ar[$l] -> translate($s);
return empty($a) ? $s : strtr($s, $a);
}
/**
* Загружает языковый файл
* @param string $l название файла
* @param string $p путь к каталогу где храняться языковые файлы
*/
public static function load($l, $p)
{
$l = Lng::l().'-'.$l.'.mo';
if(class_exists('MO', FALSE) == FALSE)
{
throw new Exception('The "MO" library does not included');
}
try
{
$MO = new MO();
if($MO -> import_from_file($p.$l) == FALSE)
{
return FALSE;
}
if(isset(Lng::$_lang_ar[$l]))
{
$MO -> merge_with(Lng::$_lang_ar[$l]);
}
Lng::$_lang_ar[$l] = &$MO;
}
catch (Exception $e)
{
throw new Exception('The language file "'.$l.'" does not exist');
}
return TRUE;
}
/**
* Возвращает текущий язык
* @return string
*/
public static function l()
{
return 'ru';
}
}
А еще ниже, пример использования:
<?php
header('Content-Type: text/html; charset=utf-8');
try
{
/* Подключаем библиотеки */
require_once('vendor/pomo/mo.php');
require_once('library/lng.php');
/* Подключаем первый языковый файл */
Lng::load('first','languages/');
/* Подключаем второй языковый файл */
Lng::load('second','languages/');
}
catch(Exception $e)
{
echo $e -> getMessage();
}
$name_first = 'Андрей';
$name_second = 'Алексей';
?>
<p><?php echo Lng::_('Hello {name}, how are you?','first',array('{name}' => $name_first))?></p>
<p><?php echo Lng::_('I am fine, thx {name}. And you?','second',array('{name}' => $name_second))?></p>
Не думаю что код нуждается в описании, т. к. везде есть комментарии. Обращу лишь ваше внимание на то, что языковые файлы должны храниться в каталоге «languages» и иметь названия «first» и «second».Результат работы кода следующий:
Привет Андрей, как ты? Нормально, спасибо Алексей. А как ты?
Рабочий пример можно скачать по этой ссылке. Удачных вам переводов 🙂

Не понял а что мешает создать простенькую функцию, например:
function lang($s){ global $lang; return isset($lang[$s]) ? $lang[$s] : $s; }
где $lang тот самый массив, о котором вы упоминали в начале. Его можно прописать в отдельном файле, например: langs/ru.php - ну и подключать нужные файлы по необходимости.
С переменными в строке тоже как бы проблем не должно возникнуть, если слегка дописать функцию, но суть вы, думаю, уловили. Или я не уловил сути сказанного вами?
Здравствуйте, Сергей!
Возможно я плохо изложил суть проблемы. Проблема не в возможности отображать преводимые фразы. Проблема в удобстве поиска фраз которые необходимо пеоевести. А редактор poedit ее очень хорошо решает. А использование poedit, "тянет" за собой расширение php gettext или pomo от WordPress.