i18n Multi language Library Helper

EddieNejadi edited this page Apr 22, 2013 · 59 revisions
Clone this wiki locally

Category:Core Category:Core::Community Category:Core::Language Category:Libraries Category:Libraries::Language Category:Libraries::Internationalization

These methods should work from CI 1.7.2 and higher (maybe also lower)

Version 0.9 (optimization tips are welcome, CI not in root is not tested)

This library / helper uses the default way CI stores language-files.

This tutorial is about language things:

1) Using multi-language websites

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/...)

2) Using multi-language segments

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.)

3) Using multi-language domains

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))

4) Using multi-language text with parameters



// 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

// OR use i18n() to replace lang(), they are the same, just added for name compatibility

Used ideas from:

Internationalization (i18n) library by Jérôme Jaglale URI Language Identifier by Wiredesignz

Others: Internationalization Views i18n LangBuilder Placeholders substitution in language strings MY Language

application/config/routes.php



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

// Urls start

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

// Urls end

The part "Urls" is only for the controllers (or modules).

application/config/language.php

The language setting $config['language'] in config.php is not used, because it is also in this file and overrides the one in config.php



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

// 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})
// of course if you change, make your change in this config file also

// 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');

// debug if line is not found as [line]

$config['language_debug'] = TRUE;

/* End of file language.php */
/* Location: ./system/application/config/language.php */

application/libraries/MY_Config.php



<?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");

        $this->load('language');
    }

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

    /**
     * I18n URL
     * 
     * Note: 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. A
     * second param is added to change the url language.
     * 
     * @access    public
     * @param    string    the URI string
     * @param    string
     * @return    string
     */
    function i18n_url($uri = '', $language = '')
    {
        global $LANG;

        $uri = $LANG->i18n($uri, $language);

        return parent::site_url($uri);
    }
}

// END MY_Config Class

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

application/libraries/MY_Language.php



<?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 $CFG;
        global $RTR;

        $languages = $CFG->item('languages');

        $language_url = '';
        $language_default = array_search($CFG->item('language'), $languages);
        $language_current = $language_default;

        // check if language code exists
        // if yes and scaffolding remove
        // if yes and is default remove
        // if yes change language
        if (isset($RTR->uri->segments[1]))
        {
            $segment1 = $RTR->uri->segments[1];

            // first segment in languages
            if (isset($languages[$segment1]))
            {
                $language_url = $segment1;

                if ($RTR->scaffolding_request === TRUE)
                {
                    $RTR->uri->segments = $RTR->uri->rsegments;
                }
                // if url language is default remove first segment
                else if ($language_url == $language_default)
                {
                    $uri = $RTR->uri->segments;

                    array_shift($uri);

                    $uri = implode('/', $uri);

                    header("Location: /" . $uri, TRUE, 302);
                    exit;
                }

                $CFG->set_item('language', $languages[$language_url]);

                $language_current = $language_url;
            }
        }

        $CFG->set_item('language_url', $language_url);
        $CFG->set_item('language_default', $language_default);
        $CFG->set_item('language_current', $language_current);
    }

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

    /**
     * Load a language file
     * 
     * Note: Same as original but if a language file needs to be loaded and
     * returned and the files is already loaded nothing gets returned. This 
     * is fixed with remove from loaded lsit first. This way you can load 
     * (other) language files while one stays active.
     * If you use HMVC and want this also in the modules the Controller.php
     * from around line 162 needs to be edited. See HMVC differences:
     * 
     * language($langfile, $lang = '')
     * language($langfile, $lang = '', $return = FALSE)
     * 
     * if (in_array($langfile.'_lang'...
     * if ($return == FALSE && in_array($langfile.'_lang'...
     * 
     * parent::language($langfile, $lang);
     * parent::language($langfile, $lang, $return);
     * 
     * CI::$APP->lang->language...
     * if ($return == TRUE) return $lang; CI::$APP->lang->language...
     * 
     * @access    public
     * @param    mixed    the name of the language file to be loaded. Can be an array
     * @param    string    the language (english, etc.)
     * @return    mixed
     */
    function load($langfile = '', $idiom = '', $return = FALSE)
    {
        if ($return == TRUE)
        {
            $langfile_name = str_replace(EXT, '', str_replace('_lang.', '', $langfile)).'_lang'.EXT;

            if ($key = array_search($langfile_name, $this->is_loaded, TRUE))
            {
                unset($this->is_loaded[$key]);
            }
        }

        return parent::load($langfile, $idiom, $return);
    }


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

    /**
     * Fetch a single line of text from the language array
     * 
     * Note: CI returns nothing if line for given language is not found. 
     * This function does exactly the same as the original one but two 
     * params are added. First param to add sub-segments to the line.
     * Second to override the debug_language set in config. With debug
     * TRUE the line returns [line] instead of nothing when no match in 
     * the language files is found. Debug is FALSE for i18n_url().
     * 
     * @access    public
     * @param    string    $line     the language line
     * @param    string
     * @param    string
     * @param    bool    override config debug setting
     * @return    string
     */
    function line($line = '', $param = '', $debug = NULL)
    {
        if ($line == '') return FALSE;

        global $CFG;

        $language_debug = ($CFG->item('language_debug') == NULL) ? FALSE : $CFG->item('language_debug');

        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 if (($language_debug == TRUE && $debug !== FALSE) || $debug == TRUE)
        {
            log_message('error', 'Language: unknown line [' . $line . '] in ' . $_SERVER['REQUEST_URI']);

            $line = "[" . $line . "]";
        }
        else
        {
            return FALSE;
        }

        return $line;
    }

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

    /**
     * I18n, change, add or remove language code in front of the url
     * depanding on the second param "language" and the current language
     * Also change the uri segments to the right language if exists
     * 
     * @access    public
     * @param    string    the URI string
     * @return    string
     */
    function i18n($uri = '', $language = '')
    {
        if ($uri == '') return FALSE;

        if ( ! is_array($uri))
        {
            $uri = explode('/', trim($uri, '/'));
        }

        global $CFG;

        $languages = $CFG->item('languages');

        $language_url = $CFG->item('language_url');
        $language_default = $CFG->item('language_default');
        $language_current = $CFG->item('language_current');

        if ($language != '')
        {
            // language not in languages (as key)
            if ( ! isset($languages[$language]))
            {
                // language in languages (as value)
                if ($key = array_search($language, $languages))
                {
                    $language = $key;
                }
                // language not in languages (at all)
                else
                {
                    $language = '';
                }
            }
        }

        if (isset($uri[0]))
        {
            // first segment in languages
            if (isset($languages[$uri[0]]))
            {
                if ($language == '')
                {
                    $language = $uri[0];
                }

                array_shift($uri);
            }
        }

        // only if language is set and is not same as current
        if ($language != '' && $language != $language_current)
        {
            $language_new = $this->load('urls', $languages[$language], TRUE);

            foreach ($uri as $key => $segment)
            {
                // get line form current language
                if ($line = array_search($segment, $this->language))
                {
                    $segment = substr($line, 4);
                }

                // get value form new line language
                if (isset($language_new['url_' . $segment]))
                {
                    $uri[$key] = $language_new['url_' . $segment];
                }
            }
        }
        else
        {
            foreach ($uri as $key => $segment)
            {
                $line = $this->line('url_' . $segment, '', FALSE);

                $uri[$key] = ($line == NULL) ? $segment : $line;
            }
        }

        if ($language != '' && $language != $language_default)
        {
            array_unshift($uri, $language);
        }
        else if ($language == '' && $language_current != $language_default)
        {
            array_unshift($uri, $language_current);
        }

        return implode('/', $uri);
    }
}

// END MY_Language class

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

application/helper/MY_language_helper.php

Added "param" and "language" to lang() and the new function i18n()



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

/**
 * Lang
 *
 * Fetches a language variable and optionally outputs a form label
 * 
 * Note: Difference with original is the added $param and language
 * 
 * @access    public
 * @param    string    the language line
 * @param    string    the id of the form element
 * @param    string
 * @return    string
 */    
function lang($line, $id = '', $param = '', $language = '')
{
    $CI =& get_instance();
    $line = $CI->lang->line($line, $param, '', $language);

    if ($id != '')
    {
        $line = '<label for="'.$id.'">'.$line."</label>";
    }

    return $line;
}

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

/**
 * I18n
 *
 * Fetches a language variable and optionally outputs a form label
 * 
 * Note: Exact same as new lang(), just added for the name
 * 
 * @access    public
 * @param    string    the language line
 * @param    string    the id of the form element
 * @param    string
 * @return    string
 */    
function i18n($line, $id = '', $param = '', $language = '')
{
    return lang($line, $id, $param, $language);
}

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

Examples:



// in the language files dutch and english

$lang['time_met_you'] = "Dit is de %s keer dat ik jouw heb ontmoet";
$lang['time_met_you'] = "This is the %s time I met you";

// in view file
// lang() or i18n() are the same functions
// param can also be an array to have more params

echo lang('time_met_you', '', '3e');
echo lang('time_met_you', '', '3rd');

// prints if dutch: Dit is de 3e keer dat ik jouw heb ontmoet
// prints if english: This is the 3rd time I met you

echo lang('time_met_you', '', '3e', 'en');
echo lang('time_met_you', '', '3rd', 'nl');

// prints if english: Dit is de 3e keer dat ik jouw heb ontmoet
// prints if dutch: This is the 3rd time I met you

application/helpers/MY_url_helper.php

Added "language" to current_url() and new function i18n_url()



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

/**
 * I18n URL
 * 
 * Note: Almost the same as site_url() but each segment is first checked 
 * if it exists in the language files with an "url_" prefix, if not then 
 * the given segment is used. A second parameter is added to change the 
 * language in the url.
 * 
 * @access    public
 * @param    string
 * @param    string
 * @return    string
 */
function i18n_url($uri = '', $language = '')
{
    $CI =& get_instance();
    return $CI->config->i18n_url($uri, $language);
}

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

/**
 * Current URL
 * 
 * Note: Same as original but param added to change url to specified language
 * 
 * @access    public
 * @param    string
 * @return    string
 */
function current_url($language = '')
{
    $CI =& get_instance();

    if ($language == '')
    {
        return $CI->config->site_url($CI->uri->uri_string());
    }
    else
    {
        return $CI->config->i18n_url($CI->uri->uri_string(), $language);
    }
}

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

Examples:



// in the language files

$lang['url_test'] = "test123";

// in view file

echo i18n_url('test');
echo site_url(i18n('url_test'));
echo site_url(i18n_url('test'));

// prints: test123

// REMEMBER that functions like anchor, anchor_popup, form_open etc.
// use site_url() and therefore if you want to translate the url segments
// you have to use these functions like this:

echo anchor(i18n_url('test'), 'Link');
echo anchor(i18n('url_test'), 'Link');

// prints: <a href="test123">Link</a>

echo anchor(i18n_url('test/test/test'), 'Link');
echo anchor(i18n_url(array('test','test','test')), 'Link');
echo anchor(i18n('url_test').'/'.i18n('url_test').'/'.i18n('url_test'), 'Link');

// prints: <a href="test123/test123/test123">Link</a>

echo anchor(i18n_url('test/test/test'), 'Link');
echo anchor(i18n_url(array('test','test','test')), 'Link');
echo anchor(i18n('url_test').'/'.i18n('url_test').'/'.i18n('url_test'), 'Link');

// prints: <a href="test123/test123/test123">Link</a>