4.3. Using translation Adapters

The next step will be to use the adapter within your code. So let's look at a small example.

<?php
print "Example\n";
print "=======\n";
print "Here is line one\n";
print "Today is the " . date("d.m.Y") . "\n";
print "\n";
print "Fix language here is line two\n";
?>

You will probably write your code in your native language. Our example shows some output. Generally you will not only translate the output but also error or log messages.

The next step will be to include Zend Translate into your existing code. Of course it will be much easier if you are writing your code already by using Zend Translate instead of changing your code afterwards...

<?php
require_once("Zend/Translate.php");

$translate = new Zend_Translate('gettext', '/my/path/source-de.mo', 'de');
$translate->addTranslation('//my/path/fr-source.mo', 'fr');

print $translate->_("Example")."\n";
print "=======\n";
print $translate->_("Here is line one")."\n";
printf($translate->_("Today is the %1\$s") . "\n", date("d.m.Y"));
print "\n";

$translation->setLanguage('fr');
print $translate->_("Fix language here is line two") . "\n";
?>

Now lets get a deeper look into what has been done and how to integrate Zend Translate into your code.

First of all you will have to create a new Translation Object and define the base adapter.

<?php
require_once("Zend/Translate.php");

$translate = new Zend_Translate('gettext', '/my/path/source-de.mo', 'de');
?>

In our example we decided the Gettext Adapter. We will place our file source-de.mo into the directory /my/path. The gettext file will have german translation included. And we also added another language source for french.

The next step is to wrap all strings which will be translated. The simplest approach is to have only simple strings or sentences like this:

<?php
print $translate->_("Example")."\n";
print "=======\n";
print $translate->_("Here is line one")."\n";
?>

Of course some strings will not needed to be translated... a seperating line is always a seperated line, even in other languages.

Having informations integrated into a translation string is also no big issue.

<?php
printf($translate->_("Today is the %1\$s") . "\n", date("d.m.Y"));
?>

Instead of print use the printf function and exclude all informations with %1\$s parts. The first is %1\$s, the second %2\$s and so on. This way a translation can be done without knowing the exact information. In our example the date is always the actual day, but the string can be translated without the knowledge of the actual day.

Of course you can also use message id's instead of strings. In the above examples the message id's are identical with the strings. So we could also do it like this:

<?php
print $translate->_(1)."\n";
print "=======\n";
print $translate->_(2)."\n";
?>

But doing so has several disadvantages.

You will not be able to see what your code should output just by viewing your code.

Also you will get problems if some strings are not translated. You always must imagine how translation works. First Zend Translate looks if the set language has a translation for the given message id / string. If no translation string has been found it refers to the next lower language as defined within Zend Locale. So de_AT will become de only. And if also for de there is no translation found the original message id will be returned. This way you will always have an output. Zend Translate will never throw an error or exception when translating strings.

4.3.1. Translation Source structures

Your next step will be to create the translation sources for the several languages you want to translate to. Every adapter is created it's own way as described here. But there are some general feature which are relevant for all adapters.

First of all you should know where to store your translation source files. With Zend_Translate you are not bundled to any restriction. The following structures are preferable:

  • Single structured source

    /application
    /languages
      lang.en
      lang.de
    /library
    

    Positive: All source files for every languages can be found in one directory. No splitting of related files.

  • Language structured source

    /application
    /languages
      /en
        lang.en
        other.en
      /de
        lang.de
        other.de
    /library
    

    Positive: Every language is based in one directory. Easy translation as only one directory has to be translated by a language team. Also the useage of multiple files is transparent.

  • Application structured source

    /application
      /languages
        lang.en
        lang.de
        other.en
        other.de
    

    Positive: All source files for every languages can be found in one directory. No splitting of related files.

    Negative: Having multiple files for the same language is problematic.

  • Gettext structured source

    /languages
      /de
        /LC_MESSAGES
          lang.mo
          other.mo
      /en
        /LC_MESSAGES
          lang.mo
          other.mo
    

    Positive: Old gettext sources can be used without changing structure.

    Negative: Having sub-sub directories is for people which have not used gettext before problematic.

  • File structured source

    /application
      /models
        mymodel.php
        mymodel.de
        mymodel.en
      /views
      /controllers
        mycontroller.de
    /document_root
      /images
      /styles
      .htaccess
      index.php
      index.de
    /library
      /Zend
    

    Positive: Every file is realted to its own translation source.

    Negative: Multiple small translation source files makes it harder to translate. Also every file has to be added as translation source.

Single structured and language structured source files are most useable for Zend_Translate.

So now, that we know which structure we want to have, we should create our translation source files.

4.3.2. Creating array source files

Array source files are just arrays. But you have to define them manually as there is no tool for this. But because they are so simple, it's the fastest way to look if your code works as expected and it's the best adapter to get started with translation business.

$english = array('message1' => 'message1',
                 'message2' => 'message2',
                 'message3' => 'message3');
$german = array('message1' => 'Nachricht1',
                'message2' => 'Nachricht2',
                'message3' => 'Nachricht3');

$translate = new Zend_Translate('array', $english, 'en');
$translate->addTranslation($deutsch, 'de');

4.3.3. Creating gettext source files

Gettext source files are created by GNU's gettext library. There are several free tools avaiable which can parse your code files and create the needed gettext source files. These files have the ending *.mo and are binary files. A freeware tool for creating the files is for example poEdit (http://www.poedit.net). This tool also supports you for the translation process itself.

// We expect that we have created the mo files and translated them
$translate = new Zend_Translate('gettext', 'path/to/english.mo', 'en');
$translate->addTranslation('path/to/german.mo', 'de');

As you can see the adapters are used exactly the same way, with only just one small difference. We changed 'array' to 'gettext'... all other useages are exactly the same as with all other adapters. So with the gettext adapter you have no longer be aware of gettext's standard directory structure, bindtextdomain and textdomain. Just give the path and filename to the adapter and that's it.

[Note] Note

You should always use UTF-8 as source encoding. Otherwise you will have problems if you are using two different source encodings. Expect you have one source file with ISO-8815-11 and one with CP815. But you can only set one encoding for your source file. So one of your languages will probably not be displayed correct.

UTF-8 is a portable format which supports all languages. So you will not have problems with different languages by using it.

4.3.4. Creating tmx source files

Tmx source files are a new industry standard. They have the advantage to be xml files and so they are readable by every editor and of course human readable. You can eighter create TMX files manually or use a tool. But most tools are not freeware.

Example tmx file:

<?xml version="1.0" ?>
<!DOCTYPE tmx SYSTEM "tmx14.dtd">
<tmx version="1.4">
 <header creationtoolversion="1.0.0" datatype="winres" segtype="sentence" adminlang="en-us" srclang="de-at" o-tmf="abc" creationtool="XYZTool" >
 </header>
 <body>
  <tu tuid='message1'>
   <tuv xml:lang="de"><seg>Nachricht1</seg></tuv>
   <tuv xml:lang="en"><seg>message1</seg></tuv>
  </tu>
  <tu tuid='message2'>
   <tuv xml:lang="en"><seg>message2</seg></tuv>
   <tuv xml:lang="de"><seg>Nachricht2</seg></tuv>
  </tu>
$translate = new Zend_Translate('tmx', 'path/to/mytranslation.tmx', 'en');
// TMX can have several languages within one Tmx file.

Tmx files can have several languages within the same file. All other included languages will be added automatically, so you will not have to call addLanguage();

4.3.5. Creating csv source files

Csv source files are small and human readable. If your customers want to translate their own, you will probably use the csv adapter.

Example csv file:

#Example csv file
message1;Nachricht1
message2;Nachricht2
$translate = new Zend_Translate('csv', 'path/to/mytranslation.csv', 'de');
$translate->addTranslation('path/to/other.csv', 'fr');