Skip to content

i18n Multi language Library Helper

World Wide Web Server edited this page Jul 4, 2012 · 60 revisions

This library / helper uses the usual way CI stores language-files. Inside the [b]application[/b] folder a folder named [b]languages[/b]. Inside laguages the language folders (dutch english etc.) and inside the language folders the language-files. For ex. application/languages/dutch/url_lang.php

[size=4][b]This tutorial is about four language things:[/b][/size]

[b]1) Using multi-language websites[/b]

http://domain.com [default language] http://domain.com/nl/...

(Default is redirected to root: if default is dutch and the url is http://domain.com/nl/... the page is redirected to http://domain.com/...)

[b]2) Using multi-language segments[/b]

http://domain.com/news http://domain.com/nl/nieuws

(Default segment is the one of the default language. If a segment for and language is not set/given in the specific language file the default is given. If that one is not present the one given in the code is used.)

[b]3) Using multi-language domains[/b]

http://domain.nl/nieuws http://domain.nl/en/news http://domain.com/news http://domain.com/nl/nieuws

(This sounds strange but imagine if you have many domains (like me) using the same website and it is not so simple to just say .com is english and .nl is dutch and at the same time you want it to be possible to have a multi-language domain (each domain) without changing the domain (because the sites have nothing to do with each other for example))

[b]4) Using multi-language text[/b]

[code]

// dutch $lang['news'] = "nieuws"; $lang['news_item'] = "nieuws item %s"; $lang['news_items'] = "nieuws item %s en %s";

// english $lang['news'] = "news"; $lang['news_item'] = "news item %s"; $lang['news_items'] = "nieuws item %s and %s";

// if dutch echo lang('news'); // prints: nieuws echo lang('news_item', '123'); // prints: nieuws item 123 echo lang('news_items', array('123','456')); // prints: nieuws item 123 en 456

// if english echo lang('news'); // prints: news echo lang('news_item', '123'); // prints: news item 123 echo lang('news_items', array('123','456')); // prints: news item 123 and 456

[/code]

[b]Used ideas from:[/b]

[url=http://maestric.com/doc/php/codeigniter_i18n]Internationalization (i18n) library by Jérôme Jaglale[/url] [url=http://codeigniter.com/wiki/URI_Language_Identifier/]URI Language Identifier by Wiredesignz[/url]

[b]application/config/routes.php[/b]

[code]

$route['^(\w{2})/(.*)$'] = '$2'; $route['^(\w{2})$'] = $route['default_controller'];

// Urls start

$route['^mobiel|^handy|^movil|^cell(/:any)?'] = "mobile$1";

// Urls end

[/code]

The part "Urls" is only for the controllers (or modules). Automated generated at the bottom of this tutorial...

[b]application/config/config.php[/b]

[code]

// instead "dutch" you can also use "nl_NL" ("english" > "en_UK") etc. // don't forget to change the folder names in languages also // if you want to use this also as http://domain.com/nl_NL/... // don't forget to change the router (\w{2}) to (\w{2}_\w{2})

// default language

#$config['language'] = 'dutch';

$domains = array( 'domain.nl' => 'dutch', 'domain.com' => 'english' );

$host = implode('.', array_slice(explode('.', $_SERVER['HTTP_HOST']), -2));

if (key_exists($host, $domains)) { $config['language'] = $domains[$host]; } else { $config['language'] = 'dutch'; }

// available languages (key: language code, value: language name)

$config['languages'] = array('nl' => 'dutch', 'en' => 'english');

// available countries (key: country code, value: language code)

$config['country_languages'] = array('nl' => 'nl', 'uk' => 'en', 'us' => 'en', 'au' => 'en');

[/code]

[b]application/libraries/MY_Config.php[/b]

[code]

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

class MY_Config extends CI_Config {

/**
 * Constructor
 *
 * @access    public
 */
function MY_Config()
{
    parent::CI_Config();
    
    log_message('debug', "MY Config Class Initialized");
}

// --------------------------------------------------------------------

/**
 * Site URL
 * 
 * Note: First I renamed the function to i18n_url() but
 * site_url() is used all over other functions as well.
 * There is only one difference with the original site_url()
 * Each segment is first checked if it exists in the language
 * files with an "url_" prefix, if not then the given segment
 * is used. 
 * 
 * @access    public
 * @param    string    the URI string
 * @param    bool    en-/disable lang
 * @return    string
 */
function site_url($uri = '', $lang = TRUE)
{
    $CI =& get_instance();
    
    if (is_array($uri))
    {
        $uri = implode('/', $uri);
    }
    
    $uri = $CI->lang->localize($uri);            
    
    if ($uri == '' || $lang == FALSE)
    {
        return parent::site_url($uri);
    }
    
    $uri = explode('/', $uri);
    
    $segments = array();
    
    foreach ($uri as $segment)
    {
        $line = $CI->lang->line('url_' . $segment);
        
        $segments[] = ($line == NULL) ? $segment : $line;
    }
    
    $uri = implode('/', $segments);
    
    return parent::site_url($uri);
}

}

// END MY_Config Class

/* End of file MY_Config.php / / Location: ./system/application/libraries/MY_Config.php */

[/code]

[b]application/libraries/MY_Language.php[/b]

[code]

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

class MY_Language extends CI_Language {

/**
 * Constructor
 *
 * @access    public
 */
function MY_Language()
{
    parent::CI_Language();
    
    log_message('debug', "MY Language Class Initialized");
    
    global $URI;
    global $CFG;
    global $RTR;
    
    $segment = $URI->segment(1);
    
    if ( ! empty($segment))
    {
        $languages = $CFG->item('languages');
        
        if ($RTR->scaffolding_request === TRUE)
        {
            $RTR->uri->segments = $RTR->uri->rsegments;
        }
        else if (isset($languages[$segment]))
        {
            $language = $languages[$segment];
            
            if ($language == $CFG->item('language'))
            {
                $uri = $URI->segment_array();
                
                array_shift($uri);
                
                $uri = implode('/', $uri);
                
                header("Location: /" . $uri, TRUE, 302);
                exit;
            }
            
            $CFG->set_item('language', $language);
        }
    }
}

// --------------------------------------------------------------------

/**
 * Fetch a single line of text from the language array
 * 
 * Note: CI returns nothing (by default) if line for given language is 
 * not found. Adding this function the default line language is given.
 * If there is no default line found either then an english line is
 * given if there is any else the line wrapped in brackets {}.
 * There is also a new feature "param" introdused. This way you can add 
 * stuff in the line return as well.
 * 
 * @access    public
 * @param    string    $line     the language line
 * @param    string
 * @param    string
 * @return    string
 */
function line($line = '', $param = '')
{
    if ($line == '') return "";
    
    if (isset($this->language[$line]))
    {
        $line = $this->language[$line];
        
        if ($param != '')
        {
            if (is_array($param))
            {
                $line = vsprintf($line, $param);
            }
            else
            {
                $line = sprintf($line, $param);
            }
        }
    }
    else
    {
        log_message('error', 'Language: unknown line [' . $line . '] in ' . $_SERVER['REQUEST_URI']);
        
        $line = "{" . $line . "}";
    }
    
    return $line;
}

// --------------------------------------------------------------------

/**
 * Localize URL
 * 
 * @access    public
 * @param    string    the URI string
 * @return    string
 */
function localize($uri = '')
{
    global $CFG;
    
    if ( ! is_array($uri))
    {
        $uri = explode('/', $uri);
    }
    
    if ( ! empty($uri[0]))
    {
        $languages = $CFG->item('languages');
        
        if (isset($languages[$uri[0]]))
        {
            $language = $CFG->item('language');
            
            if ($languages[$uri[0]] == $language)
            {
                array_shift($uri);
            }
            
            return implode('/', $uri);
        }
    }
    
    return implode('/', $uri);
}

}

// END MY_Language class

/* End of file MY_Language.php / / Location: ./system/application/libraries/MY_Language.php */

[/code]

[b]application/languages/dutch/urls_lang.php[/b]

[code]

$lang['url_forum'] = "prikbord"; $lang['url_general'] = "algemeen";

[/code]

[b]application/languages/english/urls_lang.php[/b]

[code]

$lang['url_forum'] = "forum"; $lang['url_general'] = "general";

[/code]

When you begin coding the line "url_forum" without url_ is the same as text "forum". But later in your project when the line is used everywhere you can still change the text by simply change it in the language-file.

[b]application/helpers/MY_url_helper.php[/b]

[code]

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

/**

  • I18n URL
  • Create a local URL based on your basepath. Segments can be passed via the
  • first parameter either as a string or an array. The only difference with
  • site_url() is that i18n_url() will try to get the segments from your
  • language url_lang.php files. If one or more segments do not exists in these
  • files, the segments which are not found the ones you entered are used.
  • Note: If you don't want to rename all site_url() functions to i18n_url(),
  • just rename the function i18n_url() below to site_url().
  • @access public
  • @param string
  • @return string */ function i18n_url($uri = '') { $CI =& get_instance(); return $CI->config->i18n_url($uri); }

/* End of file MY_url_helper.php / / Location: ./system/application/helpers/MY_url_helper.php */

[/code]

[b]application/helper/i18n_helper.php[/b]

[code]

function i18n ($item, $param = null) { $CI =& get_instance();

$line = $CI->lang->line($item);

if ($line == null)
{
    log_message('error', 'i18n: unknown item [' . $item . '] in ' . $_SERVER['REQUEST_URI']);
    
    $line = "{" . $item . "}";
}

if ($param == null)
{
    return $line;
}
else
{
    if (is_array($param)) return vsprintf($line, $param); else return sprintf($line, $param);
}

}

function i18n_url ($item, $param = null) { $CI =& get_instance();

if (is_array($item))
{
    $items = $item;
    
    $segments = array();
    
    foreach ($items as $item)
    {
        $line = $CI->lang->line('url_' . $item);
        
        $segments[] = ($line == null) ? $item : $line;
    }
    
    $line = implode('/', $segments);
}
else if (stripos($item, '/'))
{
    $items = explode('/', $item);
    
    foreach ($items as $item)
    {
        $line = $CI->lang->line('url_' . $item);
        
        $segments[] = ($line == null) ? $item : $line;
    }
    
    $line = implode('/', $segments);
}
else
{
    $line = $CI->lang->line('url_' . $item);
    
    if ($line == null)
    {
        $line = $item;
    }
}

$line = site_url($line);

if ($param == null)
{
    return $line;
}
else
{
    if (is_array($param)) return vsprintf($line, $param); else return sprintf($line, $param);
}

}

[/code]

i18n() is a replacement for lang() and does almost the same. Parameter is added to do the folowing:

[code]

// in language file

$lang['time_met_you'] = "This is the % time I met you";

// in view file

echo i18n('time_met_you', '3rd'); // produces: This is the 3rd time I met you

[/code]

i18n() is used for text (not urls) but you can if you would like to use it for urls also. If the line is not found it produces {line} to identify the text is missing. (In the future I would like to rebuild this to show always the default language if line is not found instead of the {line} text.

  1. i18n_url('forum')
  2. i18n_url('forum/general')
  3. i18n_url(array('forum','general'))

In dutch produces same as:

  1. site_url('prikbord')
  2. site_url('prikbord/algemeen')
  3. site_url('prikbord/algemeen')

In english produces same as:

  1. site_url('forum')
  2. site_url('forum/general')
  3. site_url('forum/general')

If a "segment" does not exist, the name used to call the segment is used.

....

In my case I have a database with three tables:

Table "url_languages" > columns: lang_id, lang_ci, lang_cc Table "url_segments" > columns: urlseg_id, urlsec_first (bool), urlsec_line, urlsec_text Table "url_languages" > columns: urllang_id, urlseg_id, lang_id, urllang_text

And I will generate the routes and language-files automaticaly. Later more on this...

Clone this wiki locally