Use in Templates and Views

You can use the Transifex Native SDK both inside Django templates as well as inside views. Transifex Native SDK supports the ICU Message Format in order to express more complex localization strings.

Internationalization in template code

To start using Transifex SDK in your templates add the following template tag on the top of your file:

{% load transifex %} 

Translations in Django templates use one of the two template tags, {% t %} and {% ut %}. These tags are used to translate strings or variables that contain strings.

<p>{% t "This is a great sentence." %}</p>
<h2>{% t "Welcome, {username}" username=user.name %}</h2>
<pre>{% t snippet.code  %}</pre>

Each string can contain any combination of the below items:

  • Constant strings.
  • Variables, that can be resolved either from the parameters of the template tags or the context of the template
  • Additional ICU Message Format to express complex localization forms, that are resolved by Transifex Native SDK.

You can read more about the differences of the two tags in the section XML escaping of this article.

To find out more about ICU Message Format syntax please check here.

For a nice introduction to ICU syntax you can checkout this tutorial and online editor.

Internationalization in Python Code

In order to mark translatable strings inside Python code, import a function on your Python file top section and wrap your strings with it.

from transifex.native.django import t
from django.http import HttpResponse

def my_view(request):
    output = t("Welcome aboard!")
    return HttpResponse(output)

Similar to template tags, each Transifex Native function call can use the same string syntax, with any combination of the below:

  • Constant strings.
  • Variables, as named arguments.
  • ICU Message Format to express complex localization forms, that are resolved by Transifex Native SDK.
text = t("Welcome, {username}", username=user.name)
text = t("Contact", _context="support")
text = t(
    "{num, plural, "
    "    one {There is {num} user in this team.} "
    "    other {There are {num} users in this team.}"
    "}",
    num=total_users,
)
text = t("""
  {gender_of_host, select,
    female {
      {total_guests, plural, offset:1
        =0 {{host} does not give a party.}
        =1 {{host} invites {guest} to her party.}
        =2 {{host} invites {guest} and one other person to her party.}
        other {{host} invites {guest} and # other people to her party.}
      }
    }
    male {
      {total_guests, plural, offset:1
        =0 {{host} does not give a party.}
        =1 {{host} invites {guest} to his party.}
        =2 {{host} invites {guest} and one other person to his party.}
        other {{host} invites {guest} and # other people to his party.}
      }
    }
    other {
      {total_guests, plural, offset:1
        =0 {{host} does not give a party.}
        =1 {{host} invites {guest} to their party.}
        =2 {{host} invites {guest} and one other person to their party.}
        other {{host} invites {guest} and # other people to their party.}
      }
    }
  }""",
  gender_of_host="female",
  total_guests=current_event.total_guests,
  host=current_event.host.user.name,
  guest=guest.name,
)

📘

Note about lazy translations and ModelForms

There is a special case when using lazy translations for verbose names of model fields and ModelForms. Consider this example

# models.py
class Article(models.Model):
    title = models.CharField(max_length=100, verbose_name=lazyt("title"))
  
# forms.py
class ArticleForm(forms.ModelForm):
    class Meta:
        model = Article
        fields = ["title"]
    
# views.py
def index(request):
    return render(request, "index.html", {form: ArticleForm()})
<!-- index.html -->
{{ form }}

The problem with this is that the translation for the verbose name of the form field will be retrieved when the form class is defined, not when the form variable is initialized. Thus, the rendered version of the form will not be translated to the user's language. In order to fix this, you must make sure that the form class is defined inside the view:

# forms.py
def get_article_form_class():
    class ArticleForm(forms.ModelForm):
        class Meta:
            model = Article
            fields = ["title"]
            
    return ArticleForm

# views.py
def index(request):
    ArticleForm = get_article_form_class()
    return render(request, "index.html", {form: ArticleForm()})

Inline and Block Syntax

Both template tags support two styles:

  • The inline syntax
{% t  <source>[|filters...] [key=param[|filters...]...] [as <var_name>] %}
{% ut <source>[|filters...] [key=param[|filters...]...] [as <var_name>] %}

*The block syntax

{% t  [|filters...] [key=param[|filters...]...] [as <var_name>] %}
<source>
{% endt %}

{% ut [|filters...] [key=param[|filters...]...] [as <var_name>] %}
<source>
{% endut %} 

In general, the outcome of the block syntax will be identical to the inline syntax, if you had supplied the block contents as a literal argument. ie this:

{% t ... %}hello world{% endt %} 

should be identical to this:

{% t "hello world" ... %} 

By using the block syntax, you can more easily include characters that would be problematic when using the inline syntax, e.g. quote characters (") or newlines.

To find out more about supported filters, marked as filters... ,view Applying filters .

Localization syntax

The Transifex Toolkit supports the ICU Message Format.

Using the Message Format syntax you can support various types of logic, with the same template tag:

{% 
    t "{num, plural, one {Found {num} user} other {Found {num} users} }" 
        num=total_users 
%} 

{% t num=total_users visit_type=user.visit.type username=user.name %}
  {visit_type, select,
    first {Welcome, {username}}
    returning {Welcome back, {username}}
  }
{% endt %}

A more complex example, using nested rules, is the following:

{% t gender_of_host="female" total_guests=current_event.total_guests host=current_event.host.user.name guest=guest.name %}
  {gender_of_host, select,
    female {
      {total_guests, plural, offset:1
        =0 {{host} does not give a party.}
        =1 {{host} invites {guest} to her party.}
        =2 {{host} invites {guest} and one other person to her party.}
        other {{host} invites {guest} and # other people to her party.}
      }
    }
    male {
      {total_guests, plural, offset:1
        =0 {{host} does not give a party.}
        =1 {{host} invites {guest} to his party.}
        =2 {{host} invites {guest} and one other person to his party.}
        other {{host} invites {guest} and # other people to his party.}
      }
    }
    other {
      {total_guests, plural, offset:1
        =0 {{host} does not give a party.}
        =1 {{host} invites {guest} to their party.}
        =2 {{host} invites {guest} and one other person to their party.}
        other {{host} invites {guest} and # other people to their party.}
      }
    }
  }
{% endt %}

Transifex Native supports a subset of the ICU Message format including:

  • Plurals
  • ICU Select, that includes gender definitions and placeholders

The above example is showcasing all the supported syntax.

Parameter keys

Several parameter keys can be used in order to annotate the string with metadata that will be used in Transifex. The supported parameter keys are:

  • _context, to define additional context for the specific phrase. You can add a list of strings and separate them with a comma. Context data will be visible in the context tab of the editor.
  • _comment, to provide instructions to the localization team working on this phrase. This information is displayed in the editor's translation area as string instructions.
  • _charlimit, to add a character limit count for the translations. This information is displayed both in the editor's context tab and in the translation area.
  • _tags, to define tag-keywords that can help the localization team organize their work better over phrases. To add multiple tags use a comma-separator. Tags are visible in the editor's context tab.
  • _key, to define a custom string key instead of an auto-generated one based on source string and context. Amend the --verbose option of the push command so that it always prints the key of each string.

📘

Defining context makes it possible to distinguish between two identical source strings and disambiguate the translation.

{% t "Contact us" _context="Support page CTA" _tags="important,footer" %}
{% t "A string" _comment="Developer comment" _charlimit=30 _tags="t1,t2" %}

t(
    "A string",
    _comment="A comment to the translators",
    _charlimit=30,
    _tags="t1,t2",
)

Outcome as variable

By using the as <var_name> suffix, instead of displaying the outcome of the translation, you can save the resulting translation in a variable that you can later use however you like.

{% t "Your credit card was accepted" as success_msg %}
{% t "Your credit card was declined" as failure_msg %}
{% if success %}
    {{ success_msg }}
{% else %}
    {{ failure_msg }}
{% endif %}

# A block syntax example

{% t as text %}
    Hello world
{% endt %}
{{ text }}

Applying filters

Apart from using filters for parameters, you can also apply them on the source string as shown in the example below:

{% t "Hello {username}"|capfirst %}
{% t source_string|capfirst %}

Any filters added will be applied to the translation, not the source string. For example, if you had the following translations in French:

{
    "hello": "bonjour",
    "Hello": "I like pancakes"
}

and you translate into French using this tag {% t "hello"|capfirst %} you will get Bonjour.

If the filter had been applied to the source string before a translation was looked up, then the second translation would have been matched and you would have gotten I like pancakes.

Source string filters work with block syntax as well, just make sure you prepend the filter(s) with |:

{% t |capfirst %}
    hello world
{% endt %}

Transifex Native supports all possible filters, both built-in or user created.

In the section below we highlight two specific filters that are the most commonly used:

escapejs

This filter is provided by Django and is very useful when you want to set translations as the values of javascript variables.

Consider the following example:

<script>var msg = '{% ut "hello world" %}';</script>

If a translation has the single-quote (') character in it, this would break your javascript code as the browser would end up reading something like:

<script>var msg = 'one ' two';</script>

To counter this, you can use the escapejs filter:

<script>var msg = '{% ut "hello world"|escapejs %}';</script>

In which case your browser would end up reading something like:

<script>var msg = 'one \u0027 two';</script>

Which is the correct way to include a single-quote character in a javascript string literal.

trimmed

This is a filter included in our template library, so it will be available to you since you included the library with {% load transifex %}. Its purpose is to allow you to split a long source string into multiple lines using the block syntax, without having the splitting appear in the translation outcome. It essentially returns all non-empty lines of the translation joined with a single space. So this:

{% t |trimmed %}
  First line
  Second line
{% endt %}

would be rendered as:

Πρώτη γραμμή Δεύτερη γραμμή

XML Escaping

Choosing between the {% t %} or the {% ut %} tags will determine whether you want to perform escaping on the translation (or the source string if a translation isn't available).

Using t will apply escaping on the translation while ut will not. So for example, if you use:

{% t '<a href="{url}" title="help page">click here</a>' %}

your application will actually render the XML-escaped version of the translation (or source string if a translation isn't found):

&lt;a href=&quot;https://some.url/&quot; title=&quot;Σελίδα βοήθειας&quot;&gt;Κάντε κλικ εδώ&lt;/a&gt;

If you want to avoid the above behaviour, you should use the {% ut %} tag instead.

📘

When using the {% ut %} tag please keep in mind that your translators would be able to include malicious content in the translations, so make sure you have a proofreading process in place.

Escaping of the ICU parameters is not affected by the choice of tag. If a context variable is used without specifying a parameter in the tag, then whether the variable will be escaped or not depends on the choice of the auto escape setting, which is usually set to true. Otherwise, you can apply the |safe or |escape filters on the parameters to specify the behaviour you want. For example, assuming you have the following translation:

{"<b>hello</b> {var}": "<b>καλημέρα</b> {var}"}

and the following context variable:

{"var": "<b>world</b>"}

you can expect the following outcomes:

{% t "<b>hello</b> {var}" var=var|escape %}
# expected result:
&lt;b&gt;καλημέρα&lt;/b&gt; &lt;b&gt;world&lt;/b&gt;

{% t "<b>hello</b> {var}" var=var|safe %}
# expected result:
&lt;b&gt;καλημέρα&lt;/b&gt; <b>world</b>

{% ut "<b>hello</b> {var}" var=var|escape %}
# expected result:
<b>καλημέρα</b> &lt;b&gt;world&lt;/b&gt;

{% ut "<b>hello</b> {var}" var=var|safe %}
# expected result:
<b>καλημέρα</b> <b>world</>

By using the above two mechanisms (the choice of tag and applying escape-related filters to the parameters) gives you good control over escaping, the outcome of the tag is always marked as safe and applying escape-related filters to it will not have any effect. This effectively means that the use of |escape or |safe as source string filters or as filters applied to a saved translation outcome will be ignored, ie the following examples in each column should behave identically:

Source string filtersSaved variable filters
{% t/ut <source_string> ... %}{% t/ut <source_string> ... as text %}
{{ text }}
{% t/ut <source_string>|safe ... %}{% t/ut <source_string> ... as text %}
{{ text|safe }}
{% t/ut <source_string>|escape ... %}{% t/ut <source_string> ... as text %}
{{ text|escape }}

📘

Considering the complexity of the cases with regards to how escaping works, the toolkit comes with a Django management command that acts as a sandbox for all combinations of tags, filters etc.

You can invoke it with:

./manage.py transifex try-templatetag --interactive