Introducing Zend_Translate
November 13th, 2009One of the big things that the Zend Framework has over other frameworks is the built-in locale and language tools provided by Zend_Translate and Zend_Locale. All this comes with plugins into Zend’s templating system via Zend View Helpers. This video covers setting up some language-friendly routes, writing a custom language switcher Zend Controller Plugin and then running some a CSV-formatted language file.
Grab a copy of the project or browse the repository.

Nice zendcast! Thank you very much.
One suggestion: for next series on Zend_Translate, what about the ” Approach”
http://devzone.zend.com/article/4513-Zend-Framework-and-Translation
I founded it very useful.
I mean on last comment the “i18n Approach”
(wrote “i18n” in nHTML tags)
Sorry.
Nice!
Just one thing. Are you sure you need en.csv? You are translating en->en, that have no sense, right?
Imagine how much extra work you’ll have to maintain yet another unnecessary language.
ie Great like all the screen casts Jon .
You are really helping the Zend framework developers .
Thank you and you have a great future .
Good luck.
mmm, that’s a good question. I think that you might be able to get away with not needing the language, however it wouldn’t properly illustrate the multi-lingual features of Zend_Translate (since we’d basically just have the keys for a language without values and one with values)
@Jon I rhink letting Zend_Translate to be null for en will do the job.
Yep, umpirsky but It’s not a good practice
@Aleksandar Hm, ok, what is the right way to do it (without messing with yet another language – since it is a source language)?
[...] Zum Webcast Kommentar abgeben | Trackback Keine Kommentare [...]
@umpirsky I am not sure that I get u 100%. But keys and values on English will not be always the same, so it’s much easier to just change the values only. For example consider this:
#Serbian csv file
error_message;Popunite sva polja oznacena crvenom
#English csv file
error_message;Please fill all fields marked in red
@Aleksandar Znaš Srpski???
Well, maybe if your keys are not correct language phrases. But, for example, if you use gettext, then it’s pretty common to use poedit to parse templates, and automatically pick all strings under $this->tranlate() helper and easyer generate po files. In that case, english po file (if english is your source language) will have same keys and values, which is, you’ll agree a total waste of time and memorry. With this mechanism it’s much easyer to add multilang to existing non-multilang site as well.
@umpirsky pa srbin bre, normalno
Yeap that’s true in the case u mentioned. But all I am saying is, that it simply isn’t best practice, because if u analyze any opensource multilingual application u will discover the same concept which Jon used in this tutorial.
@Aleksandar Wow
Odakle si? Daj da razmenimo kontakte.
Once again, thanks Jon for this useful screencast.
I personally like putting key-value pairs only for longer strings (ie. form fields description) for my defaulted language, while keeping simple phrases like “Home”, “Contact” or “Sign Up” intact.
One alternative solution for large applications is to have language files in {locale}-{module}.csv format, although you would probably want to use some other translation adapter in that case. My personal preference is database adapter that caches values in array adapter format, that way I can easily edit/add more languages directly from admin panel and even hire someone to do that without the need to edit any file.
@Aleksandar, umpirsky: vise nas je nego sto mislimo =)
@Milos Woohoo!
I watched your tutorial about setting up Zend Translate. I am using Zend Translate with gettext adapter for translating text on my website.
Now I want to translate the url, so that the module, controller and action names are also translated.
In my bootstrap I define a Zend_Controller_Router_Route_Module to translate the url, and this is working for the default controllers, but not for the module I have. when I translate this ‘order’ module, I get the error that ‘order’ is not a valid controller.
I found this issue: http://framework.zend.com/issues/browse/ZF-7298?page=com.atlassian.jirafisheyeplugin%3Acrucible-issuepanel
and I am using this patched version, but it still not working to translate the module/controller/action urls.
Can you help me with this problem?
what about text appearing other places besides views and using poedit. Can poedit be configured to either use ->translate (in views) or something else like _() in say controllers.
@Colleen Sure, you can add multiple keywords.
Hello!
Thank you for good tutorial.
I’ve a simple question:
Is it a good way to initialize Zend Cache in preDispath in order to cache Zend Translate?
Or it’s better to initialize cache in bootstrap and call it in preDispatch?
Thank you!
Hi Kuzma,
I think it depends entirely on what you’re caching. For Zend_Translate, since it’s likely site-wide, I would probably initialize the cache in the bootstrap. if only some controller’s were multi-lingual, then I would either use preDispatch or write a simple controller plugin that would load the caching code on those particular controllers.
Hey Jon, great video
, I just found this another tutorial to achieve the same but using the config and the route chain object (http://www.m4d3l-network.com/2009/06/29/add-language-route-to-your-zend-framework-project/)
Actually there is one big drawback of this:
I am building menus for navigation view helper from XML config file. ZF then creates links in menu only for default language (in this case en, because it is set as default lang in routes).
So if I switched from ‘en’ to ‘pl’ polish language i.e. http://example.com/pl/links I have menu with:
/en/contact
/en/another and ect.
I have to workaround this using:
if (preg_match(‘(en|pl)’, substr($_SERVER['REQUEST_URI'], 1, 2), $match)) {
$lang = $match[0]; //LOOK HERE
$locale = new Zend_Locale($availableLangs[$lang]);
}
$langModuleControllerActionRoute = new Zend_Controller_Router_Route(
‘:lang/:module/:controller/:action/*’,
array(
‘module’ => ‘default’,
‘controller’ => ‘index’,
‘action’ => ‘index’,
‘lang’ => $lang //AND HERE
),
array (‘lang’ => $regex, ‘module’ => ‘[a-z]+’ )
);
Hey Jon,
Another great ZendCast on a topic that I’m faced with a lot here in Belgium. We need to provide web applications using English, Dutch, French and sometimes German (DFG being our official languages), so I’m dealing a lot with Zend Translate.
My approach is to use an array containing the lang param as key and the locale as value, to make a swift transition between languages and locales.
$availableLanguages = array (
‘en’ => ‘en_US’,
‘nl’ => ‘nl_BE’,
‘fr’ => ‘fr_BE’,
‘de’ => ‘de_BE’,
);
When setting up translations specifications, I already add them to Zend_Translate en using setLocale() method to switch languages.
// language given as param $lang
// if language is not available default to en
$lang = strtolower($lang);
if (!in_array($lang, array_keys($availableLanguages)) {
$lang = ‘en’;
}
$translate = new Zend_Translate(‘csv’, APPLICATION_PATH . ‘/configs/lang/en.csv’, ‘en_US’);
$translate->addTranslation(APPLICATION_PATH . ‘/configs/lang/nl.csv’, ‘nl_BE’)
->addTranslation(APPLICATION_PATH . ‘/configs/lang/fr.csv’, ‘fr_BE’)
->addTranslation(APPLICATION_PATH . ‘/configs/lang/de.csv’, ‘de_BE’);
$translate->setLocale($defaultLocale);
This way you can add more languages to your application as they become available, while maintaining a structured way of development.
This also caches very good, even if the translation source is a database, web service or any other kind of ‘expensive’ datasource.
Hope this works great for you all.
Michelangelo
[...] I have no clue about this but maybe look at this [...]
Hi.
My trouble is a little different but I’m at my wits end
I should set up a router with dynamic translation for a better SEO
for instance
en
live/write
it
dalvivo/scrivi
I ended up with this code
public function routeStartup(Zend_Controller_Request_Abstract $request) { //$this->_layout = Zend_Controller_Action_HelperBroker::getStaticHelper('Layout'); // $this->_view = $this->_layout->getView(); try{ $i18n = Zend_Registry::get('i18n'); $locale = $i18n['locale']; $lang = $i18n['lang']; } catch(Zend_Exception $e){ $locale = self::DEFAULT_LOCAL; $lang = self::DEFAULT_LANG; } $zl = new Zend_Locale(); $zl->setLocale($locale); Zend_Registry::set('Zend_Locale', $zl); $fileName = APPLICATION_PATH . '/configs/lang/'. $lang . '.php'; if(!file_exists($fileName)){ $fileName = APPLICATION_PATH . '/configs/lang/'. self::DEFAULT_LANG . '.php'; } if(strpos($lang, '_') !== false){ list($lang,$region)= explode('_',$lang); } $translate = new Zend_Translate('array', $fileName , $lang); Zend_Registry::set('Zend_Translate', $translate); Zend_Form::setDefaultTranslator($translate); $route = new Zend_Controller_Router_Route( ':module/:@controller/:@action/*', array( 'module'=>'default', 'controller' => 'index', 'action' => 'index' ) ); $this->_router->addRoute('default', $route); } The trouble is with default module. it works with en default/live/write it default/dalvivo/scrivi en admin/login it admin/logati but it doesn't work with en live/write it dalvivo/scrivi even if I have 'module'=>'default' Can you help me please ? Bye. PS Sorry for the quite OT[...] gibt einen neuen Zend Cast über Zend_Translate von Jon [...]
Hey, great video!
I try to implement parts in to my existing code and gets “Route default is not defined “. Any direct clues what this is?
sounds like you don’t have a route in your router called “default” and you’ve disabled the default routes… you need some kind of catch-all setup.
@Nicklas
It is probable that you have somewhere url helper with default route, like:
<a href="url(
array(‘module’ => ‘admin’), ‘default’, true) ?>”>Administration
In this case default route doesn’t exist, so you need to change to:
<a href="url(
array(‘module’ => ‘admin’), ‘myRouteName’, true) ?>”>Administration
And as I said this entire example is useless if you want your Zend_Navigation to work correctly.
Did a write up on how to configure POEdit with Zend Framework for those of you that want to work with the gettext (.po / .mo) adapter.
http://blog.hackix.com/2010/01/configuring-poedit-for-zend-framework-projects/
[...] thanks to Zend Cast for the inspiration! Series Navigation«How to make POEdit detect source strings in Zend [...]
Can anybody explain how to dynamically translate labels coming from the navigation.xml?
@Jochen
It is well explained in documentation, so look for it.
Basically, if you initiate Zend_Translate before reading from xml, and register it in Zend_Registry, Zend_Navigation will find all translations automatically.
It (Zend_Navigation) will look for keys, like if you have in xml:
Generators
the key for translation is ‘Generators’. To simply put, it works out of the box.
This #$%#$ reply script, stripped some things from my comment. What I meant was:
[title]Generators[/title] , (replace square brackets).
hi!
nice screencast – really helped me to got through ZF details.
can i ask what are you using to beautify var_dump() output?
Hi Mylonov,
I’m using XDebug
hey Jon a little offtop. But how did u get ur var_dump() to print out nicely, U din’t even put like pre tag, as I saw. thanks
xdebug!
sorry It was asked already looked the upper posts but missed it =)
Nice Tutorial!
I’m not only learned about Zend_Translate, but also how to create my own routers and controller plugins.
Jon, you’re a great teacher.
Btw, there is also a resource for setting up routes with the config file. Refer to Zend_Application_Resource_Router on http://framework.zend.com/manual/en/zend.application.available-resources.html
I followed a numerous tutorials here on zendcasts, including the one about the navigation and translate.
Translating works fine, but clicking on a link from youre menu sets the language back to ‘en’.
I came up with this solution (not the nicest one, but maybe someone has a better one):
frontController = Zend_Controller_Front::getInstance();
$router = $this->frontController->getRouter();
$language = $request->getParam(‘language’,”);
if($language !== ‘en’ && $language !== ‘nl’ && $language !== ‘de’ && $language !== ‘fr’ && $language !== ‘be’)
{
$request->setParam(‘language’,'en’);
}
$language = $request->getParam(‘language’);
switch($language)
{
case ‘en’:
$locale = ‘en_US’;
break;
case ‘nl’:
$locale = ‘nl_NL’;
break;
case ‘de’:
$locale = ‘de_DE’;
break;
case ‘fr’:
$locale = ‘fr_FR’;
break;
case ‘be’:
$locale = ‘nl_BE’;
break;
}
$setLanguage = new Zend_Locale();
$setLanguage->setLocale($locale);
Zend_Registry::set(‘Zend_Locale’, $setLanguage);
$translate = new Zend_Translate(‘gettext’, APPLICATION_PATH . ‘/languages/’. $locale . ‘.mo’ , $locale);
Zend_Registry::set(‘Zend_Translate’, $translate);
$router->removeDefaultRoutes();
$router->addRoute(
‘languagecontroller’,
new Zend_Controller_Router_Route(‘/:language/:controller/:action’,
array(‘language’ => $language,
‘controller’ => ‘index’,
‘action’ => ‘index’
)
)
);
}
}