| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312 |
- <?xml version="1.0" encoding="UTF-8"?>
- <!-- Reviewed: no -->
- <sect1 id="zend.translate.using">
- <title>Using Translation Adapters</title>
- <para>
- The next step is to use the adapter within your code.
- </para>
- <example id="zend.translate.using.example1">
- <title>Example of single-language PHP code</title>
- <programlisting language="php"><![CDATA[
- print "Example\n";
- print "=======\n";
- print "Here is line one\n";
- print "Today is the " . date("d.m.Y") . "\n";
- print "\n";
- print "Here is line two\n";
- ]]></programlisting>
- </example>
- <para>
- The example above shows some output with no support for translation.
- You probably write your code in your native language.
- Generally you need to translate not only the output,
- but also error and log messages.
- </para>
- <para>
- The next step is to integrate Zend Translate into your existing code.
- Of course it is much easier if you had already written your code with
- translation in mind, than changing your code afterwards.
- </para>
- <example id="zend.translate.using.example2">
- <title>Example of multi-lingual PHP code</title>
- <programlisting language="php"><![CDATA[
- $translate = new Zend_Translate('gettext', '/my/path/source-de.mo', 'de');
- $translate->addTranslation('/path/to/translation/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";
- $translate->setLocale('fr');
- print $translate->_("Here is line two") . "\n";
- ]]></programlisting>
- </example>
- <para>
- Now let's take a deeper look into what has been done and how to
- integrate <classname>Zend_Translate</classname> into your own code.
- </para>
- <para>
- Create a new <classname>Zend_Translate</classname> object and define the base adapter:
- <programlisting language="php"><![CDATA[
- $translate = new Zend_Translate
- 'gettext',
- '/path/to/translation/source-de.mo',
- 'de'
- );
- ]]></programlisting>
- In this example we chose the
- <emphasis>Gettext Adapter</emphasis>.
- We place our file <emphasis>source-de.mo</emphasis>
- into the directory <emphasis>/path/to/translation</emphasis>.
- The gettext file will have German translation included,
- and we also added another language source for French.
- </para>
- <para>
- The next step is to wrap all strings which are to be translated.
- The simplest approach is to have only simple strings or sentences
- like this:
- <programlisting language="php"><![CDATA[
- print $translate->_("Example") . "\n";
- print "=======\n";
- print $translate->_("Here is line one") . "\n";
- ]]></programlisting>
- Some strings do not needed to be translated.
- The separating line is always a separating line,
- even in other languages.
- </para>
- <para>
- Having data values integrated into a translation string is also
- supported through the use of embedded parameters.
- <programlisting language="php"><![CDATA[
- printf($translate->_("Today is the %1\$s") . "\n", date("d.m.Y"));
- ]]></programlisting>
- Instead of <code>print()</code>, use the <code>printf()</code>
- function and replace all parameters with <code>%1\$s</code> parts.
- The first is <code>%1\$s</code>, the second is <code>%2\$s</code>,
- and so on. This way a translation can be done without knowing
- the exact value. In our example, the date is always the actual day,
- but the string can be translated without the knowledge of the actual
- day.
- </para>
- <para>
- Each string is identified in the translation storage by a message ID.
- You can use message IDs instead of strings in your code, like this:
- <programlisting language="php"><![CDATA[
- print $translate->_(1) . "\n";
- print "=======\n";
- print $translate->_(2) . "\n";
- ]]></programlisting>
- But doing this has several disadvantages:
- </para>
- <para>
- You can not see what your code should output just by viewing your code.
- </para>
- <para>
- Also you will have problems if some strings are not translated.
- You must always keep in mind how translation works.
- First <classname>Zend_Translate</classname> checks whether the specified language has a translation
- for the given message ID or string.
- If no translation string has been found it refers to the next lower
- level language as defined within <classname>Zend_Locale</classname>.
- So "<emphasis>de_AT</emphasis>" becomes
- "<emphasis>de</emphasis>" only.
- If there is no translation found for
- "<emphasis>de</emphasis>" either,
- then the original message is returned.
- This way you always have an output, even in case the message translation
- does not exist in your message storage.
- <classname>Zend_Translate</classname> never throws an error or exception when translating
- strings.
- </para>
- <sect2 id="zend.translate.using.structure">
- <title>Translation Source Structures</title>
- <para>
- Your next step is to create the translation sources for the
- languages you want to translate.
- Every adapter is created its own way as described here,
- but there are common features applicable for all adapters.
- </para>
- <para>
- You have to decide where to store your translation source files.
- Using <classname>Zend_Translate</classname> you are not restricted in any way.
- The following structures are preferable:
- </para>
- <itemizedlist>
- <listitem>
- <para>
- Single structured source
- </para>
- <programlisting><![CDATA[
- /application/
- /languages/
- /languages/lang.en
- /languages/lang.de
- /library/
- ]]></programlisting>
- <para>
- Positive: all source files for every languages are stored
- in one directory. No splitting of related files.
- </para>
- </listitem>
- <listitem>
- <para>
- Language structured source
- </para>
- <programlisting><![CDATA[
- /application/
- /languages/
- /languages/en/
- /languages/en/first.en
- /languages/en/second.en
- /languages/de/
- /languages/de/first.de
- /languages/de/second.de
- /library
- ]]></programlisting>
- <para>
- Positive: Every language is stored in their own directories.
- Easy translation, as every language team has to translate
- only one directory. Also the usage of multiple files is transparent.
- </para>
- </listitem>
- <listitem>
- <para>
- Application structured source
- </para>
- <programlisting><![CDATA[
- /application/
- /application/languages/
- /application/languages/first.en
- /application/languages/first.de
- /application/languages/second.en
- /application/languages/second.de
- /library/
- ]]></programlisting>
- <para>
- Positive: all source files for every language are stored
- in one directory. No splitting of related files.
- </para>
- <para>
- Negative: having multiple files for the same language can be
- problematic.
- </para>
- </listitem>
- <listitem>
- <para>
- Gettext structured source
- </para>
- <programlisting><![CDATA[
- /application/
- /languages/
- /languages/de/
- /languages/de/LC_MESSAGES/
- /languages/de/LC_MESSAGES/first.mo
- /languages/de/LC_MESSAGES/second.mo
- /languages/en/
- /languages/en/LC_MESSAGES/
- /languages/en/LC_MESSAGES/first.mo
- /languages/en/LC_MESSAGES/second.mo
- /library/
- ]]></programlisting>
- <para>
- Positive: existing gettext sources can be used without changing
- structure.
- </para>
- <para>
- Negative: having sub-sub directories may be confusing
- for people who have not used gettext before.
- </para>
- </listitem>
- <listitem>
- <para>
- File structured source
- </para>
- <programlisting><![CDATA[
- /application/
- /application/models/
- /application/models/MyModel.php
- /application/models/MyModel.de
- /application/models/MyModel.en
- /application/controllers/
- /application/controllers/MyController.php
- /application/controllers/MyController.de
- /application/controllers/MyController.en
- /library/
- ]]></programlisting>
- <para>
- Positive: translation files are localted near their source.
- </para>
- <para>
- Negative: too many and also small translation files result in
- being tendious to translate.
- Also every file has to be added as translation source.
- </para>
- </listitem>
- </itemizedlist>
- <para>
- Single structured and language structured source files are most
- usable for <classname>Zend_Translate</classname>.
- </para>
- <para>
- So now, that we know which structure we want to have,
- we should create our translation source files.
- </para>
- </sect2>
- <sect2 id="zend.translate.using.source.array">
- <title>Creating Array source files</title>
- <para>
- Array source files are plain arrays. But you have to define them
- manually since there is no tool to aid this.
- But because they are so simple, it's the fastest way to look up
- messages if your code works as expected. It's generally the best
- adapter to get started with translation business.
- </para>
- <programlisting language="php"><![CDATA[
- $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');
- ]]></programlisting>
- <para>
- Since release 1.5 it is also supported to have arrays included within an external file.
- You just have to provide the filename and <classname>Zend_Translate</classname> will automatically
- include it and look for the array. See the following example for details:
- </para>
- <programlisting language="php"><![CDATA[
- // myarray.php
- return array(
- 'message1' => 'Nachricht1',
- 'message2' => 'Nachricht2',
- 'message3' => 'Nachricht3');
- // controller
- $translate = new Zend_Translate('array', '/path/to/myarray.php', 'de');
- ]]></programlisting>
- <note>
- <para>
- Files which do not return an array will fail to be included.
- Also any output within this file will be ignored and suppressed.
- </para>
- </note>
- </sect2>
- <sect2 id="zend.translate.using.source.gettext">
- <title>Creating Gettext source files</title>
- <para>
- Gettext source files are created by GNU's gettext library.
- There are several free tools available that can parse your
- code files and create the needed gettext source files.
- These have the extension <emphasis>*.mo</emphasis>
- and they are binary files.
- An open source tool for creating the files is
- <ulink url="http://sourceforge.net/projects/poedit/">poEdit</ulink>.
- This tool also supports you during the translation process itself.
- </para>
- <programlisting language="php"><![CDATA[
- // We accume 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');
- ]]></programlisting>
- <para>
- As you can see the adapters are used exactly the same way,
- with one small difference:
- change <emphasis>array</emphasis> to <emphasis>gettext</emphasis>. All other usages are exactly
- the same as with all other adapters.
- With the gettext adapter you no longer have to be aware of
- gettext's standard directory structure,
- bindtextdomain and textdomain.
- Just give the path and filename to the adapter.
- </para>
- <note>
- <para>
- You should always use UTF-8 as source encoding.
- Otherwise you will have problems when using two
- different source encodings.
- E.g. one of your source files is encoded
- with ISO-8815-11 and another one with CP815.
- You can set only one encoding for your source file,
- so one of your languages probably will not display correctly.
- </para>
- <para>
- UTF-8 is a portable format which supports all languages.
- When using UTF-8 for all languages, you will eliminate
- the problem of incompatible encodings.
- </para>
- </note>
- <para>
- Many gettext editors add adapter informations as empty translation string.
- This is the reason why empty strings are not translated when using the
- gettext adapter. Instead they are erased from the translation table and
- provided by the <code>getAdapterInfo()</code> method. It will return
- the adapter informations for all added gettext files as array using the
- filename as key.
- </para>
- <programlisting language="php"><![CDATA[
- // Getting the adapter informations
- $translate = new Zend_Translate('gettext', '/path/to/english.mo', 'en');
- print_r($translate->getAdapterInfo());
- ]]></programlisting>
- </sect2>
- <sect2 id="zend.translate.using.source.tmx">
- <title>Creating TMX source files</title>
- <para>
- TMX source files are a new industry standard.
- They have the advantage of being XML files and so they are
- readable by every editor and of course by humans.
- You can either create TMX files manually with a text editor,
- or you can use a special tool. But most tools currently available for
- creating TMX source files are not freely available.
- </para>
- <example id="zend.translate.using.source.tmx.example">
- <title>Example TMX file</title>
- <programlisting language="xml"><![CDATA[
- <?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>
- ]]></programlisting>
- <programlisting language="php"><![CDATA[
- $translate = new Zend_Translate('tmx', 'path/to/mytranslation.tmx', 'en');
- ]]></programlisting>
- </example>
- <para>
- TMX files can have several languages within the same file.
- All other included languages are added automatically,
- so you do not have to call <code>addLanguage()</code>.
- </para>
- <para>
- If you want to have only specified languages from the source translated
- you can set the option '<code>defined_language</code>' to <code>true</code>.
- With this option you can add the wished languages explicitly with
- <code>addLanguage()</code>. The default value for this option is to add all
- languages.
- </para>
- </sect2>
- <sect2 id="zend.translate.using.source.csv">
- <title>Creating CSV source files</title>
- <para>
- CSV source files are small and human readable.
- If your customers want to translate their own,
- you will probably use the CSV adapter.
- </para>
- <example id="zend.translate.using.source.csv.example">
- <title>Example CSV file</title>
- <programlisting><![CDATA[
- #Example csv file
- message1;Nachricht1
- message2;Nachricht2
- ]]></programlisting>
- <programlisting language="php"><![CDATA[
- $translate = new Zend_Translate('csv', '/path/to/mytranslation.csv', 'de');
- $translate->addTranslation('path/to/other.csv', 'fr');
- ]]></programlisting>
- </example>
- <para>
- There are three different options for the CSV adapter.
- You can set '<code>delimiter</code>', '<code>limit</code>' and
- '<code>enclosure</code>'.
- </para>
- <para>
- The default delimiter for CSV string is '<code>;</code>', but
- with the option '<code>delimiter</code>'
- you can decide to use another one.
- </para>
- <para>
- The default limit for a line within a CSV file is '<code>0</code>'. This means
- that the end of a CSV line is searched automatically. If you set
- '<code>limit</code>' to any value, then the CSV file will be
- read faster, but any line exceeding this limit will be truncated.
- </para>
- <para>
- The default enclosure to use for CSV files is '<code>"</code>'. You can
- set a different one using the option '<code>enclosure</code>'.
- </para>
- <example id="zend.translate.using.source.csv.example2">
- <title>Second CSV file example</title>
- <programlisting><![CDATA[
- # Example CSV file
- "message,1",Nachricht1
- message2,"Nachricht,2"
- "message3,",Nachricht3
- ]]></programlisting>
- <programlisting language="php"><![CDATA[
- $translate = new Zend_Translate(
- 'csv',
- '/path/to/mytranslation.csv',
- 'de',
- array('delimiter' => ','));
- $translate->addTranslation('/path/to/other.csv', 'fr');
- ]]></programlisting>
- </example>
- </sect2>
- <sect2 id="zend.translate.using.source.ini">
- <title>Creating INI source files</title>
- <para>
- INI source files are human readable but normally not very small as they also
- include other data beside translations. If you have data which shall be
- editable by your customers you can use the INI adapter.
- </para>
- <example id="zend.translate.using.source.ini.example">
- <title>Example INI file</title>
- <programlisting><![CDATA[
- [Test]
- ;TestPage Comment
- Message_1="Nachricht 1 (de)"
- Message_2="Nachricht 2 (de)"
- Message_3="Nachricht :3 (de)"
- ]]></programlisting>
- <programlisting language="php"><![CDATA[
- $translate = new Zend_Translate('ini', '/path/to/mytranslation.ini', 'de');
- $translate->addTranslation('/path/to/other.ini', 'it');
- ]]></programlisting>
- </example>
- <para>
- INI files have several restrictions. If a value in the ini file contains any
- non-alphanumeric characters it needs to be enclosed in double-quotes (<code>"</code>).
- There are also reserved words which must not be used as keys for ini files.
- These include: <code>null</code>, <code>yes</code>, <code>no</code>, <code>true</code>,
- and <code>false</code>. Values <code>null</code>, <code>no</code> and <code>false</code> results
- in <code>""</code>, <code>yes</code> and <code>true</code> results in <code>1</code>. Characters <code>{}|&~![()"</code> must not be used anywhere
- in the key and have a special meaning in the value. Do not use them as it will
- produce unexpected behaviour.
- </para>
- </sect2>
- <sect2 id="zend.translate.using.options">
- <title>Options for adapters</title>
- <para>
- Options can be used with all adapters. Of course the options are different for all adapters.
- You can set options when you create the adapter. Actually there is one option which is available
- to all adapters: '<code>clear</code>' sets if translation data should be added to existing
- one or not. Standard behaviour is to add new translation data to existing one. But the
- translation data is only cleared for the selected language. So other languages remain
- untouched.
- </para>
- <para>
- You can set options temporarily when using <code>addTranslation($data, $locale, array $options = array())</code>
- as third and optional parameter. And you can use the method <code>setOptions()</code> to
- set the options permanently.
- </para>
- <example id="zend.translate.using.options.example">
- <title>Using translation options</title>
- <programlisting language="php"><![CDATA[
- // define ':' as separator for the translation source files
- $options = array('delimiter' => ':');
- $translate = new Zend_Translate(
- 'csv',
- '/path/to/mytranslation.csv',
- 'de',
- $options);
- ...
- // clear the defined language and use new translation data
- $options = array('clear' => true);
- $translate->addTranslation('/path/to/new.csv', 'fr', $options);
- ]]></programlisting>
- </example>
- <para>
- Here you can find all available options for the different adapters with a description of their usage:
- </para>
- <table id="zend.translate.using.options.alloptions">
- <title>Options for translation adapters</title>
- <tgroup cols="4">
- <thead>
- <row>
- <entry>Option</entry>
- <entry>Adapter</entry>
- <entry>Description</entry>
- <entry>Default value</entry>
- </row>
- </thead>
- <tbody>
- <row>
- <entry>clear</entry>
- <entry>all</entry>
- <entry>
- If set to true, the already read translations will be cleared. This can be used
- instead of creating a new instance when reading new translation data
- </entry>
- <entry><emphasis>false</emphasis></entry>
- </row>
- <row>
- <entry>disableNotices</entry>
- <entry>all</entry>
- <entry>
- If set to true, all notices regarding not available translations will be
- disabled. You should set this option to true in production environment
- </entry>
- <entry><emphasis>false</emphasis></entry>
- </row>
- <row>
- <entry>ignore</entry>
- <entry>all</entry>
- <entry>
- All directories and files beginning with this prefix will be ignored when
- searching for files. This value defaults to <emphasis>'.'</emphasis>
- which leads to the behavior that all hidden files will be ignored. Setting this
- value to <code>'tmp'</code> would mean that directories and files like
- <code>'tmpImages'</code> and <code>'tmpFiles'</code>
- would be ignored as well as all subsequent directories
- </entry>
- <entry><emphasis>.</emphasis></entry>
- </row>
- <row>
- <entry>log</entry>
- <entry>all</entry>
- <entry>
- An instance of <classname>Zend_Log</classname> where untranslated messages and notices will be
- written to
- </entry>
- <entry><emphasis>null</emphasis></entry>
- </row>
- <row>
- <entry>logMessage</entry>
- <entry>all</entry>
- <entry>
- The message which will be written into the log
- </entry>
- <entry><emphasis>Untranslated message within '%locale%': %message%</emphasis></entry>
- </row>
- <row>
- <entry>logUntranslated</entry>
- <entry>all</entry>
- <entry>
- When this option is set to true, all message IDs which can not be
- translated will be written into the attached log
- </entry>
- <entry><emphasis>false</emphasis></entry>
- </row>
- <row>
- <entry>scan</entry>
- <entry>all</entry>
- <entry>
- If set to null, no scanning of the directory structure will be done.
- If set to <classname>Zend_Translate::LOCALE_DIRECTORY</classname> the locale will be detected within the
- directory. If set to <classname>Zend_Translate::LOCALE_FILENAME</classname> the locale will be detected
- within the filename. See <xref linkend="zend.translate.using.detection" />
- for details
- </entry>
- <entry><emphasis>null</emphasis></entry>
- </row>
- <row>
- <entry>delimiter</entry>
- <entry>Csv</entry>
- <entry>Defines which sign is used as delimiter for separating source and translation</entry>
- <entry><emphasis>;</emphasis></entry>
- </row>
- <row>
- <entry>enclosure</entry>
- <entry>Csv</entry>
- <entry>Defines the enclosure character to be used. Defaults to a doublequote</entry>
- <entry><emphasis>"</emphasis></entry>
- </row>
- <row>
- <entry>length</entry>
- <entry>Csv</entry>
- <entry>Defines the maximum length of a csv line. When set to 0 it will be detected automatically</entry>
- <entry><emphasis>0</emphasis></entry>
- </row>
- </tbody>
- </tgroup>
- </table>
- <para>
- When you want to have self defined options, you are also able to use them within all adapters.
- The <code>setOptions()</code> method can be used to define your option. <code>setOptions()</code>
- needs an array with the options you want to set. If an given option exists it will be signed over.
- You can define as much options as needed as they will not be checked by the adapter. Just make sure
- not to overwrite any existing option which is used by an adapter.
- </para>
- <para>
- To return the option you can use the <code>getOptions()</code> method. When <code>getOptions()</code>
- is called without a parameter it will return all options set. When the optional parameter is given
- you will only get the specified option.
- </para>
- </sect2>
- <sect2 id="zend.translate.using.languages">
- <title>Handling languages</title>
- <para>
- When working with different languages there are a few methods which will be useful.
- </para>
- <para>
- The <code>getLocale()</code> method can be used to get the currently set language. It can either hold
- an instance of <classname>Zend_Locale</classname> or the identifier of a locale.
- </para>
- <para>
- The <code>setLocale()</code> method sets a new standard language for translation. This prevents the
- need of setting the optional language parameter more than once to the <code>translate()</code> method.
- If the given language does not exist, or no translation data is available for the language,
- <code>setLocale()</code> tries to downgrade to the language without the region if any was given.
- A language of <code>en_US</code> would be downgraded to <code>en</code>. When even the downgraded
- language can not be found an exception will be thrown.
- </para>
- <para>
- The <code>isAvailable()</code> method checks if a given language is already available. It returns
- <code>true</code> if data for the given language exist.
- </para>
- <para>
- And finally the <code>getList()</code> method can be used to get all currently set languages for an adapter
- returned as array.
- </para>
- <example id="zend.translate.using.languages.example">
- <title>Handling languages with adapters</title>
- <programlisting language="php"><![CDATA[
- // returns the currently set language
- $actual = $translate->getLocale();
- // you can use the optional parameter while translating
- echo $translate->_("my_text", "fr");
- // or set a new language
- $translate->setLocale("fr");
- echo $translate->_("my_text");
- // refer to the base language
- // fr_CH will be downgraded to fr
- $translate->setLocale("fr_CH");
- echo $translate->_("my_text");
- // check if this language exist
- if ($translate->isAvailable("fr")) {
- // language exists
- }
- ]]></programlisting>
- </example>
- <sect3 id="zend.translate.using.languages.automatic">
- <title>Automatical handling of languages</title>
- <para>
- Note that as long as you only add new translation sources with the <code>addTranslation()</code>
- method <classname>Zend_Translate</classname> will automatically set the best fitting language for your
- environment when you use one of the automatic locales which are '<code>auto</code>' or '<code>browser</code>'. So
- normally you will not need to call <code>setLocale()</code>. This should only be used in
- conjunction with automatic source detection.
- </para>
- <para>
- The algorithm will search for the best fitting locale depending on the user's browser and
- your environment. See the following example for details:
- </para>
- <example id="zend.translate.using.languages.automatic.example">
- <title>Automatically language detection</title>
- <programlisting language="php"><![CDATA[
- // Let's expect the browser returns these language settings:
- // HTTP_ACCEPT_LANGUAGE = "de_AT=1;fr=1;en_US=0.8";
- // Example 1:
- // When no fitting language is found, the message ID is returned
- $translate = new Zend_Translate(
- 'gettext',
- 'my_it.mo',
- 'auto',
- array('scan' => Zend_Translate::LOCALE_FILENAME));
- // Example 2:
- // Best found fitting language is 'fr'
- $translate = new Zend_Translate(
- 'gettext',
- 'my_fr.mo',
- 'auto',
- array('scan' => Zend_Translate::LOCALE_FILENAME));
- // Example 3:
- // Best found fitting language is 'de' ('de_AT' will be degraded)
- $translate = new Zend_Translate(
- 'gettext',
- 'my_de.mo',
- 'auto',
- array('scan' => Zend_Translate::LOCALE_FILENAME));
- // Example 4:
- // Returns 'it' as translation source and overrides the automatic settings
- $translate = new Zend_Translate(
- 'gettext',
- 'my_it.mo',
- 'auto',
- array('scan' => Zend_Translate::LOCALE_FILENAME));
- $translate->addTranslation('my_ru.mo', 'ru');
- $translate->setLocale('it_IT');
- ]]></programlisting>
- </example>
- <para>
- After setting a language manually with the <code>setLocale()</code> method the automatic
- detection will be switched off and overridden.
- </para>
- <para>
- If you want to use it again, you can set the language
- <emphasis>auto</emphasis> with <code>setLocale()</code> which will reactivate
- the automatic detection for <classname>Zend_Translate</classname>.
- </para>
- <para>
- Since Zend Framework 1.7.0 <classname>Zend_Translate</classname> also recognises an application
- wide locale. You can simply set a <classname>Zend_Locale</classname> instance to the registry like shown
- below. With this notation you can forget about setting the locale manually with each instance
- when you want to use the same locale multiple times.
- </para>
- <programlisting language="php"><![CDATA[
- // in your bootstrap file
- $locale = new Zend_Locale();
- Zend_Registry::set('Zend_Locale', $locale);
- // default language when requested language is not available
- $defaultlanguage = 'en';
- // somewhere in your application
- $translate = new Zend_Translate('gettext', 'my_de.mo');
- if (!$translate->isAvailable($locale->getLanguage())) {
- // not available languages are rerouted to another language
- $translate->setLocale($defaultlanguage);
- }
- $translate->getLocale();
- ]]></programlisting>
- </sect3>
- </sect2>
- <sect2 id="zend.translate.using.detection">
- <title>Automatic source detection</title>
- <para>
- <classname>Zend_Translate</classname> can detect translation sources automatically. So you don't have
- to declare each source file manually. You can let <classname>Zend_Translate</classname> do this job and
- scan the complete directory structure for source files.
- </para>
- <note>
- <para>
- Automatic source detection is available since Zend Framework version 1.5 .
- </para>
- </note>
- <para>
- The usage is quite the same as initiating a single translation source with one difference.
- You must give a directory which has to be scanned instead a file.
- </para>
- <example id="zend.translate.using.languages.directory.example">
- <title>Scanning a directory structure for sources</title>
- <programlisting language="php"><![CDATA[
- // assuming we have the following structure
- // /language/
- // /language/login/login.tmx
- // /language/logout/logout.tmx
- // /language/error/loginerror.tmx
- // /language/error/logouterror.tmx
- $translate = new Zend_Translate('tmx', '/language');
- ]]></programlisting>
- </example>
- <para>
- So <classname>Zend_Translate</classname> does not only search the given directory, but also all subdirectories for
- translation source files. This makes the usage quite simple. But <classname>Zend_Translate</classname> will ignore all
- files which are not sources or which produce failures while reading the translation data. So you
- have to make sure that all of your translation sources are correct and readable because you will
- not get any failure if a file is bogus or can not be read.
- </para>
- <note>
- <para>
- Depending on how deep your directory structure is and how much files are within this structure
- it can take a long time for <classname>Zend_Translate</classname> to complete.
- </para>
- </note>
- <para>
- In our example we have used the TMX format which includes the language to be used within the
- source. But many of the other source formats are not able to include the language within the
- file. Even this sources can be used with automatic scanning if you do some pre-requisits as
- described below:
- </para>
- <sect3 id="zend.translate.using.detection.directory">
- <title>Language through naming directories</title>
- <para>
- One way to include automatic language detection is to name the directories related to the
- language which is used for the sources within this directory. This is the easiest way and
- is used for example within standard gettext implementations.
- </para>
- <para>
- <classname>Zend_Translate</classname> needs the '<code>scan</code>' option to know that it should search the names of all
- directories for languages. See the following example for details:
- </para>
- <example id="zend.translate.using.detection.directory.example">
- <title>Directory scanning for languages</title>
- <programlisting language="php"><![CDATA[
- // assuming we have the following structure
- // /language/
- // /language/de/login/login.mo
- // /language/de/error/loginerror.mo
- // /language/en/login/login.mo
- // /language/en/error/loginerror.mo
- $translate = new Zend_Translate(
- 'gettext',
- '/language',
- null,
- array('scan' => Zend_Translate::LOCALE_DIRECTORY));
- ]]></programlisting>
- </example>
- <note>
- <para>
- This works only for adapters which do not include the language within the source file.
- Using this option for example with TMX will be ignored. Also language definitions within
- the filename will be ignored when using this option.
- </para>
- </note>
- <note>
- <para>
- You should be aware if you have several subdirectories under the same
- structure. Assuming we have a structure like
- <code>/language/module/de/en/file.mo</code>. In this case the path contains
- multiple strings which would be detected as locale. It could be either
- <code>de</code> or <code>en</code>. In such a case the behaviour is
- undefined and it is recommended to use file detection in such situations.
- </para>
- </note>
- </sect3>
- <sect3 id="zend.translate.using.detection.filename">
- <title>Language through filenames</title>
- <para>
- Another way to detect the language automatically is to use special filenames. You can either
- name the complete file or parts of a file after the used language. To use this way of detection
- you will have to set the '<code>scan</code>' option at initiation. There are several ways of naming the
- sourcefiles which are described below:
- </para>
- <example id="zend.translate.using.detection.filename.example">
- <title>Filename scanning for languages</title>
- <programlisting language="php"><![CDATA[
- // assuming we have the following structure
- // /language/
- // /language/login/login_en.mo
- // /language/login/login_de.mo
- // /language/error/loginerror_en.mo
- // /language/error/loginerror_de.mo
- $translate = new Zend_Translate(
- 'gettext',
- '/language',
- null,
- array('scan' => Zend_Translate::LOCALE_FILENAME));
- ]]></programlisting>
- </example>
- <sect4 id="zend.translate.using.detection.filename.complete">
- <title>Complete filename</title>
- <para>
- Having the whole file named after the language is the simplest way but only viable
- if you have only one file per language.
- </para>
- <programlisting><![CDATA[
- /languages/
- /languages/en.mo
- /languages/de.mo
- /languages/es.mo
- ]]></programlisting>
- </sect4>
- <sect4 id="zend.translate.using.detection.filename.extension">
- <title>Extension of the file</title>
- <para>
- Another simple way to use the extension of the file for language detection.
- But this may be confusing since you will no longer have an idea which extension the file
- originally had.
- </para>
- <programlisting><![CDATA[
- /languages/
- /languages/view.en
- /languages/view.de
- /languages/view.es
- ]]></programlisting>
- </sect4>
- <sect4 id="zend.translate.using.detection.filename.token">
- <title>Filename tokens</title>
- <para>
- <classname>Zend_Translate</classname> is also capable of detecting the language if it is included within the
- filename. But if you go this way you will have to separate the language with a token.
- There are three supported tokens which can be used: a dot '.', an underscore '_', or
- a hyphen '-'.
- </para>
- <programlisting><![CDATA[
- /languages/
- /languages/view_en.mo -> detects english
- /languages/view_de.mo -> detects german
- /languages/view_it.mo -> detects italian
- ]]></programlisting>
- <para>
- The first found string delimited by a token which can be interpreted as a locale will be used. See the following
- example for details.
- </para>
- <programlisting><![CDATA[
- /languages/
- /languages/view_en_de.mo -> detects english
- /languages/view_en_es.mo -> detects english and overwrites the first file
- /languages/view_it_it.mo -> detects italian
- ]]></programlisting>
- <para>
- All three tokens are used to detect the locale. When the filename contains multiple tokens,
- the first found token depends on the order of the tokens which are used. See the following
- example for details.
- </para>
- <programlisting><![CDATA[
- /languages/
- /languages/view_en-it.mo -> detects english because '_' will be used before '-'
- /languages/view-en_it.mo -> detects italian because '_' will be used before '-'
- /languages/view_en.it.mo -> detects italian because '.' will be used before '_'
- ]]></programlisting>
- </sect4>
- </sect3>
- </sect2>
- <sect2 id="zend.translate.using.istranslated">
- <title>Checking for translations</title>
- <para>
- Normally text will be translated without any computation. But sometimes it is necessary to
- know if a text is translated or not, therefor the <code>isTranslated()</code>
- method can be used.
- </para>
- <para>
- <code>isTranslated($messageId, $original = false, $locale = null)</code> takes
- the text you want to check as its first parameter, and as optional third parameter the locale
- for which you want to do the check. The optional second parameter declares whether translation
- is fixed to the declared language or a lower set of translations can be used. If you have a text which
- can be returned for 'en' but not for 'en_US' you will normally get the translation returned, but by
- setting <code>$original</code> to true, <code>isTranslated()</code> will return false.
- </para>
- <example id="zend.translate.using.istranslated.example">
- <title>Checking if a text is translatable</title>
- <programlisting language="php"><![CDATA[
- $english = array(
- 'message1' => 'Nachricht 1',
- 'message2' => 'Nachricht 2',
- 'message3' => 'Nachricht 3');
- $translate = new Zend_Translate('array', $english, 'de_AT');
- if ($translate->isTranslated('message1')) {
- print "'message1' can be translated";
- }
- if (!($translate->isTranslated('message1', true, 'de'))) {
- print "'message1' can not be translated to 'de'"
- . " as it's available only in 'de_AT'";
- }
- if ($translate->isTranslated('message1', false, 'de')) {
- print "'message1' can be translated in 'de_AT' as it falls back to 'de'";
- }
- ]]></programlisting>
- </example>
- </sect2>
- <sect2 id="zend.translate.using.logging">
- <title>How to log not found translations</title>
- <para>
- When you have a bigger site or you are creating the translation files manually, you often have
- the problem that some messages are not translated. But there is an easy solution for you when you
- are using <classname>Zend_Translate</classname>.
- </para>
- <para>
- You have to follow two or three simple steps. First, you have to create an instance of
- <classname>Zend_Log</classname>. Then you have to attach this instance to <classname>Zend_Translate</classname>.
- See the following example:
- </para>
- <example id="zend.translate.using.logging.example">
- <title>Log translations</title>
- <programlisting language="php"><![CDATA[
- $translate = new Zend_Translate('gettext', $path, 'de');
- // Create a log instance
- $writer = new Zend_Log_Writer_Stream('/path/to/file.log');
- $log = new Zend_Log($writer);
- // Attach it to the translation instance
- $translate->setOptions(array(
- 'log' => $log,
- 'logUntranslated' => true));
- $translate->translate('unknown string');
- ]]></programlisting>
- </example>
- <para>
- Now you will have a new notice in the log: <code>Untranslated message within 'de': unknown string</code>.
- </para>
- <note>
- <para>
- You should note that any translation which can not be found will be logged. This means
- all translations when a user requests a language which is not supported. Also every request
- for a message which can not be translated will be logged. Be aware, that 100 people
- requesting the same translation, will result 100 logged notices.
- </para>
- </note>
- <para>
- This feature can not only be used to log messages but also to attach this untranslated messages
- into an empty translation file. To do so you will have to write your own log writer which
- writes the format you want to have and strips the prepending "Untranslated message".
- </para>
- <para>
- You can also set the '<code>logMessage</code>' option when you want to have your own log message.
- Use the '<code>%message%</code>' token for placing the messageId within your log message, and the
- '<code>%locale%</code>' token for the requested locale. See the following example for a self
- defined log message:
- </para>
- <example id="zend.translate.using.logging.example2">
- <title>Self defined log messages</title>
- <programlisting language="php"><![CDATA[
- $translate = new Zend_Translate('gettext', $path, 'de');
- // Create a log instance
- $writer = new Zend_Log_Writer_Stream('/path/to/file.log');
- $log = new Zend_Log($writer);
- // Attach it to the translation instance
- $translate->setOptions(array(
- 'log' => $log,
- 'logMessage' => "Missing '%message%' within locale '%locale%'",
- 'logUntranslated' => true));
- $translate->translate('unknown string');
- ]]></programlisting>
- </example>
- </sect2>
- <sect2 id="zend.translate.using.sourcedata">
- <title>Accessing source data</title>
- <para>
- Sometimes it is useful to have access to the translation source data. Therefor
- the following two functions are provided.
- </para>
- <para>
- The <code>getMessageIds($locale = null)</code> method returns all known message IDs as array.
- </para>
- <para>
- The <code>getMessages($locale = null)</code> method returns the complete translation source as
- an array. The message ID is used as key and the translation data as value.
- </para>
- <para>
- Both methods accept an optional parameter <code>$locale</code> which, if set, returns the
- translation data for the specified language. If this parameter is not given, the actual set
- language will be used. Keep in mind that normally all translations should be available in all
- languages. Which means that in a normal situation you will not have to set this parameter.
- </para>
- <para>
- Additionally the <code>getMessages()</code> method can be used to return the complete
- translation dictionary using the pseudo-locale 'all'. This will return all available
- translation data for each added locale.
- </para>
- <note>
- <para>
- Attention: the returned array can be <emphasis>very big</emphasis>,
- depending on the number of added locales and the amount of translation data.
- </para>
- </note>
- <example id="zend.translate.using.sourcedata.example">
- <title>Handling languages with adapters</title>
- <programlisting language="php"><![CDATA[
- // returns all known message IDs
- $messageIds = $translate->getMessageIds();
- print_r($messageIds);
- // or just for the specified language
- $messageIds = $translate->getMessageIds('en_US');
- print_r($messageIds);
- // returns all the complete translation data
- $source = $translate->getMessages();
- print_r($source);
- ]]></programlisting>
- </example>
- </sect2>
- </sect1>
- <!--
- vim:se ts=4 sw=4 et:
- -->
|