Example of a multi-language website

First we add some config lines to tell what languages are available.

<?php defined('SYSPATH') or die('No direct script access.');
 
return array(
    'language' => 'english',
    'language_abbr' => 'en',
    'lang_uri_abbr' => array("fr" => "french", "en" => "english"),
    'lang_ignore' => 'xx', //Note that it will look for a language named 'xx' in i18n. Therefore i18n:get('txt_sometext') will return 'txt_sometext'.
    'lang_desc' => array("en" => "English version", "fr"=>"French version"),
);
//End of ./application/config/appconf.php

Then the route part of the boostrap.php will look like this:

/**
 * Load language conf
 */
$langs 		= Kohana::config('appconf.lang_uri_abbr');
$default_lang 	= Kohana::config('appconf.language_abbr');
$lang_ignore	= Kohana::config('appconf.lang_ignore');
$langs_abr 	= implode('|',array_keys($langs)) ;
if(!empty($langs_abr))
	$langs_abr .= '|' . $lang_ignore;
 
/**
 * Set the routes. Each route must have a minimum of a name, a URI and a set of
 * defaults for the URI.
 */
Route::set('default', '((<lang>)(/)(<controller>)(/<action>(/<id>)))', array('lang' => "({$langs_abr})",'id'=>'.+'))
	->defaults(array(
	    'lang' => $default_lang,
		'controller' => 'welcome',
		'action'     => 'index',
	));

The route pattern will allow us to work with urls such like:

http://mywebsite
http://mywebsite/en/
http://mywebsite/fr/
http://mywebsite/fr/mycontroller/myaction
http://mywebsite/mycontroller/myaction

But we do not want url duplicated contents and also want to force the language to always appear in the url. We need to extend the request class to do that. The below code will re-route urls such like:

http://mywebsite/mycontroller/myaction  will redirect to default language => http://mywebsite/en/mycontroller/myaction
http://mywebsite will redirect to default language => http://mywebsite/en/
<?php defined('SYSPATH') or die('No direct script access.');
 
class Request extends Kohana_Request {
	/**
	 * Main request singleton instance. If no URI is provided, the URI will
	 * be automatically detected using PATH_INFO, REQUEST_URI, or PHP_SELF.
	 *
	 * @param   string   URI of the request
	 * @return  Request
	 */
	public static function instance( & $uri = TRUE)
	{
		$instance = parent::instance($uri);
 
		$index_page     = Kohana::$index_file;
		$lang_uri_abbr 	= Kohana::config('appconf.lang_uri_abbr');
		$default_lang 	= Kohana::config('appconf.language_abbr');	
		$lang_ignore	= Kohana::config('appconf.lang_ignore');	
 
		/* get the lang_abbr from uri segments */
		$segments = explode('/',$instance->uri);
		$lang_abbr = isset($segments[0]) ? $segments[0]:'';
 
		/* get current language */
		$cur_lang = $instance->param('lang',$default_lang);
 
		/* check for invalid abbreviation */
		if( ! isset($lang_uri_abbr[$lang_abbr]))
		{		
			/* check for abbreviation to be ignored */
			if ($cur_lang != $lang_ignore) {
				/* check and set the default uri identifier */
				$index_page .= empty($index_page) ? $default_lang : "/$default_lang";
 
			 	/* redirect after inserting language id */
 				header('Location: '.Url::base().$index_page . '/' . $instance->uri);
				die();
			}
 
 
		}
 
 
		return $instance;
	}
}
//End of ./application/classes/request.php

In the before() method of the 'layout.php' class, We overload the lang parameter so the i18n can find the files in i18n/ folder (/i18n/en.php /i18n/fr.php) Normally we have to use language-culture format in url (website/en-us/mycontroller/myaction) which is quite awkward.

Example of a layout controller extending Controller_Template:

  <?php defined('SYSPATH') or die('No direct script access.');
 
  class Controller_Layout extends Controller_Template 
  {
 
      public $template = 'layout';
 
 
      /**
       * The before() method is called before your controller action.
       * In our template controller we override this method so that we can
       * set up default values. These variables are then available to our
       * controllers if they need to be modified.
       */
      public function before()
      {
        parent::before();
 
        #Set the language with URI
        $lng = Request::instance()->param('lang');
        i18n::$lang = $lng . '-' . $lng;  //Fake the culture as we won't need cultures in our website, only languages.
 
 
  	    if ($this->auto_render)
  	    {
  	    	// Initialize empty values
  	    	$this->template->title   = '';
  	    	$this->template->content = '';
 
  			$this->template->styles = array();
  			$this->template->scripts = array();   
 
          }
      }
 
      /**
       * The after() method is called after your controller action.
       * In our template controller we override this method so that we can
       * make any last minute modifications to the template before anything
       * is rendered.
       */
      public function after()
      {
		if ($this->auto_render)
		{
			$styles = array(
				'css/main.css',
			);
 
			$scripts = array(
				'js/jquery-1.3.2.min.js',
			);
 
			$this->template->styles 	= array_merge( $this->template->styles, $styles );
			$this->template->scripts 	= array_merge( $this->template->scripts, $scripts );
 
		}
		parent::after();
      }
  }
 
//End of ./application/controller/layout.php

We create a few methods in URL class so we won't care about user language for urls in views.

<?php defined('SYSPATH') or die('No direct script access.');
 
class URL extends Kohana_URL {
 
/**
* href link generator
*
* Returns a href tag after adding the user language in it
*
*/
public static function link_to($title, $url, $options=array()){
 
	$option_str = '';
	$site_url 	= Url::base();
	$lng = Request::instance()->param('lang');
 
    foreach($options as $key => $option){
    	$option_str .= "{$key}='{$option}' ";
    }
 
    return "<a href='{$site_url}{$lng}/{$url}' {$option_str}>{$title}</a>";
}
 
/**
* redirect
*
* redirect to the given controller/myaction after adding the user language
*
*/
public static function redirect($to = ''){
	$site_url 	= Url::base();
	$lng = Request::instance()->param('lang');
 
	header("Location: {$site_url}{$lng}/{$to}");
	die();
}
 
}
//End of ./application/classes/url.php

Note that if you use the layout.php example from above, your controllers should look like this:

<?php defined('SYSPATH') or die('No direct script access.');
 
class Controller_Home extends Controller_Layout {
 
	function action_index(){
		$this->template->content = View::factory('home');	
 
	}
 
 
} // End of ./application/controller/home.php
example_of_a_multi-language_website.txt · Last modified: 2009/10/31 12:00 by dsoyez