Replace specifier in i18n plural translation

I 'm struggling with plural forms in my translation files. The *.po files are set up correctly. All works fine so far. Editing with Poedit works like a charme. But when it comes to the translation of plural forms, I 'm facing some problems.

The plural form definition in the *.po file:

"Plural-Forms: nplurals=2; plural=(n != 1);\n"

The typical translation message:

#: view/application/index/index.phtml:112
msgid "Eine offene Aufgabe"
msgid_plural "%d offene Aufgaben"
msgstr[0] "One open task"
msgstr[1] "%d open tasks"

The translatePlural call in my template:

<?= $this->translatePlural('Eine offene Aufgabe', '%d offene Aufgaben', 3) ?>

Actually the %d specifier is not replaced with the third parameter of the translatePlural view helper. The output is just the plain translation “%d open tasks”. Am I getting something wrong? Does the translatePlural view helper not replace specifiers? Do I have to use something like …

<?= sprintf($this->translatePlural('offene Aufgabe', '%d offene Aufgaben', 3), 3) ?>

… or is there another way, that automatically replaces c-format specifiers in translations?

Okay, answer to myself …

After I had a look into the translatePlural view helper I 've seen, that the third parameter $number is only used to evaluate the rule for plural values. No values are automatically replaced. No c-styled specifiers like %s or %d will be recognized. That in mind I have to replace the specifiers for myself.

Solution

<?= sprintf($this->translatePlural('offene Aufgabe', '%d offene Aufgaben', 3), 3) ?>

An even better approach would be an inherited view helper, that takes the determined translation from the translatePlural and replaces the specifier with the given $number parameter.

The formatting of messages should be based on a correct format: ICU MessageFormat

An open feature request can be found in the issue tracker of laminas-i18n:

The implementation of the ICU message format would be a logical further development. But wouldn’t the ICU message format make some things far too complex? I am thinking of the simple plural rules of the gettext format, which cannot be written down in such a simple way by the ICU format.

Otherwise the format of a token in curly brackets couldn 't be easier to be replaced in a way @matthew mentioned in the feature request. The strtrworks like a charme. As long as you don 't want to implement the whole ICU logic behind the message format, this could be the solution. Couldn 't believe that this feature request was from 2018 and isn 't implemented yet.

Working example :beer::beer::grin:

<?php
declare(strict_types=1);
namespace Marcel;

$string = 'Marcel did already {amount} {drink} on {day}.';
$parameters = [
    '{amount}' => 2,
    '{drink}' => 'beers',  // value could also be a plural translation
    '{day}' => 'ascension day' // value could also be a translation
];

$result = strtr($string, $parameters);

// possible call in a template: <?= $this->translate('Marcel did already {amount} {drink} on {day}.', $parameters)
// string(44) "Marcel did already 2 beers on ascension day."
var_dump($result);

Thank you for pointing out the feature request. If I find the time, I will submit a proposal on how to solve the problem. Matthew has already mentioned a perfectly functioning solution. Only the message format still needs to be clarified.

The goal is not to invent a custom or proprietary format but to use an existing format / standard. I do not see a step-by-step implementation as a problem.

Great and thanks in advance! :+1:t3: