ZF-9976: Zend_Translate_Adapter rerouting problem
Description
Hello!
When there are 2 (or more) untranslated MessageIDs (one directly after the other), I got only the first one translated in the "routed" "fallback" language.
it's about these lines of code: "if (array_key_exists($locale, $this->_options['route']) && !array_key_exists($locale, $this->_routed)" and: "$this->_routed[$locale] = true;" in line 738 - 740 (and also 683 - 685) of Zend/Translate/Adapter.php
Example: to translate: ID1 (available in user-language) ID2 (not available in user-language, routing to standard-language) ID3 (not available in user-language, routing to standard-language)
ID1 is found in user-language-source and successfully returned - OK!
ID2 is NOT found in user-language-source; setting "$this->_routed[USER_LANGUAGE] = true" and successfully return text from standard language - OK
ID3 is NOT found in user-language-source; "$this->_routed[USER_LANGUAGE]" already is true in lines 738/739 we have: "if (array_key_exists($locale, $this->_options['route']) && !array_key_exists($locale, $this->_routed)" an because of "!array_key_exists($locale, $this->_routed)" we won't route, because we thougth, we already did... we don't return the translation from the standard language, so only the MessageID (ID3) will be returned... -ERROR!!!
it's eroneous, that "$this->_routed" will be resetted AFTER: "return $this->translate($messageId, $this->_options['route'][$locale]); }" by "$this->_routed = array();" in line 744 but because of that "return $this->translate(..." we don't reach that point.
Best regards, René Kerner.
Comments
Posted by Thomas Weidner (thomas) on 2010-06-10T12:15:43.000+0000
The code works as expected. It would be nice if you give a real example instead of framework code.
This is a simple recursive methodcall as used in many other components and projects. When you follow the workflow you would see that _route is resetted before the rerouted translation is returned.
I can't see a failure wether in code nor within the testbed which tests the code.
Posted by René Kerner (johndoe) on 2010-06-10T13:56:09.000+0000
Ok, I'll explain a bit precisely.
In my application german (de) is the standard-locale and the german language-file would always be the complete one. for test-reasons, i changed my firefox-language-option to "en", so Zend_Locale retrieves english as the actual locale. but it's not completly translated. so i wanted to reroute "en" to "de" if the identifiers aren't found in the "en"-languagefile.
as you can see, the navigation labels are "identifiers", which should be translated by Zend_Translate.
The Zend_View_Helper_SOMETHING (?) is used to create the menu-structure and there the labels got translated.
In that example the View_Helper_???... (i'm @home at the moment and can't say, which is used. tomorrow i'll again debug that and add that information) gets the Translator (I expect Zend_Translate from Zend_Registry) and calls ->translate("navigation::login"); locale is null and gets the currently user-locale (line 656, Zend/Translate/Adapter.php).
the next lines until line 707 are skipped, because the conditions don't match. after that, because of missing languagefile-entries in the english-language-file, the next lines are skipped (conditions don't match) until line 735/736:
then we come to the routing-part (lines 737 - 745):
in that navigation-menu, the next identifier also isn't translated in the english-language-file.
so we are in the same helper and have the same translator-object (TO1), which "$this->_routed[$locale]" value is still not resetted (I debugged that!!!). so: $this->_routed["en"] is true.
the view helper now calls with that translator (TO1) ->translate("the_next_identifier"); as before no line matches one of the if-conditions and we come to lines 737 - 745:
the reason is, that the reset:
is only resetted on three positions: line 53, on initialization: private $_routed = array(); line 689, $this->_routed = array(); after Zend_Locale::isLocale-checks line 744, $this->_routed = array(); after rerouting
more specific code-examples I can only support from work tomorrow.
what code examples do you need?
best regards, rené
Posted by René Kerner (johndoe) on 2010-06-11T00:48:36.000+0000
wrong translation example (application code follows!)
Posted by René Kerner (johndoe) on 2010-06-11T01:37:39.000+0000
now some explanation for my screenshots:
I hava a StartController with an index-action.
In german PO/MO-files I translated identifiers test1 - test4 and test7 - test12. test5, test6, test13 and test14 aren't translated. This is correctly shown in picture one: "TRANSLATE_DE.png" !TRANSLATE_DE.png|thumbnail!
Then I switched my browser LANGUAGE_ACCEPT to "English [en]" and Zend_Locale will give this to Zend_Translate. In the english languagefile nothing is translated at this time. !TRANSLATE_EN_no-translations.png|thumbnail! This should look like picture one and there should always be the german translation: ü1 - ü4 test5, test6 ü7 - ü12 test13, test14 BUT I got: ü1 - OK test2 - ERROR ü3 - OK test4 - ERROR test5 - OK test6 - OK ü7 - OK test8 - ERROR ü9 - OK test10 - ERROR ü11 - OK test12 - ERROR test13 - OK test14 - OK As you can see, only every second untranslated translation is correctly routed!
Then I translated some identifiers in the english languagefile, but only identifiers test4 - test7. As you can see, !TRANSLATE_EN_translated-ID-4-5-6-7.png|thumbnail! I should get: ü1 - ü3 en4 - en7 ü8 - ü12 test13, test14
BUT I got: ü1 - OK test2 - ERROR ü3 - OK en4 - OK en5 - OK en6 - OK en7 - OK test8 - ERROR ü9 - OK test10 - ERROR ü11 - OK test12 - ERROR test13 - OK test14 - OK
So you can see only every second translation in case of language-rerouting is correct.
The reason is in the recursion. Because of the RETURN command in "return $this->translate($messageId, $route_language)" the reset of $this->_routed[$routed_language] will only be reseted in a later call of $this->translate() for the next identifier. The right solution would be to save the returned value of the $this->translate($messageId, $route_language) to a variable. Then after the recursion reset or in code "unset($this->_routed[$currently_routed_language])". So you can prevent endless recursion in cyclic routing definitions (like EN => DE => FR => EN) when the identifier is not found.
And more precisly in german / Und auf Deutsch: Es liegt daran, wie ich bereits beschrieben habe, dass im beim re-routen der rekursive Abstieg direkt mit einem RETURN gemacht wird. Richtig wäre doch, das Ergebnis des rekursiven Abstiegs von $this->translate($messageId, $route_language) in eine variable zu speichern. Nach erfolgreichem rekursivem Aufstieg "unset($this->_routed[$geroutete_sprache])" zu machen. und dann erst das RETURN. So kann man verhindern, dass es eine endlose Rekursionschleife gibt, wenn der Bezeichner/Identifier bei einer zyklischen Routen-Definition (z.B. EN => DE => FR => EN) nicht gefunden wird.
Posted by René Kerner (johndoe) on 2010-06-11T01:38:25.000+0000
wrong screenshot replaced
Posted by René Kerner (johndoe) on 2010-06-11T02:25:18.000+0000
In my project I'm using a self extended Zend_Translate_Adaper_Gettext. It's called Tacticx_Translate_Adaper_Gettext. It's not the right place because it should be in a Tacticx_Translate_Adaper class, but for me it's acceptable there atm. Here is an example for a corrected Zend/Translate/Adapter.php I only show the added function and the corrected translate(...)-function.
Posted by Thomas Weidner (thomas) on 2010-06-12T13:49:05.000+0000
Please write your issues in english and not in other languages which people don't understand. Thnx.
Fixed with r22425.