Skip to content

CodeIgniter 2.1 internationalization i18n

opbokel edited this page Dec 12, 2013 · 30 revisions

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

Internationalization (i18n) for CodeIgniter 2.x is small modification to: http://maestric.com/doc/php/codeigniter_i18n to work with CodeIgniter 2.1.

Credits:

Original Author: Jérôme Jaglale. Original Post (and instructions): http://maestric.com/doc/php/codeigniter_i18n. Modifications by Yeb Reitsma Placeholders substitution in language strings: alvil (original wiki page: http://codeigniter.com/wiki/Placeholders_substitution_in_language_strings/)

What it does:

Use CodeIgniter's Language Class through the language in the URI: [pre]http://example.com/en/welcome http://example.com/es/welcome[/pre] with the possibility to add placeholders ("%s") inside your language strings, to make it more dinamic.

Installation and configuration:

1. Use pretty URLs (without index.php, not mandatory)

a) With Apache it's usually achieved with mod_rewrite through an .htaccess. (See CodeIgniter User Guide for URLs for more information about this)

b) In .application/config/config.php set to blank $config['index_page'] = ””

2. Create application/core/MY_Lang.php:

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

// Originaly CodeIgniter i18n library by Jérôme Jaglale
// http://maestric.com/en/doc/php/codeigniter_i18n
// modification by Yeb Reitsma

/*
in case you use it with the HMVC modular extension
uncomment this and remove the other lines
load the MX_Loader class */

//require APPPATH."third_party/MX/Lang.php";

//class MY_Lang extends MX_Lang {
 
class MY_Lang extends CI_Lang {


  /**************************************************
   configuration
  ***************************************************/
 
  // languages
  private $languages = array(
    'en' => 'english',
    'de' => 'german',
    'fr' => 'french',
    'nl' => 'dutch'
  );
 
  // special URIs (not localized)
  private $special = array (
    "admin"
  );
  
  // where to redirect if no language in URI
  private $uri;
  private $default_uri;
  private $lang_code;
 
  /**************************************************/
    
    
  function MY_Lang()
  {
    parent::__construct();
    
    global $CFG;
    global $URI;
    global $RTR;
    
    $this->uri = $URI->uri_string();
    $this->default_uri = $RTR->default_controller;
    
    $uri_segment = $this->get_uri_lang($this->uri);
    $this->lang_code = $uri_segment['lang'] ;
    
    $url_ok = false;
    if ((!empty($this->lang_code)) && (array_key_exists($this->lang_code, $this->languages)))
    {
        $language = $this->languages[$this->lang_code];
        $CFG->set_item('language', $language);
        $url_ok = true;
    }
      
    if ((!$url_ok) && (!$this->is_special($uri_segment['parts'][0]))) // special URI -> no redirect
    { 
      // set default language
      $CFG->set_item('language', $this->languages[$this->default_lang()]);
      
      $uri = (!empty($this->uri)) ? $this->uri: $this->default_uri;
      //OPB - modification to use i18n also without changing the .htaccess (without pretty url) 
      $index_url = empty($CFG->config['index_page']) ? '' : $CFG->config['index_page']."/";
      $new_url = $CFG->config['base_url'].$index_url.$this->default_lang().'/'.$uri;
      
      header("Location: " . $new_url, TRUE, 302);
      exit;
    }
  }

    
    
  // get current language
  // ex: return 'en' if language in CI config is 'english' 
  function lang()
  {
    global $CFG;        
    $language = $CFG->item('language');
    
    $lang = array_search($language, $this->languages);
    if ($lang)
    {
      return $lang;
    }
    
    return NULL;    // this should not happen
  }
    
    
  function is_special($lang_code)
  {
    if ((!empty($lang_code)) && (in_array($lang_code, $this->special)))
      return TRUE;
    else
      return FALSE;
  }
 
   
  function switch_uri($lang)
  {
    if ((!empty($this->uri)) && (array_key_exists($lang, $this->languages)))
    {

      if ($uri_segment = $this->get_uri_lang($this->uri))
      {
        $uri_segment['parts'][0] = $lang;
        $uri = implode('/',$uri_segment['parts']);
      }
      else
      {
        $uri = $lang.'/'.$this->uri;
      }
    }

    return $uri;
  }
    
  //check if the language exists
  //when true returns an array with lang abbreviation + rest
  function get_uri_lang($uri = '')
  {
    if (!empty($uri))
    {
      $uri = ($uri[0] == '/') ? substr($uri, 1): $uri;
      
      $uri_expl = explode('/', $uri, 2);
      $uri_segment['lang'] = NULL;
      $uri_segment['parts'] = $uri_expl;  
      
      if (array_key_exists($uri_expl[0], $this->languages))
      {
        $uri_segment['lang'] = $uri_expl[0];
      }
      return $uri_segment;
    }
    else
      return FALSE;
  }

    
  // default language: first element of $this->languages
  function default_lang()
  {
    $browser_lang = !empty($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? strtok(strip_tags($_SERVER['HTTP_ACCEPT_LANGUAGE']), ',') : '';
    $browser_lang = substr($browser_lang, 0,2);
    if(array_key_exists($browser_lang, $this->languages))
        return $browser_lang;
    else{
        reset($this->languages);
        return key($this->languages);
    }
  }
    
    
  // add language segment to $uri (if appropriate)
  function localized($uri)
  {
    if (!empty($uri))
    {
      $uri_segment = $this->get_uri_lang($uri);
      if (!$uri_segment['lang'])
      {

        if ((!$this->is_special($uri_segment['parts'][0])) && (!preg_match('/(.+)\.[a-zA-Z0-9]{2,4}$/', $uri)))
        {
          $uri = $this->lang() . '/' . $uri;
        }
      }
    }
    return $uri;
  }


/**
     * Same behavior as the parent method, but it can load the first defined 
     * lang configuration to fill other languages gaps. This is very useful
     * because you don't have to update all your lang files during development
     * each time you update a text. If a constant is missing it will load
     * it in the first language configured in the array $languages. (OPB)
     * 
     * 
     * @param boolean $load_first_lang false to keep the old behavior. Please
     * modify the default value to true to use this feature without having to 
     * modify your code 
     */
    function load($langfile = '', $idiom = '', $return = FALSE, $add_suffix = TRUE, $alt_path = '', $load_first_lang = false) {
        if ($load_first_lang) {
            reset($this->languages);
            $firstKey = key($this->languages);
            $firstValue = $this->languages[$firstKey];

            if ($this->lang_code != $firstKey) {
                $addedLang = parent::load($langfile, $firstValue, $return, $add_suffix, $alt_path);
                if ($addedLang) {
                    if ($add_suffix) {
                        $langfileToRemove = str_replace('.php', '', $langfile);
                        $langfileToRemove = str_replace('_lang.', '', $langfileToRemove) . '_lang';
                        $langfileToRemove .= '.php';
                    }
                    $this->is_loaded = array_diff($this->is_loaded, array($langfileToRemove));
                }
            }
        }
        return parent::load($langfile, $idiom, $return, $add_suffix, $alt_path);
    }

} 

// END MY_Lang Class

/* End of file MY_Lang.php */
/* Location: ./application/core/MY_Lang.php */

Add your languages to the "$language" array. The first language will be the default language.

Special URIs:

A special URI is not prefixed by a language. The root URI (/) is by default a special URI.

You might need other special URIs, like for an "admin" section, which would be in just one language.

Add "admin" to the "$special" array.

Now, links to "admin" won't be prefixed by the current language.

3. Create application/core/MY_Config.php:

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

// Originaly CodeIgniter i18n library by Jérôme Jaglale
// http://maestric.com/en/doc/php/codeigniter_i18n
//modification by Yeb Reitsma

/* 
in case you use it with the HMVC modular extention
uncomment this and remove the other lines
load the MX_Loader class */
//require APPPATH."third_party/MX/Config.php";

//class MY_Config extends MX_Config {
 

class MY_Config extends CI_Config {

    function site_url($uri = '')
    {    
        if (is_array($uri))
        {
            $uri = implode('/', $uri);
        }
        
        if (function_exists('get_instance'))        
        {
            $CI =& get_instance();
            $uri = $CI->lang->localized($uri);            
        }

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

// END MY_Config Class

/* End of file MY_Config.php */
/* Location: ./application/core/MY_Config.php */ 

4. Replace original Language helper

In case you are going to use placeholders (add dynamic variables to your language strings), you'll need to modify the language helper in order to substitute each placeholder ("%s") with its correspondent value.

Create application/helpers/MY_language_helper.php:

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

 function lang($line, $id = '')
 {
  $CI =& get_instance();
  $line = $CI->lang->line($line);
  
  $args = func_get_args();
  
  if(is_array($args)) array_shift($args);
  
  if(is_array($args) && count($args))
  {
      foreach($args as $arg)
      {
          $line = str_replace_first('%s', $arg, $line);
      }
  }

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

 function str_replace_first($search_for, $replace_with, $in)
 {
     $pos = strpos($in, $search_for);
     if($pos === false)
     {
         return $in;
     }
     else
     {
         return substr($in, 0, $pos) . $replace_with . substr($in, $pos + strlen($search_for), strlen($in));
     }
 }

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

5. Add routes

Add these lines to ./application/config/routes.php:

// URI like '/en/about' -> use controller 'about'
$route['^(en|de|fr|nl)/(.+)$'] = "$2";

// '/en', '/de', '/fr' and '/nl' URIs -> use default controller
$route['^(en|de|fr|nl)$'] = $route['default_controller'];

where en|de|fr|nl are the same languages as in your "MY_Lang"'s "$languages" array.

6. Creating languages files

As explained in CodeIgniter's Language Class user guide. Read this carefully to learn how to load, store and write language files.

You can add placeholders inside your strings. For instance, if you like to add the "username" to a string, you can by writing this lang string:

$lang['welcome'] = "Welcome, %s.";

See 8. Create View for further reference.

7. Create Controller

<?php
class About extends Controller {
 
 function index()
 {
  // you might want to just autoload these two helpers
  $this->load->helper('language');
  $this->load->helper('url');
 
  // load language file
  $this->lang->load('about');
 
 
  $this->load->view('about');
 }
}
 
/* End of file about.php */
/* Location: ./application/controllers/about.php */

8. Create View

To fetch a lang line or add an anchor:

<p><?=lang('about.gender')?></p>
 
<p><?=anchor('music','Shania Twain')?></p>

Read CodeIgniter's Language Helper user guide to learn more on how to fetch language lines.

In case you use placeholders inside your language strings, you can use them like this:

<p><?php $username = "John Doe";
echo lang('welcome', $username)?></p>

This should appear like:

Welcome, John Doe.

(Using the language string of step 6)

That's all. Try it, it should work!

Notes

  • You might need to translate some of CodeIgniter's language files in system/language. Example: if you're using the “Form Validation” library for French pages, translate system/language/form_validation_lang.php to system/application/language/french/form_validation_lang.php.

  • Links to internal pages are prefixed by the current language, but links to files are not:

site_url('about/my_work');
// http://mywebsite.com/en/about/my_work

site_url('css/styles.css');
// http://mywebsite.com/css/styles.css
  • Get the current language:
$this->lang->lang();
// en
  • Switch to another language:
anchor($this->lang->switch_uri('fr'),'Display current page in French');

Further information and useful modifications

Disclaimer

It works on a scratch installation of CodeIgniter 2.0.3, on MAPM 2.0.3 (localhost) in Mac OS X 10.7.2 (2011-10-23)

Clone this wiki locally