CakePHP i18n __ function returns array

483 Views Asked by At

I'm running into a problem with the __ function, and I'm not sure whether it's a bug in Cake (3.2.8), Aura\Intl, or my code. I've tried the same thing in Cake 1.3, and it works as I expect it to, but it's possible that my expectations are simply that way because that's how it worked in 1.3. :-)

When I am building my menus, I use things like __('Teams'), but I also have pages that use things like __n('Team', 'Teams', count($player->teams)). The i18n shell extracts these into the default.pot separately, so when I translate it to French, it's like this:

msgid "Teams"
msgstr "Équipe"

msgid "Team"
msgid_plural "Teams"
msgstr[0] "Équipe"
msgstr[1] "Équipes"

If I call __('Team'), I correctly get 'Équipe' returned, and if I call __n('Team', 'Teams', $x), I correctly get 'Équipe' or 'Équipes' returned, depending on the value of $x. But if I call __('Teams') I get back

Array
(
    [0] => Équipe
    [1] => Équipes
)

This is the case even if I eliminate the msgid "Teams" section, leaving only the plural definition.

In Cake 1.3, __('Teams') would simply return 'Équipes'. (Don't know what it might do in 2.x, as I skipped over that entirely.) So, whose bug is this?

2

There are 2 best solutions below

0
ndm On

You have two Teams message IDs. The problem is that the CakePHP message file parser stores the messages in a key => value fashion, where the message ID is used as the key, resulting in the Teams messages for msgid_plural to override the Teams message from the preceding msgid.

https://github.com/cakephp/cakephp/blob/3.2.8/src/I18n/Parser/PoFileParser.php#L149 https://github.com/cakephp/cakephp/blob/3.2.8/src/I18n/Parser/PoFileParser.php#L172

https://github.com/cakephp/cakephp/blob/3.2.8/src/I18n/Parser/MoFileParser.php#L137-L140

Since gettext seems to be able to handle this, I'd say that it's at least a missing feature (which might be intentional), however it might even be a bug, I can't tell for sure (but I'd tend towards bug). For clarification, open an issue over at GitHub.

To (temporarily) workaround this issue you could use contexts.

5
AD7six On

Duplicate message definition

This is problematic:

msgid "Teams" <- same string
msgstr "Équipe"

msgid "Team"
msgid_plural "Teams" <- same string

The first means you have or are expecting to have __('Teams') in the application code, which expects to return a string.

The second scenario is going to create ambiguous data when the po file is parsed. The class responsible for converting po files to the array format is the PoFileParser, which contains these lines:

$messages[$singular] = $translation; // <- a string
...
$messages[$key] = $plurals; // <- an array

Where $messages is the array used to lookup translations, indexed by the translation key.

So, the reason for the observed behavior is because this code:

__('Teams'); 

is going to look for $messages['Teams']

This code:

__n('Team', 'Teams', 2); 

is going to look for $messages['Teams'][<index>], and the $messages array is going to contain the parsed data from the plural translation only, which overwrote the "singular" version of the string as it was earlier in the file.

The code-level solution is to ensure that all msgid and msgid_plural keys are unique (since they are essentially, the same thing).

Bad translation definition

You may well find at some point in the future that translations like __('Teams') are very problematic, depending on how that loose word is being used, they are an indicator of bad translation definitions.

To give an example, CakePHP used to have translations of this form in baked output:

...
sprintf(__('Invalid %s', true), 'Team');
...

Which was later changed to:

__('Invalid Team', true)

Because the translation of Invalid %s can change depending on what %s is - so can the translation of %s.