• Home
  • -
  • Twig: Migrate transchoice to trans with ICU messages

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