symfony Forms in Action

Chapter 8 - Internationalization and Localization

You are currently browsing
the website for symfony 1

Visit the Symfony2 website


About

You are currently reading "symfony Forms in Action" which is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported License license.

Master symfony

Be trained by SensioLabs experts (2 to 6 day sessions -- French or English).
trainings.sensiolabs.com

Books on symfony

Learn more about symfony with the official guides.
books.sensiolabs.com

L'audit Qualité par SensioLabs

200 points de contrôle de votre applicatif web.
audit.sensiolabs.com

Chapter Content

Form Internationalization

Specify the catalogue to use for translations

Error Messages Internationalization

Customization of the Translation object

Translation Callable Accepted Parameters

Propel Objects Internationalization

Localized Widgets

Dates selectors

Country selector

Culture selector

symfony training
Be trained by symfony experts
Feb 13: Paris (Web Development with Symfony2 - Français)
Feb 13: Paris (Getting Started with Symfony2 - Français)
Feb 15: Paris (Mastering Symfony2 - Français)
Feb 21: Köln (Getting Started with Symfony2 - English)
Feb 27: Köln (Mastering Symfony2 - English)
and more...

Search


powered by google
You are currently browsing "symfony Forms in Action" in English for the 1.2 version - Switch to version: - Switch to language:
Creative Commons License This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
This version of symfony is not maintained anymore.
If some of your projects still use this version, consider upgrading as soon as possible.

A lot of popular Web applications are available in several languages, and sometimes, they are even customized based on the user culture. Symfony comes with a built-in framework that eases the management of these features (see chapter "I18n And L10n" (http://www.symfony-project.org/book/1_2/13-I18n-and-L10n) of the symfony book).

The form framework also comes with built-in support for the user interface translation and provides an easy way to manage internationalized objects.

Form Internationalization

A symfony form is internationalizable by default. The translation of the labels, the help texts, and the errors messages can be done by editing the translation files, be they in the XLIFF, gettext, or any other symfony supported format.

Listing 8-1 shows the contact form we have developed in the previous chapters.

Listing 8-1 - Contact Form

class ContactForm extends sfForm
{
  public function configure()
  {
    $this->setWidgets(array(
      'name'  => new sfWidgetFormInput(),    // the default label is "Name"
      'email' => new sfWidgetFormInput(),    // the default label is "Email"
      'body'  => new sfWidgetFormTextarea(), // the default label is "Body"
    ));
 
    // Change the email widget label
    $this->widgetSchema->setLabel('email', 'Email address');
  }
}

We can now define the label translations in the XLIFF file as shown in Listing 8-2 for the french language.

Listing 8-2 - XLIFF translation file

// apps/frontend/i18n/messages.fr.xml
<?xml version="1.0" ?>
<xliff version="1.0">
  <file original="global" source-language="en" datatype="plaintext">
    <body>
      <trans-unit>
        <source>Name</source>
        <target>Nom</target>
      </trans-unit>
      <trans-unit>
        <source>Email address</source>
        <target>Adresse email</target>
      </trans-unit>
      <trans-unit>
        <source>Body</source>
        <target>Message</target>
      </trans-unit>
    </body>
  </file>
</xliff>

Specify the catalogue to use for translations

If you use the catalogue feature of the symfony i18n framework (http://www.symfony-project.org/book/1_2/13-I18n-and-L10n#chapter_13_sub_managing_dictionaries), you can bind a form to a given catalogue. In Listing 8-3, we associate the ContactForm form with the contact_form catalogue. So, the form element translations will be looked for in the contact_form.fr.xml file.

Listing 8-3 - Translation Catalogue Customization

class ContactForm extends sfForm
{
  public function configure()
  {
    // ...
 
    $this->widgetSchema->getFormFormatter()->setTranslationCatalogue('contact_form');
  }
}

The usage of catalogues allows a better organization of your translations by using one file per form for example.

Error Messages Internationalization

Sometimes, the error messages embed the value submitted by the user (for example, "The email address user@domain is not valid."). We have already seen in Chapter 2 that this can be done easily in the form class by defining customized error messages and using references to the user submitted values. These references follow the %parameter_name% pattern.

The Listing 8-4 shows how to apply this principle to the name field of the contact form.

Listing 8-4 - Error Messages Internationalization

class ContactForm extends sfForm
{
  public function configure()
  {
    // ...
 
    $this->validatorSchema['name'] = new sfValidatorEmail(
      array('min_length' => 2, 'max_length' => 45),
      array('min_length' => 'Name "%value%" must be at least %min_length% characters.',
            'max_length' => 'Name "%value%" must not exceed %max_length% characters.',
      ),
    );
  }
}

We can now translate these error messages by editing the XLIFF file as shown in Listing 8-5.

Listing 8-5 - XLIFF Translation File for Error Messages

<trans-unit>
  <source>Name "%value%" must be at least %min_length% characters</source>
  <target>Le nom "%value%" doit comporter un minimum de %min_length% caractères</target>
</trans-unit>
<trans-unit>
  <source>Name "%value%" must not exceed %max_length% characters</source>
  <target>Le nom "%value%" ne peut comporter plus de %max_length% caractères</target>
</trans-unit>

Customization of the Translation object

If you want to use the symfony form framework without the symfony i18n framework, you need to provide your own translation object.

A translation object is just a callable PHP. It can be one of the following three things:

A PHP callable is a reference to a function or a method instance. It is also a PHP variable that returns true when passed to the is_callable() function.

Let's take an example. You have to migrate a project which already has its own internationalization mechanism provided by the class show in Listing 8-6.

Listing 8-6 - Custom I18N class

class myI18n
{
  static protected $default_culture = 'en';
  static protected $messages = array('fr' => array(
    'Name'    => 'Nom',
    'Email'   => 'Courrier électronique',
    'Subject' => 'Sujet',
    'Body'    => 'Message',
  )); 
 
  static public function translateText($text)
  {
    $culture = isset($_SESSION['culture']) ? $_SESSION['culture'] : self::$default_culture; 
    if (array_key_exists($culture, self::$messages)
        && array_key_exists($text, self::$messages[$culture]))
    {
      return self::$messages[$_SESSION['culture']][$text];
    }
    return $text;
  }
}
 
// Class usage
$myI18n = new myI18n();
 
$_SESSION['culture'] = 'en';
echo $myI18n->translateText('Subject'); // => display "Subject"
 
$_SESSION['culture'] = 'fr';
echo $myI18n->translateText('Subject'); // => display "Sujet"

Each form can define its very own callable which will manage the internationalization of the form elements as shown in Listing 8-7.

Listing 8-7 - Overriding of the Internationalization Method for a Form

class ContactForm extends sfForm
{
  public function configure()
  {
    // ...
    $this->widgetSchema->getFormFormatter()->setTranslationCallable(array(new myI18n(), 'translateText'));
  }
}

Translation Callable Accepted Parameters

The translation callable can take up to three arguments :

Here is the call used by the sfFormWidgetSchemaFormatter::translate() method to call the translation callable:

return call_user_func(self::$translationCallable, $subject, $parameters, $catalogue);

The self::$translationCallable is the reference to the translation callable. So, the previous code is equivalent to:

$myI18n->translateText($subject, $parameters, $catalogue);

Here is the updated version of the MyI18n class that supports those extra arguments:

class myI18n
{
  static protected $default_culture = 'en';
  static protected $messages = array('fr' => array(
    'messages' => array(
      'Name'    => 'Nom',
      'Email'   => 'Courrier électronique',
      'Subject' => 'Sujet',
      'Body'    => 'Message',
    ),
  ));
 
  static public function translateText($text, $arguments = array(), $catalogue = 'messages')
  {
    $culture = isset($_SESSION['culture']) ? $_SESSION['culture'] : self::$default_culture; 
    if (array_key_exists($culture, self::$messages) &&
        array_key_exists($messages, self::$messages[$culture] &&
        array_key_exists($text, self::$messages[$culture][$messages]))
    {   
      $text = self::$messages[$_SESSION['culture']][$messages][$text];
      $text = strtr($text, $arguments);
    }   
    return $text;
  }
}

Propel Objects Internationalization

The form framework has built-in support for Propel objects that are internationalized. Let's take an internationalized model example to illustrate the way it works:

propel:
  article:
    id:
    author:     varchar(255)
    created_at:
  article_i18n:
    title:      varchar(255)
    content:    longvarchar

You can generate the Propel classes and the related form classes with the following commands:

$ php symfony build:model
$ php symfony build:forms

Those commands generate some files in your symfony project:

lib/
  form/
    ArticleForm.class.php
    ArticleI18nForm.class.php
    BaseFormPropel.class.php
  model/
    Article.php
    ArticlePeer.php
    ArticleI18n.php
    ArticleI18nPeer.php

Listing 8-8 shows how to configure the ArticleForm to be able to edit the French and the English version of the article in the same form.

Listing 8-8 - I18n forms for an internationalized Propel Object

class ArticleForm extends BaseArticleForm
{
  public function configure()
  {
    $this->embedI18n(array('en', 'fr'));
  }
}

You can also customize the language labels of the form by adding the following code to the configure() method as shown in Listing 8-9.

Listing 8-9 - Language Labels Customizations

$this->widgetSchema->setLabel('en', 'English');
$this->widgetSchema->setLabel('fr', 'French');

Figure 8-1 - Internationalized Propel Form

Internationalized Propel Form

That's all there is to it. When you call the save() method of the form object, the associated Propel object and all the i18n objects are saved automatically.

Localized Widgets

The symfony form framework is bundled with some widgets that are i18n "aware". They can be used to localize some widgets according to the user culture.

Dates selectors

Here are the available widgets to localize a date:

Country selector

The sfWidgetFormI18nSelectCountry widget displays a select box filled with a list of countries. The country names are translated in the given language:

$this->widgetSchema['country'] = new sfWidgetFormI18nSelectCountry(array('culture' => 'fr'));

You can also restrict the countries in the select box, thanks to the countries option:

$countries = array('fr', 'en', 'es', 'de', 'nl');
$this->widgetSchema['country'] = new sfWidgetFormI18nSelectCountry(array('culture'   => 'fr',
                                                                         'countries' => $countries));

Culture selector

The sfWidgetFormI18nSelectLanguage widget displays a select box filled with a list of languages. The language names are translated in the given language:

$this->widgetSchema['language'] = new sfWidgetFormI18nSelectLanguage(array('culture' => 'fr'));

You can also restrict the languages in the select box, thanks to the languages option:

$languages = array('fr', 'en', 'es', 'de', 'nl');
$this->widgetSchema['language'] = new sfWidgetFormI18nSelectLanguage(array('culture'   => 'fr',
                                                                           'languages' => $languages));

Questions & Feedback

If you find a typo or an error, please register and open a ticket.

If you need support or have a technical question, please post to the official user mailing-list.