Nate Wiebe
Contributed by Nate Wiebe in #37670

Translating contents usually require more information than the original message, such as the translation parameters and the translation domain. In order to make templates simpler, you could translate the entire messages in the backend and pass the translations to the templates.

However, this is sometimes cumbersome and also requires injecting the translator service in several layers of your application. Another alternative is to pass all the translation data (message, parameters, domain) to the template:

1
2
3
4
5
6
7
return $this->render('...', [
    'order_status' => [
        'message' => 'order.status_message',
        'params' => ['%status%' => $order->getStatus(), '%order_id%' => $order->getId()],
        'domain' => 'admin',
    ],
]);

This no longer requires injecting the translator, but makes templates more complex:

1
{{ order_status.message|trans(order_status.params, order_status.domain) }}

In Symfony 5.2 we're introducing a new TranslatableMessage class that solves this problem in an easier way. First, create a TranslatableMessage object and pass all the translation data to it:

1
2
3
4
5
6
7
8
9
use Symfony\Component\Translation\TranslatableMessage;

return $this->render('...', [
    'order_status' => new TranslatableMessage(
        'order.status_message',
        ['%status%' => $order->getStatus(), '%order_id%' => $order->getId()],
        'admin'
    ),
]);

Now you can translate this object in your template with the same trans filter as before, but in a much simpler way:

1
{{ order_status|trans }}

The translatable message holds all the translation data, so the trans filter can extract it automatically. This solution allows to simplify both the templates and the backend code, because you don't need to inject the translator or mock it when writing unit tests.

This feature also introduces a new t() function, usable both in PHP and Twig templates, to quickly create this kind of objects. The following example uses that function to create a fallback translation which uses different parameters than the default translation:

1
2
3
4
5
{# Before #}
{{ message is defined ? message|trans : fallback|trans({'%param%': value}) }}

{# After #}
{{ message|default(t(fallback, {'%param%': value}))|trans }}

Kudos to the amazing folks at Symfony Slack chat who helped me better understand this feature so I could prepare this blog post.

Published in #Living on the edge