This component has an optional dependency on the Cache component by means of
the TranslationCacheTiein component. When the latter is installed the
Translation system can make use of a cache to store data to, and retrieve data
from.
The Template component also has an optional dependency on this component, by
means of the TemplateTranslationTiein component. This component adds a template
function that uses the Translation component to fetch translated data.
In the most simple case all that your application wants to do is access
translated versions of the strings that it uses. In most cases the strings used
in an application will be English, but of course that is not necessary. In the
first version of the component we only support Qt's Linguist files (TS
files) which groups translatable strings together in contexts. The TS file
format is handled by the ezcTranslationTsBackend class. This backend requires
one setting (the location where to find the translation) and has one option
(the format for the filename for each locale).
In the first example we assume that all translations are stored in the
"translations/" directory, and that the filename consists of "translation-"
followed by the locale name and ".xml". The locale name itself is a freeform
field, but we recommend to use the ISO639-1 language code, followed by a _,
followed by the ISO3166 country code. e.g. nb_NO for Bokmål/Norway or nl_BE
for Dutch/Belgium.
1. <?php
2. require_once 'tutorial_autoload.php';
3.
4. $backend = new ezcTranslationTsBackend( dirname( __FILE__ ). '/translations' );
5. $backend->setOptions( array( 'format' => 'translation-[LOCALE].xml' ) );
6.
7. $manager = new ezcTranslationManager( $backend );
8. $headersContext = $manager->getContext( 'nb_NO', 'tutorial/headers' );
9. $descriptionContext = $manager->getContext( 'nb_NO', 'tutorial/descriptions' );
10.
11. echo $headersContext->getTranslation( 'header1' ), "\n";
12. echo $descriptionContext->getTranslation( 'desc1' ), "\n";
13. echo $descriptionContext->getTranslation( 'desc2' ), "\n";
14.
15. ?>
In the above example we create a backend object in lines 4 and 5. We tell the
backend where to find the translations, and what the format of the translation
filename is. The string "[LOCALE]" will automatically be replaced with the
locale name when opening the translation file. With the configured backend we
then construct a manager in line 7. In line 8 and 9 we ask the manager to
return the contexts "tutorial/headers" and "tutorial/descriptions" for the
"nb_NO" locale. When you ask the manager to retrieve the content it first
checks its internal cache if the ezcTranslation object for that context was
already retrieved. If the object for the context is available in the cache it
will simply return the ezcTranslation object. If the context is not in the
cache, it will defer the retrieving to the backend, store the results in its
cache and return the context.
In many cases there are parameters to your translated strings, for example to
fill in a name of an article. In this case a solution would be to separate the
translatable string into two parts, and just concatenate them together with the
parameter on the correct place. However, it is also possible that the order of
parameters changes when you translate a string. For example the
English string "Search for 'appelmoes' returned 3 matches" can be translated in Dutch
to: "Er zijn 3 items gevonden bij het zoeken naar 'appelmoes'". The simple
concatenation mechanism then no longer works. Luckily the Translation
component supports parameterized strings in two different ways: with
numerical replacement identifiers (%1, %2, etc.) and with associative
identifiers (%search_string, %matches). In the following example we show how to
use this:
1. <?php
2. require_once 'tutorial_autoload.php';
3.
4. $backend = new ezcTranslationTsBackend( dirname( __FILE__ ). '/translations' );
5. $backend->setOptions( array( 'format' => 'translation-[LOCALE].xml' ) );
6.
7. $manager = new ezcTranslationManager( $backend );
8. $dutch = $manager->getContext( 'nl_NL', 'search' );
9. $norsk = $manager->getContext( 'nb_NO', 'search' );
10.
11. $params = array( 'search_string' => 'appelmoes', 'matches' => 4 );
12. echo $dutch->getTranslation( "Search for '%search_string' returned %matches matches.", $params ), "\n";
13.
14. $params = array( 'fruit' => 'epplet' );
15. echo $norsk->getTranslation( "The %fruit is round.", $params ), "\n";
16. ?>
The first lines are the same as in Example 1. But this time we retrieve
an ezcTranslation object for the same context for two different locales (in
line 8 and 9). In line 11 and 12 we request the translation for "Search for
'%search_string' returned %matches matches.". This sentence has two parameters
(search_string and matches) for which the values are provided in array that is
passed as second parameter to the getTranslation() method.
The translation for the English "The apple is round" is in Norwegian "Applet er
rund". With the name of the fruit being the parameter you can see that in
Norwegian the parameter value needs to have its first letter uppercased, as
it's the start of a sentence. The translation system supports this by
specifying the first letter of the parameter name in the translated string as a
capital letter. For a TS format file this looks like:
<source>The %fruit is round.</source>
<translation>%Fruit er rund.</translation>
When the first letter of a parameter name in the translated string is a
capital, the translation system will also uppercase the first letter of the
parameter value. The output of the whole script is therefore:
Er zijn 4 items gevonden bij het zoeken naar 'appelmoes'.
Epplet er rund.
In some cases not all the translation files are up to date. For example the
original translation file was updated with new strings, but your translator had
no time to translate the strings yet. For your application to show at least the
original strings (often English) the Translation component offers filters.
Filters are applied after the backend retrieved the data, but before it is placed
in the internal cache of the manager.
1. <?php
2. require_once 'tutorial_autoload.php';
3.
4. $backend = new ezcTranslationTsBackend( dirname( __FILE__ ). '/translations' );
5. $backend->setOptions( array( 'format' => 'translation-[LOCALE].xml' ) );
6.
7. $manager = new ezcTranslationManager( $backend );
8. $manager->addFilter( ezcTranslationComplementEmptyFilter::getInstance() );
9. $headersContext = $manager->getContext( 'nl_NL', 'tutorial/headers' );
10. echo $headersContext->getTranslation( 'header1' ), "\n";
11. ?>
In this example we add a filter to the manager which we create in line 8. In
line 9 we then request translation for a string that is marked as "unfinished".
The Complement Empty Filter fills in the original string for every translation
that is still marked as "unfinished" and your application shows the original
language's text.
There are a few extra (less useful) filters in the Translation component as
well. The next example shows the "Leet" and "Bork" filters in action. The Bork
filter mangles non-finished or non-translated text so that it is obvious which
text is translatable, but not yet translated. The "Leet" filter renders your
text using Leetspeak. Both filters are demonstrated in the following example:
1. <?php
2. require_once 'tutorial_example_03.php';
3.
4. $manager = new ezcTranslationManager( $backend );
5. $manager->addFilter( ezcTranslationBorkFilter::getInstance() );
6. $search = $manager->getContext( 'nl_NL', 'search' );
7. $params = array( 'search_string' => 'appelmoes', 'matches' => 4 );
8. echo $search->getTranslation( "Search for '%search_string' returned %matches matches.", $params ), "\n";
9.
10. $manager = new ezcTranslationManager( $backend );
11. $manager->addFilter( ezcTranslationLeetFilter::getInstance() );
12. $search = $manager->getContext( 'nl_NL', 'search' );
13. $params = array( 'search_string' => 'appelmoes', 'matches' => 4 );
14. echo $search->getTranslation( "Search for '%search_string' returned %matches matches.", $params ), "\n";
15. ?>
Lines 4 to 8 show the usage of the ezcTranslationBorkFilter and lines 10 to 14
the usage of the ezcTranslationLeetFilter. The output of this script is:
header1
[Seerch for 'appelmoes' retoorned 4 metches.]
3r zijn 4 i73ms g3v0nd3n bij h37 z03k3n n44r 'appelmoes'.
The first line is "header1" because this script includes the previous one. For
the Bork filter you can see that it uses the original string and not the
translated version. The Leet filter however uses the translated string
exclusively. In case you want to implement your own filters, you need to create
a class that implements the ezcTranslationFilter interface. Have a look at the
implementation for the ezcTranslationBorkFilter filter, which shows how to
implement such a class.
In some situations it might be useful to iterate over all the contexts in a
specific translation file. The backends which implement the
ezcTranslationContextRead interface provide this functionality in the form of
an Iterator. The ezcTranslationTsBackend is such a class. Using this interface
is extremely easy, as you can see in the next example:
1. <?php
2. require_once 'tutorial_autoload.php';
3.
4. $backend = new ezcTranslationTsBackend( dirname( __FILE__ ). '/translations' );
5. $backend->setOptions( array( 'format' => 'translation-[LOCALE].xml' ) );
6.
7. $backend->initReader( 'nb_NO' );
8.
9. foreach ( $backend as $contextName => $contextData )
10. {
11. echo $contextName, "\n";
12. foreach ( $contextData as $context )
13. {
14. echo "\toriginal string: {$context->original}\n";
15. echo "\ttranslated string: {$context->translation}\n\n";
16. }
17. }
18. ?>
In line 7 we initialize the reader with the locale 'nb_NO'. After this is done,
we can simply use foreach() to loop over all the contexts in the translation
definition as you can see in lines 9 to 17.
As reading from an XML file for every translation context is not very fast -
especially when the translation file has a lot of strings - the translation
system benefits from a caching solution. Caching is implemented in the Cache
component and the links between this component and the caching component are
implemented in the ezcTranslationCacheBackend which is located in the
TranslationCacheTiein component.
Using the cache backend is similar to using the Ts backend as you can see in
the next example:
1. <?php
2. require_once 'tutorial_autoload.php';
3.
4. $reader = new ezcTranslationTsBackend( dirname( __FILE__ ). '/translations' );
5. $reader->setOptions( array( 'format' => 'translation-[LOCALE].xml' ) );
6. $reader->initReader( 'nb_NO' );
7.
8. $cacheObj = new ezcCacheStorageFileArray( dirname( __FILE__ ). '/translations-cache' );
9. $writer = new ezcTranslationCacheBackend( $cacheObj );
10. $writer->initWriter( 'nb_NO' );
11.
12. foreach ( $reader as $contextName => $contextData )
13. {
14. $writer->storeContext( $contextName, $contextData );
15. }
16.
17. $reader->deInitReader();
18. $writer->deInitWriter();
19. ?>
In the lines 4 to 6 we set up the reader interface, like we did in
Example 4. Then we continue in lines 8 to 10 to initialize the writer. You
would want to keep the locale for both the reader and the writer the same of
course. In line 12 we use foreach() to iterate over all the contexts through the
reader interface and we use the ezcTranslationContextWrite::storeContext()
method in line 14 to store the read context object to the cache. After we
iterated over all the contexts and stored them we initialize the reader and
writer in lines 17-18. After you ran this script, the script from Example 5
will work fine too (as the cache has now all the contexts).