123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106 |
- ===========
- Translation
- ===========
- .. module:: django.utils.translation
- Overview
- ========
- In order to make a Django project translatable, you have to add a minimal
- number of hooks to your Python code and templates. These hooks are called
- :term:`translation strings <translation string>`. They tell Django: "This text
- should be translated into the end user's language, if a translation for this
- text is available in that language." It's your responsibility to mark
- translatable strings; the system can only translate strings it knows about.
- Django then provides utilities to extract the translation strings into a
- :term:`message file`. This file is a convenient way for translators to provide
- the equivalent of the translation strings in the target language. Once the
- translators have filled in the message file, it must be compiled. This process
- relies on the GNU gettext toolset.
- Once this is done, Django takes care of translating Web apps on the fly in each
- available language, according to users' language preferences.
- Django's internationalization hooks are on by default, and that means there's a
- bit of i18n-related overhead in certain places of the framework. If you don't
- use internationalization, you should take the two seconds to set
- :setting:`USE_I18N = False <USE_I18N>` in your settings file. Then Django will
- make some optimizations so as not to load the internationalization machinery.
- .. note::
- There is also an independent but related :setting:`USE_L10N` setting that
- controls if Django should implement format localization. See
- :doc:`/topics/i18n/formatting` for more details.
- .. note::
- Make sure you've activated translation for your project (the fastest way is
- to check if :setting:`MIDDLEWARE_CLASSES` includes
- :mod:`django.middleware.locale.LocaleMiddleware`). If you haven't yet,
- see :ref:`how-django-discovers-language-preference`.
- Internationalization: in Python code
- ====================================
- Standard translation
- --------------------
- Specify a translation string by using the function
- :func:`~django.utils.translation.ugettext`. It's convention to import this
- as a shorter alias, ``_``, to save typing.
- .. note::
- 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,
- :func:`~django.utils.translation.ugettext` is more useful than
- ``gettext()``. Sometimes, you should be using
- :func:`~django.utils.translation.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 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)
- Translation works 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,
- :djadmin:`django-admin makemessages <makemessages>`, won't be able to find
- these strings. More on :djadmin:`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.
- .. _translator-comments:
- 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.
- .. note:: Just for completeness, this is the corresponding fragment of the
- resulting ``.po`` file:
- .. code-block:: po
- #. 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 :ref:`translator-comments-in-templates` for
- more details.
- Marking strings as no-op
- ------------------------
- Use the function :func:`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 :func:`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
- <https://www.gnu.org/software/gettext/manual/gettext.html#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,
- }
- .. _pluralization-var-notes:
- .. note::
- 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 :djadmin:`django-admin
- compilemessages <compilemessages>`::
- a format specification for argument 'name', as in 'msgstr[0]', doesn't exist in 'msgid'
- .. note:: Plural form and po files
- Django does not support custom plural equations in po files. As all
- translation catalogs are merged, only the plural form for the main Django po
- file (in ``django/conf/locale/<lang_code>/LC_MESSAGES/django.po``) is
- considered. Plural forms in all other po files are ignored. Therefore, you
- should not use different plural equations in your project or application po
- files.
- .. _contextual-markers:
- 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
- :func:`django.utils.translation.pgettext()` function, or the
- :func:`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:
- .. code-block:: po
- msgctxt "month name"
- msgid "May"
- msgstr ""
- Contextual markers are also supported by the :ttag:`trans` and
- :ttag:`blocktrans` template tags.
- .. _lazy-translations:
- Lazy translation
- ----------------
- Use the lazy versions of translation functions in
- :mod:`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 ``verbose_name`` and ``help_text`` option values
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 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 :class:`~django.db.models.ForeignKey`,
- :class:`~django.db.models.ManyToManyField` or
- :class:`~django.db.models.OneToOneField` relationship as translatable by using
- their :attr:`~django.db.models.Options.verbose_name` options::
- class MyThing(models.Model):
- kind = models.ForeignKey(
- ThingKind,
- on_delete=models.CASCADE,
- related_name='kinds',
- verbose_name=_('kind'),
- )
- Just like you would do in :attr:`~django.db.models.Options.verbose_name` you
- should provide a lowercase verbose name text for the relation as Django will
- automatically titlecase it when required.
- Model verbose names values
- ~~~~~~~~~~~~~~~~~~~~~~~~~~
- It is recommended to always provide explicit
- :attr:`~django.db.models.Options.verbose_name` and
- :attr:`~django.db.models.Options.verbose_name_plural` options rather than
- relying on the fallback 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')
- 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,
- on_delete=models.CASCADE,
- 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-plural-translations:
- 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 ungettext_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
- :func:`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
- ----------------------------
- .. function:: get_language_info
- 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.
- .. _specifying-translation-strings-in-template-code:
- Internationalization: in template code
- ======================================
- .. highlightlang:: html+django
- Translations in :doc:`Django templates </ref/templates/language>` 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.
- .. templatetag:: trans
- ``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
- :func:`~django.utils.translation.ugettext` call.
- In case a template var (``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
- :ttag:`{% blocktrans %}<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 %}
- <title>{{ the_title }}</title>
- <meta name="description" content="{{ the_title }}">
- In practice you'll use this to get a string you can use in multiple places in a
- template or so you can use the output as an argument 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 :ref:`contextual markers<contextual-markers>`
- using the ``context`` keyword:
- .. code-block:: html+django
- {% trans "May" context "month name" %}
- .. templatetag:: blocktrans
- ``blocktrans`` template tag
- ---------------------------
- Contrarily to the :ttag:`trans` 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 %}
- .. note:: 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 :func:`~django.utils.translation.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 :ref:`notes regarding ungettext variables <pluralization-var-notes>`
- 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 %}
- If you'd like to retrieve a translated string without displaying it, you can
- use the following syntax::
- {% blocktrans asvar the_title %}The title is {{ title }}.{% endblocktrans %}
- <title>{{ the_title }}</title>
- <meta name="description" content="{{ the_title }}">
- In practice you'll use this to get a string you can use in multiple places in a
- template or so you can use the output as an argument for other template tags or
- filters.
- .. versionchanged:: 1.9
- The ``asvar`` syntax was added.
- ``{% blocktrans %}`` also supports :ref:`contextual
- markers<contextual-markers>` using the ``context`` keyword:
- .. code-block:: html+django
- {% 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.
- .. note::
- 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).
- .. _translator-comments-in-templates:
- Comments for translators in templates
- -------------------------------------
- Just like with :ref:`Python code <translator-comments>`, these notes for
- translators can be specified using comments, either with the :ttag:`comment`
- tag:
- .. code-block:: html+django
- {% comment %}Translators: View verb{% endcomment %}
- {% trans "View" %}
- {% comment %}Translators: Short intro blurb{% endcomment %}
- <p>{% blocktrans %}A multiline translatable
- literal.{% endblocktrans %}</p>
- or with the ``{#`` ... ``#}`` :ref:`one-line comment constructs <template-comments>`:
- .. code-block:: html+django
- {# 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 %}
- .. note:: Just for completeness, these are the corresponding fragments of the
- resulting ``.po`` file:
- .. code-block:: po
- #. 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 ""
- .. templatetag:: language
- Switching language in templates
- -------------------------------
- If you want to select a language within a template, you can use the
- ``language`` template tag:
- .. code-block:: html+django
- {% 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.
- .. _template-translation-vars:
- Other tags
- ----------
- These tags also require a ``{% load i18n %}``.
- .. templatetag:: get_available_languages
- ``get_available_languages``
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~
- ``{% get_available_languages as LANGUAGES %}`` returns a list of tuples in
- which the first element is the :term:`language code` and the second is the
- language name (translated into the currently active locale).
- .. templatetag:: get_current_language
- ``get_current_language``
- ~~~~~~~~~~~~~~~~~~~~~~~~
- ``{% get_current_language as LANGUAGE_CODE %}`` returns the current user's
- preferred language as a string. Example: ``en-us``. See
- :ref:`how-django-discovers-language-preference`.
- .. templatetag:: get_current_language_bidi
- ``get_current_language_bidi``
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- ``{% 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.
- .. templatetag:: get_language_info
- ``get_language_info``
- ~~~~~~~~~~~~~~~~~~~~~
- 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 }}
- Name in the active language: {{ lang.name_translated }}
- .. versionadded:: 1.9
- The ``name_translated`` attribute was added.
- .. templatetag:: get_language_info_list
- ``get_language_info_list``
- ~~~~~~~~~~~~~~~~~~~~~~~~~~
- 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
- :setting:`LANGUAGES`). See :ref:`the section about the set_language redirect
- view <set_language-redirect-view>` for an example of how to display a language
- selector using ``{% get_language_info_list %}``.
- In addition to :setting:`LANGUAGES` style list of tuples,
- ``{% get_language_info_list %}`` supports simple lists of language codes.
- If you do this in your view:
- .. code-block:: python
- 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 %}
- .. templatefilter:: language_name
- .. templatefilter:: language_name_local
- .. templatefilter:: language_bidi
- .. templatefilter:: language_name_translated
- Template filters
- ~~~~~~~~~~~~~~~~
- There are also simple filters available for convenience:
- * ``{{ LANGUAGE_CODE|language_name }}`` ("German")
- * ``{{ LANGUAGE_CODE|language_name_local }}`` ("Deutsch")
- * ``{{ LANGUAGE_CODE|language_bidi }}`` (False)
- * ``{{ LANGUAGE_CODE|language_name_translated }}`` ("německy", when active language is Czech)
- .. versionadded:: 1.9
- The ``language_name_translated`` filter was added.
- .. _Django templates: ../templates_python/
- Internationalization: in JavaScript code
- ========================================
- .. highlightlang:: python
- Adding translations to JavaScript poses some problems:
- * JavaScript code doesn't have access to a ``gettext`` implementation.
- * JavaScript code doesn't have access to ``.po`` or ``.mo`` files; they need to
- be delivered by the server.
- * The translation catalogs for JavaScript should be kept as small as
- possible.
- Django provides an integrated solution for these problems: It passes the
- translations into JavaScript, so you can call ``gettext``, etc., from within
- JavaScript.
- .. _javascript_catalog-view:
- The ``javascript_catalog`` view
- -------------------------------
- .. module:: django.views.i18n
- .. function:: javascript_catalog(request, domain='djangojs', packages=None)
- The main solution to these problems is the
- :meth:`django.views.i18n.javascript_catalog` view, which sends out a JavaScript
- code library with functions that mimic the ``gettext`` interface, plus an array
- of translation strings. Those translation strings are taken from applications or
- Django core, according to what you specify in either the ``info_dict`` or the
- URL. Paths listed in :setting:`LOCALE_PATHS` are also included.
- You hook it up like this::
- from django.views.i18n import javascript_catalog
- js_info_dict = {
- 'packages': ('your.app.package',),
- }
- urlpatterns = [
- url(r'^jsi18n/$', javascript_catalog, js_info_dict, name='javascript-catalog'),
- ]
- Each string in ``packages`` should be in Python dotted-package syntax (the
- same format as the strings in :setting:`INSTALLED_APPS`) and should refer to a
- package that contains a ``locale`` directory. If you specify multiple packages,
- all those catalogs are merged into one catalog. This is useful if you have
- JavaScript that uses strings from different applications.
- The precedence of translations is such that the packages appearing later in the
- ``packages`` argument have higher precedence than the ones appearing at the
- beginning, this is important in the case of clashing translations for the same
- literal.
- By default, the view uses the ``djangojs`` gettext domain. This can be
- changed by altering the ``domain`` argument.
- You can make the view dynamic by putting the packages into the URL pattern::
- urlpatterns = [
- url(r'^jsi18n/(?P<packages>\S+?)/$', javascript_catalog, name='javascript-catalog'),
- ]
- With this, you specify the packages as a list of package names delimited by '+'
- signs in the URL. This is especially useful if your pages use code from
- different apps and this changes often and you don't want to pull in one big
- catalog file. As a security measure, these values can only be either
- ``django.conf`` or any package from the :setting:`INSTALLED_APPS` setting.
- You can also split the catalogs in multiple URLs and load them as you need in
- your sites::
- js_info_dict_app = {
- 'packages': ('your.app.package',),
- }
- js_info_dict_other_app = {
- 'packages': ('your.other.app.package',),
- }
- urlpatterns = [
- url(r'^jsi18n/app/$', javascript_catalog, js_info_dict_app),
- url(r'^jsi18n/other_app/$', javascript_catalog, js_info_dict_other_app),
- ]
- If you use more than one ``javascript_catalog`` on a site and some of them
- define the same strings, the strings in the catalog that was loaded last take
- precedence.
- .. versionchanged:: 1.9
- Before Django 1.9, the catalogs completely overwrote each other and you
- could only use one at a time.
- The JavaScript translations found in the paths listed in the
- :setting:`LOCALE_PATHS` setting are also always included. To keep consistency
- with the translations lookup order algorithm used for Python and templates, the
- directories listed in :setting:`LOCALE_PATHS` have the highest precedence with
- the ones appearing first having higher precedence than the ones appearing
- later.
- Using the JavaScript translation catalog
- ----------------------------------------
- .. highlightlang:: javascript
- To use the catalog, just pull in the dynamically generated script like this:
- .. code-block:: html+django
- <script type="text/javascript" src="{% url 'javascript-catalog' %}"></script>
- This uses reverse URL lookup to find the URL of the JavaScript catalog view.
- When the catalog is loaded, your JavaScript code can use the following methods:
- * ``gettext``
- * ``ngettext``
- * ``interpolate``
- * ``get_format``
- * ``gettext_noop``
- * ``pgettext``
- * ``npgettext``
- * ``pluralidx``
- ``gettext``
- ~~~~~~~~~~~
- The ``gettext`` function behaves similarly to the standard ``gettext``
- interface within your Python code::
- document.write(gettext('this is to be translated'));
- ``ngettext``
- ~~~~~~~~~~~~
- The ``ngettext`` function provides an interface to pluralize words and
- phrases::
- var object_count = 1 // or 0, or 2, or 3, ...
- s = ngettext('literal for the singular case',
- 'literal for the plural case', object_count);
- ``interpolate``
- ~~~~~~~~~~~~~~~
- The ``interpolate`` function supports dynamically populating a format string.
- The interpolation syntax is borrowed from Python, so the ``interpolate``
- function supports both positional and named interpolation:
- * Positional interpolation: ``obj`` contains a JavaScript Array object
- whose elements values are then sequentially interpolated in their
- corresponding ``fmt`` placeholders in the same order they appear.
- For example::
- fmts = ngettext('There is %s object. Remaining: %s',
- 'There are %s objects. Remaining: %s', 11);
- s = interpolate(fmts, [11, 20]);
- // s is 'There are 11 objects. Remaining: 20'
- * Named interpolation: This mode is selected by passing the optional
- boolean ``named`` parameter as ``true``. ``obj`` contains a JavaScript
- object or associative array. For example::
- d = {
- count: 10,
- total: 50
- };
- fmts = ngettext('Total: %(total)s, there is %(count)s object',
- 'there are %(count)s of a total of %(total)s objects', d.count);
- s = interpolate(fmts, d, true);
- You shouldn't go over the top with string interpolation, though: this is still
- JavaScript, so the code has to make repeated regular-expression substitutions.
- This isn't as fast as string interpolation in Python, so keep it to those
- cases where you really need it (for example, in conjunction with ``ngettext``
- to produce proper pluralizations).
- ``get_format``
- ~~~~~~~~~~~~~~
- The ``get_format`` function has access to the configured i18n formatting
- settings and can retrieve the format string for a given setting name::
- document.write(get_format('DATE_FORMAT'));
- // 'N j, Y'
- It has access to the following settings:
- * :setting:`DATE_FORMAT`
- * :setting:`DATE_INPUT_FORMATS`
- * :setting:`DATETIME_FORMAT`
- * :setting:`DATETIME_INPUT_FORMATS`
- * :setting:`DECIMAL_SEPARATOR`
- * :setting:`FIRST_DAY_OF_WEEK`
- * :setting:`MONTH_DAY_FORMAT`
- * :setting:`NUMBER_GROUPING`
- * :setting:`SHORT_DATE_FORMAT`
- * :setting:`SHORT_DATETIME_FORMAT`
- * :setting:`THOUSAND_SEPARATOR`
- * :setting:`TIME_FORMAT`
- * :setting:`TIME_INPUT_FORMATS`
- * :setting:`YEAR_MONTH_FORMAT`
- This is useful for maintaining formatting consistency with the Python-rendered
- values.
- ``gettext_noop``
- ~~~~~~~~~~~~~~~~
- This emulates the ``gettext`` function but does nothing, returning whatever
- is passed to it::
- document.write(gettext_noop('this will not be translated'));
- This is useful for stubbing out portions of the code that will need translation
- in the future.
- ``pgettext``
- ~~~~~~~~~~~~
- The ``pgettext`` function behaves like the Python variant
- (:func:`~django.utils.translation.pgettext()`), providing a contextually
- translated word::
- document.write(pgettext('month name', 'May'));
- ``npgettext``
- ~~~~~~~~~~~~~
- The ``npgettext`` function also behaves like the Python variant
- (:func:`~django.utils.translation.npgettext()`), providing a **pluralized**
- contextually translated word::
- document.write(npgettext('group', 'party', 1));
- // party
- document.write(npgettext('group', 'party', 2));
- // parties
- ``pluralidx``
- ~~~~~~~~~~~~~
- The ``pluralidx`` function works in a similar way to the :tfilter:`pluralize`
- template filter, determining if a given ``count`` should use a plural form of
- a word or not::
- document.write(pluralidx(0));
- // true
- document.write(pluralidx(1));
- // false
- document.write(pluralidx(2));
- // true
- In the simplest case, if no custom pluralization is needed, this returns
- ``false`` for the integer ``1`` and ``true`` for all other numbers.
- However, pluralization is not this simple in all languages. If the language does
- not support pluralization, an empty value is provided.
- Additionally, if there are complex rules around pluralization, the catalog view
- will render a conditional expression. This will evaluate to either a ``true``
- (should pluralize) or ``false`` (should **not** pluralize) value.
- .. highlight:: python
- The ``json_catalog`` view
- -------------------------
- .. versionadded:: 1.9
- .. function:: json_catalog(request, domain='djangojs', packages=None)
- In order to use another client-side library to handle translations, you may
- want to take advantage of the ``json_catalog()`` view. It's similar to
- :meth:`~django.views.i18n.javascript_catalog` but returns a JSON response.
- The JSON object contains i18n formatting settings (those available for
- `get_format`_), a plural rule (as a ``plural`` part of a GNU gettext
- ``Plural-Forms`` expression), and translation strings. The translation strings
- are taken from applications or Django's own translations, according to what is
- specified either via ``urlpatterns`` arguments or as request parameters. Paths
- listed in :setting:`LOCALE_PATHS` are also included.
- The view is hooked up to your application and configured in the same fashion as
- :meth:`~django.views.i18n.javascript_catalog` (namely, the ``domain`` and
- ``packages`` arguments behave identically)::
- from django.views.i18n import json_catalog
- js_info_dict = {
- 'packages': ('your.app.package',),
- }
- urlpatterns = [
- url(r'^jsoni18n/$', json_catalog, js_info_dict),
- ]
- The response format is as follows:
- .. code-block:: text
- {
- "catalog": {
- # Translations catalog
- },
- "formats": {
- # Language formats for date, time, etc.
- },
- "plural": "..." # Expression for plural forms, or null.
- }
- .. JSON doesn't allow comments so highlighting as JSON won't work here.
- Note on performance
- -------------------
- The :func:`~django.views.i18n.javascript_catalog` view generates the catalog
- from ``.mo`` files on every request. Since its output is constant — at least
- for a given version of a site — it's a good candidate for caching.
- Server-side caching will reduce CPU load. It's easily implemented with the
- :func:`~django.views.decorators.cache.cache_page` decorator. To trigger cache
- invalidation when your translations change, provide a version-dependent key
- prefix, as shown in the example below, or map the view at a version-dependent
- URL::
- from django.views.decorators.cache import cache_page
- from django.views.i18n import javascript_catalog
- # The value returned by get_version() must change when translations change.
- @cache_page(86400, key_prefix='js18n-%s' % get_version())
- def cached_javascript_catalog(request, domain='djangojs', packages=None):
- return javascript_catalog(request, domain, packages)
- Client-side caching will save bandwidth and make your site load faster. If
- you're using ETags (:setting:`USE_ETAGS = True <USE_ETAGS>`), you're already
- covered. Otherwise, you can apply :ref:`conditional decorators
- <conditional-decorators>`. In the following example, the cache is invalidated
- whenever you restart your application server::
- from django.utils import timezone
- from django.views.decorators.http import last_modified
- from django.views.i18n import javascript_catalog
- last_modified_date = timezone.now()
- @last_modified(lambda req, **kw: last_modified_date)
- def cached_javascript_catalog(request, domain='djangojs', packages=None):
- return javascript_catalog(request, domain, packages)
- You can even pre-generate the JavaScript catalog as part of your deployment
- procedure and serve it as a static file. This radical technique is implemented
- in django-statici18n_.
- .. _django-statici18n: http://django-statici18n.readthedocs.org/en/latest/
- .. _url-internationalization:
- Internationalization: in URL patterns
- =====================================
- .. module:: django.conf.urls.i18n
- Django provides two mechanisms to internationalize URL patterns:
- * Adding the language prefix to the root of the URL patterns to make it
- possible for :class:`~django.middleware.locale.LocaleMiddleware` to detect
- the language to activate from the requested URL.
- * Making URL patterns themselves translatable via the
- :func:`django.utils.translation.ugettext_lazy()` function.
- .. warning::
- Using either one of these features requires that an active language be set
- for each request; in other words, you need to have
- :class:`django.middleware.locale.LocaleMiddleware` in your
- :setting:`MIDDLEWARE_CLASSES` setting.
- Language prefix in URL patterns
- -------------------------------
- .. function:: i18n_patterns(*pattern_list)
- 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
- :func:`~django.conf.urls.i18n.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'),
- ], 'news')
- 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.urls 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/'
- .. warning::
- :func:`~django.conf.urls.i18n.i18n_patterns` is only allowed in your root
- URLconf. Using it within an included URLconf will throw an
- :exc:`~django.core.exceptions.ImproperlyConfigured` exception.
- .. warning::
- Ensure that you don't have non-prefixed URL patterns that might collide
- with an automatically-added language prefix.
- Translating URL patterns
- ------------------------
- URL patterns can also be marked translatable using the
- :func:`~django.utils.translation.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'),
- ], 'news')
- 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 :func:`~django.urls.reverse`
- function will return the URL in the active language. Example::
- >>> from django.urls 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/'
- .. warning::
- In most cases, it's best to use translated URLs only within a
- language-code-prefixed block of patterns (using
- :func:`~django.conf.urls.i18n.i18n_patterns`), to avoid the possibility
- that a carelessly translated URL causes a collision with a non-translated
- URL pattern.
- .. _reversing_in_templates:
- 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 :ttag:`language`
- template tag. It enables the given language in the enclosed template section:
- .. code-block:: html+django
- {% 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 :ttag:`language` tag expects the language code as the only argument.
- .. _how-to-create-language-files:
- Localization: how to create language files
- ==========================================
- Once the string literals of an application have been tagged for later
- translation, the translation themselves need to be written (or obtained). Here's
- how that works.
- Message files
- -------------
- The first step is to create a :term:`message file` for a new language. A message
- file is a plain-text file, representing a single language, that contains all
- available translation strings and how they should be represented in the given
- language. Message files have a ``.po`` file extension.
- Django comes with a tool, :djadmin:`django-admin makemessages
- <makemessages>`, that automates the creation and upkeep of these files.
- .. admonition:: Gettext utilities
- The ``makemessages`` command (and ``compilemessages`` discussed later) use
- commands from the GNU gettext toolset: ``xgettext``, ``msgfmt``,
- ``msgmerge`` and ``msguniq``.
- The minimum version of the ``gettext`` utilities supported is 0.15.
- To create or update a message file, run this command::
- django-admin makemessages -l de
- ...where ``de`` is the :term:`locale name` for the message file you want to
- create. For example, ``pt_BR`` for Brazilian Portuguese, ``de_AT`` for Austrian
- German or ``id`` for Indonesian.
- The script should be run from one of two places:
- * The root directory of your Django project (the one that contains
- ``manage.py``).
- * The root directory of one of your Django apps.
- The script runs over your project source tree or your application source tree
- and pulls out all strings marked for translation (see
- :ref:`how-django-discovers-translations` and be sure :setting:`LOCALE_PATHS`
- is configured correctly). It creates (or updates) a message file in the
- directory ``locale/LANG/LC_MESSAGES``. In the ``de`` example, the file will be
- ``locale/de/LC_MESSAGES/django.po``.
- When you run ``makemessages`` from the root directory of your project, the
- extracted strings will be automatically distributed to the proper message files.
- That is, a string extracted from a file of an app containing a ``locale``
- directory will go in a message file under that directory. A string extracted
- from a file of an app without any ``locale`` directory will either go in a
- message file under the directory listed first in :setting:`LOCALE_PATHS` or
- will generate an error if :setting:`LOCALE_PATHS` is empty.
- By default :djadmin:`django-admin makemessages <makemessages>` examines every
- file that has the ``.html`` or ``.txt`` file extension. In case you want to
- override that default, use the ``--extension`` or ``-e`` option to specify the
- file extensions to examine::
- django-admin makemessages -l de -e txt
- Separate multiple extensions with commas and/or use ``-e`` or ``--extension``
- multiple times::
- django-admin makemessages -l de -e html,txt -e xml
- .. warning::
- When :ref:`creating message files from JavaScript source code
- <creating-message-files-from-js-code>` you need to use the special
- 'djangojs' domain, **not** ``-e js``.
- .. admonition:: Using Jinja2 templates?
- :djadmin:`makemessages` doesn't understand the syntax of Jinja2 templates.
- To extract strings from a project containing Jinja2 templates, use `Message
- Extracting`_ from Babel_ instead.
- Here's an example ``babel.cfg`` configuration file::
- # Extraction from Python source files
- [python: **.py]
- # Extraction from Jinja2 templates
- [jinja2: **.jinja]
- extensions = jinja2.ext.with_
- Make sure you list all extensions you're using! Otherwise Babel won't
- recognize the tags defined by these extensions and will ignore Jinja2
- templates containing them entirely.
- Babel provides similar features to :djadmin:`makemessages`, can replace it
- in general, and doesn't depend on ``gettext``. For more information, read
- its documentation about `working with message catalogs`_.
- .. _Message extracting: http://babel.pocoo.org/docs/messages/#message-extraction
- .. _Babel: http://babel.pocoo.org/
- .. _working with message catalogs: http://babel.pocoo.org/docs/messages/
- .. admonition:: No gettext?
- If you don't have the ``gettext`` utilities installed,
- :djadmin:`makemessages` will create empty files. If that's the case, either
- install the ``gettext`` utilities or just copy the English message file
- (``locale/en/LC_MESSAGES/django.po``) if available and use it as a starting
- point; it's just an empty translation file.
- .. admonition:: Working on Windows?
- If you're using Windows and need to install the GNU gettext utilities so
- :djadmin:`makemessages` works, see :ref:`gettext_on_windows` for more
- information.
- The format of ``.po`` files is straightforward. Each ``.po`` file contains a
- small bit of metadata, such as the translation maintainer's contact
- information, but the bulk of the file is a list of **messages** -- simple
- mappings between translation strings and the actual translated text for the
- particular language.
- For example, if your Django app contained a translation string for the text
- ``"Welcome to my site."``, like so::
- _("Welcome to my site.")
- ...then :djadmin:`django-admin makemessages <makemessages>` will have created
- a ``.po`` file containing the following snippet -- a message:
- .. code-block:: po
- #: path/to/python/module.py:23
- msgid "Welcome to my site."
- msgstr ""
- A quick explanation:
- * ``msgid`` is the translation string, which appears in the source. Don't
- change it.
- * ``msgstr`` is where you put the language-specific translation. It starts
- out empty, so it's your responsibility to change it. Make sure you keep
- the quotes around your translation.
- * As a convenience, each message includes, in the form of a comment line
- prefixed with ``#`` and located above the ``msgid`` line, the filename and
- line number from which the translation string was gleaned.
- Long messages are a special case. There, the first string directly after the
- ``msgstr`` (or ``msgid``) is an empty string. Then the content itself will be
- written over the next few lines as one string per line. Those strings are
- directly concatenated. Don't forget trailing spaces within the strings;
- otherwise, they'll be tacked together without whitespace!
- .. admonition:: Mind your charset
- Due to the way the ``gettext`` tools work internally and because we want to
- allow non-ASCII source strings in Django's core and your applications, you
- **must** use UTF-8 as the encoding for your PO files (the default when PO
- files are created). This means that everybody will be using the same
- encoding, which is important when Django processes the PO files.
- To reexamine all source code and templates for new translation strings and
- update all message files for **all** languages, run this::
- django-admin makemessages -a
- Compiling message files
- -----------------------
- After you create your message file -- and each time you make changes to it --
- you'll need to compile it into a more efficient form, for use by ``gettext``. Do
- this with the :djadmin:`django-admin compilemessages <compilemessages>`
- utility.
- This tool runs over all available ``.po`` files and creates ``.mo`` files, which
- are binary files optimized for use by ``gettext``. In the same directory from
- which you ran :djadmin:`django-admin makemessages <makemessages>`, run
- :djadmin:`django-admin compilemessages <compilemessages>` like this::
- django-admin compilemessages
- That's it. Your translations are ready for use.
- .. versionchanged:: 1.9
- ``compilemessages`` now matches the operation of :djadmin:`makemessages`,
- scanning the project tree for ``.po`` files to compile.
- .. admonition:: Working on Windows?
- If you're using Windows and need to install the GNU gettext utilities so
- :djadmin:`django-admin compilemessages <compilemessages>` works see
- :ref:`gettext_on_windows` for more information.
- .. admonition:: .po files: Encoding and BOM usage.
- Django only supports ``.po`` files encoded in UTF-8 and without any BOM
- (Byte Order Mark) so if your text editor adds such marks to the beginning of
- files by default then you will need to reconfigure it.
- .. _creating-message-files-from-js-code:
- Creating message files from JavaScript source code
- --------------------------------------------------
- You create and update the message files the same way as the other Django message
- files -- with the :djadmin:`django-admin makemessages <makemessages>` tool.
- The only difference is you need to explicitly specify what in gettext parlance
- is known as a domain in this case the ``djangojs`` domain, by providing a ``-d
- djangojs`` parameter, like this::
- django-admin makemessages -d djangojs -l de
- This would create or update the message file for JavaScript for German. After
- updating message files, just run :djadmin:`django-admin compilemessages
- <compilemessages>` the same way as you do with normal Django message files.
- .. _gettext_on_windows:
- ``gettext`` on Windows
- ----------------------
- This is only needed for people who either want to extract message IDs or compile
- message files (``.po``). Translation work itself just involves editing existing
- files of this type, but if you want to create your own message files, or want to
- test or compile a changed message file, you will need the ``gettext`` utilities:
- * Download the following zip files from the GNOME servers
- https://download.gnome.org/binaries/win32/dependencies/
- * ``gettext-runtime-X.zip``
- * ``gettext-tools-X.zip``
- ``X`` is the version number, we are requiring ``0.15`` or higher.
- * Extract the contents of the ``bin\`` directories in both files to the
- same folder on your system (i.e. ``C:\Program Files\gettext-utils``)
- * Update the system PATH:
- * ``Control Panel > System > Advanced > Environment Variables``.
- * In the ``System variables`` list, click ``Path``, click ``Edit``.
- * Add ``;C:\Program Files\gettext-utils\bin`` at the end of the
- ``Variable value`` field.
- You may also use ``gettext`` binaries you have obtained elsewhere, so long as
- the ``xgettext --version`` command works properly. Do not attempt to use Django
- translation utilities with a ``gettext`` package if the command ``xgettext
- --version`` entered at a Windows command prompt causes a popup window saying
- "xgettext.exe has generated errors and will be closed by Windows".
- .. _customizing-makemessages:
- Customizing the ``makemessages`` command
- ----------------------------------------
- If you want to pass additional parameters to ``xgettext``, you need to create a
- custom :djadmin:`makemessages` command and override its ``xgettext_options``
- attribute::
- from django.core.management.commands import makemessages
- class Command(makemessages.Command):
- xgettext_options = makemessages.Command.xgettext_options + ['--keyword=mytrans']
- If you need more flexibility, you could also add a new argument to your custom
- :djadmin:`makemessages` command::
- from django.core.management.commands import makemessages
- class Command(makemessages.Command):
- def add_arguments(self, parser):
- super(Command, self).add_arguments(parser)
- parser.add_argument('--extra-keyword', dest='xgettext_keywords',
- action='append')
- def handle(self, *args, **options):
- xgettext_keywords = options.pop('xgettext_keywords')
- if xgettext_keywords:
- self.xgettext_options = (
- makemessages.Command.xgettext_options[:] +
- ['--keyword=%s' % kwd for kwd in xgettext_keywords]
- )
- super(Command, self).handle(*args, **options)
- Miscellaneous
- =============
- .. _set_language-redirect-view:
- The ``set_language`` redirect view
- ----------------------------------
- .. currentmodule:: django.views.i18n
- .. function:: set_language(request)
- As a convenience, Django comes with a view, :func:`django.views.i18n.set_language`,
- that sets a user's language preference and redirects to a given URL or, by default,
- back to the previous page.
- Activate this view by adding the following line to your URLconf::
- url(r'^i18n/', include('django.conf.urls.i18n')),
- (Note that this example makes the view available at ``/i18n/setlang/``.)
- .. warning::
- Make sure that you don't include the above URL within
- :func:`~django.conf.urls.i18n.i18n_patterns` - it needs to be
- language-independent itself to work correctly.
- The view expects to be called via the ``POST`` method, with a ``language``
- parameter set in request. If session support is enabled, the view
- saves the language choice in the user's session. Otherwise, it saves the
- language choice in a cookie that is by default named ``django_language``.
- (The name can be changed through the :setting:`LANGUAGE_COOKIE_NAME` setting.)
- After setting the language choice, Django redirects the user, following this
- algorithm:
- * Django looks for a ``next`` parameter in the ``POST`` data.
- * If that doesn't exist, or is empty, Django tries the URL in the
- ``Referrer`` header.
- * If that's empty -- say, if a user's browser suppresses that header --
- then the user will be redirected to ``/`` (the site root) as a fallback.
- Here's example HTML template code:
- .. code-block:: html+django
- {% load i18n %}
- <form action="{% url 'set_language' %}" method="post">{% csrf_token %}
- <input name="next" type="hidden" value="{{ redirect_to }}" />
- <select name="language">
- {% get_current_language as LANGUAGE_CODE %}
- {% get_available_languages as LANGUAGES %}
- {% get_language_info_list for LANGUAGES as languages %}
- {% for language in languages %}
- <option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected="selected"{% endif %}>
- {{ language.name_local }} ({{ language.code }})
- </option>
- {% endfor %}
- </select>
- <input type="submit" value="Go" />
- </form>
- In this example, Django looks up the URL of the page to which the user will be
- redirected in the ``redirect_to`` context variable.
- Explicitly setting the active language
- --------------------------------------
- .. highlightlang:: python
- You may want to set the active language for the current session explicitly. Perhaps
- a user's language preference is retrieved from another system, for example.
- You've already been introduced to :func:`django.utils.translation.activate()`. That
- applies to the current thread only. To persist the language for the entire
- session, also modify :data:`~django.utils.translation.LANGUAGE_SESSION_KEY`
- in the session::
- from django.utils import translation
- user_language = 'fr'
- translation.activate(user_language)
- request.session[translation.LANGUAGE_SESSION_KEY] = user_language
- You would typically want to use both: :func:`django.utils.translation.activate()`
- will change the language for this thread, and modifying the session makes this
- preference persist in future requests.
- If you are not using sessions, the language will persist in a cookie, whose name
- is configured in :setting:`LANGUAGE_COOKIE_NAME`. For example::
- from django.utils import translation
- from django import http
- from django.conf import settings
- user_language = 'fr'
- translation.activate(user_language)
- response = http.HttpResponse(...)
- response.set_cookie(settings.LANGUAGE_COOKIE_NAME, user_language)
- Using translations outside views and templates
- ----------------------------------------------
- While Django provides a rich set of i18n tools for use in views and templates,
- it does not restrict the usage to Django-specific code. The Django translation
- mechanisms can be used to translate arbitrary texts to any language that is
- supported by Django (as long as an appropriate translation catalog exists, of
- course). You can load a translation catalog, activate it and translate text to
- language of your choice, but remember to switch back to original language, as
- activating a translation catalog is done on per-thread basis and such change
- will affect code running in the same thread.
- For example::
- from django.utils import translation
- def welcome_translated(language):
- cur_language = translation.get_language()
- try:
- translation.activate(language)
- text = translation.ugettext('welcome')
- finally:
- translation.activate(cur_language)
- return text
- Calling this function with the value 'de' will give you ``"Willkommen"``,
- regardless of :setting:`LANGUAGE_CODE` and language set by middleware.
- Functions of particular interest are
- :func:`django.utils.translation.get_language()` which returns the language used
- in the current thread, :func:`django.utils.translation.activate()` which
- activates a translation catalog for the current thread, and
- :func:`django.utils.translation.check_for_language()`
- which checks if the given language is supported by Django.
- To help write more concise code, there is also a context manager
- :func:`django.utils.translation.override()` that stores the current language on
- enter and restores it on exit. With it, the above example becomes::
- from django.utils import translation
- def welcome_translated(language):
- with translation.override(language):
- return translation.ugettext('welcome')
- Language cookie
- ---------------
- A number of settings can be used to adjust language cookie options:
- * :setting:`LANGUAGE_COOKIE_NAME`
- * :setting:`LANGUAGE_COOKIE_AGE`
- * :setting:`LANGUAGE_COOKIE_DOMAIN`
- * :setting:`LANGUAGE_COOKIE_PATH`
- Implementation notes
- ====================
- .. _specialties-of-django-i18n:
- Specialties of Django translation
- ---------------------------------
- Django's translation machinery uses the standard ``gettext`` module that comes
- with Python. If you know ``gettext``, you might note these specialties in the
- way Django does translation:
- * The string domain is ``django`` or ``djangojs``. This string domain is
- used to differentiate between different programs that store their data
- in a common message-file library (usually ``/usr/share/locale/``). The
- ``django`` domain is used for Python and template translation strings
- and is loaded into the global translation catalogs. The ``djangojs``
- domain is only used for JavaScript translation catalogs to make sure
- that those are as small as possible.
- * Django doesn't use ``xgettext`` alone. It uses Python wrappers around
- ``xgettext`` and ``msgfmt``. This is mostly for convenience.
- .. _how-django-discovers-language-preference:
- How Django discovers language preference
- ----------------------------------------
- Once you've prepared your translations -- or, if you just want to use the
- translations that come with Django -- you'll just need to activate translation
- for your app.
- Behind the scenes, Django has a very flexible model of deciding which language
- should be used -- installation-wide, for a particular user, or both.
- To set an installation-wide language preference, set :setting:`LANGUAGE_CODE`.
- Django uses this language as the default translation -- the final attempt if no
- better matching translation is found through one of the methods employed by the
- locale middleware (see below).
- If all you want is to run Django with your native language all you need to do
- is set :setting:`LANGUAGE_CODE` and make sure the corresponding :term:`message
- files <message file>` and their compiled versions (``.mo``) exist.
- If you want to let each individual user specify which language they
- prefer, then you also need to use the ``LocaleMiddleware``.
- ``LocaleMiddleware`` enables language selection based on data from the request.
- It customizes content for each user.
- To use ``LocaleMiddleware``, add ``'django.middleware.locale.LocaleMiddleware'``
- to your :setting:`MIDDLEWARE_CLASSES` setting. Because middleware order
- matters, you should follow these guidelines:
- * Make sure it's one of the first middlewares installed.
- * It should come after ``SessionMiddleware``, because ``LocaleMiddleware``
- makes use of session data. And it should come before ``CommonMiddleware``
- because ``CommonMiddleware`` needs an activated language in order
- to resolve the requested URL.
- * If you use ``CacheMiddleware``, put ``LocaleMiddleware`` after it.
- For example, your :setting:`MIDDLEWARE_CLASSES` might look like this::
- MIDDLEWARE_CLASSES = [
- 'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.middleware.locale.LocaleMiddleware',
- 'django.middleware.common.CommonMiddleware',
- ]
- (For more on middleware, see the :doc:`middleware documentation
- </topics/http/middleware>`.)
- ``LocaleMiddleware`` tries to determine the user's language preference by
- following this algorithm:
- * First, it looks for the language prefix in the requested URL. This is
- only performed when you are using the ``i18n_patterns`` function in your
- root URLconf. See :ref:`url-internationalization` for more information
- about the language prefix and how to internationalize URL patterns.
- * Failing that, it looks for the :data:`~django.utils.translation.LANGUAGE_SESSION_KEY`
- key in the current user's session.
- * Failing that, it looks for a cookie.
- The name of the cookie used is set by the :setting:`LANGUAGE_COOKIE_NAME`
- setting. (The default name is ``django_language``.)
- * Failing that, it looks at the ``Accept-Language`` HTTP header. This
- header is sent by your browser and tells the server which language(s) you
- prefer, in order by priority. Django tries each language in the header
- until it finds one with available translations.
- * Failing that, it uses the global :setting:`LANGUAGE_CODE` setting.
- .. _locale-middleware-notes:
- Notes:
- * In each of these places, the language preference is expected to be in the
- standard :term:`language format<language code>`, as a string. For example,
- Brazilian Portuguese is ``pt-br``.
- * If a base language is available but the sublanguage specified is not,
- Django uses the base language. For example, if a user specifies ``de-at``
- (Austrian German) but Django only has ``de`` available, Django uses
- ``de``.
- * Only languages listed in the :setting:`LANGUAGES` setting can be selected.
- If you want to restrict the language selection to a subset of provided
- languages (because your application doesn't provide all those languages),
- set :setting:`LANGUAGES` to a list of languages. For example::
- LANGUAGES = [
- ('de', _('German')),
- ('en', _('English')),
- ]
- This example restricts languages that are available for automatic
- selection to German and English (and any sublanguage, like de-ch or
- en-us).
- * If you define a custom :setting:`LANGUAGES` setting, as explained in the
- previous bullet, you can mark the language names as translation strings
- -- but use :func:`~django.utils.translation.ugettext_lazy` instead of
- :func:`~django.utils.translation.ugettext` to avoid a circular import.
- Here's a sample settings file::
- from django.utils.translation import ugettext_lazy as _
- LANGUAGES = [
- ('de', _('German')),
- ('en', _('English')),
- ]
- Once ``LocaleMiddleware`` determines the user's preference, it makes this
- preference available as ``request.LANGUAGE_CODE`` for each
- :class:`~django.http.HttpRequest`. Feel free to read this value in your view
- code. Here's a simple example::
- from django.http import HttpResponse
- def hello_world(request, count):
- if request.LANGUAGE_CODE == 'de-at':
- return HttpResponse("You prefer to read Austrian German.")
- else:
- return HttpResponse("You prefer to read another language.")
- Note that, with static (middleware-less) translation, the language is in
- ``settings.LANGUAGE_CODE``, while with dynamic (middleware) translation, it's
- in ``request.LANGUAGE_CODE``.
- .. _settings file: ../settings/
- .. _middleware documentation: ../middleware/
- .. _session: ../sessions/
- .. _request object: ../request_response/#httprequest-objects
- .. _how-django-discovers-translations:
- How Django discovers translations
- ---------------------------------
- At runtime, Django builds an in-memory unified catalog of literals-translations.
- To achieve this it looks for translations by following this algorithm regarding
- the order in which it examines the different file paths to load the compiled
- :term:`message files <message file>` (``.mo``) and the precedence of multiple
- translations for the same literal:
- 1. The directories listed in :setting:`LOCALE_PATHS` have the highest
- precedence, with the ones appearing first having higher precedence than
- the ones appearing later.
- 2. Then, it looks for and uses if it exists a ``locale`` directory in each
- of the installed apps listed in :setting:`INSTALLED_APPS`. The ones
- appearing first have higher precedence than the ones appearing later.
- 3. Finally, the Django-provided base translation in ``django/conf/locale``
- is used as a fallback.
- .. seealso::
- The translations for literals included in JavaScript assets are looked up
- following a similar but not identical algorithm. See the
- :ref:`javascript_catalog view documentation <javascript_catalog-view>` for
- more details.
- In all cases the name of the directory containing the translation is expected to
- be named using :term:`locale name` notation. E.g. ``de``, ``pt_BR``, ``es_AR``,
- etc.
- This way, you can write applications that include their own translations, and
- you can override base translations in your project. Or, you can just build
- a big project out of several apps and put all translations into one big common
- message file specific to the project you are composing. The choice is yours.
- All message file repositories are structured the same way. They are:
- * All paths listed in :setting:`LOCALE_PATHS` in your settings file are
- searched for ``<language>/LC_MESSAGES/django.(po|mo)``
- * ``$APPPATH/locale/<language>/LC_MESSAGES/django.(po|mo)``
- * ``$PYTHONPATH/django/conf/locale/<language>/LC_MESSAGES/django.(po|mo)``
- To create message files, you use the :djadmin:`django-admin makemessages <makemessages>`
- tool. And you use :djadmin:`django-admin compilemessages <compilemessages>`
- to produce the binary ``.mo`` files that are used by ``gettext``.
- You can also run :djadmin:`django-admin compilemessages
- --settings=path.to.settings <compilemessages>` to make the compiler process all
- the directories in your :setting:`LOCALE_PATHS` setting.
|