CakePHP: переключение языка приложения при помощи URL (i18n & l10n)

December 11, 2008 | By admin | Filed in: CakePHP, PHP, Переводы.

Оригинал

==

Данная статья не описывает основы интернационализации и локализации в cakePHP – для ознакомления с ними лучше заглянуть в мануал.

Сначала опишем требования, чтобы лучше понимать, зачем и почему что и как сделано в данной статье:

  1. Приложение должно поддерживать два языка или более (в нашем случае – английский и русский)
  2. Язык по умолчанию – английский
  3. Переключение языка производится с помощью параметра в URL
  4. Формат URL должен быть таким: example.com/eng/controller/action
  5. Выбранный язык должен сохраняться в сессии и куках

Небольшое примечание: есть и другие способы определить, какой язык запросил пользователь, например, поддомены вида eng.example.com или rus.example.com. Надеюсь, что предлагаемый подход пригодится и для тех методов переключения языка, которые вы используете в своем приложении…

Также стоит отметить, что передача названия языка в URL (а не просто считывание его из сессии или куки) хорошо сказывается на поисковой оптимизации сайта. В основном это нужно для того, чтобы обеспечить индексацию всех языковых вариантов одной страницы поисковой системой. То есть каждая страница позже может быть найдена пользователем на родном языке.

И еще – CakePHP использует трехбуквенные сокращения для названия языка (на основании этого), поэтому логично будет использовать таковые и в адресах страниц.

Выбранный формат URL вызывает естественный вопрос – как мы будем добавлять название языка в “начало” адреса?

К счастью, Router позволяет достичь этого довольно простым способом (в файле app/config/routes.php):

Router::connect('/:language/:controller/:action/*',
array(),
array('language' => '[a-z]{3}'));

Он подставляет параметр ‘language’ в начало URL, как нам и нужно.

Теперь укажем язык, используемый по умолчанию, добавив следующую строчку в app/config/core.php


Configure::write(’Config.language’, ‘eng’);

Теперь, когда посетитель заходит на http://example.com/users/home, страница будет отображена на английском языке. Ну и где-нибудь на странице есть ссылка для переключения на другой язык.

В Cake мы можем создавать такие ссылки следующим образом:


$html->link(’Русский’, array(’language’=>’rus’));

Заметьте, что мы указали параметр “language”, который отвечает за переключение. Поскольку других параметров мы не передаем, переход по такой ссылке приведет к перезагрузке текущей страницы, но на выбранном языке.

Примечание: не стоит использовать функцию перевода __() в ссылках для смены языка… Если посетитель приходит на сайт, который отображается на абсолютно незнакомом языке, единственное, что может помочь в данной ситуации – ссылка с текстом на родном языке, указывающая на возможность сменить язык.

Теперь создадим код, который будет собственно переключать языки, когда пользователь переходит по соответствующей ссылке.

Лучше всего это делать в App Controller:

var $components = array('Session', 'Cookie');

function beforeFilter() {
$this->_setLanguage();
}

function _setLanguage() {

if ($this->Cookie->read('lang') && !$this->Session->check('Config.language')) {
$this->Session->write('Config.language', $this->Cookie->read('lang'));
}
else if (isset($this->params['language']) && ($this->params['language']
!=  $this->Session->read('Config.language'))) {

$this->Session->write('Config.language', $this->params['language']);
$this->Cookie->write('lang', $this->params['language'], null, '20 days');
}
}

Я создал отдельный метод _setLanguage();. Во-первых, это позволяет не засорять beforeFilter(), в котором обычно и так много всего понаписано.
Во-вторых, данный метод можно переопределить в дочерних классах контроллеров.

Теперь рассмотрим несколько сценариев действий пользователя:

  1. Пользователь на сайте впервыеВ данном случае приложение считывает определенный по умолчанию язык из core.php, поэтому сайт отображается на английском
  2. Пользователь кликает по ссылкам на сайте (язык – английский)Ничего особенного делать не нужно, данный сценарий пропускаем
  3. Посетитель хочет переключить язык на русскийОн видит нужную ссылку и кликает по ней. В данном случае срабатывает вариант else if, так как пока ни в куки, ни в сессии язык не сохранен. Мы видим, что в ссылке был передан параметр /rus/, и в сессии его еще нет, поэтому мы записываем данное название языка в сессию и куки.
  4. Тот же посетитель, посмотрев сайт, уходит с него, а затем возвращается сноваСессия еще существует, поэтому сайт автоматом переводится на русский язык. Это хорошо в случае, когда пользователь забыл про (забил на) нужный формат ссылок, так как переход по ссылкам без явно присутствующего параметра все равно отобразит сайт на нужном языке (т.к. он хранится в сессии).
  5. Тот же пользователь закрывает браузер, идет охотиться на диких кабанов [Россия? :)] , и потом снова приходит на сайтТеперь язык считывается из сохраненной ранее куки, и мы в первом условии if проверяем, не заменит ли это значение то, что уже есть в сессии.
  6. Теперь пользователь хочет посмотреть сайт на английском языкеПроисходит то же самое, что и в предыдущих пунктах.

Теперь нужно убедиться, что языковой параметр автоматически добавляется ко всем ссылкам на сайте, если выбран какой-либо язык. Помните, что наличие таких ссылок важно для SEO.

Само собой, мы не будем добавлять параметр ‘language’ вручную для каждой ссылки, поэтому давайте переопределим стандартный метод кейка url().

Создадим app_helper.php в папке /app/ (там же, где app_controller.php и app_model.php):

class AppHelper extends Helper {

function url($url = null, $full = false) {
if(!isset($url['language']) && isset($this->params['language'])) {
$url['language'] = $this->params['language'];
}

return parent::url($url, $full);
}

}

Мы проверяем, задан ли уже параметр ‘language’ в URL, и если да, то ничего делать не надо. Если же нет, а значение $this->params['language'] задано, мы добавляем нужный язык к ссылке.
Теперь все ссылки на сайте будут содержать параметр ‘language’ в начале (снова SEO).

P.S. Вот пример тестового представления, для которого можно сгенерировать .po-файлы (что весьма просто делается при помощи консольной команды i18n, но это уже совсем другая история).

<?php
__('This is only a test message');
?>

<p>
<?php echo $html->link(__('Regular link', true), array('action'=>'test')); ?>
</p>

<p>
<?php echo $html->link(__('Regular link two', true), array('controller'=>'users', 'action'=>'test5', 'some stuff')); ?>
</p>

<p>
<?php echo $html->link('English', array('language'=>'eng')); ?>
</p>

<p>
<?php echo $html->link('Русский', array('language'=>'rus')); ?>
</p>


Tags: , , ,

Leave a Reply