Internationalization Of Active Records

Internationalization of Active Records

Active Records may be internationalized by prefixing column names with a locale, then using certain AkActiveRecord functions to process them. This section will provide a detailed explanation with an example. The example will take a field from a non-internationalized table and write it's value in an internationalized table in a project called esimerkki (Finnish for example).

Define the locales to be recognized

The first thing we should do is to define AK_ACTIVE_RECORD_DEFAULT_LOCALES in config/config.php. Originally, mine looked like this:

define('AK_ACTIVE_RECORD_DEFAULT_LOCALES', 'en,en_us');

I changed it to look like this:

define('AK_ACTIVE_RECORD_DEFAULT_LOCALES', 'en,fi,sv');

I said “should” because, if AK_ACTIVE_RECORD_DEFAULT_LOCALES isn't defined, AkActiveRecord::getAvailableLocales() will try to get values from Ak::langs().

Create the tables

This is done by creating and running a migration. The code for doing so is shown in the linked section. Running the migration will give you two tables, mono_langs and multi_langs.

Create the models

We need to create models for the two tables: Change the directory (if necessary) to your project's root.

./script/generate model MonoLang
./script/generate model MultiLang

Create the controller code

We are going to create app/controllers/refmt_controller.php. To access it, though, we need to change the first line in config/routes.php to reference it:

$Map->connect('/:controller/:action/:id', array('controller' => 'refmt', 'action' => 'index'));

The first things we'll do are to reference the models, write the index function and, then write the code to populate the non-internationalized mono_langs table

<?php
 
class RefmtController extends ApplicationController
{
    var $models = 'mono_lang, multi_lang';
 
    function index()
    {
        if($this->MonoLang->count() == 0) {
          $this->populate_mono_lang();
        }
        $this->display_func_results();
        $this->populate_multi_lang();
        exit; // This is to keep from trying to access a view
    }

We're not commenting on how to populate this table because we assume that you know how to do that. We're showing the code so that you know what the input table contains.

    function populate_mono_lang()
    {
        $mono = new $this->MonoLang(array('lang' => 'en','name' => 'municipality'));
        $mono->save();
        $mono = new $this->MonoLang(array('lang' => 'fi','name' => 'kunta'));
        $mono->save();
        $mono = new $this->MonoLang(array('lang' => 'sv','name' => 'kommun'));
        $mono->save();
    }

To understand what Active Record thinks we have, we'll display the results of locale related “get” functions as shown by Page Source.

  function display_func_results()
  {
    $result = array();
    $result['available_locales']          = $this->MultiLang->getAvailableLocales();
    $result['internationalized_columns']  = $this->MultiLang->getInternationalizedColumns();
    $result['locales_for_name_attribute'] = $this->MultiLang->getAttributeLocales('name');
    $result['current_locale']             = $this->MultiLang->getCurrentLocale();
    print_r($result);
  }
Array
(
    [available_locales] => Array
        (
            [0] => en
            [1] => fi
            [2] => sv
        )

    [internationalized_columns] => Array
        (
            [name] => Array
                (
                    [0] => en
                    [1] => fi
                    [2] => sv
                )
        )

The multi_langs table has the fields id, en_name, fi_name, sv_name, updated_at and created_at. What we see here is that en_name, fi_name and sv_name are interpreted as internationalized columns. The columns id, updated_at and created_at are not recognized as internationalized columns. Notice that Active Record stores the related internationalized columns as an array.

    [locales_for_name_attribute] => Array
        (
            [en] => 
            [fi] => 
            [sv] => 
        )

    [current_locale] => en
)

This function reads the non-internationalized table and writes a record to an internationalized table.

  function populate_multi_lang()
  {
    $mono_langs = $this->MonoLang->find('all');   // $mono_langs is an array of objects
    $multi = $this->MultiLang;                    // $multi is an instance of the internationalized table
    foreach($mono_langs as $mono_lang) {
      $multi->setAttributeByLocale('name',  $mono_lang->name,  $mono_lang->lang);
    }
    $multi->save();
    // The following code shows that the data was written to the table.
    // The real reason for including it is to demonstrate the getAttributebyLocale function.
    $result = array();
    $result['name_by_locale-en'] = $multi->getAttributeByLocale('name','en');
    $result['name_by_locale-fi'] = $multi->getAttributeByLocale('name','fi');
    $result['name_by_locale-sv'] = $multi->getAttributeByLocale('name','sv');
    print_r($result);
  }
}
 
?>

Following is the code to do this conversion in one block. Comments and code not needed to do the job have been removed.

<?php
 
class RefmtController extends ApplicationController
{
  var $models = 'mono_lang, multi_lang';
 
  function index()
  {
    if($this->MonoLang->count() == 0) {
      $this->populate_mono_lang();
    }
    $this->populate_multi_lang();
    exit;
  }
 
  function populate_mono_lang()
  {
    $mono = new $this->MonoLang(array('lang' => 'en','name' => 'municipality'));
    $mono->save();
    $mono = new $this->MonoLang(array('lang' => 'fi','name' => 'kunta'));
    $mono->save();
    $mono = new $this->MonoLang(array('lang' => 'sv','name' => 'kommun'));
    $mono->save();
  }
 
  function populate_multi_lang()
  {
    $mono_langs = $this->MonoLang->find('all');
    $multi = $this->MultiLang;
    foreach($mono_langs as $mono_lang) {
      $multi->setAttributeByLocale('name',  $mono_lang->name,  $mono_lang->lang);
    }
    $multi->save();
  }
}
 
?>

Changing the current locale

The locale is established by appending the locale to the project name in the URL. You might open a non-internationalized application with

  http://<host>/esimerkki

The internationalized application might be opened with

  http://<host>/esimerkki/en
  http://<host>/esimerkki/fi or
  http://<host>/esimerkki/sv

You may also open the internationalized application as a non-internationalized one and set the language within the program with this:

    Ak::lang('fi');
    $_SESSION['lang'] = 'fi';

where 'fi' is the new current locale. The second line is apparently necessary to get text strings to reflect the new language. (I found this on the forum.) Of course, if you do this, you will have to make all locale changes programmatically; you won't be able to access different languages by appending the locale onto the URL.

In this example, this code would be put right at the beginning of the index() function.

An ActiveRecord locale-related function not discussed here is

setAttributeLocales($attribute [, $values = array()]);

I haven't tried to use it, but I suppose that it might be used in an installer to create i18n columns.

Go internationalize yourself.

 
activerecord_i18n.txt · Last modified: 2008/07/14 18:44 by 82.103.221.224
 

The Akelos Framework was created by Bermi Ferrer and other contributors.
Potions of the code and documentation have been ported from Ruby on Rails.

The Akelos Framework is released under the LGPL license.

"Akelos", "Akelos Framework", and the Akelos logo are trademarks of Bermi Labs All rights reserved.

Wiki driven by DokuWiki