PHP framework CodeIgniter. Создание многоязычных сайтов

В этой статье речь пойдет о возможностях, которые предоставляет CodeIgniter для поддержки многоязычных сайтов.
Разберемся, как реализована поддержка языков.
Идея очень простая. Вы определяете, какие фразы должны быть переведены и создаете файлы с переводами.
После этого, даете возможность пользователю выбрать язык и загружаете строки из файлов выбранного языка.
Таким образом, при формировании страниц, текст должен загружаться из файлов с переводами, а не прописываться в шаблонах.
Примечание. В этой статье речь идет о локализации фрэймворка и сайта, а не контента. Т.к. контент (текстовый) обычно находится в базе данных, то для размещения переводов вам, скорее всего, придется создать дополнительные таблицы и изменять запросы в зависимости от выбранного языка.
Создание файлов переводов
Т.к. CodeIgniter это PHP фрэймворк, то и файлы с переводами представляют собой обычные PHP скрипты, в которых объявлены массивы строк. Имена этих файлов должны обязательно содержать окончание «_lang.php».
Формат строк должен быть такой:
-
$lang[‘ключ’] = "Текст, который будет показан на сайте";
с помощью «ключа» осуществляется выбор нужной строки.
Названия ключей желательно делать осмысленными, иначе можно очень легко запутаться.
Размещение файлов
CodeIgniter имеет две папки, предназначенных для размещения файлов переводов.
1) \system\language\ - общая папка;
2) \system\application\language\ - это папка с переводами конкретного сайта.
Разница, думаю, очевидна. Если у вас несколько сайтов используют одни и те же системные файлы, и файлы переводов должны быть доступны для всех сайтов, то нужно использовать первую папку. А если каждый сайт использует свои собственные файлы переводов, то размещать их лучше во второй папке.
При загрузке переводов поиск осуществляется в следующем порядке. Сначала – в папке приложения (\system\application\language\), а затем – в общей папке (\system\language\).
Все файлы переводов должны быть сгруппированы в папки, название которых, совпадает с названием языка.
Например, мы создали два файла переводов для русского и английского языка, в которых разместили текст главной страницы сайта.
Что-то вроде:
-
<?php
-
$lang[‘title’] = "Hello";
-
?>
и
-
<?php
-
$lang[‘title’] = "Привет";
-
?>
Оба файла будут иметь одно и тоже имя main_lang.php, но размещены будут в разных папках:
\language\russian\main_lang.php – русский вариант;
\language\english\main_lang.php – английский вариант.
Загрузка файлов переводов
Теперь можно загружать файлы. Для этого используется метод load() объекта lang.
-
$this->lang->load(‘main’, ‘english’);
В первом параметре мы указываем имя файла (без окончания «_lang.php»), а во втором – названия языка (это название должно совпадать с именем папки, в которой находится файл перевода).
Кроме того, можно настроить CodeIgniter так, чтобы он автоматически загружал файлы выбранного языка. Для этого в файле \application\config\autoload.php находим переменную $autoload['language'] и добавляем нужный язык.
Для получения нужной строки используется метод line
-
$pageData[‘t’] = $this->lang->line(‘title’);
которому в первом параметре передается ключ строки.
Собираем все вместе
Допустим, посетитель выбрал язык, а вы сохранили его выбор в сессии.
После этого можно в конструкторе контроллера загрузить файлы с переводами для выбранного языка, а потом использовать их при создании страниц.
-
class Welcome extends Controller {
-
function Welcome()
-
{
-
parent::Controller();
-
$userLang = $this->session->userdata(‘userlang’);
-
$this->lang->load(‘main’, $userLang);
-
}
-
function index()
-
{
-
$pageData[‘t’] = $this->lang->line(‘title’);
-
$this->load->view(‘welcome_message’, $pageData);
-
}
-
}
Как видите, в конструкторе мы загрузили выбранный язык (строки 5, 6), а в методе index (формирует главную страницу сайта) – получили текстовую строку с ключом 'title', которую теперь можно использовать для формирования страницы.
Примечание. Это максимально упрощенный пример. В реальной ситуации вы должны будете, как минимум проверить установлен ли параметр 'userlang' и если нет – выбрать язык по-умолчанию.
Локализация стандартных библиотек
В состав CodeIgniter входит ряд библиотек, которые тоже полезно локализировать. Особенных усилий тут прикладывать не придется, т.к. файлы переводов можно скачать здесь.
После того, как скачаете архив, и распакуете его в одну из папок language, можно будет загружать нужные файлы.
Кстати, убедитесь, что кодировка файлов с переводами совпадает с кодировкой сайта.
Удачи!
Понравилась статья? Подпишитесь на продолжение
!
Опубликовано в CodeIgniter, PHP
Комментарии (35)
Вы можете отслеживать обсуждение записи с помощью RSS 2.0 ![]()
Вы также можете оставить комментарий, или трекбек с Вашего сайта.
Оставить комментарий








Прелестно… просто прелестно
Спасибо за заметку. Читать мануал CI - радость, но читать ещё более понятные заметки на родном языке - ещё большая радость.
Спасибо за отзыв!
Мануал в CI прекрасный (наверное один из лучших), но хотелось бы еще хорошую книжку (хотя бы на английском)
насчёт книжки могу поделится
Packt Publishing - CodeIgniter for Rapid PHP Application Development 2007
по моему единственная книга о CI правда 1.6 в ней нету
Если не сложно, вышлите.
Заранее спасибо.
выслал на gala.net
мне не очень нравится этот способ потому как
$this->lang->line(‘title’)гораздо менее удобней на мой взгляд, чем
_TITLE_то есть просто константы, на мой взгляд гораздо лучше - принцип такой-же, а кода меньше.
В вашем варианте возможны две проблемы.
Во-первых, большое количество констант может привести к тому, что они будут повторяться. В варианте CI все строки переводов находятся внутри одного массива.
Во-вторых, вам все равно придется написать код для загрузке нужной строки
_TITLE_.Ведь
$this->lang->line(‘title’)возвращает текст из заданного файла с переводом.С другой стороны, использование обычных констант вместо ассоциативных массивов должно дать выигрыш в быстродействии… ну и сокращение кода.
P.S. В принципе ничто не мешает написать альтернативный вариант библиотеки и использовать его. Было бы интересно потестировать и сравнить.
система с константами на самом деле такая же как и родная CI - писать почти нифига не нужно.
стандартный вариант:
$lang = $this->uri->segment(1);
switch($lang):
case 'lv':
$this->lang->load('main', 'latvian');
break;
case 'en':
$this->lang->load('main', 'english');
break;
case 'ru':
$this->lang->load('main', 'russian');
break;
default:
$this->lang->load('main', 'english');
break;
endswitch;
}
вариант с константами:
$lang = $this->uri->segment(1);
switch($lang):
case 'lv':
include_once('/lang/main_latvian.php');
break;
case 'en':
include_once('/lang/main_english.php');
break;
case 'ru':
include_once('/lang/main_russian.php');
break;
default:
include_once('/lang/main_english.php');
break;
endswitch;
}
и соответственно в языковом файле вместо
$lang['title'] = 'Hello';получается
define('title','Hello');вот и получается ваше во-вторых, по объёму, такое-же как и в оригинале
ах да
segment(1) потому что я обычно пишу route
$route['(en|lv|ru)'] = $route['default_controller'];
$route['(en|lv|ru)/(.+)'] = "$2";
а как вы поступаете с роутером ?
Да, получается довольно компактно. Вы не тестировали быстродействие? По-идее ваш вариант должен быть быстрее. Все-таки работа идет с константами, а не ассоциативным массивом.
По поводу роутинга…
.
Дело в том, что я старался обойти этот вопрос в статье, но, похоже, не получилось
Проблема в том, что я для себя не определился какой метод лучше, а точнее не протестировал все идеи. У меня есть три варианта.
Первый. Посетитель сам выбирает язык, а мы сохраняем его в сессии. Параллельно пробуем прочитать Accept-Language, т.е. «угадать» выбор пользователя. Тут явный минус в том, что посетителю придется выбирать язык каждый раз при входе на сайт (если мы его не «угадали»).
Второй. Это ваш вариант. Т.е. используются адреса вроде
http://www.site.com/ru/controller/function/…
http://www.site.com/en/controller/function/…
Тут решение вы показали в своем примере. У меня получалось практически тоже самое. Разве что использовал «:any» вместо «(.+)», но это не важно. Главное, что в этом варианте посетитель может сохранить ссылку на версию сайта с нужным языком и можно не использовать сессии.
Третий. Использовать субдомены.
ru.site.com/….
en.site.com/….
Вот это вариант я как раз и не доделал. Идея простая. Раз есть три домена, то есть и три папки DOCUMENT_ROOT, в каждую их которых помещаем свой index.php, причем все они будут ссылаться на одну папку application. Но тут возникает проблема. Что делать с $config[’base_url’] (из файла config.php)? Можно, конечно, ее проигнорировать и установить глобальные переменные для каждого домена в index.php. Но тогда придется игнорировать и функции типа anchor
В общем, похоже ваш вариант самый подходящий.
Насчёт быстродействия:
нет, не тестил как то не было необходимости.
Насчёт роутинга
Первый вариант я не рассматриваю, минусов слишком много как вы и сказали
второй вариант - штатное использование роутинга, в принципе дающий всё что нужно.
Третий вариант - даже не знаю зачем такое надо, но если вдруг надо, то насчёт $config[’base_url’]
вот вам линк в помощь:
http://codeigniter.com/wiki/Automatic_configbase_url/
для удобства приведу код и здесь:
$config['base_url'] = "http://".$_SERVER['HTTP_HOST'] . str_replace(basename($_SERVER['SCRIPT_NAME']),"",$_SERVER['SCRIPT_NAME']);и всё же я считаю что Codeigniter’ский роутер самое лучшее решение.
P.S. не забывайте кстати о mod_rewrite и его возможностях
За ссылку спасибо! Попробую.
mod_rewrite… Я о нем не забываю, но постоянно не хватает времени научиться нормально его использовать. Т.е. переделать готовые правила под свои задачи не проблема, а вот написать правила с нуля - гораздо сложнее.
Честно говоря, я сильно сомневаюсь, что кто-то полностью представляет все его возможности и варианты использования
Я представляю, хоть и не все… Поэтому мне не нравится ни один роутер в существующих фреймворках
а чем собственно не нравятся? чего не хватает?
Согласен с mihailt. Сам подход довольно тяжеловат с точки зрения синтаксиса. Поэтому, на мой взгляд оптимальным будет использование дополнительной функции, по аналогии с WordPress _e(’ключ’), которая и будет возвращать переведенную фразу.
Да, хороший вариант. Я столкнулся с этой этой функцией когда переводил на русский один из плагинов к WP. Всего два символа, очень удобно.
Напоминает функции типа $() в Prototype
Спасибо за полезную информацию.
Спасибо за Automatic_configbase_url - нужная вещь.
Уж коли пошла такая “пьянка”
спрошу, может подскажете. В CodeIgniter происходит адресация на основе контролеров/методов. Когда контролера/метод, указанного в url нет, происходит переброс на страницу error_404.php. Можно ли сделать так, чтобы при всех несуществующих методах происходил вызов своего метода (внутри своего контролера), например page_404()?
Сам на данным момент решил эту проблему путем модицикации CodeIgniter.php - где перед вызовом show_404() проверяется метод page_404.
Есть более элегантное решение?
да как и сказал Sam решается при помощи _remap() ты же вроде у меня читал пост где это упомянуто?
Можно:
http://codeigniter.com/user_guide/general/controllers.html#remapping
Супер! Так сразу и не догадался. Вот код, если вдруг кому понадобится.
function _remap($method)
{
if ( method_exists($this, $method) ) $this->$method();
else $this->page_404();
}
Спасибо!
Отдельный риспект за ссылочку на файлы с переводами
Есть еще вариант использования _remap(). Для авторизации.
Принцип такой:
1) в контроллере создаем массив с перечнем методов которые можно вызывать без входа на сайт.
$freeAccess = array(’index’,'login’,'about’);
2) в методе _remap() что-то вроде
function _remap($method) { if (in_array($method, $freeAccess)) { $this->$method(); } else if (//проверка авторизации) { $this->$method(); } else { $this->login(); } }P.S. Вообще-то собирался написать пост на эту тему
.
только начинаю изучать CI, в тему вопроса многоязычности:
иногда, в целях улучщения CEO, есть необходимость держать основной язык на верхнем уровне:
1. основной язык(lang0): http://www.host.com/
2. дополнительный язык (lang1): http://www.host.com/lang1/
3. дополнительный язык (lanh2): http://www.host.com/lang2/
если ли возможность сделать переадресацию с помощю routes или другим методом так, что бы при вызове $this->uri->segment(1); получать значение `lang0|lang1|lang2`.
Да, можно. В принципе метод описал mihailt (в комментариях выше).
Просто нужно установить язык по-умолчанию (lang0) и если $this->uri->segment(1) не содержит названия языка, то использовать lang0.
да, установка языка не проблема. вопрос именно в том что при использовании при:
- основной язык: http://www.host.com/articles/ $this->uri->segment(1) будет `articles`
- дополнительный язык: http://www.host.com/lang1/articles/ $this->uri->segment(1) будет `lang1`, а $this->uri->segment(2) будет `articles`
а нужно что бы значение $this->uri->segment(1) было одинаковым во всех языках.
именно для этого и нужна проверка
Создаем массив со всеми поддерживаемыми языками
$langs = array(”ru”,”en”,”ua”);
выполняем проверку есть ли название языка в адресе
if (in_array($this->uri->segment(1), $langs)) {
$curLang = $this->uri->segment(1);
}
else {
$curLang = “en”; //значение по-умолчанию
}
Шикарная статейка. Спасибо!
Простите, а не могли бы Вы и мне выслать книжку?
a.koposov(собака)reneissance.org
у меня на блоге, в последнем посте лежит ссылка.
Уже отправил.
А пост mihailt обязательно посмотрите. Там выложено 17 очень интересных книг.
Большое Вам спасибо!
Огромное спасибо! Не ожидал тако доброты =) Респект вам Люди.
да уж, мир не без добрых людей!
Огромное спасибо за статью, а можно ссылочку на исходник готового решения? дело в том что я новичек. И не совсем понятно следующее:
#
{
#
parent::Controller();
#
$userLang = $this->session->userdata(‘userlang’);
#
$this->lang->load(‘main’, $userLang);
#
}
Это надо писать в каждом контроллере и для каждой необходимой строки?? Заранее благодарен за ответ.
Прошу прощения, не то вставил…
$pageData[‘t’] = $this->lang->line(‘title’);
$this->load->view(‘welcome_message’, $pageData);
Пример отправил на email (если честно, он не сильно отличается от приведенного в статье).
$pageData[‘t’] = $this->lang->line(‘title’);
В этой строке мы загружаем перевод и сохраняем в массив, который передается представлению. Т.е. использовать ее придется перед каждой загрузкой представления.
$this->load->view(‘welcome_message’, $pageData);
Это обычная загрузка представления. В большинстве случаев вызывается один раз в каждом методе.