Software localization

The Difference between _() and {% trans %} in Django Templates

How do the _() and {% trans %} approaches in Django templates differ, and why should be one preferable rather than the other? A comparison.
Software localization blog category featured image | Phrase

In Django templates, the translate tag allows you to translate either a constant string or variable content. In fact, you can mark a string to be translated via {{ _("Hello World") }} or {% trans "Hello World" %}. Learn more about both approaches and figure out which method will suit best your next Django internationalization project.

As of Django's version 3.1, the trans tag is officially renamed to translate. Nevertheless, you can still use the trans tag as part of the backwards compatibility feature in Django. In fact, you can mix both the _() and trans tag in your Django template depending on your use case. This guide will walk you through the ins and outs of both approaches.

Using the _() method in Django Templates

Let's review some of the core advantages of the _() approach.

A lower level of verbosity

One of the main advantages of the _() approach is that it keeps your code less verbose. Instead of having lots of trans tags in your code, you can easily navigate through your code when localizing or internationalizing.

Support for placeholders

Additionally, the _() approach allows you to include multiple placeholders in your translation string. For example_

_("I am {first_name} {last_name}").format(first_name="John", last_name="Doe")

This is not possible when using the trans tag. However, you can use another template tag, blocktranslate, that is mainly used to mark complex sentences via placeholders. You can use the alias form, blocktrans, to create the same translation string as the code above.

{% blocktrans with first_name="John" last_name="Doe" %}

  I am {{ first_name }} {{ last_name }}

{% endblocktrans %}

The translated text for both methods is the same: I am John Doe.

Using the method with filters

In fact, you can pass string literals to filters. Normally, filters are defined as follows:

{{ value|yesno:"yes,no" }}

Depending on the value variable, it will output a translation text as either yes or no.

You can wrap the string inside _() to translate it.

{{ value|yesno:_("yes,no") }}

In this case, the translation system will get a whole string ("yes,no") instead of two strings ("yes" and "no"). That's why you should include a comma in the translated string. The filter will split the argument automatically on its own. Have a look at the following examples of how the translations will be in the PO files for different locales:

# German

msgid "yes,no"

msgstr "ja,nein"

# Simplified Chinese

msgid "yes,no"

msgstr "是,否"

# Polish

msgid "yes,no"

msgstr "tak,nie"

Using the trans tag approach in Django Templates

Here is an overview of the main benefits of the trans approach.

Contextual markers

In comparison to the _() approach, the trans or translate tag has the advantage of supporting message contexts. You can use contextual markers to translate a word based on the context as certain words can have several meanings. For example, the word "may" can refer to a month or verb.

{% trans "May" context "month name" %}

It will appear as follows in your po file:

msgctxt "month name"

msgid "May"

msgstr "Mai"

Store a translated string as a variable

The trans tag approach also allows you to store a translated string as a variable without immediately displaying it. Later on, you can retrieve it in your code by calling the variable. This is extremely useful if you intend to reuse the same translation string over and over again.

{% trans "Welcome to Phrase Blog" as the_title %}

<title>{{ the_title }}</title>

<meta name="description" content="{{ the_title }}">

Option to skip translation

Furthermore, there is an option called noop when using the trans template tag. When it's enabled, the variable lookup will still be performed, but the translation process will be skipped. This works really well for stubbing content that will require translation in the future. Django will still mark the string for translation in PO files. However, no translation will be performed when you render the application.

Let's say you have a title for your webpage, which is not translated at the time of developing the site. To make the whole process simpler in the future, you want to mark it as a translation text:

<title>{% trans "Welcome to Phrase!" %}</title>

All you need to do is to add the noop options inside the template tag:

<title>{% trans "Welcome to Phrase!" noop %}</title>

There will be no translation involved, and Django will render the output as just Welcome to Phrase regardless of the locale.

Wrapping Up Our Comparison

The _() and trans tag methods have their own advantages and disadvantages for Django internationalization. For brevity, you can just use the _() method. The trans tag approach can be a better choice if you are looking for contextual markers, storing translation strings as variables, and the option to skip translation.

If you are looking for a way to streamline your i18n process, consider using Phrase. It is the fastest, leanest, and most reliable localization management platform on the market which helps you to:

  • Build production-ready integrations with your development workflow,
  • Invite as many users as you wish to collaborate on your projects,
  • Edit and convert localization files with more context for higher translation quality.

Sign up for a free 14-day trial, and see for yourself how it can make your life as a developer easier.