Arguments in a label: sprintf and ICU plurals 

A label can carry values that TYPO3 fills in when it translates. Pass them as an array to the translation call, after the label reference. The keys of that array decide how TYPO3 reads the label:

  • A list, so keys 0, 1, 2, makes TYPO3 treat the label as a sprintf() format string, with %s and %d as the placeholders.
  • An array with names as keys makes TYPO3 read the label as an ICU MessageFormat pattern, with {name} as the placeholder.

Plurals and selections with ICU MessageFormat 

ICU MessageFormat picks one wording by a value. Use it where a count or a gender changes the sentence, because the rules for that differ per language. A German label needs two forms for a count, a Polish one needs four, and sprintf() cannot express either.

Write the pattern into the label as it stands:

EXT:my_extension/Resources/Private/Language/locallang.xlf
<?xml version="1.0" encoding="UTF-8"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
  <file source-language="en" datatype="plaintext" original="locallang.xlf">
    <header/>
    <body>
      <trans-unit id="file_count">
        <source>{count, plural, one {# file} other {# files}}</source>
      </trans-unit>
      <trans-unit id="item_count">
        <source>{count, plural, =0 {no items} one {# item} other {# items}}</source>
      </trans-unit>
      <trans-unit id="greeting">
        <source>Hello {name}, you have {count, plural, one {# message} other {# messages}}.</source>
      </trans-unit>
      <trans-unit id="profile_update">
        <source>{gender, select, male {He} female {She} other {They}} updated the profile.</source>
      </trans-unit>
      <trans-unit id="welcome">
        <source>Welcome, {name}!</source>
      </trans-unit>
    </body>
  </file>
</xliff>
Copied!
{count, plural, …}
Chooses by the plural category of the value: one, other, and the categories of the current language. =0 matches exactly zero, which gives a sentence like "no items" its own wording. A # inside a branch prints the number.
{gender, select, …}
Chooses by an exact value. other catches the rest.
{name}
Prints the value.

Then pass the values under their names:

EXT:my_extension/Classes/MyClass.php (excerpt)
$this->getTranslator()->label(
    'my_extension.messages:file_count',
    ['count' => 5],
);
Copied!
EXT:my_extension/Resources/Private/Templates/MyTemplate.fluid.html
<f:translate key="my_extension.messages:file_count"
    arguments="{count: files}" />
Copied!

TYPO3 reads the plural rules of the current locale, and falls back to en_US where it knows no locale. So the same label gives "5 files" in English and "5 Dateien" in German, each with the forms of that language.

Where a pattern is invalid, TYPO3 returns the label unchanged, with the braces still in it. A sentence of curly braces on a page is the sign to look for a typo in the pattern.

Positional values with sprintf() 

A list of values keeps the behavior that TYPO3 always had: the label is a sprintf() format string.

EXT:my_extension/Classes/MyClass.php (excerpt)
$this->getTranslator()->label(
    'my_extension.messages:greeting_sprintf',
    ['World'],
);
Copied!

TYPO3 uses vsprintf() here, which reports a count of values that does not match the count of placeholders. A label with one %s and no value therefore raises an error instead of printing an empty string.

Do not mix the two. A label written for sprintf() keeps its %s untouched when the call passes named values. An ICU pattern reaches the output with its braces when the call passes a list.