Saturday
21
February
2009

Proper handling of untranslated messages

In past the handling of untranslated messages within Zend_Translate was somewhat unhandy.
You had to check if the message can be translated or not, and then you had to notify yourself somehow. This leaded mainly to self written view helpers or an own class as middleware which did this job for you.

So I began thinking and found a really nice solution for you guys which was integrated yesterday.

Think of a site with a huge amount of pages. For simplification you have created the translation sources yourself. You choosed cvs for example. Now your site grows from day to day. Several people are working on it. Of course, depending on the size of the site and how pedantic your helpers are, you would have forgotten to add some messages to the translation files.
For the visitors of your site it seems like you’ve overlooked things and the site seems unfinished and unprofessional because languages are mixed.

There is now a solution to this problem:
Logging

You can define that Zend_Translate should write messages which can not be translated into a log. This does even work with your existing code. You just have to add 3 lines. Let’s get into practice:

First, create a log instance:

$writer = new Zend_Log_Writer_Stream('/path/mylog.log');
$log = new Zend_Log($writer);

Then attach it to Zend_Translate:

$t = new Zend_Translate('csv', $path);
$t->setOptions(
    array('log' => $log,
           'logUntranslated' => true));

Additionally we activated logging for messages with the option logUntranslated.
That’s it. From now on, when a message has to be translated and there is no translation found, you will have a log entry which reads Untranslated message: My requested message. Now all you have to do is to check your log regulary and look if there are any untranslated messages.

Better than before, but with this feature you can do even more.
Now you have to add this new translations manually to a translation source file. But Zend_Log could do this for you when you want to invest some lines of code.

All you have to do is to write your own extension of a Zend_Log_Writer. Your Log_Writer instance has to handle the source format you want to use. Strip the leading “Untranslated message: ” from the thrown notice and then write the new message to the file.

As short example:

My_Csv_Log_Writer extends Zend_Log_Writer_Stream
{
    protected function <em>write($event)
    {
        $event = substr($event, 22);
        return parent::</em>write($event . ";\n");
    }
}

This just strips out the leading untranslated stuff and add a separator and a new line to the file.
Of course, for a real working implementation this is not enough.

You would have to create a log writer for the source format you want to use. But it should show you how simple a new log writer can be.

As always this new feature is available with the next release which is ZF 1.8 or when you use trunk.
I hope you love this new feature. ;-)

Greetings
Thomas Weidner
I18N Team Leader, Zend Framework

Zend Framework Advisory Board Member
Zend Certified Engineer for Zend Framework

Back to top
  1. Matthew Weier O'Phinney

    Saturday, February 21, 2009 - 22:10:43

    This is a nice solution, particularly as there is not a _() function used within ZF to translate messages, making collection of them difficult. Nice job, Thomas!

    One note: you might want to register a new priority for the logger, something like “i18n”, and have the log writer use that priority. That way you could have a logger for application items that filters out i18n messages, and another for i18n only — allowing the use of a single Zend_Log instance with multiple writers for the entire application.

  2. seba

    Sunday, February 22, 2009 - 01:57:46

    Nice, When can we expect the 1.8 release?

  3. Ralf Eggert

    Sunday, February 22, 2009 - 12:58:22

    Nice work, Thomas! Thanks and keep up the good work!

  4. Dave

    Monday, February 23, 2009 - 19:52:59

    While I dont use ZF myself, I have spent a lot of time looking at Zend_Translate, picking it over seeing how it works. That was part of a project researching i18n systems and I think it is one of the better implementations particularly with the support for such a range of backends.

    Anyway, to my point(s); first ZF_Translate does have an _() method, which aliases translate(). So if you are using gettext (for example) you need to specify your function as _() or use whatever the object name is e.g. $oTranslate->_(). To make it easier, you could hook Translate into the View directly exposing it and then use $this->_() in your templates (views, whatever). Or you could just add an alias _() function in a bootstrap file.

    The second point is: it is really trivial to write a script to parse and rip out all text that is marked up using whatever method, either function or tag (e.g. for smarty templates). I dont know why ZF does not have this as a component as it makes sense - otherwise you have a translation system but how do you get the text to translate out?! ;)

    I wrote my own engine which took a few hours but it will parse functions or tags (tags for Smarty templates). Then you just hook it into the build process and make sure that you rip the text from your files as part of the build. Add in some kind of merge to existing files (or use Translate to handle that) and jobs a’good’un.

    Of course you then have to avoid the temptation to “fix” the text in the original files as much as possible. Perhaps an extractor is something to add to ZF 1.9?

  5. thomasw

    Monday, February 23, 2009 - 21:38:16

    As author, commiter and maintainer of Zend_Translate I know what I coded. I know that there is a _() method.

    What Matthew refers to is that you can not use _() in default ZF applications due to restrictions of Zend_View. The “_” sign is used as path seperator for locating the class/file position. I created a _ view helper in past but it was not accepted by Zend.

    However, when you refer to _() which is only known and used by gettext, you should also know that all tools like poEdit can be configured to search for another string than _( to get the messages from the code.

    Related to your second point: There is no need to add a extractor class as there are multiple tools available for the different adapters. Why should I recreate what already exists with exact this functionallity ? ;-)

    I for myself will not add such. The reason is simple.. Zend_Translate is for translating and not for extracting strings, or creating source files. This should be done by build processes, external software or own components.

    So as sum:
    Please stick to the theme of this post. ;-)

  6. Robert Kummer

    Monday, April 20, 2009 - 15:05:05

    This feature is the main feature I always looking at…Thank you for the good job.

  7. Stijn Huyberechts

    Thursday, August 6, 2009 - 17:37:18

    Great feature you’ve introduced to the ZF. I’m using it heavily!

    I’ve just discovered that not only Zend_Translate::translate(), but also Zend_Translate::isTranslated() logs untranslated messages. I was expecting that the isTranslated() would only return a Boolean, and that nothing would be logged.

    Any chances the behaviour of the isTranslated() method is going to change?