25

Inside a Twig template I would need to have a translated text that contains a link (the path should be generated by the Router, not statically embedded). Twig does not allow to render a variable inside a trans block - I'm also aware of the following:

{% trans with {'%name%': 'Fabien'} from "app" %} Hello %name% {% endtrans %} 

but I can't see how to use that to inject inside the translation a piece like this

<a href="{{ path('privacy') }}">privacy policy</a> 

(of course, the anchor text should be translated as well)

4 Answers 4

43

The approach I've taken is this:

In the translation file:

page.privacy.policy: Please read our %link_start%privacy policy%link_end% 

In the twig file:

<p>{{ 'page.privacy.policy' | trans({'%link_start%' : '<a href="'~path('privacy-policy')~'">', '%link_end%' : '</a>'}, 'account') | raw }}</p> 

I'm not sure if this can be done using the block syntax you mentioned above as I found it didn't work unless I piped the result of the translation through the 'raw' filter. Also I use message domains to split the translations, hence the 'account' parameter.

I think the closest to your example would be:

<p>{{ 'Please read our %link_start%privacy policy%link_end%' | trans({'%link_start%' : '<a href="'~path('privacy-policy')~'">', '%link_end%' : '</a>'}) | raw }}</p> 

EDIT:

The only issue with this approach I've come across is where I need to programmatically follow a translated link in a unit test as there isn't a single translation representing the link text. Although messy I think it would be possible to get round this by providing a separate translation for the link text and substituting it's translated value into the full text as an additional variable.

Sign up to request clarification or add additional context in comments.

4 Comments

Also make sure that the %link_start% and %link_end% replacements do not contain any user-supplied input as the final string is not encoded (|raw). This would make the application vulnerable to a cross-site scripting (XSS) attack.
Shorter (No need to use %xxx%) : in translation file : page.privacy.policy: Please read our [privacy policy] ; in twig file : {{ 'page.privacy.policy' | trans({'[' : '<a href="'~path('privacy-policy')~'">', ']' : '</a>'}, 'account') | raw }}
@ka whilst that is shorter and prettier, in many organisations translation values like this can be arbitrarily changed by non-developers - i.e. they are used as a non-technical way of updating page text. If you use a single character like '[' as a delimiter there's a much greater chance it will clash with genuine use of that char at some point. The %xxx% convention is the one the symfony docs use in their examples for message placeholders.
To prevent raw xss, I use ...|trans(...)|raw|striptags('<a>')|raw if you know that your string should contains only html links
8

rebdirdo's solution is not really safe as it's not escaping whole message. It is not working correctly for messages like "don't use <b> tag, use <strong> tag instead. %link_start%Here%link_end% you can find why.", because the tags will be not escaped and will be not visible.

working approach:

translation file:

advises.strong: don't use <b> tag, use <strong> tag instead. %link_start%Here%link_end% you can find why.

twig file:

{{ 'advises.strong'|trans|nl2br|replace({'%link_start%': '<a href="'~path('privacy-policy')~'">', '%link_end%': '</a>'})|raw }}

Note the nl2br filter. It is necessary to put some filter there to make the raw filter working only for the link tags.

2 Comments

Yes, this is worth pointing. If you want to literally render HTML tags, use this answer. If you want your HTML tags to be interpreted as HTML, use the main answer.
I'd recommend using the escape filter instead of nl2br. Same result, no possibly unwanted side-effects, and it's much more logical and self-explanatory.
5

This is a better way:

{{ 'Please read our %privacy_policy%'|trans({ '%privacy_policy%': '<a href="' ~ path('privacypolicy') ~ '"> ' ~ 'Privacy Policy'|trans ~ '</a>' })|raw }} 

Comments

0

Twig:

{{'body.term'|trans('%link_terms%' :app.request.getSchemeAndHttpHost()~path('terms')},'AcmeTerm')|raw }} 

AcmeTerm.yml

body term: > <ul> <li>Read<a href="%link_terms%">Terms</a>.</li> </ul> 

where path('terms') is the route like:

it__RG__terms ANY ANY ANY /it/termini-e-condizioni en__RG__terms ANY ANY ANY /en/terms-and-conditions 

1 Comment

You can also simply use url('terms') function instead of app.request.getSchemeAndHttpHost()~path('terms').

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.