Internationalization in Python Code

Standard Translation

Specify a translation string by using the function ugettext(). It’s convention to import this as a shorter alias, _, to save typing.

Python’s standard library gettext module installs _() into the global namespace, as an alias for gettext(). In Django, we have chosen not to follow this practice, for a couple of reasons:

  1. For international character set (Unicode) support, ugettext() is more useful than gettext(). Sometimes, you should be using ugettext_lazy() as the default translation method for a particular file. Without _() in the global namespace, the developer has to think about which is the most appropriate translation function.
  2. The underscore character (_) is used to represent the previous result in Python’s interactive shell and doctest tests. Installing a global _() function causes interference. Explicitly importing ugettext() as _() avoids this problem.

In this example, the text “Welcome to my site.” is marked as a translation string:

from django.utils.translation import ugettext as _
from django.http import HttpResponse

def my_view(request):
    output = _("Welcome to my site.")
    return HttpResponse(output)

Obviously, you could code this without using the alias. This example is identical to the previous one:

from django.utils.translation import ugettext
from django.http import HttpResponse

def my_view(request):
    output = ugettext("Welcome to my site.")
    return HttpResponse(output)

Translation also works on computed values. This example is identical to the previous two:

def my_view(request):
    words = ['Welcome', 'to', 'my', 'site.']
    output = _(' '.join(words))
    return HttpResponse(output)

… and on variables. Again, here’s an identical example:

def my_view(request):
    sentence = 'Welcome to my site.'
    output = _(sentence)
    return HttpResponse(output)

(The caveat with using variables or computed values, as in the previous two examples, is that Django’s translation-string-detecting utility, django-admin makemessages, won’t be able to find these strings. More on makemessages later.)

The strings you pass to _() or ugettext() can take placeholders, specified with Python’s standard named-string interpolation syntax. Example:

def my_view(request, m, d):
    output = _('Today is %(month)s %(day)s.') % {'month': m, 'day': d}
    return HttpResponse(output)

This technique lets language-specific translations reorder the placeholder text. For example, an English translation may be “Today is November 26.”, while a Spanish translation may be “Hoy es 26 de Noviembre.” – with the month and the day placeholders swapped.

For this reason, you should use named-string interpolation (e.g., %(day)s) instead of positional interpolation (e.g., %s or %d) whenever you have more than a single parameter. If you used positional interpolation, translations wouldn’t be able to reorder placeholder text.

Comments for Translators

If you would like to give translators hints about a translatable string, you can add a comment prefixed with the Translators keyword on the line preceding the string, e.g.:

def my_view(request):
    # Translators: This message appears on the home page only
    output = ugettext("Welcome to my site.")

The comment will then appear in the resulting .po file associated with the translatable construct located below it and should also be displayed by most translation tools.

Just for completeness, this is the corresponding fragment of the resulting .po file:

#. Translators: This message appears on the home page only
# path/to/python/file.py:123
msgid "Welcome to my site."
msgstr ""

This also works in templates. See translator-comments-in-templates for more details.

Marking Strings as No-Op

Use the function django.utils.translation.ugettext_noop() to mark a string as a translation string without translating it. The string is later translated from a variable.

Use this if you have constant strings that should be stored in the source language because they are exchanged over systems or users – such as strings in a database – but should be translated at the last possible point in time, such as when the string is presented to the user.

Pluralization

Use the function django.utils.translation.ungettext() to specify pluralized messages.

ungettext takes three arguments: the singular translation string, the plural translation string and the number of objects.

This function is useful when you need your Django application to be localizable to languages where the number and complexity of plural forms is greater than the two forms used in English (‘object’ for the singular and ‘objects’ for all the cases where count is different from one, irrespective of its value.)

For example:

from django.utils.translation import ungettext
from django.http import HttpResponse

def hello_world(request, count):
    page = ungettext(
        'there is %(count)d object',
        'there are %(count)d objects', 
        count
        ) % {
            'count': count, 
        }
    return HttpResponse(page)

In this example the number of objects is passed to the translation languages as the count
variable.

Note that pluralization is complicated and works differently in each language. Comparing count
to 1 isn’t always the correct rule. This code looks sophisticated, but will produce incorrect results for some languages:

from django.utils.translation import ungettext
from myapp.models import Report

count = Report.objects.count()
if count == 1:
    name = Report._meta.verbose_name
else:
    name = Report._meta.verbose_name_plural

text = ungettext(
    'There is %(count)d %(name)s available.',
    'There are %(count)d %(name)s available.',
    count
    ) % {
      'count': count,
      'name': name
    }

Don’t try to implement your own singular-or-plural logic, it won’t be correct. In a case like this, consider something like the following:

text = ungettext(
    'There is %(count)d %(name)s object available.',
    'There are %(count)d %(name)s objects available.',
    count
    ) % {
      'count': count,
      'name': Report._meta.verbose_name,
    }

When using ungettext(), make sure you use a single name for every extrapolated variable included in the literal. In the examples above, note how we used the name Python variable in both translation strings. This example, besides being incorrect in some languages as noted above, would fail:

text = ungettext(
    'There is %(count)d %(name)s available.',
    'There are %(count)d %(plural_name)s available.',
    count
    ) % {
      'count': Report.objects.count(),
      'name': Report._meta.verbose_name,
      'plural_name': Report._meta.verbose_name_plural
    }

You would get an error when running django-admin compilemessages:

a format specification for argument 'name', as in 'msgstr[0]', doesn't exist in 'msgi\
d'

Contextual Markers

Sometimes words have several meanings, such as “May” in English, which refers to a month name and to a verb. To enable translators to translate these words correctly in different contexts, you can use the django.utils.translation.pgettext() function, or the django.utils.translation.npgettext() function if the string needs pluralization. Both take a context string as the first variable.

In the resulting .po file, the string will then appear as often as there are different contextual markers for the same string (the context will appear on the msgctxt line), allowing the translator to give a different translation for each of them.

For example:

from django.utils.translation import pgettext

month = pgettext("month name", "May")

or:

from django.db import models
from django.utils.translation import pgettext_lazy

class MyThing(models.Model):
    name = models.CharField(help_text=pgettext_lazy( 
        'help text for MyThing model', 'This is the help text'))

will appear in the .po file as:

msgctxt "month name"
msgid "May"
msgstr ""

Contextual markers are also supported by the trans and blocktrans template tags.

Lazy Translation

Use the lazy versions of translation functions in django.utils.translation (easily recognizable by the lazy suffix in their names) to translate strings lazily – when the value is accessed rather than when they’re called.

These functions store a lazy reference to the string – not the actual translation. The translation itself will be done when the string is used in a string context, such as in template rendering.

This is essential when calls to these functions are located in code paths that are executed at module load time.

This is something that can easily happen when defining models, forms and model forms, because Django implements these such that their fields are actually class-level attributes. For that reason, make sure to use lazy translations in the following cases.

Model fields and relationships

For example, to translate the help text of the name field in the following model, do the following:

from django.db import models
from django.utils.translation import ugettext_lazy as _

class MyThing(models.Model):
    name = models.CharField(help_text=_('This is the help text'))

You can mark names of ForeignKey, ManyToManyField or OneToOneField relationship as translatable by using their verbose_name options:

class MyThing(models.Model):
    kind = models.ForeignKey(ThingKind, related_name='kinds',   verbose_name=_('kind'\
))

Just like you would do in verbose_name you should provide a lowercase verbose name text for the relation as Django will automatically title case it when required.

Model verbose names values

It is recommended to always provide explicit verbose_name and verbose_name_plural options rather than relying on the fall-back English-centric and somewhat naive determination of verbose names Django performs by looking at the model’s class name:

from django.db import models
from django.utils.translation import ugettext_lazy as _

class MyThing(models.Model):
    name = models.CharField(_('name'), help_text=_('This is the help   text'))

    class Meta:
        verbose_name = _('my thing')
        verbose_name_plural = _('my things')
Model methods short_description attribute values

For model methods, you can provide translations to Django and the admin site with the short_description attribute:

from django.db import models
from django.utils.translation import ugettext_lazy as _

class MyThing(models.Model):
    kind = models.ForeignKey(ThingKind, related_name='kinds', verbose_name=_('kind'))

    def is_mouse(self):
        return self.kind.type == MOUSE_TYPE
        is_mouse.short_description = _('Is it a mouse?')

Working with Lazy Translation Objects

The result of a ugettext_lazy() call can be used wherever you would use a Unicode string (an object with type unicode) in Python. If you try to use it where a bytestring (a str object) is expected, things will not work as expected, since a ugettext_lazy() object doesn’t know how to convert itself to a bytestring. You can’t use a Unicode string inside a bytestring, either, so this is consistent with normal Python behavior. For example:

# This is fine: putting a unicode proxy into a unicode string.
"Hello %s" % ugettext_lazy("people")

# This will not work, since you cannot insert a unicode object
# into a bytestring (nor can you insert our unicode proxy there)
b"Hello %s" % ugettext_lazy("people")

If you ever see output that looks like "hello <django.utils.functional...>", you have tried to insert the result of ugettext_lazy() into a bytestring. That’s a bug in your code.

If you don’t like the long ugettext_lazy name, you can just alias it as (underscore), like so:

from django.db import models
from django.utils.translation import ugettext_lazy as _

class MyThing(models.Model):
    name = models.CharField(help_text=_('This is the help text'))

Using ugettext_lazy() and ungettext_lazy() to mark strings in models and utility functions is a common operation. When you’re working with these objects elsewhere in your code, you should ensure that you don’t accidentally convert them to strings, because they should be converted as late as possible (so that the correct locale is in effect). This necessitates the use of the helper function described next.

Lazy translations and plural

When using lazy translation for a plural string ([u]n[p]gettext_lazy), you generally don’t know the number argument at the time of the string definition. Therefore, you are authorized to pass a key name instead of an integer as the number argument. Then number will be looked up in the dictionary under that key during string interpolation. Here’s example:

from django import forms
from django.utils.translation import ugettext_lazy

class MyForm(forms.Form):
    error_message = ungettext_lazy("You only provided %(num)d    
      argument", "You only provided %(num)d arguments", 'num')

    def clean(self):
        # ...
        if error:
            raise forms.ValidationError(self.error_message %  
              {'num': number})

If the string contains exactly one unnamed placeholder, you can interpolate directly with the number argument:

class MyForm(forms.Form):
    error_message = ungettext_lazy("You provided %d argument",
        "You provided %d arguments")

    def clean(self):
        # ...
        if error:
            raise forms.ValidationError(self.error_message % number)
Joining strings: string_concat()

Standard Python string joins (''.join([...])) will not work on lists containing lazy translation objects.
Instead, you can use django.utils.translation.string_concat(), which creates a lazy object that concatenates its contents and converts them to strings only when the result is included in a string. For example:

from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy
# ...
name = ugettext_lazy('John Lennon')
instrument = ugettext_lazy('guitar')
result = string_concat(name, ': ', instrument)

In this case, the lazy translations in result will only be converted to strings when result itself is used in a string (usually at template rendering time).

Other uses of lazy in delayed translations

For any other case where you would like to delay the translation, but have to pass the translatable string as argument to another function, you can wrap this function inside a lazy call yourself. For example:

from django.utils import six  # Python 3 compatibility
from django.utils.functional import lazy
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _

mark_safe_lazy = lazy(mark_safe, six.text_type)

And then later:

lazy_string = mark_safe_lazy(_("<p>My <strong>string!</strong></p>"))

Localized Names of Languages

The get_language_info() function provides detailed information about languages:

>>> from django.utils.translation import get_language_info
>>> li = get_language_info('de')
>>> print(li['name'], li['name_local'], li['bidi'])
German Deutsch False 

The name and name_local attributes of the dictionary contain the name of the language in English and in the language itself, respectively. The bidi attribute is True only for bi-directional languages.

The source of the language information is the django.conf.locale module. Similar access to this information is available for template code. See below.