Soluling home   Document home

PHP Localization and Internationalization

PHP Localization and Internationalization

PHP (Wikipedia) is a very popular scripting language to build web pages and applications. PHP is a server-side scripting language. PHP scripts are embedded inside HTML files, and the scripts in the files are processed in the server before sent to the browser. Any HTML file that contains PHP is called a PHP file. In many cases, PHP is used together with client-side scripting (i.e., JavaScript). Even PHP is a scripting language, it is also a full-featured object-oriented language with very extensive library. The library objects and functions contain a full set of internationalization functions that let you write world ready PHP applications. However, there is no standard way to localize PHP applications, but two different methods are used. They are the gettext method and the localized PHP files method. Soluling localization tool and service support both PHP methods.

Localization using gettext

This is the most common way to localize PHP applications. The main idea is that you identify all those strings that need to be translated, and then you replace the strings with calls to gettext functions. On runtime, the gettext function returns the right string matching the language that is currently selected. gettext is by default included in PHP platforms. In most cases, you don't have to install it.

A typical PHP file contains strings in three element types: HTML, PHP scripts, and JavaScript scripts. Most often, a string is a part of the HTML element. For example, the following line show a paragraph.

<p>Hello World</p>

The above string is hardcoded and if you run the PHP file, it always returns the above string. To use gettext, replace the string with the following PHP script.

<p><?=gettext("Hello World")?></p>

You can also use the shortcut of the function: _(...)

<p><?=_("Hello World")?></p>

When the PHP file is run, the above script is executed, and the gettext function returns localized "Hello World" string.

If your PHP script contains strings that need to be localized, you use the same approach. For example, if you have the following script.

<?php 
...
echo "<p>Hello World</p>"; 
...
?>

Modify the script to use gettext.

<?php 
...
echo "<p>"._("Hello World")."</p>"; 
...
?>

If your JavaScript scripts use strings that need to be localized, the best way to handle it is to localize those string on the server-side using PHP's gettext.

document.getElementById("myLabel").innerHTML = "Hello World";

Modify the script to use PHP's gettext.

document.getElementById("myLabel").innerHTML = "<?=_("Hello World")?>";

When the PHP file is executed on the server, the above string gets updated using your active language.

Creating PO and MO files

Now you have replaced the hardcoded strings with calls to gettext. This is not enough. You still have to use gettext tools to create the string files. gettext library contains gettext or xgettext command-line file that you use to create and update the files.

xgettext index.php

or

xgettext *.php

When reading the PHP files, xgettext searched all locations of code that use either gettext() or _() functions and extracts the strings and adds it to a PO file. This is a text file that contains all strings that were extracted from the PHP file(s). Add this file to Soluling. Once translated, Soluling will create localized PO files and also localized MO files. MO file is a binary version of PO file. When you deploy your application, you don't deploy the PO file but the MO files. MO files need to be placed into a specific localization, so gettext can locate them. By default, this location is locale sun directory on your root directory. The locale directory then contains one subdirectory for each language. Each language-specific directory then contains LC_MESSAGES subdirectory that actually contains the localized MO file. The following image shows a situation where there are German and Japanese MO files deployed with the application.

MO files

Selecting the language

In order gettext to use the right language, you have to specify it. Each PHP file must contain code that initializes the language. The best place for such code is the beginning of the file. Here is a sample

<?php
  if (isSet($_GET["locale"])) 
    $locale = $_GET["locale"];
  else
    $locale = "en";
    
  putenv("LC_ALL=$locale");
  setlocale(LC_ALL, $locale);
  
  textdomain("messages");
  bindtextdomain("messages", "locale");
?>

First, the code checks if the URL contains a locale parameter. For example, the URL might be http://www.myapp.com?locale=de where the locale parameter is de that if the language code of German. If no locale parameter is given application uses English. Once we have the locale code, we set the locale using putenv and setlocale functions. These tell gettext the language it should use. Next, we give a domain a name using the text-domain function. Finally, we bind the domain to a directory using the bindtextdomain function. This tells gettext a location where to look for localized MO files.

Runtime language switch

Now we have correctly localized PHP application that starts at the language we want. There is one more thing we should do. It is let the user change the language. How you implement this depends pretty much on your needs and how you want to language switch interface to look alike. The following code creates a combo box that contains available languages, and the currently selected language is selected in the combo box.

Select language:
<select id="languageSelect" onchange="languageChanged()">
<?php
  $languages = array
  (
    "en" => "English",
    "de" => "Deutsch",
    "ja" => "日本語"
  );
   
  foreach ($languages as $id => $name) 
  {
    if ($id == $locale)
      $selected = " selected";
    else 
      $selected = "";
      
    echo " <option value=\"".$id."\"".$selected.">".$name."</option>";
  }
?>
</select>

The script contains an array that has all the supported languages. Then it populates the combo box. Whenever the user selects a language languageChanged JavaScript event is called. The even forces the browser to reload the page using the selected language.

<script>
function languageChanged()
{
  var locale = document.getElementById("languageSelect").value;
  var url = document.URL.split('?')[0] + "?locale=" + locale;
  window.location.href = url;
}
</script>

You are free to implement any kind of language switch. Instead of a combo box, you can use radio buttons, flag images, or just simple buttons.

Comments

gettext lets you assign comments to PO files. You add comments by adding comments in your PHP code. The comment must exist a line before the call to gettext or one the same line just before the call. The comment must also exist in a PHP block. It can not be in HTML or JavaScript block.

<p><?=_("Hello World")?></p>

If you want to add a comment for the above string, you can do it like this

<p><?=/* This is a comment */ _("Hello World")?></p>

You must remember to add --add-comments command-line option for xgettext.

xgettext --add-comments index.php

Now the PO file contains the comment, and the comment gets automatically imported to the Soluling project so your translator can also see it.

Learn more about gettext/po localization.

There are several frameworks that work on top of PHP. It may be that the markup that the framework uses is not compatible with the standard .php markup. However, if you use gettext in your project, Soluling can localize it.

Create a Soluling project

Start by creating a new project for your gettext file, for example messages.po. The Project Wizard will first show the options sheet where you can configure how the file is localized.

Localize all items of selected types

Click Next to get to the languages sheet where you can select the original language and add the target languages. You can later add any number of languages to th project.

Localize all items of selected types

Select English as the original language and add one target language. In our sample, we chose Finnish. Click Finish to finish the wizard. Soluling creates a project for the gettext file, scans the file, and shows the elements in the editor. When you have translated the project, you can build the localized files. Click Home | Project | Build all to build the localized files. The Finnish gettext file will be created into fi subdirectory under the same name as the original file (e.g., locale\fi\LC_MESSAGES\messages.po). Soluling also compiles the .po file to the binary .mo file that is ready to be deployed.

Localization with Localized Files

In addition to using gettext to localized PHP, there is another suitable method. The basic idea here is to localize all PHP files. When you write PHP files, you can hardcode all strings. When localizing, Soluling makes localized copies of PHP files. One copy for each language. The main advantage of this method over gettext is that you really can hard code all HTML, PHP script, and JavaScript strings into PHP file. There is no need to replace the string with the _() function. This makes the development process faster and also your PHP/HTML code much easier to read. The downside is that you end up having different URLs for each language. If you use gettext the same URL can serve all languages

Start by creating a new project for your PHP files. Follow either Multiple files or Directory method.

Plurals

gettext has a plural enabled format function: ngettext.

See GitHub or <data-dir>\Samples\PHP\DrivingGetText sample.

Samples

GitHub and <data-dir>\Samples\PHP contains following samples:

Directory Description
Driving A simple driving time calculator sample that uses hard coded strings. PHP files are localized.
DrivingJavaScript As above, but the application uses JavaScipt, too.
DrivingGetText As Driving but hardcoded strings have been replaced with calls to gettext. PO files are localized.
DrivingGetTextJavaScript As above but, the application uses JavaScript, too.

In addition, PO and Python samples show how to localize PO files.