Многоязычность субдоменов и URL-правила
Рубрика: Перевод Cookbook
25 Дек. 2009
Это руководство предназначено для более-менее продвинутых пользователей. Если вы только что начали работу с Yii Framework-ом, вам стоит вернутся на эту страницу позднее :-)
Некоторые размышления
В Yii мы обычно определяем статические правила маршрутизации (url-rules) в основном конфигурационном файле. В настоящее время в Yii нет встроенной поддержки субдоменов и динамических маршрутов. При разработке многоязычного приложения, вы обычно определяете url-правила на английском языке, независимо от языка клиента. Это хорошо работает, но для профессиональных сайтов и порталов — вы можете использовать совершенно другой подход!
Давайте представим что мы создаем сайт на двух языках (английском и немецком). Правила для страницы «контакты» скорее всего выглядело бы следующим образом:
<?php
...
'components' => array(
'urlManager' => array(
'rules' => array(
'contact' => 'site/contact',
),
),
),
...
?>
Доступ к странице мы могли бы получить по ссылке:
http://www.example.com/contact
Теперь подумаем как сделать вот так:
http://en.example.com/contact
http://de.example.com/kontakt
Как вы видите, мы используем субдомен — как определитель языка пользователя (EN – для английскойго, DE – для немецкого) и слово «Контакт» как url-правило. Как я уже говорил ранее, такой подход Yii не поддерживает.
Прежде чем я покажу вам решение — ознакомтесь со всеми недостатками и приемуществами. Оцените самостоятельно стоит ли использовать данный подход для ваших приложений.
Недостатки
- Если вы используете субдомены для размещения основной информации на них, это скорее всего приведет к падению вашего PageRank основного домена. (например example.com вероятно пострадает)
- Google заявляет что лучше использовать домен верхнего уровня вместо субдоменов. Так что если вы являетесь владельцем example.com , example.de – тогда вы не должны использовать данный подход. Но вы конечно можете переписать часть кода под работы с доменами верхнего уровня, вместо субдоменов.
- Для небольших сайтов нет смысла использовать данный подход. Реальное приемущество вы получите на веб-сайтах с посещаемостью не меньше чем 10 000 уникалов в сутки.
Приемущества
- Вы можете развить ваш сайт в реальный многоязычный портал, как например YouTube, и проверять статистику с одно аккаунта Google Analytics для разных субдоменов.
- Google будет отображать ссылки (http://www.google.com/support/webmasters/bin/answer.py?hl=en&answer=47334) для каждого языка. Это правильнее с точки зрения SEO
- Пользователи будут счастливы :)
Решение
Создадим новый компонент, который будет включать в себя все необходимые свойства и функции:
<?php
class I18n extends CApplicationComponent
{
public $supportedLanguages;
public $urlRulesPath;
public $activeLanguage;
public function init()
{
if (true === (bool)preg_match("/^(?<protocol>(http|https):\/\/)(((?<languageCode>[a-z]{2})\.)*)((.*\.)*(?<domain>.+\.[a-z]+))$/", Yii::app()->request->hostInfo, $matches))
{
if (2 !== strlen($matches['languageCode']) && false !== ($language = $this->isSupportedLanguage($matches['languageCode'])))
{
$this->activeLanguage = $language;
}
else
{
$this->activeLanguage = $this->guessClientLanguage();
$redirectUrl = "{$matches['protocol']}{$this->activeLanguage['code']}.{$matches['domain']}";
}
}
else
{
throw new CException("Unable to parse host info - please request this page with a domain (eg http://example.com/) instead of an ip-address!");
}
Yii::app()->setLanguage($this->activeLanguage['code']);
Yii::app()->urlManager->rules = include("{$this->urlRulesPath}/{$this->activeLanguage['code']}.php");
if (false !== Yii::app()->urlManager->cacheID && null !== ($cache = Yii::app()->getComponent(Yii::app()->urlManager->cacheID)))
{
$keyPrefix = $cache->keyPrefix;
$cache->keyPrefix = "{$keyPrefix}.{$this->activeLanguage['code']}";
Yii::app()->urlManager->init();
$cache->keyPrefix = $keyPrefix;
}
else
{
Yii::app()->urlManager->init();
}
if (true === isset($redirectUrl))
{
Yii::app()->request->redirect($redirectUrl);
}
}
public function guessClientLanguage()
{
if (false === isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) || false === (bool)preg_match("/([a-z]{2}+)[-_][A-z]{2}+/", $_SERVER['HTTP_ACCEPT_LANGUAGE'], $match) || false === ($language = $this->isSupportedLanguage($match[1])))
{
if (false === ($language = $this->getFallbackLanguage()))
{
throw new CException("Unable to return the fallback language - please set it in config!");
}
}
return $language;
}
public function getFallbackLanguage()
{
foreach ($this->supportedLanguages as $supportedLanguage)
{
if (true === isset($supportedLanguage['fallback']) && true === $supportedLanguage['fallback'])
{
return $supportedLanguage;
}
}
return false;
}
public function isSupportedLanguage($languageCode)
{
foreach ($this->supportedLanguages as $supportedLanguage)
{
if ($supportedLanguage['code'] === $languageCode)
{
return $supportedLanguage;
}
}
return false;
}
}
?>
В основной конфигурации мы добавим этот компонент в массив предварительной загрузки:
<?php
...
preload => array('log', 'i18n'),
...
?>
Еще нам требуется добавить компонент в тело конфигурационнго файла, с описанием основных параметров работы:
<?php
...
'components' => array(
'i18n' => array(
'class' => 'I18n',
'urlRulesPath' => dirname(__FILE__) . '/i18n/urlRules',
'supportedLanguages' => array(
array('code' => 'en', 'name' => 'english', 'fallback' => true),
array('code' => 'de', 'name' => 'german'),
),
),
),
...
?>
Файлы правил маршрутизации (protected/i18n/urlRules/*.php ) должны иметь примерно следующий формат:
<?php
/* ../protected/i18n/urlRules/de.php */
return array(
'kontakt' => 'site/contact',
)
Вывод
Не стесняйтесь поделиться своими мыслями и замечаниями! Спасибо.
- Оригинал "I18n subdomains and url-rules" [англ]
Если хотите опубликовать этот материал у себя - пожалуйста, разместите ссылку на страницу откуда вы его взяли.
- Эта статья устарела т.к. была написана для yii версии 1.0.х; Если вы используете более новую версию - у вас могут ... "Урок 7 : Комментарии"
- Спасибо команде разработчиков нашего чудесного фреймворка за отличный подарочек под ёлку - новую версию yii. В релиз включено более 50 ... "Обновляемся, Yii 1.1.13"
- Странно, каждый день по несколько раз посещаю официальный сайт Yii Фреймворка – и только сегодня заметил новость за 15е ... "Видео урок "Как создать свой блог""

NickSun
Было сказано: Пятница, 18 Июнь 2010
Че-то не могу понять куда это писать:
<?php
...
preload => array('log', 'i18n'),
...
?>

NickSun
Было сказано: Понедельник, 21 Июнь 2010
Кстати походу косяк в кукбуке :)
Сначала регуляркой ищем два символа языка: (?<languageCode>[a-z]{2}), а потом перед тем как поменять язык проверяем чтоб не дай Бог язык оказался длинной в 2 символа: if (2 !== strlen($matches['languageCode']) ...
Может все-таки нужно 2 == strlen($matches['languageCode']) ?

[guest] kitt
Было сказано: Понедельник, 12 Июль 2010
Как насчет национальных символов в UTF8 в урлах и правилах к ним?
Что то вроде:
...
'components' => array(
'urlManager' => array(
'rules' => array(
'Контакты' => 'site/contact',
'Дневники/<username:\w+>'=>'diary/user'
),
),
),
...
У меня preg_match('/^\w+$/u','РусскиеБуквы') возращает 0.
И соотвественно компоненты CUrlManager и CUrlRule, завязанные на них не отрабатывают с русскими буквами в УРЛах.
Ось убунту 10.04, apache2+mod_php, системная локаль utf8. Уже не знаю куда копать(((
Если кто вкурсе - черканите на xzombi@ya.ru

[guest] zolter
Было сказано: Вторник, 13 Июль 2010
У меня
'Контакты' => 'site/contact',проходит вполне нормально

[guest] DropSQL
Было сказано: Четверг, 12 Август 2010
У меня preg_match('/^\w+$/u','РусскиеБуквы') возращает 0.Потому что \w+ - содержит только латиницу.

[guest] butch
Было сказано: Четверг, 09 Сентябрь 2010
'РусскиеБуквы' должны быть в utf8 кодировке, тогда php \w будет воспринимать правильно

[guest] butch
Было сказано: Четверг, 09 Сентябрь 2010
Только что проверил :) действительно \w работает только с латиницей.
Для Unicode \w нужно заменить можно на \p{L}
preg_match("/^\p{L}+$/u", "РусскиеБуквы")http://www.php.net/manual/en/regexp.reference.unicode.php


