ZF-9237: New fix for plural translation returns only one letter

Description

This issue has been raised multiple times and the solutions do not work as they misstate the problem.

When using {{plural()}} on already translated strings the {{.po}} and {{.mo}} files may not contain the strings stored as plurals. But they do contain translations of the original texts, when these are found only one letter is returned instead of a string.


$one = $t->_('One');
$two = $t->_('Two');
        
echo $t->plural($one, $two, $i);

This now returns {{'O'}} or {{'w'}} depending on the value {{$i}} when "translating" from English to English.

Now I understand this is not the correct use of plural, but it does seems a very common use. When using plural on two variables you have little choice, e.g. poEdit will not see {{'One'}} and {{'Two'}} as instances of plural.

However, we can make this case work without breaking the correct working of translate files by extending the Translate Adapter to check for the plural translation choosen being an array versus a string.


$locale = (string) $locale;
if (isset($this->_translate[$locale][$messageId])) {
    // return original translation
    if ($plural === null) {
        return $this->_translate[$locale][$messageId];
    }

    $rule = Zend_Translate_Plural::getPlural($number, $locale);

    // BUG FIX  added: && is_array($this->_translate[$locale][$plural[0]])
    if (isset($this->_translate[$locale][$plural[0]][$rule]) && is_array($this->_translate[$locale][$plural[0]])) {
        return $this->_translate[$locale][$plural[0]][$rule];
    } elseif (isset($this->_translate[$locale][$plural[$rule]])) { // FIX Added
        return $this->_translate[$locale][$plural[$rule]]; // FIX Added
    }
} else if (strlen($locale) != 2) {
    // faster than creating a new locale and separate the leading part
    $locale = substr($locale, 0, -strlen(strrchr($locale, '_')));

    if (isset($this->_translate[$locale][$messageId])) {
        // return regionless translation (en_US -> en)
        if ($plural === null) {
           return $this->_translate[$locale][$messageId];
        }

        $rule = Zend_Translate_Plural::getPlural($number, $locale);
        // BUG FIX  added: && is_array($this->_translate[$locale][$plural[0]])
        if (isset($this->_translate[$locale][$plural[0]][$rule]) && is_array($this->_translate[$locale][$plural[0]])) {
           return $this->_translate[$locale][$plural[0]][$rule];
        } elseif (isset($this->_translate[$locale][$plural[$rule]])) { // FIX Added
           return $this->_translate[$locale][$plural[$rule]]; // FIX Added
        }
    }
}

As this makes the translate tools more flexible there seems little reason no to include it.

Comments

Sorry, my first time reporting in, I assumed that cloning this issue I would be able to edit the original text.

This issue has been raised multiple times and the solutions do not work as they misstate the problem.

When using plural() on already translated strings the .po and .mo files may not contain the strings as plurals. But they do contain translations of the original texts, when these are found only one letter is returned.

Now maybe I misunderstand how I should use plural, but my use seems a very common one. Especially when using plural on two variable values.

A solution that does not break the correct workings of translate files is to replace th current code:

        if (isset($this->_translate[$locale][$plural[0]][$rule])) {
            return $this->_translate[$locale][$plural[0]][$rule];
        }

With this slightly long version that check for the _translate entry being a string versus an array:

        if (isset($this->_translate[$locale][$plural[0]][$rule]) && is_array($this->_translate[$locale][$plural[0]])) {
            return $this->_translate[$locale][$plural[0]][$rule];
        } elseif (isset($this->_translate[$locale][$plural[$rule]])) {
            return $this->_translate[$locale][$plural[$rule]];
        }

This has to haappen twice in plural() in Zend_Translate_adapter.php

Sorry, there is no preview available. Here is a second try to communicate the code.

if (isset($this->_translate[$locale][$plural[0]][$rule])) {

return $this->_translate[$locale][$plural[0]][$rule];

}

Should become:

if (isset($this->_translate[$locale][$plural[0]][$rule]) && is_array($this->_translate[$locale][$plural[0]])) {

return $this->_translate[$locale][$plural[0]][$rule];

} elseif (isset($this->_translate[$locale][$plural[$rule]])) {

return $this->_translate[$locale][$plural[$rule]];

}

FInally found out how this works. Sorry for the messy comments.

Please give something reproducable... po file & mo file.

In past other files were broken or did have a false locale where plural rule and mo content did not match.

I added a clarifying example and changed the priority and issue type.

This is not a bug, but including these changes would make the use of translations a little more flexible. If nothing else this issue will make the problem more clear to other people having the same problem.

PS: As I did not add the {{.po}} and {{.mo}} files because this is not about their correctness but about how you can use the translation tool.

The newly added code example does not add a plural to gettext but 2 singulars.

When you use singular within gettext then you can't expect that Zend_Translate will make plural from singular. The created mo file from gettext differs between singular and plural.

We will not add plural usage for singulars. This would introduce severe problems not only with gettext itself but also all other adapters.

For details about how to create plural source files please take a look into the gettext documentation, poedit or even the php manual.

Closing as won't fix