28 октября 2013 в 11:51

Как я делал мультиязычность на Codeigniter из песочницы tutorial

В первую очередь хочу сказать спасибо за комментарии. Думаю данная тема будет еще для многих актуальна, так как периодически получаю от пользователей хабры сообщения в скайп, помочь в создании «модуля» мультиязычности.
image
Данный пост полностью переписан, так как на данный момент использую совершенно другой подход к разработки мультиязычности для CodeIgniter 3. Также пост максимально короткий с примером и кратким описанием.

Решение проблемы



В общем, со временем и более детальным изучением данного фреймворка, пришел к выводу — мультиязычность нужно делать в роутере.

Принцип работы мультиязычности


Логика мультиязычности, до боли проста. Одна функция в обновленный MY_Router.php которая добавит в начало всех правил — языки сайта. После работы функции, CodeIgniter 3 будет работать с новым списком route. Получится примерно так :)

Было
$route['default_controller'] = "pages/index";
$route['pages/(.+)'] = 'pages/index/$1';
$route['news/(.+)'] = 'news/view/$1';


Станет
$route['default_controller'] = "pages/index";
$route['(by|ru|kz|en)/pages/(.+)'] = 'pages/index/$2';
$route['(by|ru|kz|en)/news/(.+)'] = 'news/view/$2';


Но для начала, создадим файл c настройками, где будем хранить доступные языки сайта и установим язык по-умолчанию config/localize_config.php

<?php defined('BASEPATH') OR exit('No direct script access allowed');

/**
 * Настройки локализации сайта.
 *
 *     default_key => язык сайта по умолчанию
 *     list        => список доступных языков
 *
 * @author Sergey Makhlenko
 * @version 1.0
 */
$config['ROUTE_LOCALIZE'] = array(
    'default_key' => 1, // Язык по-умолчанию, указывается ключ из массива "list" (0 -> by, 1 -> ru, .... 4 -> en)
    'list'        => array('by', 'ru', 'kz', 'ua', 'en'), // Доступные языки для сайта
    );


Беремся расширять стандартный system/core/Router.php.

Расширяем CI_Router (MX_Router)

Создаем в каталоге application/core файл MY_Router.php Если вы используете HMVC, вам нужно сначала загрузить его Router.php
В начало MY_Router.php добавьте следующий блок кода

<?php (defined('BASEPATH')) OR exit('No direct script access allowed');

// load the MX_Router class
if ( file_exists(APPPATH."third_party/MX/Router.php") ) {
    require APPPATH."third_party/MX/Router.php";
}

....


2) После этого, создаем уже расширение класса CI_Router или если с HMVC MX_Router
class MY_Router extends CI_Router {

    /**
     * Language user or default language
     * Язык пользователя или язык по-умолчанию
     */
    public $user_lang = '';


    /**
     * Class constructor
     *
     * Run the route mapping function.
     *
     * @param   array   $routing
     * @return  void
     */
    public function __construct($routing = NULL)
    {
        parent::__construct();
    }

    ...
}


3) А теперь самое интересное. Нам нужно расширить стандартную функцию CI_Router -> _set_routing. Возможно от версии к версии эта функция будет меняться разработчиками CI, советую просто разобраться в этих строчках, чтобы после любого обновления фреймворка, ваш файл MY_Routing был в актуальном состоянии. Тем более вам нужно добавить всего 1 строчку.

Итак начнем, копируем из system/core/Router.php функцию (метод) _set_routing и вставляем в наш созданный класс MY_Routing.
находим строчки, после которой функция подгрузила routes.php

.....
        if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/routes.php'))
        {
            include(APPPATH.'config/'.ENVIRONMENT.'/routes.php');
        }

        // Validate & get reserved routes
        if (isset($route) && is_array($route))
        {
            // Тут мы допишем нашу строку...
.....


и включаем в этот участок кода нашу строку для изменения полученных роутеров.

// Update Routing Localize
$this->__localize_init($route);


4) А вот и сама функция которая переделает наши правила под понимание различных языков.

/**
     * Append to routing localize lang
     *
     * @param array $route Route is config/routes.php
     * @return array
     */
    private function __localize_init( &$route = array() ) {

        // Loader config localize
        if (file_exists(APPPATH.'config/localize_config.php'))
        {
            include(APPPATH.'config/localize_config.php');
            $localize = $config['ROUTE_LOCALIZE'];
        } else {
            return FALSE;
        }

        /* --------------------------------------------------------- */

        // Check config localize
        if ( !isset($localize) or !isset($localize['list']) ) {
            return FALSE;
        }

        if ( !isset($localize['default_key']) ) {
            $localize['default_key'] = 0;
        }

        $localize['default_key'] = intval($localize['default_key']);

        /* --------------------------------------------------------- */

        // Language join list
        $lang_list = implode('|', $localize['list']);

        // Create new route list
        foreach ( $route as $key => $item ) {
            $_route[$key] = $item;
            if ( $key == 'default_controller' ) {
                $_route['('.$lang_list.')'] = $route['default_controller'];
                $_route['('.$lang_list.')/(.+)'] = '$2';
            }
        }

        /* --------------------------------------------------------- */

        // Check default language
        if ( isset( $localize['list'][ $localize['default_key'] ] ) ) {
            $this->user_lang = $localize['list'][ $localize['default_key'] ];
        }

        // User select language
        if ( array_search( $this->uri->segment(1), $localize['list'] ) !== FALSE ) {
            $this->user_lang = $this->uri->segment(1);
        }

        $route = $_route;
    }


Вот и всё!


Теперь ваш сайт будет понимать ссылки vashsite.com/by, vashsite.com/ru или vashsite.com (с использованием языка по-умолчанию).

Вы всегда имеете возможность получить выбранный язык (by,ru,.....kz) используя ниже описанную переменную в своих контроллерах или моделях.
$this->router->user_lang;


Дальше уже дело за вашей фантазией как работать с user_lang :) всё что нужно, у вас уже есть.

Готовый вариант на bitbucket.org
Сергей Махленко @weblive
карма
0,0
рейтинг 0,0
Похожие публикации
Самое читаемое Разработка

Комментарии (7)

  • 0
    if (file_exists(APPPATH . 'modules/lang'))
    {
        $route['default_controller'] = "lang";
        $route['(ru|en)'] = $this->config->item('default_controller');
        $route['(ru|en)/(:any)'] = "$2";
    } else {
        $route['default_controller'] = $this->config->item('default_controller');
    }
    

    Это же конфигурационный файл, не надо так.
    • 0
      Если честно, другого способа не придумал, а чем так плох данных способ?
      • 0
        Ну в конфигах не принято хранить логику.
        Вы можете использовать хуки, и оттуда уже инжектить свои роуты в зависимости от условия в глобальный объект CI.
        • 0
          Спасибо, буду пробовать, как выйдет сделать, обновлю статью
  • 0
    И, простите, зачем тут HMVC в заголовке, если он в принципе никакой роли в статье не играет и вы ничего об этом не пишите?
    • 0
      а HMVC потому, что я использую его структуру.
  • 0
    Народ, подскажите.
    Только недавно начал пробовать работать с Codeigniter. Делаю проект с несколькими языками. База будет иметь отдельные таблицы для каждого языка. Например pages_ru, pages_ua и т.д…
    Правильно ли я понимаю, в контролер должен принимать значения языка и передавать его модели для запроса в bd, а как быть тогда с Form Validation? там ошибки в отдельном файле как их менять?!

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