Software localization

How to Master the Translation of PHP Apps

How to translate php apps. This post reviews the available solutions. Make your application available in various languages.
Software localization blog category featured image | Phrase

Despite alternative web programming languages like Ruby (on Rails), Python or NodeJS dominating the industry headlines, PHP remains a popular choice for building web applications.

Despite this fact, there is still no real standard solution for localizing PHP applications.

Building a truly international application is not just about translating strings. Other issues to consider are date and time formats, currency symbols and pluralization. Programmers often underestimate the complexity of localization and get stuck with homemade code that is a pain to maintain. So, let’s talk about PHP Arrays, gettext, frameworks, and Intl.

PHP Arrays

Associative Arrays have been the primitive approach of many big and small PHP projects for a long time. Translatable strings are stored in an associative array, one file per language.

<?php

/* en.php - english language file */

$messages['hello'] = 'Hello';

$messages['signup'] = 'Sign up for free';

?>
<?php

/* de.php - german language file */

$messages['hello'] = 'Hallo';

$messages['signup'] = 'Kostenlos registrieren';

?>

The appropriate language file is loaded at the beginning of your application code:

<?php

    require('de.php');

    echo($messages['hello']);

    echo($messages['signup']);

?>

This approach is simple and easy to use, but it quickly reaches its limits. Consider an application that displays a notification when the user uploads files:

3 files uploaded successfully

What if it was only one file? Maybe do this:

1 file(s) uploaded successfully

Nah! That’s just plain ugly. I could come up with a hack like:

<?php

if($number_of_files == 1 ) {

  echo $number_of_files.messages['upload_success_single'];

  // 1 file uploaded successfully

} else {

  echo $number_of_files.messages['upload_success_multiple'];

  // 4 files uploaded successfully

}

?>

Not only does this clutter up the code, it doesn’t really solve the problem. In the English language appending a ‘s’ is enough, but pluralization works differently in other languages. For example, the pluralization of the world “file” (plik) in the Polish language works like this:

1 plik

2,3,4 pliki

5-21 pliko'w

22-24 pliki

25-31 pliko'w

gettext

The GNU gettext system has been around for more than 20 years. It is widely used and is the de-facto standard for localization in many programming languages.

Using gettext with PHP can be tricky in some setups.

If you are running a stock Ubuntu VPS, gettext will only support the locales installed on the machine. Or perhaps you are on a hosting plan where the gettext extension isn’t available.

In both cases, php-gettext can help. php-gettext it is a drop-in replacement for PHP enviroments where the gettext extension isn’t installed.

Basic Setup

In this example, i want to use English and German. I create the following directory structure:

index.php

    /locale

        /en_US

            /LC_MESSAGES

                messages.po

                messages.mo

        /de_DE

            /LC_MESSAGES

                messages.po

                messages.mo

Translations are stored in .po files, a simple plain-text file format. Using just a text editor I create en/LC_MESSAGES/messages.po:

msgid "hello"

msgstr "Hello"

msgid "signup"

msgstr "Sign up for free"

I also create a messages.po for the de_DE locale:

msgid "hello"

msgstr "Hallo"

msgid "signup"

msgstr "Kostenlos registrieren"

In the next step, .po files are compiled to .mo files. This can be done using the msgfmt command line utility:

msgfmt messages.po -o messages.mo

This is done for each .po file.

Usage

I can now use the .mo files via gettext in my PHP app:

<?php

    $language = "de_DE";

    putenv("LANG=".$language);

    setlocale(LC_ALL, $language);

    $domain = "messages";

    bindtextdomain($domain, "locale");

    textdomain($domain);

    echo gettext("hello");

    /* echo _("hello"); // Hint: _() is equal to gettext() */

?>

This script creates a new gettext enviroment using the de_DE locale. Then the message with the id ‘hello’ is echo’ed which will output the german “Hallo”.

The gettext system supports plural forms. Using the example from above, I create a plural msgid:

m
sgid "upload"

msgid_plural "uploads"

msgstr[0] "file uploaded successfully"

msgstr[1] "files uploaded successfully"

which I can use in my app code:

<?php

  echo $number_of_files." ".ngettext('upload', 'uploads', $number_of_files);

?>

gettext can handle pluralization but it has no tools for working with numbers, currency, date/time formats.

Frameworks

All major PHP frameworks have built-in support for creating translations. Some offer additional features such as classes for currency and date/time formatting.

Language files Plurals Date/Time Currency
Symfony YAML, XLIFF, PHP Arrays
F3 PHP Arrays, INI
CodeIgniter PHP Arrays
Kohana PHP Arrays
CakePHP gettext
Zend PHP Arrays, CSV, TBX/TMX, gettext, Qt, XLIFF, INI, ...

The localization modules of these frameworks can be used as a standalone tool without a lot of overhead code from the framework itself. Check out our other tutorials to see what that could potentially look like:

And of course, let's not forget about Laravel, one of the most popular PHP frameworks:

4. Intl

PHP 5.3 introduces the Intl class. Intl is a set of convenient helpers for formatting dates, time, numbers and working with currency. It can be used to complement gettext or frameworks that lack some of the functionality.

Conclusion

Localization can sometimes seem challenging. Fortunately, you don’t have to try to solve the problems with homemade code.

Unfortunately, gettext doesn’t play very smoothly with PHP so I'd recommend using a framework.

Symfony, Zend, and F3 all do a great job and are easy to use. After playing around with all the frameworks, I really like the F3 approach. Here's a step-by-step guide on getting started with F3.