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:
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._
) 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 (for example, %(day)s
) instead of positional interpolation (for example, %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.
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, for example:
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.
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.
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 'msgid'
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.
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.
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.
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 naïve 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')
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?')
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.
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)
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).
For any other case where you would like to delay the translation, but have to pass the translatable string as an 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>"))
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.