34

In Django templates, how would I translate a block that contains HTML? For example:

{% trans "Please" %}
    <a href="{% url login %}?next={{ currentUrlPath }}">
        {% trans "log in" %}
    </a>
{% trans "in order to use MyApplicationName." %}

Splitting up translated strings allows me to change the HTML in the template at any time, but I guess it would make more sense to put it into a single translation string, like so:

{% url login as loginUrl %}
{% blocktrans %}
    Please
    <a href="{{ loginUrl }}?next={{ currentUrlPath }}">
        log in
    </a>
    in order to use MyApplicationName.
{% endblocktrans %}

But then the HTML markup is in the translation string, i.e. if I wanted to change the HTML (e.g. CSS class for the anchor), I'd have to retranslate the string for each language.

Are there any better alternatives?

AndiDog
  • 68,631
  • 21
  • 159
  • 205

3 Answers3

23

From the docs:

It's not possible to mix a template variable inside a string within {% trans %}. If your translations require strings with variables (placeholders), use {% blocktrans %} instead.

Then under blocktrans:

To translate a template expression -- say, accessing object attributes or using template filters -- you need to bind the expression to a local variable for use within the translation block. Examples:

{% blocktrans with article.price as amount %}
That will cost $ {{ amount }}.
{% endblocktrans %}

{% blocktrans with value|filter as myvar %}
This will have {{ myvar }} inside.
{% endblocktrans %}

This way your translated strings have the placeholders. In your case:

{% blocktrans with login_object.anchor as anchor %}
    Please {{ anchor|safe }}log in</a> in order to use MyApplicationName.
{% endblocktrans %}

You will need to generate the text that goes in anchor in your view function. This keeps it out of your translated string.

Mike DeSimone
  • 41,631
  • 10
  • 72
  • 96
  • That's a good solution, thanks for this. I'll use Ned Batchelder's tweak (storing only the attributes in a variable). Unfortunately you're right that the `anchor` variable can't be created easily using Django's builtin template language, so I have to do it in my view. – AndiDog Jan 09 '11 at 18:32
  • 6
    You can do this: {% url login as anchor_url %}{% blocktrans with anchor=''|safe %}Please {{ anchor }}log in in order to use MyApplicationName.{% endblocktrans %} – Pascal Polleunus Feb 14 '13 at 00:22
14

Not only does it make more sense to put the entire sentence in one translation string, it may be impossible for translators to get the sentence correct when it's split into pieces. Remember that the different parts of the sentence can affect each other, with tenses, cases, gender, and so on. Not to mention that other languages behave differently than English. The word "please" for example could be different when making a request and making a demand, for example.

Always use complete sentences in your translation strings so that the translators can make a correct sentence in the target language.

Mike DeSimone makes the right recommendation, I would make just one tweak to it:

{% blocktrans with login_object.anchor_attr as anchor_attr %}
    Please <a {{ anchor_attr|safe }}>log in</a> in order to use MyApplicationName.
{% endblocktrans %}

This keeps the HTML in the translation string balanced. Without the opening tag in the string, it could easily look like an error in the string.

Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
  • 1
    But in this case we have tag 'a' in our translation .po file, right? Is it a way to have a .po file without any html tags? Thank you. – sergzach Feb 04 '12 at 07:35
  • 1
    There isn't a simple way. If you need to mark up a few words in the sentence, only the translator can tell you where those words are, so they will have to mark them somehow. You can either use HTML, or make up your own markup. – Ned Batchelder Feb 04 '12 at 17:39
10

I can offer a convenient solution for only partial fragments which are constant for any translation.

In this case you can avoid to use any HTML or CSS inside of your .po file when using custom template tag such as the next:

@register.filter( name='safewrap' )
def safewrap( val, arg ):
    return val.format( arg )
safewrap.is_safe = True

...

{% blocktrans with sum2="<a href='mysite.com/offer'>{0}</a>"|safewrap:sum %}
    It costs {{sum2}} dollars.
{% endblocktrans %}

So, in your .po file you have:

It costs %(sum2)s dollars.

But it's a difficult question - what to do with partial fragments which require translation (like in your case).

Bouke
  • 11,768
  • 7
  • 68
  • 102
sergzach
  • 6,578
  • 7
  • 46
  • 84