Introducing Zend_Translate

November 13th, 2009

One 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.
 


discuss video in the forum

43 Responses to “Introducing Zend_Translate”

  1. Olagato says:

    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.

  2. Olagato says:

    I mean on last comment the “i18n Approach”
    (wrote “i18n” in nHTML tags)
    Sorry.

  3. umpirsky says:

    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.

  4. Hari K T says:

    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.

  5. jon says:

    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)

  6. umpirsky says:

    @Jon I rhink letting Zend_Translate to be null for en will do the job.

  7. Aleksandar says:

    Yep, umpirsky but It’s not a good practice

  8. umpirsky says:

    @Aleksandar Hm, ok, what is the right way to do it (without messing with yet another language – since it is a source language)?

  9. [...] Zum Webcast Kommentar abgeben | Trackback Keine Kommentare [...]

  10. Aleksandar says:

    @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

  11. umpirsky says:

    @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.

  12. Aleksandar says:

    @umpirsky pa srbin bre, normalno :D

    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.

  13. umpirsky says:

    @Aleksandar Wow :) Odakle si? Daj da razmenimo kontakte.

  14. Milos says:

    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 =)

  15. umpirsky says:

    @Milos Woohoo! :)

  16. Jim says:

    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?

  17. Colleen says:

    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.

  18. umpirsky says:

    @Colleen Sure, you can add multiple keywords.

  19. Kuzma says:

    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!

  20. jon says:

    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.

  21. Juan Felipe Alvarez Saldarriaga says:

    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/)

  22. e.s.t says:

    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]+’ )
    );

  23. 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

  24. [...] I have no clue about this but maybe look at this [...]

  25. Whisher says:

    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
  26. [...] gibt einen neuen Zend Cast über Zend_Translate von Jon [...]

  27. Nicklas says:

    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?

  28. jon says:

    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.

  29. e.s.t says:

    @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

  30. e.s.t says:

    And as I said this entire example is useless if you want your Zend_Navigation to work correctly.

  31. Danny says:

    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/

  32. [...] thanks to Zend Cast for the inspi­ra­tion! Series Nav­i­ga­tion«How to make POEdit detect source strings in Zend [...]

  33. Jochen says:

    Can anybody explain how to dynamically translate labels coming from the navigation.xml?

  34. e.s.t says:

    @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.

  35. e.s.t says:

    This #$%#$ reply script, stripped some things from my comment. What I meant was:
    [title]Generators[/title] , (replace square brackets).

  36. mylonov says:

    hi!

    nice screencast – really helped me to got through ZF details.

    can i ask what are you using to beautify var_dump() output?

  37. jon says:

    Hi Mylonov,

    I’m using XDebug

  38. Simple says:

    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

  39. jon says:

    xdebug!

  40. Simple says:

    sorry It was asked already looked the upper posts but missed it =)

  41. Rob says:

    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.

  42. White says:

    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

  43. Rick says:

    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’
    )
    )
    );
    }
    }

Leave a Reply

Desktop RSS feed iPhone + iPod