Twig: Migrate transchoice to trans with ICU messages
While migrating a Symfony project from version 3 to 4 I also needed to migrate from the {% transchoice %}
filter / tag to the {% trans %}
tag as transchoice
has been deprecated and replaced
by trans
using ICU messages for pluralisation and more. As it took me a while to figure out how to use that in Twig, I thought I'd write it down in the hope that at least next time, I'll know where
to look ;). For the official Symfony documentation, see here: https://symfony.com/doc/current/translation/message_format.html
Migrating from transchoice to ICU trans in Twig
Old Syntax
In the old days, my language labels looked like this:
{0}never
|{1}once
|{2}twice
|]2,Inf[%count% times
Either used directly in twig - or preferably in a language xlf file.
Changing the language file suffix
The first step to use the new syntax is to rename the language labels file. I'm using the xlf
format and my file was called messages.en.xlf
. To make use of the new syntax, rename it to messages+intl-icu.en.xlf
. This tells the symfony/translation
component to use the new ICU message formatter.
Rewriting the labels
The new syntax looks a bit different:
<trans-unit id="countLabel">
<source>countLabel</source>
<target>{
count, plural,
=0 {never}
=1 {once}
=2 {twice}
other {# times}
}
</target>
</trans-unit>
in combination with the trans filter we could have something like -
{{ 'countlabel'| trans({'count': countVariable }) }}
rendering "never", "once", "twice" or "12 times" depending on the number in countVariable
.
Using variables in ICU labels
We can no longer use %foo%
variables in language labels, as they are invalid in ICU messages. If you want to use variables, wrap them with curly braces:
<trans-unit id="countLabel">
<source>countLabel</source>
<target>{
count, plural,
=0 {No {type} pony - so sad.}
one {One {type} pony.}
other {# {type} ponies}
}
</target>
</trans-unit>
{{ 'countlabel'| trans({'count': countVariable, 'type': 'shiny' }) }}
will result in - ideally - at least "13 shiny ponies".
More conditional formatting
Up 'til now this was basic pluralization - but with ICU we are also able to do more complex labels, for example distinguishing a count and a gender:
{ gender, select,
female {{
count, plural,
=0 {She loves no man.}
one {She loves one man.}
other {She loves many men.}
}}
other {{
count, plural,
=0 {They love no man.}
one {They love one man.}
other {They love many men.}
}}
}
For more examples and an online editor for ICU messages, check out: https://format-message.github.io/icu-message-format-for-translators/editor.html
Usage in PHP
If you are initializing your application or translation yourself and creating the twig translator in your own code, you can trigger ICU message handling by adding it to the domain of the resource - shortened example:
$i18nPath = $configuration->getResourceDirectory()->createIn('i18n');
$translator = new Translator($locale);
$translator->addLoader(
'xlf',
new XliffFileLoader()
);
$translator->addResource(
'xlf',
$i18nPath . 'messages+intl-icu.en.xlf',
'en',
'messages+intl-icu'
);
return $translator;
More Info / Links
- Official Symfony Documentation: https://symfony.com/doc/4.4/translation/message_format.html
- ICU Editor: https://format-message.github.io/icu-message-format-for-translators/index.html
- PHP MessageFormatter: https://www.php.net/manual/en/class.messageformatter.php (Part of the
intl
package)