symfony : i18n and l10n

25
The Definitive Guide to symfony I18n And L10n Doc. v. 0.1 - 15/05/09 Wildan Maulana | wildan [at] tobethink.com #13

Upload: wildan-maulana

Post on 29-May-2015

4.931 views

Category:

Technology


1 download

DESCRIPTION

The Definitive Guide to symfony I18n And L10n

TRANSCRIPT

Page 1: symfony : I18n And L10n

The Definitive Guide to symfony I18n And L10n

Doc. v. 0.1 - 15/05/09

Wildan Maulana | wildan [at] tobethink.com

#13

Page 2: symfony : I18n And L10n

TOC • User Culture

– Setting the Default Culture

– Changing the Culture for a User

– Determining the Culture Automatically

• Standards and Formats

– Outputting Data in the User's Culture

– Getting Data from a Localized Input

• Text Information in the Database

– Creating Localized Schema

– Using the Generated I18n Objects

• Interface Translation

– Configuring Translation

– Using the Translation Helper

– Using Dictionary Files

– Managing Dictionaries

– New in symfony 1.1

• Handling Other Elements Requiring Translation

• Handling Complex Translation Needs

• Calling the Translation Helper Outside a Template

• Summary

Page 3: symfony : I18n And L10n

User Culture• All the built-in i18n features in symfony are based on a

parameter of the user session called the culture.

• he culture is the combination of the country and the language of the user, and it determines how the text and culture-dependent information are displayed

Page 4: symfony : I18n And L10n

Setting the Default Culture• Setting the Default Culture, in frontend/config/settings.yml

• Keeping both the language and the country in the culture is necessary because you may have a different French translation for users from France, Belgium, or Canada, and a different Spanish content for users from Spain or Mexico. The language is coded in two lowercase characters, according to the ISO 639-1 standard (for instance, en for English). The country is coded in two uppercase characters, according to the ISO 3166-1 standard (for instance, GB for Great Britain).

all: .settings: default_culture: fr_FR

Page 5: symfony : I18n And L10n

Changing the Culture for a User

// Culture setter$this->getUser()->setCulture('en_US'); // Culture getter$culture = $this->getUser()->getCulture(); => en_US

page: url: /:sf_culture/:page requirements: { sf_culture: (?:fr|en|de) } params: ...

article: url: /:sf_culture/:year/:month/:day/:slug requirements: { sf_culture: (?:fr|en|de) } params: ...

Culture in the URL

Page 6: symfony : I18n And L10n

Determining the Culture Automatically

$language = $request->getPreferredCulture(array('en', 'fr')); // the website is available in English and French

Page 7: symfony : I18n And L10n

Standards and Formats

• Outputting Data in the User's Culture

Page 8: symfony : I18n And L10n

<?php use_helper('Date') ?> <?php echo format_date(time()) ?> => '9/14/06' <?php echo format_datetime(time()) ?> => 'September 14, 2006 6:11:07 PM CEST' <?php use_helper('Number') ?> <?php echo format_number(12000.10) ?> => '12,000.10' <?php echo format_currency(1350, 'USD') ?> => '$1,350.00' <?php use_helper('I18N') ?> <?php echo format_country('US') ?> => 'United States' <?php format_language('en') ?> => 'English' <?php use_helper('Form') ?> <?php echo input_date_tag('birth_date', mktime(0, 0, 0, 9, 14, 2006)) ?> => input type="text" name="birth_date" id="birth_date" value="9/14/06" size="11" /> <?php echo select_country_tag('country', 'US') ?> => <select name="country" id="country"><option value="AF">Afghanistan</option> ... <option value="GB">United Kingdom</option> <option value="US" selected="selected">United States</option> <option value="UM">United States Minor Outlying Islands</option> <option value="UY">Uruguay</option> ... </select>

Outputting Data in the User's Culture

Culture-Dependent Helpers

Displaying a Number for the User's Culture<?php use_helper('Number') ?> <?php $sf_user->setCulture('en_US') ?><?php echo format_number(12000.10) ?> => '12,000.10' <?php $sf_user->setCulture('fr_FR') ?><?php echo format_number(12000.10) ?> => '12 000,10'

Page 9: symfony : I18n And L10n

Getting Data from a Localized Input

Getting a Date from a Localized Format in an Action

$date= $request->getParameter('birth_date');$user_culture = $this->getUser()->getCulture(); // Getting a timestamp$timestamp = $this->getContext()->getI18N()->getTimestampForCulture($date, $user_culture); // Getting a structured datelist($d, $m, $y) = $this->getContext()->getI18N()->getDateForCulture($date, $user_culture);

Page 10: symfony : I18n And L10n

Text Information in the Database

• A localized application offers different content according to the user's culture. For instance, an online shop can offer products worldwide at the same price, but with a custom description for every country. This means that the database must be able to store different versions of a given piece of data, and for that, you need to design your schema in a particular way and use culture each time you manipulate localized model objects.

Page 11: symfony : I18n And L10n

Creating Localized Schema

Sample Schema for i18n Data, in config/schema.yml

my_connection: my_product: _attributes: { phpName: Product, isI18N: true, i18nTable: my_product_i18n } id: { type: integer, required: true, primaryKey: true, autoincrement: true } price: { type: float }

my_product_i18n: _attributes: { phpName: ProductI18n } id: { type: integer, required: true, primaryKey: true, foreignTable: my_product, foreignReference: id } culture: { isCulture: true, type: varchar, size: 7, required: true, primaryKey: true } name: { type: varchar, size: 50 }

Sample Schema for i18n Data, Short Version, in config/schema.ymlmy_connection: my_product: _attributes: { phpName: Product } id: price: float my_product_i18n: _attributes: { phpName: ProductI18n } name: varchar(50)

Page 12: symfony : I18n And L10n

Using the Generated I18n Objects

Dealing with i18n Objects

$product = ProductPeer::retrieveByPk(1);$product->setName('Nom du produit'); // By default, the culture is the current user culture$product->save(); echo $product->getName(); => 'Nom du produit' $product->setName('Product name', 'en'); // change the value for the 'en' culture$product->save(); echo $product->getName('en'); => 'Product name'

Retrieving Objects with an i18n Criteria$c = new Criteria();$c->add(ProductPeer::PRICE, 100, Criteria::LESS_THAN);$products = ProductPeer::doSelectWithI18n($c, $culture);// The $culture argument is optional// The current user culture is used if no culture is given

Page 13: symfony : I18n And L10n

Interface Translation

• The user interface needs to be adapted for i18n applications. Templates must be able to display labels, messages, and navigation in several languages but with the same presentation. Symfony recommends that you build your templates with the default language, and that you provide a translation for the phrases used in your templates in a dictionary file. That way, you don't need to change your templates each time you modify, add, or remove a translation.

Page 14: symfony : I18n And L10n

Configuring Translation

Activating Interface Translation, in frontend/config/settings.yml

all: .settings: i18n: on

Page 15: symfony : I18n And L10n

Using the Translation HelperA Multiple-Language-Ready Template

<?php use_helper('I18N') ?> <?php echo __('Welcome to our website.') ?><?php echo __("Today's date is ") ?><?php echo format_date(date()) ?>

If your application uses the I18N helper group for every page, it is probably a good idea to include it in the standard_helpers setting in the settings.yml file, so that you avoid repeating use_helper('I18N') for each template.

Page 16: symfony : I18n And L10n

Using Dictionary Files• Each time the __() function is called, symfony looks for a translation of its

argument in the dictionary of the current user's culture. If it finds a corresponding phrase, the translation is sent back and displayed in the response. So the user interface translation relies on a dictionary file.

• The dictionary files are written in the XML Localization Interchange File Format (XLIFF), named according to the pattern messages.[language code].xml, and stored in the application i18n/ directory.

• XLIFF is a standard format based on XML. As it is well known, you can use third-party translation tools to reference all text in your website and translate it. Translation firms know how to handle such files and to translate an entire site just by adding a new XLIFF translation.

Page 17: symfony : I18n And L10n

An XLIFF Dictionary, in frontend/i18n/messages.fr.xml

<?xml version="1.0" ?><xliff version="1.0"> <file original="global" source-language="en_US" datatype="plaintext"> <body> <trans-unit id="1"> <source>Welcome to our website.</source> <target>Bienvenue sur notre site web.</target> </trans-unit> <trans-unit id="2"> <source>Today's date is </source> <target>La date d'aujourd'hui est </target> </trans-unit> </body> </file></xliff>

As looking for dictionary files, parsing them, and finding the correct translation for a given string takes some time, symfony uses an internal cache to speedup the process. By default, this cache uses the filesystem. You can configure how the i18N cache works (for instance, to share the cache between several servers) in the factories.yml (see Chapter 19).

Page 18: symfony : I18n And L10n

Managing Dictionaries

If your messages.XX.xml file becomes too long to be readable, you can always split the translations into several dictionary files, named by theme. For instance, you can split the messages.fr.xml file into these three files in the application i18n/ directory:

• navigation.fr.xml

• terms_of_service.fr.xml

• search.fr.xml

<?php echo __('Welcome to our website', null, 'navigation') ?>

Another way to organize translation dictionaries is to split them by module. Instead of writing a single messages.XX.xml file for the whole application, you can write one in each modules/[module_name]/i18n/ directory. It makes modules more independent from the application, which is necessary if you want to reuse them, such as in plug-ins (see Chapter 17).

Page 19: symfony : I18n And L10n

Handling Other Elements Requiring Translation

• Images, text documents, or any other type of assets can also vary according to the user culture. The best example is a piece of text with a special typography that is actually an image. For these, you can create subdirectories named after the user culture:

• Error messages from validation files are automatically output by a __(), so you just need to add their translation to a dictionary to have them translated.

• The default symfony pages (page not found, internal server error, restricted access, and so on) are in English and must be rewritten in an i18n application. You should probably create your own default module in your application and use __() in its templates. Refer to Chapter 19 to see how to customize these pages.

The following are other elements that may require translation:

<?php echo image_tag($sf_user->getCulture().'/myText.gif') ?>

Page 20: symfony : I18n And L10n

Handling Complex Translation Needs

Translating Sentences That Contain Code

// Base exampleWelcome to all the <b>new</b> users.<br />There are <?php echo count_logged() ?> persons logged. // Bad way to enable text translation<?php echo __('Welcome to all the') ?><b><?php echo __('new') ?></b><?php echo __('users') ?>.<br /><?php echo __('There are') ?><?php echo count_logged() ?><?php echo __('persons logged') ?> // Good way to enable text translation<?php echo __('Welcome to all the <b>new</b> users') ?> <br /><?php echo __('There are %1% persons logged', array('%1%' => count_logged())) ?>

Page 21: symfony : I18n And L10n

Handling Complex Translation Needs #2

Translating Sentences Depending on the Value of Parameters

<?php echo format_number_choice( '[0]Nobody is logged|[1]There is 1 person logged| (1,+Inf]There are %1% persons logged', array('%1%' => count_logged()), count_logged()) ?>

The message/string choices are separated by the pipe (|) character followed by an array of acceptable values, using the following syntax:

– [1,2]: Accepts values between 1 and 2, inclusive

– (1,2): Accepts values between 1 and 2, excluding 1 and 2

– {1,2,3,4}: Only values defined in the set are accepted

– [-Inf,0): Accepts values greater or equal to negative infinity and strictly less than 0

– {n: n % 10 > 1 && n % 10 < 5} pliki: Matches numbers like 2, 3, 4, 22, 23, 24 (useful for languages like polish or russian)

XLIFF Dictionary for a format_number_choice() Argument

...<trans-unit id="3"> <source>[0]Nobody is logged|[1]There is 1 person logged|(1,+Inf]There are %1% persons logged</source> <target>[0]Personne n'est connecté|[1]Une personne est connectée|(1,+Inf]Il y a %1% personnes en ligne</target></trans-unit>...

Page 22: symfony : I18n And L10n

A few words about charsets

Dealing with internationalized content in templates often leads to problems with charsets. If you use a localized charset, you will need to change it each time the user changes culture. In addition, the templates written in a given charset will not display the characters of another charset properly.

This is why, as soon as you deal with more than one culture, all your templates must be saved in UTF-8, and the layout must declare the content with this charset. You won't have any unpleasant surprises if you always work with UTF-8, and you will save yourself from a big headache.

Symfony applications rely on one central setting for the charset, in the settings.yml file. Changing this parameter will change the content-type header of all responses.

all: .settings: charset: utf-8

Page 23: symfony : I18n And L10n

Calling the Translation Helper Outside a Template

Not all the text that is displayed in a page comes from templates. That's why you often need to call the __() helper in other parts of your application: actions, filters, model classes, and so on. Listing below shows how to call the helper in an action by retrieving the current instance of the I18N object through the context singleton.

$this->getContext()->getI18N()->__($text, $args, 'messages');

Page 24: symfony : I18n And L10n

Summary

Handling internationalization and localization in web applications is painless if you know how to deal with the user culture. The helpers automatically take it into account to output correctly formatted data, and the localized content from the database is seen as if it were part of a simple table. As for the interface translation, the __() helper and XLIFF dictionary ensure that you will have maximum versatility with minimum work.

Page 25: symfony : I18n And L10n

Reference

• The Definitive Guide to symfony,Fabien Potencier, Apress