Multilanguage websites with gettext and POEdit

The programmers that are creating websites with custom programming in PHP, when we need to create a multilanguage site, each developer has a different way to put the words of the web interface in different languages. The formula that I used before, is very common among programmers: Arrays. It's about having associative arrays with all the words used of every language, each language in a file.

That way if I choose the language "English", it loads the file "eng.php" containing an array with all the words of the interface in that language, then in the template, i call the corresponding array element by key.
This system is fine, but recently I changed to a more effective and standard system: Files generated by Poedit. It is the system that are using Wordpress and many other CMS / Frameworks.

First you need to download POEdit: http://www.poedit.net/ and install it.
In our multi language site, we edit all files where appear text strings in multiple languages.
For example, we find in our contact form something as:

<label>Name and surname:</label>

We change this to:

<label><?=_("Name and surname")?>:</label>

In fact, any string or set of strings that we need to translate, we enclose them in this PHP structure: _(), that is the same as write "gettext()", so if we like more the whole word, it will be:

<label><?=gettext("Name and surname")?>:</label>

Once we have all done, we create a new project in PoEdit: File -> New catalog.
Here we change some things: In "Project information" we fill out the data. In "Folders", we write on "root folder" the root local path of our website project, by example (on mac): "/Users/Enrique/Desktop/myprojects/website_project/"
Now we need to write the path of the files and folders to be translated, inside the previous path. We click the "New" button (2º icon) and there we write the paths to the folders, by example (on mac): "/Users/Enrique/Desktop/myprojects/website_project/website_files".
In "Keywords", we leave it as is because by default it brings the "_" or the "gettext" that we used.
We click OK and we have the project created. Now click on "Update" and PoEdit will scan the folders we have specified looking for the "_()" or "gettext()" keywords.
Once finished, it shows all the words found. Now we need to translate all the words one by one.
Once done, we click "Save", and POEdit creates two files: "default.mo" (compiled file) and "default.po" (editable file).
We upload the "mo" file to: "website_project/website_files/locale/en_US/LC_MESSAGES/" where "en_US" is the corresponding language acronym. The folder "website_files" is the website root folder; where is located the "index.php" file.
When we add new strings or modify existing ones on our website, you have to rebuild the "po" and "mo" and upload the "mo". To do this open the file "po" in POEdit, scan, the program finds all new or modified words, we write the new translations, save, and upload the generated "mo" file.

Now we need that our website load these translated strings, so we need to do these:
In the beginning of every PHP script, with an "include" or any other formula, we get the selected language. The language must have in this format: "en_US".
Then we put the following code:

$locale="en_US";
putenv("LC_ALL=$locale");
setlocale(LC_ALL, $locale);
bindtextdomain("default", "./locale");
textdomain("default");

And that's all for the moment. I say "for the moment" because there is a problem: The server's cache.

The cache bug

Our "mo" file will stay in the server's memory, this is fine because it makes the translations to be very fast, but we make a big deal: If we modify the file and upload it, we do not see the changes immediately, because the old file is cached into the memory. One solution is to reset the Apache, but we can not do it most of the times. To fix this, we use this code instead of the previous:

 function translation($lang) {
  $rname="default";
  if (GETTEXT_CACHING == 0) {
       $targetFolder = "locale/".$lang."/LC_MESSAGES/";
       $folder = opendir($targetFolder);
       while (false !== ($file = readdir($folder))) {
            $pathFiles = $targetFolder."/".$file;
            if ($file != ".." AND $file != "." AND !is_dir($file) AND strrchr($file,''.'') == ''.mo'' AND $file != $rname.''.mo'') {
              unlink($pathFiles);
            }
        }
        closedir($folder);
        $translatefile = $rname.time();
        copy(''locale/''.$lang.''/LC_MESSAGES/''.$rname.''.mo'',''locale/''.$lang.''/LC_MESSAGES/''.$translatefile.''.mo'');
        } else $translatefile = $rname;
      
        setlocale(LC_ALL, $lang);
        putenv("LC_ALL=".$lang);
        bindtextdomain($translatefile,''./locale'');
        bind_textdomain_codeset($translatefile, ''utf8''); 
        textdomain($translatefile);
}

translation($locale);

What it does is duplicate the file "mo" with another name and use that new file, with this we will always use a new version that is not cached.
You have to give write permissions to the folder "LC_MESSAGES". Obviously we will keep this in development time only, once the web has become final, the cache will not matter to us and we will use the first 5 lines code.

4 comments
  • Pep
    20-08-2012 15:36
    I prefer assoc arrays, less job
  • Enrique Gonzalez
    09-09-2012 10:20
    Sí, la verdad es que al final los assoc dan menos trabajo al programador, pero los PO son mas faciles de modificar para el usuario
  • Sergi
    15-03-2013 10:22
    Hola buenas enrique, me preguntava si esto funciona igual en strings guardadas en bases de datos (mysql), pues estoy remodelando una web ya hecha en un solo idioma (básicamente los textos estan en el mysql) para soportar 4 idiomas diferentes, no se realmente como ponerlo en practica, los PO parecen mi solución, agradeceria cualquier ayuda prestada y gracias por su esfuerzo en esta web.
  • Summer
    30-10-2013 14:50
    ¡Hola! Si estan interesados en localizar cualqier tipo de software, reccomendo con calor este rápido y intuitiva herramienta de localización: http://poeditor.com/.
Leave a comment
I have read and accept the Privacy Policy

The comments sent by each user will always be visible in the corresponding post, and will not be used for any other purpose. The user has the right to access, rectify and suppress said comments, as reflected in our Privacy Policy