Specifying Translation Strings in Template Code

Translations in Django templates uses two template tags and a slightly different syntax than in Python code. To give your template access to these tags, put {% load i18n %} toward the top of your template. As with all template tags, this tag needs to be loaded in all templates which use translations, even those templates that extend from other templates which have already loaded the i18n tag.

trans Template Tag
The {% trans %} template tag translates either a constant string (enclosed in single or double quotes) or variable content:

<title>{% trans “This is the title.”
%}</title>
<title>{% trans myvar %}</title>

If the noop option is present, variable lookup still takes place but the translation is skipped. This is useful when stubbing out content that will require translation in the future:

<title>{% trans “myvar” noop %}</title>

Internally, inline translations use an ugettext() call.

In case a template variable (myvar above) is passed to the tag, the tag will first resolve such variable to a string at run-time and then look up that string in the message catalogs.

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. If you’d like to retrieve a translated string without displaying it, you can use the following syntax:

{% trans “This is the title” as the_title %}

In practice you’ll use this to get strings that are used in multiple places or should be used as arguments for other template tags or filters:

{% trans “starting point” as start %}
{% trans “end point” as end %}
{% trans “La Grande Boucle” as race %}

<h1>
<a href=”/” title=”{% blocktrans %}Back to ‘{{ race }}’
homepage{% endblocktrans %}”>{{ race }}</a>
</h1>
<p>
{% for stage in tour_stages %}
{% cycle start end %}: {{ stage }}{% if forloop.counter|divisibleby:2 %}<br />{% \
else %}, {% endif %}
{% endfor %}
</p>

{% trans %} also supports contextual markers using the context keyword:

{% trans “May” context “month name” %}
blocktrans Template Tag
The blocktrans tag allows you to mark complex sentences consisting of literals and variable content for translation by making use of placeholders:

{% blocktrans %}This string will have {{ value }} inside.{% endblocktrans %}

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 amount=article.price %}
That will cost $ {{ amount }}.
{% endblocktrans %}

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

You can use multiple expressions inside a single blocktrans tag:

{% blocktrans with book_t=book|title author_t=author|title %}
This is {{ book_t }} by {{ author_t }}
{% endblocktrans %}

The previous more verbose format is still supported:

{% blocktrans with book|title as book_t and author|title as author_t %}

Other block tags (for example {% for %} or {% if %}) are not allowed inside a blocktrans tag.

If resolving one of the block arguments fails, blocktrans will fall back to the default language by deactivating the currently active language temporarily with the deactivate_all() function.

This tag also provides for pluralization. To use it:

  •  Designate and bind a counter value with the name count. This value will be the one used to select the right plural form.
  • Specify both the singular and plural forms separating them with the {% plural %} tag within the {% blocktrans %} and {% endblocktrans %} tags.

An example:
{% blocktrans count counter=list|length %}
There is only one {{ name }} object.
{% plural %}
There are {{ counter }} {{ name }} objects.
{% endblocktrans %}

A more complex example:
{% blocktrans with amount=article.price count years=i.length %}
That will cost $ {{ amount }} per year.
{% plural %}
That will cost $ {{ amount }} per {{ years }} years.
{% endblocktrans %}

When you use both the pluralization feature and bind values to local variables in addition to the counter value, keep in mind that the blocktrans construct is internally converted to an ungettext call. This means the same notes regarding ungettext variables apply.

Reverse URL lookups cannot be carried out within the blocktrans and should be retrieved (and stored) beforehand:

{% url ‘path.to.view’ arg arg2 as the_url %}
{% blocktrans %}
This is a URL: {{ the_url }}
{% endblocktrans %}

{% blocktrans %} also supports contextual using the context keyword:

{% blocktrans with name=user.username context “greeting” %}
Hi {{ name }}{% endblocktrans %}

Another feature {% blocktrans %} supports is the trimmed option. This option will remove newline characters from the beginning and the end of the content of the {% blocktrans %} tag, replace any whitespace at the beginning and end of a line and merge all lines into one using a space character to separate them.

This is quite useful for indenting the content of a {% blocktrans %} tag without having the indentation characters end up in the corresponding entry in the PO file, which makes the translation process easier.

For instance, the following {% blocktrans %} tag:

{% blocktrans trimmed %}
First sentence.
Second paragraph.
{% endblocktrans %}

will result in the entry “First sentence. Second paragraph.” in the PO file, compared to “\n First sentence.\n Second sentence.\n”, if the trimmed option had not been specified.
String Literals Passed to Tags and Filters
You can translate string literals passed as arguments to tags and filters by using the familiar _() syntax:
{% some_tag _(“Page not found”) value|yesno:_(“yes,no”) %}

In this case, both the tag and the filter will see the translated string, so they don’t need to be aware of translations.

In this example, the translation infrastructure will be passed the string “yes,no”, not the individual strings “yes” and “no”. The translated string will need to contain the comma so that the filter parsing code knows how to split up the arguments. For example, a German translator might translate the string “yes,no” as “ja,nein” (keeping the comma intact).

Comments for Translators in Templates

Just like with Python code, these notes for translators can be specified using comments, either with the comment tag:

{% comment %}Translators: View verb{% endcomment %}
{% trans “View” %}

{% comment %}Translators: Short intro blurb{% endcomment %}
<p>{% blocktrans %}
A multiline translatable literal.
{% endblocktrans %}
</p>

or with the {# … #} one-line comment constructs:

{# Translators: Label of a button that triggers search #}
<button type=”submit”>{% trans “Go” %}</button>

{# Translators: This is a text of the base template #}
{% blocktrans %}Ambiguous translatable block of text{% endblocktrans %}

Just for completeness, these are the corresponding fragments of the resulting .po file:

#. Translators: View verb
# path/to/template/file.html:10
msgid “View”
msgstr “”

#. Translators: Short intro blurb
# path/to/template/file.html:13
msgid “”
“A multiline translatable”
“literal.”
msgstr “”

# …

#. Translators: Label of a button that triggers search
# path/to/template/file.html:100
msgid “Go”
msgstr “”

#. Translators: This is a text of the base template
# path/to/template/file.html:103
msgid “Ambiguous translatable block of text”
msgstr “”

Switching Language in Templates

If you want to select a language within a template, you can use the language template tag:

{% load i18n %}

{% get_current_language as LANGUAGE_CODE %}
<!– Current language: {{ LANGUAGE_CODE }} –>
<p>{% trans “Welcome to our page” %}</p>

{% language ‘en’ %}

{% get_current_language as LANGUAGE_CODE %}
<!– Current language: {{ LANGUAGE_CODE }} –>
<p>{% trans “Welcome to our page” %}</p>

{% endlanguage %}

While the first occurrence of Welcome to our page uses the current language, the second will always be in English.

Other Tags

These tags also require a {% load i18n %}.

  •  {% get_available_languages as LANGUAGES %} returns a list of tuples in which the first element is the language code and the second is the language name (translated into the currently active locale).
  • {% get_current_language as LANGUAGE_CODE %} returns the current user’s preferred language, as a string.
    Example: en-us.
  • {% get_current_language_bidi as LANGUAGE_BIDI %} returns the current locale’s direction. If True, it’s a right-to-left language, e.g.: Hebrew, Arabic. If False it’s a left-to-right language, e.g.: English, French, German etc.

If you enable the django.template.context_processors.i18n context processor then each RequestContext will have access to LANGUAGES, LANGUAGE_CODE, and LANGUAGE_BIDI as defined above.

The i18n context processor is not enabled by default for new projects.

You can also retrieve information about any of the available languages using provided template tags and filters. To get information about a single language, use the {% get_language_info %} tag:

{% get_language_info for LANGUAGE_CODE as lang %}
{% get_language_info for “pl” as lang %}

You can then access the information:

Language code: {{ lang.code }}<br />
Name of language: {{ lang.name_local }}<br />
Name in English: {{ lang.name }}<br />
Bi-directional: {{ lang.bidi }}

You can also use the {% get_language_info_list %} template tag to retrieve information for a list of languages (e.g. active languages as specified in LANGUAGES).

In addition to LANGUAGES style list of tuples, {% get_language_info_list %} supports simple lists of language codes. If you do this in your view:

context = {‘available_languages’: [‘en’, ‘es’, ‘fr’]}
return render(request, ‘mytemplate.html’, context)

you can iterate over those languages in the template:

{% get_language_info_list for available_languages as langs %}
{% for lang in langs %} … {% endfor %}

There are also simple filters available for convenience:
{{ LANGUAGE_CODE|language_name }} (German)
{{ LANGUAGE_CODE|language_name_local }} (Deutsch)
{{ LANGUAGE_CODE|language_bidi }} (False)

Internationalization in URL Patterns

Django provides two mechanisms to internationalize URL patterns:

  •  Adding the language prefix to the root of the URL patterns to make it possible for LocaleMiddleware to detect the language to activate from the requested URL.
  • Making URL patterns themselves translatable via the django.utils.translation.ugettext_lazy() function.

Using either one of these features requires that an active language be set for each request; in other words, you need to have django.middleware.locale.LocaleMiddleware in your MIDDLEWARE_CLASSES setting.

Language Prefix in URL Patterns – This function can be used in your root URLconf and Django will automatically prepend the current active language code to all url patterns defined within i18n_patterns(). Example URL patterns:

from django.conf.urls import include, url
from django.conf.urls.i18n import i18n_patterns
from about import views as about_views
from news import views as news_views
from sitemap.views import sitemap

urlpatterns = [
url(r’^sitemap\.xml$’, sitemap, name=’sitemap_xml’),
]

news_patterns = [
url(r’^$’, news_views.index, name=’index’),
url(r’^category/(?P<slug>[\w-]+)/$’,
news_views.category,
name=’category’),
url(r’^(?P<slug>[\w-]+)/$’, news_views.details, name=’detail’),
]

urlpatterns += i18n_patterns(
url(r’^about/$’, about_views.main, name=’about’),
url(r’^news/’, include(news_patterns, namespace=’news’)),
)

After defining these URL patterns, Django will automatically add the language prefix to the URL patterns that were added by the i18n_patterns function. Example:

>>> from django.core.urlresolvers import reverse
>>> from django.utils.translation import activate
>>> activate(‘en’)
>>> reverse(‘sitemap_xml’)
‘/sitemap.xml’
>>> reverse(‘news:index’)
‘/en/news/’

>>> activate(‘nl’)
>>> reverse(‘news:detail’, kwargs={‘slug’: ‘news-slug’})
‘/nl/news/news-slug/’

i18n_patterns() is only allowed in your root URLconf. Using it within an included URLconf will throw an ImproperlyConfigured exception.

Translating URL Patterns – URL patterns can also be marked translatable using the ugettext_lazy() function. Example:

from django.conf.urls import include, url
from django.conf.urls.i18n import i18n_patterns
from django.utils.translation import ugettext_lazy as _

from about import views as about_views
from news import views as news_views
from sitemaps.views import sitemap

urlpatterns = [
url(r’^sitemap\.xml$’, sitemap, name=’sitemap_xml’),
] news_patterns = [
url(r’^$’, news_views.index, name=’index’),
url(_(r’^category/(?P<slug>[\w-]+)/$’),
news_views.category,
name=’category’),
url(r’^(?P<slug>[\w-]+)/$’, news_views.details, name=’detail’),
]

urlpatterns += i18n_patterns(
url(_(r’^about/$’), about_views.main, name=’about’),
url(_(r’^news/’), include(news_patterns, namespace=’news’)),
)

After you’ve created the translations, the reverse() function will return the URL in the active language. Example:

from django.core.urlresolvers import reverse
from django.utils.translation import activate

>>> activate(‘en’)
>>> reverse(‘news:category’, kwargs={‘slug’: ‘recent’})
‘/en/news/category/recent/’

>>> activate(‘nl’)
>>> reverse(‘news:category’, kwargs={‘slug’: ‘recent’})
‘/nl/nieuws/categorie/recent/’

In most cases, it’s best to use translated URLs only within a language-code-prefixed block of patterns (using i18n_patterns()), to avoid the possibility that a carelessly translated URL causes a collision with a non-translated URL pattern.

Reversing in Templates – If localized URLs get reversed in templates, they always use the current language. To link to a URL in another language use the language template tag. It enables the given language in the enclosed template section:

{% load i18n %}

{% get_available_languages as languages %}

{% trans “View this category in:” %}
{% for lang_code, lang_name in languages %}
{% language lang_code %}
<a href=”{% url ‘category’ slug=category.slug %}”>{{
lang_name }}</a>
{% endlanguage %}
{% endfor %}
The `language` tag expects the language code as the only argument.

Back to Tutorial

Specifying Translation Strings in Python Code
Creating Language Files

Get industry recognized certification – Contact us

keyboard_arrow_up
Open chat
Need help?
Hello 👋
Can we help you?