Working with gettext

  1. Creating the translation directory structure
  2. Portable Object files
  3. Translation Domain
  4. Using Translator in PHP
  5. Variable interpolation

gettext is a standard GNU internationalization / translation system which can be used with PHP and which is supported by PHPTAL.

The usage of gettext™ is simple but you will have to perform some tests to be sure everything works fine on your system.

First, PHP must be compiled with the --with-gettext flag. See PHP documentation for how to do this.

You can test your installation using following peace of code:

//
// test if gettext extension is installed with php
//

if (!function_exists("gettext"))
{
    echo "gettext is not installed\n";
}
else
{
    echo "gettext is supported\n";
}

Creating the translation directory structure

The PHP gettext™ extension requires a specific structure which will contain your translation files.

/path/to/your/translation_root/en_US/LC_MESSAGES/
/path/to/your/translation_root/en_GB/LC_MESSAGES/
/path/to/your/translation_root/fr_FR/LC_MESSAGES/
/path/to/your/translation_root/es_ES/LC_MESSAGES/
… and so on …

The language code is composed of two characters defining the language itself (en, fr, es, …) and two characters defining the country (US, GB, FR, ES, …).

The directory pattern is:

<path_to_where_you_want>/<ll_CC>/LC_MESSAGES/

Portable Object files

PO files are plain text files that contain your translation. You can safely edit them by hand.

po minimalistic example (en_US/LC_MESSAGES/mydomain.po):

msgid ""
msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"

msgid "Simple test"
msgstr "A small sentence in english"

Once edited, each PO file must be indexed using:

msgfmt mydomain.po -o mydomain.mo

This command won't work if you don't have gettext™ tools installed on your system.

This will produce a MO file (machine object) indexing your translation for quick access.

Then you have to translate this file in other languages.

po minimalistic example (fr_FR/LC_MESSAGES/mydomain.po):

msgid ""
msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"

msgid "Simple test"
msgstr "Une petite phrase en français"

The translation file must also be indexed:

msgfmt mydomain.po -o mydomain.mo

Translation Domain

The domain is matched against your translation file names. In above examples we used 'mydomain' as domain name.

You can have more than one domain for the same application, it can enhance gettext™'s performance to split your application translations in more than one file.

Using Translator in PHP

<?php
require_once 'PHPTAL.php';
require_once 'PHPTAL/GetTextTranslator.php';

try {
    $tr = new PHPTAL_GetTextTranslator();

    // set language to use for this session (first valid language will
    // be used)
    $tr->setLanguage('en_GB.utf8', 'en_GB');

    // register gettext domain to use
    $tr->addDomain('mydomain', '/path/to/your/translation_root');

    // specify current domain
    $tr->useDomain('mydomain');

    $tpl = new PHPTAL('mytemplate.xhtml');

    // tell PHPTAL to use our translator
    $tpl->setTranslator($tr);

    // output translated template
    echo $tpl->execute();
}
catch (Exception $e){
    echo $e;
}

If you need to translate some other text, that is not in the template (e.g. plaintext e-mail message), you can reuse PHPTAL's translator:

$tr = $tpl->getTranslator();

$subject = $tr->translate("Registration information");

$tr->setVar("user",$username);
$message = $tr->translate("Dear ${user}, thanks for registering!");

mail($email, $subject, $message);

If you're using PHPTAL's standard gettext™ translator, you can use gettext() too.

Variable interpolation

The I18N namespace allows some variable interpolation in your translations.

# english
msgid "welcome"
msgstr "Welcome ${name} you have ${n} mails!"

# french
msgid "welcome"
msgstr "Bienvenue ${name} vous avez recu ${n} messages!"

A template can use this interpolation as follows:

<span i18n:translate="welcome">
  Welcome
  <span i18n:name="name" tal:replace="user/name"/>
  you currently have
  <span i18n:name="n" tal:replace="user/unreadeMails"/>
  unread messages!
</span>

Because i18n:translate contains a value 'welcome', the template data will be ignored and the message given by gettext™ will be used instead.