TIP: Use Markdown or, <pre> for multi line code blocks / <code> for inline code.
These forums are read-only and for archival purposes only!
Please join our new forums at discourse.kohanaframework.org
мультиязычность сайта
  • хотелось бы на сайте с несколькими языками иметь ссылки вида mysite.com/ru/main - для русского языка mysite.com/en/main - для английского как в этом случае настроить роутинг?

  • Все это элементарно. В application/config/i18n.php:

    <?php
    return array(
        'allowedLangs' => array(
            'ru'=>'Русский',
            'en'=>'English',
            'fr'=>'Français',
        ),
        
        'allowedLangsShrt' => array(
            'ru'=>'Рус',
            'en'=>'Eng',
            'fr'=>'Fra',
        ),
        'defaultLang' => 'ru',
    );
    

    В bootstrap.php:

    
    $c_i18n = Kohana::$config->load('i18n');
    
    $allowedLangs = implode('|', array_keys((array)$c_i18n->allowedLangs));
    $allowedLangs = "(?i:$allowedLangs)";
    $defaultLang = $c_i18n->defaultLang;
    
    
    Route::set('default', '((/(/(/))))', array(
            'lang' => $allowedLangs,
        ))
        ->defaults(array(
            'lang' => $defaultLang,
            'controller' => 'welcome',
            'action' => 'index',
            'id' => NULL,
        ));
    

    ну и в application/classes/controller.php

    abstract class Controller extends Kohana_Controller
    {
        /**
         * @var string Язык системы
         */
        protected $lang;
    
        /**
         * Конструктор
         * @param Request $request Запрос
         * @param Response $response Ответ
         */
        public function __construct(Request $request, Response $response)
        {
            parent::__construct($request, $response);
    
            $this->lang = $this->request->param('lang');
            //Ставим текущим языком язык из запроса
            I18N::lang($this->lang);
        }
    }
    

    Все. В любом конроллере будет защищенное поле lang, где будет лежать текущий язык системы. Плюс, I18N уже будет настроен на работу с нужным языком :)

  • @varz62 не исключен и более простой вариант, мы используем этот.. Удобен, можно настраивать языки как угодно и исключен переход на "неиспользуемый" язык

  • блин, маршрут съелся..

    В application/config/i18n.php:

    <?php
    return array(
        'allowedLangs' => array(
            'ru'=>'Русский',
            'en'=>'English',
            'fr'=>'Français',
        ),
        
        'allowedLangsShrt' => array(
            'ru'=>'Рус',
            'en'=>'Eng',
            'fr'=>'Fra',
        ),
        'defaultLang' => 'ru',
    );
    

    В bootstrap.php:

    
    $c_i18n = Kohana::$config->load('i18n');
    
    $allowedLangs = implode('|', array_keys((array)$c_i18n->allowedLangs));
    $allowedLangs = "(?i:$allowedLangs)";
    $defaultLang = $c_i18n->defaultLang;
    
    
    Route::set('default', '(<lang>(/<controller>(/<action>(/<id>))))', array(
            'lang' => $allowedLangs,
        ))
        ->defaults(array(
            'lang' => $defaultLang,
            'controller' => 'welcome',
            'action' => 'index',
            'id' => NULL,
        ));
    

    ну и в application/classes/controller.php

    abstract class Controller extends Kohana_Controller
    {
        /**
         * @var string Язык системы
         */
        protected $lang;
    
        /**
         * Конструктор
         * @param Request $request Запрос
         * @param Response $response Ответ
         */
        public function __construct(Request $request, Response $response)
        {
            parent::__construct($request, $response);
    
            $this->lang = $this->request->param('lang');
            //Ставим текущим языком язык из запроса
            I18N::lang($this->lang);
        }
    }
    
  • большое спасибо очень удобное решение

  • Спасибо, отличное решение. :)

  • Я бы посоветовал вместо конструктора использовать метод before(). Ну и не очень удобно в каждом роуте прописывать этот сегмент. Жаль, что сейчас не существует штатного способа вычленить сегменты из любого адреса, независимо от роута (аналог before(), но для маршрутизации). Но в принципе это можно и самому реализовать.

  • @biakaveron, а смысл? Мы переписываем заглушку контроллера (из system/classes) на нашу, в которой устанавливаем язык из маршрута. На маршрутизацию в целом оно никак не влияет, лишь ее видоизменяя. Другие котроллеры то не затрагиваются (наследование), для переводов используется __('слово').

    Или я что-то недопонял?

  • Возможно, это чисто мои принципы, но если выбирать между конструктором и before() - я всегда выберу before(). Как минимум потому, что он не принимает аргументов. Если в 3.3 поменяется список параметров конструктора (что вполне возможно), то так просто проапгрейдить точно не получится. А вот before() изначально не предусматривает передачу в него параметров. Это если говорить про место, куда положить код.

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

  • @biakaveron, когда писал свой вопрос про смысл, мне пришел именно ваш ответ в голову :) Я понял отличия, согласен с вами. Собственно, оба подхода дают свои особенности и недостатки.

    Что касается маршрутизации, то да - для поддержки мультиязычности придется править все маршруты, которым она требуется. Я видел варианты с куками, с сохранением языка в сессии и прочей ересью - не считаю их как вариант нормального сайта с мультиязычным контентом. Интерфейсом - да, но не контентом :)

  • Вот потому я и говорю о том, что надо бы сделать предварительный before() для роутинга )) Было бы неплохо, но сложновато будет с его реализацией для reverse routing

  • @biakaveron, как вы это себе представляете? Я тут недавно, можете скинуть где это упоминалось, если же нет - то опишите. Вы наверно подразумеваете что-то типа preroute для найденного маршрута для инъекций/удаления параметров?

  • preroute ПЕРЕД поиском маршрута. Т.е. из uri вычленяется языковой сегмент, и дальше отдается на растерзание маршрутам. Не уверен, что навскидку найду что-нибудь, проще будет попробовать набросать.

  • Я понял :) Да, тогда было бы немного проще в целом. preroute бы анализировал маршрут, устанавливал бы язык, если он найден и удалял его из маршрута.

  • А может проще создать в public директории: ru, en, скопировать в каждую из них файл index.php в котором определить дополнительную константу:

    // файл en/index.php
    define('KOHANA_LANG', 'en');
    
    // файл ru/index.php
    define('KOHANA_LANG', 'ru');
    

    и подправить пути к приложению.

    В bootstrap.php подправить базовый url:

    Kohana::init(array(
        'base_url'   => '/'.KOHANA_LANG,
    ));
    

    И всё, при любом запросе Kohana::$base_url автоматически вырезается из uri, а например при редиректе или вызове URL::base() автоматически подставляется. Можно юзать site.com/en или en.site.com.

  • А может проще создать в public директории: ru, en, скопировать в каждую из них файл index.php в котором определить дополнительную константу:

    И потом если будет еще 3 языка и туда скопировать все :D

  • @ButscH, скопировать не всё, а только index.php

  • @SVat не удобно, проще через маршруты

  • Вы знаете: планируются ли в 3.3 какие-то штатные средства для того, чтобы разделять роутинг по разным доменным именам? По-хорошему, конечно, языки лучше на поддомены вешать.

  • Может кто-нибудь подсказать, почему у меня может не работать предложенный вариант маршрута ((/(/(/)))), но работает немного измененный ()(/(/(/)))? Пробовал на голом фреймворке.

  • Маршрут порезался.

    Может кто-нибудь подсказать, почему у меня может не работать предложенный вариант маршрута (lang(/controller(/action(/id)))), но работает немного измененный (lang)(/controller(/action(/id)))? Пробовал на голом фреймворке.

  • @chungachguk, если вы делали так как я писал то все должно работать. У вас пути (base_url, RewriteBase) правильные?

  • Сейчас перепроверил. Работают оба варианта ))) что-то где-то я затупил Поменял на такой код (lang/)(controller(/action(/id))) и теперь не обязательно в адресе указывать язык.

  • а как при такой схеме реализовать смену текущего языка пользователем?

  • В любом контроллере сделать проверку и сменить язык не зависимо от адреса. Ну, а там уже при срабатывании условия, можно и все ссылки для него поменять на нужные

    public function before() {
            if (!empty($current_user_lang)) {
                I18n::lang($current_user_lang);
            }
            parent::before();
        }
    
  • @Alec, поставьте ссылки для смены языка. В шаблоне где-то блок "Сменить язык", там ссылка для каждого языка (или выпадающий список). Вариантов много. Еще можно сделать такое. Если пользователь пришел на индекс не указав язык (пришел на имя_сайта.домен) - то ставить язык из браузера пользователя ($this->request->accept_lang()).

  • да... видать уже поздно было и я задал вопрос не уточнив задачу )))) у меня ссылки на страницы формируются согласно своих роутов и туда автоматом подставляется текущий язык $defaultLang

    //Default
    Route::set('default', '(<lang>(/))((/)<controller>(/<action>(/<id>)))', array(
            'lang' => $allowedLangs,
        ))
        ->defaults(array(
            'lang' => $defaultLang,
            'controller' => 'main',
            'action' => 'index',
        ));

    в bootstrap я читаю куку и если там установлено lang, то $defaultLang присваивается lang

       if ( Session::instance()->get('lang','') )
        $defaultLang = Session::instance()->get('lang','');

    в корневом классе я проверяю установлен ли язык в url если да, то пишу куку с языком

           $this->lang = $this->request->param('lang');
            //Ставим текущим языком язык из запроса
            i18n::lang($this->lang);
            Session::instance()->delete('lang');  
            Session::instance()->set('lang', $this->lang); 
    

    для смены языка я сделал ссылки язык/контроллер.lang типа так: /ru/lang контроллер просто делает редирект в корень, это просто чтобы обновлять страницу и чтобы в bootstrap читалась новая кука... теперь вопрос, может есть более элегантный способ?:)

  • ах, да - при такой реализации не могу пока придумать как редиректить пользователя не в корень, а на ту страницу, на которой он нажал ссылку смены языка

  • перенаправлять нужно на Request::$current->referrer(). В контексте контроллера - на $this->request->referrer(). Или же, в ссылках для смены языка нужно учитывать текущий контроллер, действие и параметры (id, параметры запроса и так далее).

  • спасибо, понятно

    посмотрю, что лучше подойдет.

  • получается при такой схеме

    Route::set('default', '((/(/(/))))', array(
            'lang' => $allowedLangs,
        ))
        ->defaults(array(
            'lang' => $defaultLang,
            'controller' => 'welcome',
            'action' => 'index',
            'id' => NULL,
        ));
    

    каждый раз для формирования например ссылок, необходимо ставить приставку языка, к примеру так

    href='<?php echo $lang; ?>/linktoFile"
    

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

  • Разобрался, можно просто расширить класс URL таким способом

        public static function link_to($title, $url, $options=array()){
    
            $option_str = '';
            $site_url   = Url::base();
            $lng = Request::$initial->param('lang');
    
            foreach($options as $key => $option){
                $option_str .= "{$key}='{$option}' ";
            }
    
            return "{$title}";
        }
    

    пример взял от сюда

    http://kerkness.ca/kowiki/doku.php?id=example_of_a_multi-language_website

  • Если не принципиально для индексирования, то можно через кукисы.

  • :) .htaccess RewriteRule ^((ru|en|es|it|jp)/)?(.*)$ /index.php/$3?lang=$1

  • Реализовал модуль preroute https://github.com/Girt/kohana-preroute. Для работы файлы необходимо скопировать в application. Реализована работа нескольких прероутов.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

In this Discussion