Internationalization

Internationalization (I18N) refers to the process of designing a software application so that it can be adapted to various languages and regions without engineering changes. For Web applications, this is of particular importance because the potential users may be worldwide.

Yii provides support for I18N in several aspects.

  • It provides the locale data for each possible language and variant.
  • It provides message and file translation service.
  • It provides locale-dependent date and time formatting.
  • It provides locale-dependent number formatting.

In the following subsections, we will elaborate each of the above aspects.

1. Locale and Language

Locale is a set of parameters that defines the user's language, country and any special variant preferences that the user wants to see in their user interface. It is usually identified by an ID consisting of a language ID and a region ID. For example, the ID en_US stands for the locale of English and United States. For consistency, all locale IDs in Yii are canonicalized to the format of LanguageID or LanguageID_RegionID in lower case (e.g. en, en_us).

Locale data is represented as a CLocale instance. It provides locale-dependent information, including currency symbols, number symbols, currency formats, number formats, date and time formats, and date-related names. Since the language information is already implied in the locale ID, it is not provided by CLocale. For the same reason, we often interchangeably use the term locale and language.

Given a locale ID, one can get the corresponding CLocale instance by CLocale::getInstance($localeID) or CApplication::getLocale($localeID).

Info: Yii comes with locale data for nearly every language and region. The data is obtained from Common Locale Data Repository (CLDR). For each locale, only a subset of the CLDR data is provided as the original data contains a lot of rarely used information. Users can also supply their own customized locale data. To do so, configure the CApplication::localeDataPath property with the directory that contains the customized locale data. Please refer to the locale data files under framework/i18n/data in order to create customized locale data files.

For a Yii application, we differentiate its target language from source language. The target language is the language (locale) of the users that the application is targeted at, while the source language refers to the language (locale) that the application source files are written in. Internationalization occurs only when the two languages are different.

Tip: It's better to leave English as a source language since it will be easier to find people translating from English to any other language.

One can configure target language in the application configuration, or change it dynamically before any internationalization occurs.

Tip: Sometimes, we may want to set the target language as the language preferred by a user (specified in the user's browser preferences). To do so, we can retrieve the user preferred language ID using CHttpRequest::preferredLanguage.

2. Translation

The most needed I18N feature is perhaps translation, including message translation and view translation. The former translates a text message to the desired language, while the latter translates a whole file to the desired language.

A translation request consists of the object to be translated, the source language that the object is in, and the target language that the object needs to be translated to. In Yii, the source language defaults to the application source language while the target language defaults to the application language. If the source and target languages are the same, translation will not occur.

Message Translation

Message translation is done by calling Yii::t(). The method translates the given message from source language to target language.

When translating a message, its category has to be specified since a message may be translated differently under different categories (contexts). The category yii is reserved for messages used by the Yii framework core code.

Messages can contain parameter placeholders which will be replaced with the actual parameter values when calling Yii::t(). For example, the following message translation request would replace the {alias} placeholder in the original message with the actual alias value.

Yii::t('app', 'Path alias "{alias}" is redefined.',
    array('{alias}'=>$alias))

Note: Messages to be translated must be constant strings. They should not contain variables that would change message content (e.g. "Invalid {$message} content."). Use parameter placeholders if a message needs to vary according to some parameters.

Translated messages are stored in a repository called message source. A message source is represented as an instance of CMessageSource or its child class. When Yii::t() is invoked, it will look for the message in the message source and return its translated version if it is found.

Yii comes with the following types of message sources. You may also extend CMessageSource to create your own message source type.

  • CPhpMessageSource: the message translations are stored as key-value pairs in a PHP array. The original message is the key and the translated message is the value. Each array represents the translations for a particular category of messages and is stored in a separate PHP script file whose name is the category name. The PHP translation files for the same language are stored under the same directory named as the locale ID. And all these directories are located under the directory specified by basePath.

  • CGettextMessageSource: the message translations are stored as GNU Gettext files.

  • CDbMessageSource: the message translations are stored in database tables. For more details, see the API documentation for CDbMessageSource.

A message source is loaded as an application component. Yii pre-declares an application component named messages to store messages that are used in user application. By default, the type of this message source is CPhpMessageSource and the base path for storing the PHP translation files is protected/messages.

In summary, in order to use message translation, the following steps are needed:

  1. Call Yii::t() at appropriate places;

  2. Create PHP translation files as protected/messages/LocaleID/CategoryName.php. Each file simply returns an array of message translations. Note, this assumes you are using the default CPhpMessageSource to store the translated messages.

  3. Configure CApplication::sourceLanguage and CApplication::language.

Tip: The yiic tool in Yii can be used to manage message translations when CPhpMessageSource is used as the message source. Its message command can automatically extract messages to be translated from selected source files and merge them with existing translations if necessary. For more details of using the message command, please run yiic help message.

When using CPhpMessageSource to manage message source, messages for an extension class (e.g. a widget, a module) can be specially managed and used. In particular, if a message belongs to an extension whose class name is Xyz, then the message category can be specified in the format of Xyz.categoryName. The corresponding message file will be assumed to be BasePath/messages/LanguageID/categoryName.php, where BasePath refers to the directory that contains the extension class file. And when using Yii::t() to translate an extension message, the following format should be used, instead:

Yii::t('Xyz.categoryName', 'message to be translated')

Yii supports choice format, which is also known as plural forms. Choice format refers to choosing a translation according to a given number value. For example, in English, the word 'book' may either take a singular form or a plural form depending on the number of books, while in other languages, the word may not use different forms (such as in Chinese) or may use more complex plural form rules (such as in Russian). Choice format solves this problem in a simple yet effective way.

To use choice format, a translated message must consist of a sequence of expression-message pairs separated by |, as shown below:

'expr1#message1|expr2#message2|expr3#message3'

where exprN refers to a valid PHP expression which evaluates to a boolean value indicating whether the corresponding message should be returned. Only the message corresponding to the first expression that evaluates to true will be returned. An expression can contain a special variable named n (note, it is not $n) which will take the number value passed as the first message parameter. For example, assuming a translated message is:

'n==1#one book|n>1#many books'

and we are passing a number value 2 in the message parameter array when calling Yii::t(), we would obtain many books as the final translated message:

Yii::t('app', 'n==1#one book|n>1#many books', array(1));
//or since 1.1.6
Yii::t('app', 'n==1#one book|n>1#many books', 1);

As a shortcut notation, if an expression is a number, it will be treated as n==Number. Therefore, the above translated message can be also be written as:

'1#one book|n>1#many books'

Plural forms format

Since version 1.1.6 CLDR-based plural choice format can be used with a simpler syntax. It is handy for languages with complex plural form rules.

The rule for English plural forms above can be written in the following way:

Yii::t('test', 'cucumber|cucumbers', 1);
Yii::t('test', 'cucumber|cucumbers', 2);
Yii::t('test', 'cucumber|cucumbers', 0);

The code above will give you:

cucumber
cucumbers
cucumbers

If you want to include number you can use the following code.

echo Yii::t('test', '{n} cucumber|{n} cucumbers', 1);

Here {n} is a special placeholder holding number passed. It will print 1 cucumber.

You can pass additional parameters:

Yii::t('test', '{username} has a cucumber|{username} has {n} cucumbers',
array(5, '{username}' => 'samdark'));

and even replace a number parameter with something else:

function convertNumber($number)
{
    // convert number to word
    return $number;
}
 
Yii::t('test', '{n} cucumber|{n} cucumbers',
array(5, '{n}' => convertNumber(5)));

The number of plural expressions will vary by language. For example:

Yii::t('app', '{n} cucumber|{n} cucumbers', 62);
Yii::t('app', '{n} cucumber|{n} cucumbers', 1.5);
Yii::t('app', '{n} cucumber|{n} cucumbers', 1);
Yii::t('app', '{n} cucumber|{n} cucumbers', 7);

when translated into Russian will have 4 instead of 2 message expressions:

'{n} cucumber|{n} cucumbers' => '{n} огурец|{n} огурца|{n} огурцов|{n} огурца',

which will produce:

62 огурца
1.5 огурца
1 огурец
7 огурцов

Info: to learn about how many expressions you should supply and in which order they should be, please refer to CLDR Language Plural Rules page.

File Translation

File translation is accomplished by calling CApplication::findLocalizedFile(). Given the path of a file to be translated, the method will look for a file with the same name under the LocaleID subdirectory. If found, the file path will be returned; otherwise, the original file path will be returned.

File translation is mainly used when rendering a view. When calling one of the render methods in a controller or widget, the view files will be translated automatically. For example, if the target language is zh_cn while the source language is en_us, rendering a view named edit would resulting in searching for the view file protected/views/ControllerID/zh_cn/edit.php. If the file is found, this translated version will be used for rendering; otherwise, the file protected/views/ControllerID/edit.php will be rendered instead.

File translation may also be used for other purposes, for example, to display a translated image or load a locale-dependent data file.

3. Date and Time Formatting

Date and time are often in different formats in different countries or regions. The task of date and time formatting is thus to generate a date or time string that fits the specified locale. Yii provides CDateFormatter for this purpose.

Each CDateFormatter instance is associated with a target locale. To get the formatter associated with the target locale of the whole application, we can simply access the dateFormatter property of the application.

The CDateFormatter class mainly provides two methods to format a UNIX timestamp.

  • format: this method formats the given UNIX timestamp into a string according to a customized date-time pattern (e.g. $dateFormatter->format('yyyy-MM-dd',$timestamp)).

  • formatDateTime: this method formats the given UNIX timestamp into a string according to a pattern predefined in the target locale data (e.g. short format of date, long format of time).

4. Number Formatting

Like data and time, numbers may also need to be formatted differently in different countries or regions. Number formatting includes decimal formatting, currency formatting and percentage formatting. Yii provides CNumberFormatter for these tasks.

To get the number formatter associated with the target locale of the whole application, we can access the numberFormatter property of the application.

The following methods are provided by CNumberFormatter to format an integer or double value.

  • format: this method formats the given number into a string according to a customized pattern (e.g. $numberFormatter->format('#,##0.00',$number)).

  • formatDecimal: this method formats the given number using the decimal pattern predefined in the target locale data.

  • formatCurrency: this method formats the given number and currency code using the currency pattern predefined in the target locale data.

  • formatPercentage: this method formats the given number using the percentage pattern predefined in the target locale data.

$Id$

Total 9 comments

#16756 report it
marcovtwout at 2014/03/25 09:59am
CLocalizedFormatter

Since Yii 1.1.14, a CLocalizedFormatter is available.

To replace the application component 'format', you can put this in your application 'components' config:

'format' => array('class' => 'CLocalizedFormatter'),

The application component 'format' is also used when formatting data in CGridView and CDetailView, for example. Replacing the default formatter is a lot more convenient, instead of manually calling dateFormatter() or numberFormatter() everywhere.

#15333 report it
Daniel Qin at 2013/10/29 10:17pm
standard way to use translation in module

in the module init() function:

Yii::app()->setComponents(array(
            'messages' => array(
                'class'=>'CPhpMessageSource',
                'basePath'=>'protected/modules/www/messages',
            ),
        ));

then you just use Yii::t('category', 'hello world');

enjoy.

#14231 report it
wadim at 2013/07/29 04:12am
Passing variables to the translation file

in your code:

echo Yii::t('task', 'keyValue', array('ticketName'=>'Name of Ticket'));

in the translation file (protected/messages/ru/task.php

...
  'keyValue'=>'Name of ticket is - ticketName'
}
#11247 report it
rAWTAZ at 2012/12/31 09:26pm
Handling missing translations

Note that CMessageSource (the base class for the standard message sources as described in this article) has an event named onMissingTranslation which can be used to handle the case when a translation is requested but is missing.

One way to use it is to define a simple handler function for it, e.g. like this:

class MyHelper
{
    public static function emailMissingTranslation($event)
    {
        $body = implode("\n", array(
                "Language: {$event->language}",
                "Category: {$event->category}",
                "Message: {$event->message}",
        ));
 
        // Add code to e-mail the contents of $body
        // ...
    }
}

Then, to make the above handler function used when there's a missing translation, configure the 'onMissingTranslation' option of the 'messages' component in the application's config file (usually protected/config/main.php):

'components' => array(
    // ...
    'messages' => array(
        'onMissingTranslation' => array('MyHelper', 'emailMissingTranslation'),
    ),
    // ...
),

You can of course make another type of handler for the missing translations, for example adding them to a database table in preparation for an administrator to provide the translation using some CRUD. The above is just an example of how to make it happen.

#10353 report it
Roman Solomatin at 2012/10/22 08:04am
File Translation

A quick note on file translation: both files

protected/views/ControllerID/edit.php
protected/views/ControllerID/LocaleID/edit.php

must exist, or you will get a "view not found" exception.

The same is true if you are using themes: both files

themes/ThemeID/views/ControllerID/edit.php
themes/ThemeID/views/ControllerID/LocaleID/edit.php

must exist.

#4440 report it
Leffe at 2011/07/09 05:04am
Testing source + target language

When you want to test if the set sourceLanguage + target language in your yii config works, look at eg. the pager of a CListView. Don't look at eg. the default front page or login page as the strings there don't make use of Yii::t.

A tip that helped me is as tasay wrote, to take a look in the framework/messages directory and pick a locale there, rather than looking on a full list of all possible locale strings. For example for Swedish there exist sv_SE and sv_FI (SE = Sweden, FI = Finland), but in framework/messages there exist only a sv.php file. So if I use "sv" as my target language, I get the core/zii translations for free rather than having to translate that as well.

#3902 report it
yiqing95 at 2011/05/19 07:15am
i18n for modules

when in a module, if I want to use i18n I should use it like

Yii::t('MyModuleNameModule.translation','it works');

every time i should type the prefix "MyModuleName", so if the method t should find the moduleName itself, eg:

function Yiit($cateName,$msg,..){
    $module = Yii::app()->controller->module;
    $prefix = '';
    if(!is_null($module)){
       $prefix = ucfirst($module->id).'Module.';
    }
    //echo $prefix.$cateName;
    return Yii::t($prefix.$cateName,$msg,...);
 
}
#380 report it
Aleksy Goroszko at 2010/06/21 11:38am
Working example

If you need to translate inside your module, just follow the steps (example uses module named Dictionary):

  1. Ensure your module class name, like: DictionaryModule
  2. Edit main config /protected/config/main.php - add sourceLanguage and language, ie:
    'sourceLanguage' => 'en_us',
    'language' => 'pl_pl',
  3. Inside module folder (protected/modules/dictionary) create file: messages/pl_pl/translation.php
  4. Edit translation.php and create array like:
    return array (
    'it works' => 'To dziala',
    );

  5. In your module view code just use:
    <?php echo Yii::t('DictionaryModule.translation','it works') ?>

If you want to use translation, but not inside module, just follow the steps:

  1. Edit main config /protected/config/main.php - add sourceLanguage and language, ie:
    'sourceLanguage' => 'en_us',
    'language' => 'pl_pl',

  2. Inside application folder (protected) create file: messages/pl_pl/translation.php
  3. Edit translation.php and create array like:
    return array (
    'it works too' => 'To tez dziala',
    );
  4. In your module code just use:
    <?php echo Yii::t('translation','it works too') ?>

Enjoy :)
Aleksy Goroszko

#420 report it
Taylor at 2010/06/08 06:30pm
Examples

I'm new to Yiii, so read at your own risk.

Here are the steps I took to get i18n working

1) if your source language is not 'en_us' then in your config file add this line in the return array: 'sourceLanguage'=>'es_en', // or whatever the locale

2) You also need to specify the target language. This also can be specified in the config file 'language'=>'es_us', // again, whatever the locale

3) Now create your translation file. In the /protected/messages directory you need to create another directory for each locale you will have translations for. (NOTICE:: Yii has translations for some of it's content. If you want to take advantage of these translations, you need to use a locale that coincides with the locales they used. For example I initially set up a directory of es_us for us Spanish. But then system messages were not working because Yii uses es.)
So, in my case, I created a file in /protected/messages/es/primary.php the name of the file is important as you will need to reference it when you call Yii::t('primary','Hello World');

The contents of the translation file look like this: <? return array( 'Hello World'=> 'Hola Mundo', ); ?> (NOTICE: Save your file as utf-8 and not ascii. Otherwise, your translations will show up funny.

4) Now call <?=Yii::t('primary','Hello World');?> wherever you need to.

One last note you can find Yii's locales in framework/messages.

Hope this helps.

Leave a comment

Please to leave your comment.