# Replace specifier in i18n plural translation

**URL:** <https://discourse.laminas.dev/t/replace-specifier-in-i18n-plural-translation/2299>\
**Category:** Components & MVC\
**Tags:** laminas-mvc, laminas-i18n\
**Created:** [May 12, 2021, 9:56am UTC](https://discourse.laminas.dev/t/replace-specifier-in-i18n-plural-translation/2299 "2021-05-12T09:56:09Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![ezkimo](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/ezkimo/32/781_2.png) [@ezkimo](https://discourse.laminas.dev/u/ezkimo)\
**Post date:** [May 12, 2021, 9:56am UTC](https://discourse.laminas.dev/t/replace-specifier-in-i18n-plural-translation/2299/1 "2021-05-12T09:56:09Z")

</div>

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:

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

```

The typical translation message:

```auto
#: 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:

```auto
<?= $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 …

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

```

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

---

<div class="post-metadata">

**Author:** ![ezkimo](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/ezkimo/32/781_2.png) [@ezkimo](https://discourse.laminas.dev/u/ezkimo)\
**Post date:** [May 12, 2021, 1:37pm UTC](https://discourse.laminas.dev/t/replace-specifier-in-i18n-plural-translation/2299/2 "2021-05-12T13:37:07Z")

</div>

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**

```auto
<?= 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.

---

<div class="post-metadata">

**Author:** ![froschdesign](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/froschdesign/32/38_2.png) [@froschdesign](https://discourse.laminas.dev/u/froschdesign)\
**Post date:** [May 13, 2021, 9:06am UTC](https://discourse.laminas.dev/t/replace-specifier-in-i18n-plural-translation/2299/3 "2021-05-13T09:06:01Z")

</div>

> [@ezkimo](#):
>
> 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](http://userguide.icu-project.org/formatparse/messages)

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

> <https://github.com/laminas/laminas-i18n/issues/7>
>
> \## The Idea
> Recently we talked on Slack about having parameters support for tr…anslated strings.
> I made a research of how other frameworks are already doing it.
> 
> \## Other Frameworks
> Here is the documentation on how it is implemented in \[Symfony\](https://symfony.com/doc/current/components/translation/usage.html#component-translation-placeholders) and in \[Laravel\](https://laravel.com/docs/5.6/localization#replacing-parameters-in-translation-strings).
> 
> \### Symfony
> I like how Symfony uses PHP's \[\`strtr\`\](http://php.net/manual/en/function.strtr.php) here: https://github.com/symfony/translation/blob/master/Formatter/MessageFormatter.php#L42-L45
> However, I don't like the usage with those percent \`%\` characters as they make the string less readable, e.g. \`You scored %scored\_points% points and you pass the exam with %score%% success!\`.
> 
> \### Laravel
> I don't like \[Laravel's replacement implementation\](https://github.com/laravel/framework/blob/5.7/src/Illuminate/Translation/Translator.php#L258-L275) as this is a fairly large amount of code for such a simple task. However, I like how they name their parameters with a leading colon \`:\`, eg. \`:scored\_points\`, \`:score\`, which makes the string a little bit easier to read. The previous example would become \`You scored :scored\_points points and you pass the exam with :score% success!\`
> 
> \## My suggested approach
> It will be best to use colon-prefixed parameters (readability) and \`strtr\`, which takes into account other parameters' names, regarding of the order they are defined (in contrast to Laravel's approach to use \`str\_replace\` with sorted parameters), meaning that if the parameterized string contains both \`:score\` and \`:scored\_points\`, the value for former would not overwrite the latter, unless the parameter has not been passed.
> 
> \## Implementation
> I want to do and will do a PR, but first I want to discuss with you the idea and what are the possible solutions, taking into account the current state of this component. I would like to hear your opinion and suggestions.
> 
> The current methods can be changed, thus introducing a BC Break (the feature will be released with the next major version). Currently there are 2 methods in the \`TranslatorInterface\`: \`translate\` and \`translatePlural\`, which may be modified as follows:
> \`\`\`
> /\*\*
> \* Translate a message.
> \*
> \* @param string $message
> \* @param array $parameters
> \* @param string $textDomain
> \* @param string $locale
> \* @return string
> \*/
> public function translate(
> $message,
> array $parameters = \[\],
> $textDomain = 'default',
> $locale = null
> );
> 
> /\*\*
> \* Translate a plural message.
> \*
> \* @param string $singular
> \* @param string $plural
> \* @param int $number
> \* @param array $parameters
> \* @param string $textDomain
> \* @param string|null $locale
> \* @return string
> \*/
> public function translatePlural(
> $singular,
> $plural,
> $number,
> array $parameters = \[\],
> $textDomain = 'default',
> $locale = null
> );
> \`\`\`
> 
> \---
> 
> Originally posted by @thexpand at https://github.com/zendframework/zend-i18n/issues/104

---

<div class="post-metadata">

**Author:** ![ezkimo](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/ezkimo/32/781_2.png) [@ezkimo](https://discourse.laminas.dev/u/ezkimo)\
**Post date:** [May 13, 2021, 10:05am UTC](https://discourse.laminas.dev/t/replace-specifier-in-i18n-plural-translation/2299/4 "2021-05-13T10:05:23Z")

</div>

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 `strtr`works 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 🍺🍺😁

```auto
<?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.

---

<div class="post-metadata">

**Author:** ![froschdesign](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/froschdesign/32/38_2.png) [@froschdesign](https://discourse.laminas.dev/u/froschdesign)\
**Post date:** [May 14, 2021, 6:34am UTC](https://discourse.laminas.dev/t/replace-specifier-in-i18n-plural-translation/2299/5 "2021-05-14T06:34:26Z")

</div>

> [@ezkimo](#):
>
> 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.

> [@ezkimo](#):
>
> If I find the time, I will submit a proposal on how to solve the problem.

Great and thanks in advance! 👍🏼
