# Documentation Django 5.2.8

# Traduction

## Aperçu

Dans le but de rendre traduisible un projet Django, il est nécessaire d’ajouter un minimum de marqueurs dans le code Python et les gabarits. Ces marqueurs s’appellent des chaînes de traduction. Ils indiquent à Django que « ce texte doit être traduit dans la langue de l’utilisateur pour autant qu’il existe une traduction de ce texte dans cette langue ». Il est de votre responsabilité de marquer les chaînes de caractères traduisibles ; le système ne peut traduire que les éléments qu’on lui a indiqués.

Django fournit ensuite des utilitaires pour extraire les chaînes à traduire dans un fichier de messages. Ce fichier sert ensuite aux traducteurs qui pourront faire leur travail en rédigeant les équivalences des chaînes à traduire dans leur langue. Lorsque que ce travail est terminé, le fichier doit être compilé. Ce processus se base sur les outils GNU gettext.

Quand tout cela est fait, Django s’occupe de traduire les applications web à la volée dans chacune des langues disponibles, en accord avec les préférences linguistiques des utilisateurs.

Le système d’internationalisation de Django est activé par défaut, ce qui peut générer un petit surplus de travail à certains endroits du système. Si vous n’utilisez pas l’internationalisation, prenez les deux secondes nécessaires à définir `USE_I18N = False` dans votre fichier de réglages. Django pourra alors effectuer certaines optimisations en ne chargeant pas tout le mécanisme d’internationalisation.

Note

Vérifiez si la traduction est activée dans votre projet (la manière la plus rapide est de contrôler la présence de `django.middleware.locale.LocaleMiddleware` dans `MIDDLEWARE`). Si ce n’est pas encore fait, lisez Processus de découverte de la préférence de langue par Django.

## Internationalisation : dans le code Python

### Traduction standard

Définissez une chaîne à traduire en utilisant la fonction `gettext()`. Par convention, il est fréquent de l’importer par un alias plus court, `_`, pour s’économiser des frappes au clavier.

Note

Le module `gettext` de la bibliothèque Python standard installe `_()` dans l’espace de noms global comme alias de `gettext()`. Dans Django, nous avons choisi de ne pas suivre cette pratique, pour plusieurs raisons :

1. Parfois, il est préférable d’utiliser `gettext_lazy()` comme méthode de traduction par défaut dans un certain fichier. En l’absence de `_()` dans l’espace de noms global, le développeur doit réfléchir à ce qui est le plus approprié comme fonction de traduction.

2. Le caractère soulignement (`_`) est utilisé pour représenter le « résultat précédent » dans le shell interactif de Python et les tests `doctest`. L’installation globale de `_()` interfère avec le précédent. L’importation explicite de `gettext()` comme `_()` évite ce problème.

Quelles fonctions peuvent-elles avoir `_` comme alias ?

En raison du fonctionnement de `xgettext` (utilisé par `makemessages`), seules les fonctions acceptant un seul paramètre textuel peuvent être importées avec l’alias `_`:

- `gettext()`

- `gettext_lazy()`

Dans cet exemple, le texte `"Welcome to my site."` est marqué comme chaîne à traduire :

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

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

On aurait pu écrire cela sans employer l’alias. Cet exemple est identique au précédent :

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

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

La traduction fonctionne sur des valeurs calculées. Cet exemple est identique aux deux précédents :

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

La traduction fonctionne avec des variables. Encore une fois, voici un exemple identique :

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

(L’inconvénient avec l’utilisation de variables ou de valeurs calculées comme dans les deux exemples précédents, c’est que l’utilitaire de détection des chaînes à traduire de Django, `django-admin makemessages`, ne sera pas capable de trouver ces chaînes. Plus de détails sur `makemessages` un peu plus tard.)

Les chaînes que vous placez dans `_()` ou `gettext()` acceptent des substituants sous la forme de la syntaxe d’interpolation par paramètres nommés standard de Python. Exemple :

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

Cette technique permet aux différentes traductions de modifier l’ordre des substituants. Par exemple, pour un texte anglais `"Today is November 26."`, la traduction espagnole pourrait être `"Hoy es 26 de noviembre."`, avec les substituants mois et jour intervertis.

Pour cette raison, il est recommandé d’utiliser l’interpolation par paramètre nommé (ex : `%(day)s`) au lieu de l’interpolation par position (ex : `%s` ou `%d`) chaque fois qu’il y a plus d’un paramètre. Avec l’interpolation par position, les traductions ne peuvent pas changer l’ordre des substituants.

Since string extraction is done by the `xgettext` command, only syntaxes supported by `gettext` are supported by Django. Python f-strings cannot be used directly with `gettext` functions because f-string expressions are evaluated before they reach `gettext`. This means `_(f"Welcome {name}")` will not work as expected, as the variable is substituted before translation occurs. Instead, use named-string interpolation:

    # Good
    _("Welcome %(name)s") % {"name": name}

    # Good
    _("Welcome {name}").format(name=name)

    # Bad
    _(f"Welcome {name}")  # f-string evaluated before translation.

Les chaînes template de Javascript ont besoin de `gettext` 0.21+.

### Commentaires pour les traducteurs

Si vous souhaitez donner des indications aux traducteurs à propos d’une chaîne à traduire, vous pouvez ajouter un commentaire préfixé par le mot-clé `Translators` à la ligne précédant la chaîne ; par exemple :

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

Le commentaire apparaîtra alors dans le fichier `.po` résultant de l’extraction des chaînes, juste avant le bloc de la chaîne à traduire, et la plupart des outils de traduction l’affichent également dans leur interface.

Note

Pour les curieux, voici le fragment correspondant dans le fichier `.po` résultant :

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

Cela fonctionne aussi dans les gabarits. Voir Commentaires pour les traducteurs dans les gabarits pour plus de détails.

### Marquage des chaînes « en blanc »

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

Cette technique est utile pour des chaînes constantes devant être stockées dans la langue source parce qu’elles sont échangées entre systèmes ou utilisateurs, comme des chaînes en base de données, mais qu’elles devraient être traduites le plus tard possible, juste avant d’être affichées pour un utilisateur.

### Pluriels

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

`ngettext()` accepte trois paramètres : la chaîne à traduire au singulier, la chaîne à traduire au pluriel et le nombre d’objets.

Cette fonction est utile lorsque vous voulez traduire votre application Django dans des langues où le nombre et la complexité des formes plurielles sont plus grands que les deux formes utilisées en anglais (« object » pour le singulier et « objects » dans tous les cas où `count` est différent de un, quelle que soit sa valeur).

Par exemple :

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

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

Dans cet exemple, le nombre d’objets est communiqué à la langue de traduction par la variable `count`.

Notez que les pluriels sont compliqués et fonctionnent différemment pour chaque langue. La comparaison de `count` avec 1 n’est pas toujours la règle correcte. Ce code semble sophistiqué, mais produira des résultats incorrects pour certaines langues :

    from django.utils.translation import ngettext
    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 = ngettext(
        "There is %(count)d %(name)s available.",
        "There are %(count)d %(name)s available.",
        count,
    ) % {"count": count, "name": name}

N’essayez pas d’implémenter votre propre logique de singulier/pluriel, vous n’y arriverez pas. Dans un cas comme celui-ci, pensez plutôt à faire quelque chose comme :

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

Note

En utilisant `ngettext()`, prenez soin de toujours utiliser le même nom pour chaque variable extrapolée incluse dans la chaîne. Dans les exemples ci-dessus, notez comment nous avons utilisé la variable Python `name` dans les deux chaînes à traduire. Cet exemple, en plus d’être faux pour certaines langues comme nous l’avons mentionné, ne fonctionnera pas :

    text = ngettext(
        "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,
    }

Vous obtiendriez une erreur lors du lancement de `django-admin compilemessages`:

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

### Marqueurs contextuels

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.

Dans le fichier `.po` résultant, la même chaîne apparaîtra aussi souvent qu’il y a de marqueurs contextuels différents (le contexte apparaît à la ligne `msgctxt`), permettant ainsi au traducteur d’attribuer des traductions différentes à chacune d’elles.

Par exemple :

    from django.utils.translation import pgettext

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

ou :

    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")
        )

apparaîtra dans le fichier `.po` comme :

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

Les marqueurs contextuels sont également pris en charge par les balises de gabarit `translate` et `blocktranslate`.

### Traduction différée

Les versions différées des fonctions de traduction dans `django.utils.translation` (facilement reconnaissables par le suffixe `lazy` dans leur nom) permettent de traduire du texte en différé, au moment où la chaîne est réellement utilisée plutôt qu’au moment où la fonction est appelée.

Ces fonctions stockent une référence différée à la chaîne originale, et non pas la traduction réelle. La traduction en tant que telle est effectuée lorsque le texte est utilisé dans un contexte textuel, par exemple dans le rendu d’un gabarit.

Ce procédé est essentiel lorsque les appels à ces fonctions sont situés dans des endroits du code qui sont exécutés lors du chargement des modules.

Ceci peut facilement se produire lors de la définition des modèles, des formulaires et des formulaires de modèle, car la manière dont Django les implémente fait que leurs champs sont des attributs de classe. C’est pour cette raison qu’il faut utiliser des traductions différées dans les cas suivants :

#### Valeurs d’options `verbose_name` et `help_text` des champs et relations de modèle

Par exemple, pour traduire le texte d’aide du champ `name` dans le modèle suivant, faites comme ceci :

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

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

Vous pouvez marquer les noms des relations `ForeignKey`, `ManyToManyField` ou `OneToOneField` comme chaînes à traduire en utilisant leur option `verbose_name`:

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

Comme vous le feriez pour `verbose_name`, le texte de nom verbeux de la relation devrait être en minuscules, car Django s’occupe de mettre les majuscules dans les situations appropriées.

#### Valeurs de noms verbeux de modèle

Il est recommandé de toujours fournir les options explicites `verbose_name` et `verbose_name_plural` plutôt que de vous rabattre sur la version anglo-centrique et déterminée de manière quelque peu naïve du nom verbeux que Django crée en se basant sur le nom de classe du modèle :

    from django.db import models
    from django.utils.translation import gettext_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")

#### L’argument `description` des méthodes de modèles passé au décorateur `@display`

Pour les méthodes de modèle, vous pouvez indiquer à Django et au site d’administration des descriptions traduisibles par l’argument `description` du décorateur `display()`:

    from django.contrib import admin
    from django.db import models
    from django.utils.translation import gettext_lazy as _

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

        @admin.display(description=_("Is it a mouse?"))
        def is_mouse(self):
            return self.kind.type == MOUSE_TYPE

### Manipulation des objets de traduction différée

Le résultat d’un appel à `gettext_lazy()` peut être utilisé partout là où une chaîne (un objet `str`) peut être utilisée dans le code Django, mais cela ne fonctionne par forcément avec n’importe quel code Python. Par exemple, le code suivant ne marchera pas car la bibliothèque requests ne gère pas les objets `gettext_lazy`:

    body = gettext_lazy("I \u2764 Django")  # (Unicode :heart:)
    requests.post("https://example.com/send", data={"body": body})

Vous pouvez éviter ce genre de problème en forçant les objets `gettext_lazy()` en chaînes de texte avant de les passer à du code non Django :

    requests.post("https://example.com/send", data={"body": str(body)})

Si vous n’aimez pas le long nom de `gettext_lazy`, vous pouvez en faire un alias `_` (soulignement), comme ceci :

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

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

L’utilisation de `gettext_lazy()` et `ngettext_lazy()` pour marquer des chaînes dans des modèles et des fonctions utilitaires est une opération courante. Lorsque que vous manipulez ces objets ailleurs dans votre code, vous devriez prendre soin de ne jamais les convertir accidentellement en chaînes de caractères, car ils devraient être convertis le plus tard possible (afin que la langue correcte soit active). Ceci nécessite l’emploi de la fonction utilitaire décrite ci-après.

#### Traductions différées et pluriels

Lors de l’utilisation de traductions différées avec une chaîne contenant des pluriels (`n[p]gettext_lazy`), vous ne connaissez généralement pas le paramètre `number` au moment de la définition de la chaîne. C’est pourquoi vous êtes autorisé dans ce cas à passer un nom de clé au lieu d’un nombre comme paramètre `number`. Ensuite, cette clé `number` sera cherchée dans le dictionnaire durant l’extrapolation de la chaîne. Voici un exemple :

    from django import forms
    from django.core.exceptions import ValidationError
    from django.utils.translation import ngettext_lazy

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

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

Si la chaîne contient un seul substituant non nommé, vous pouvez directement interpoler avec le paramètre `number`:

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

        def clean(self):
            # ...
            if error:
                raise ValidationError(self.error_message % number)

#### Mise en forme de chaînes : `format_lazy()`

Python’s `str.format()` method will not work when either the `format_string` or any of the arguments to `str.format()` contains lazy translation objects. Instead, you can use `django.utils.text.format_lazy()`, which creates a lazy object that runs the `str.format()` method only when the result is included in a string. For example:

    from django.utils.text import format_lazy
    from django.utils.translation import gettext_lazy

    ...
    name = gettext_lazy("John Lennon")
    instrument = gettext_lazy("guitar")
    result = format_lazy("{name}: {instrument}", name=name, instrument=instrument)

Dans ce cas, les traductions différées dans `result` ne seront converties en chaînes qu’au moment où `result` sera elle-même utilisée dans une chaîne (habituellement au moment du rendu du gabarit).

#### Autres usages de traductions différées avec `lazy`

Dans tout autre situation où il serait utile de différer la traduction mais qu’il est nécessaire de passer la chaîne à traduire comme paramètre d’une autre fonction, il est possible d’envelopper vous-même cette fonction à l’intérieur d’un appel à `lazy`. Par exemple :

    from django.utils.functional import lazy
    from django.utils.translation import gettext_lazy as _

    def to_lower(string):
        return string.lower()

    to_lower_lazy = lazy(to_lower, str)

Puis, plus tard :

    lazy_string = to_lower_lazy(_("My STRING!"))

### Noms de langues régionalisés

get_language_info(lang_code)

La fonction `get_language_info()` fournit des informations détaillées sur les langues :

    >>> from django.utils.translation import activate, get_language_info
    >>> activate("fr")
    >>> li = get_language_info("de")
    >>> print(li["name"], li["name_local"], li["name_translated"], li["bidi"])
    German Deutsch Allemand False

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

La source des informations sur les langues se trouve dans le module `django.conf.locale`. Ces informations sont également accessibles dans le code des gabarits. Voir ci-dessous.

## Internationalisation : dans le code des gabarits

Les traductions dans les gabarits Django utilisent deux balises de gabarit et une syntaxe légèrement différente que dans le code Python. Pour donner accès à ces balises dans vos gabarits, ajoutez `{% load i18n %}` au sommet de votre gabarit. Comme pour toutes les balises de gabarit, cette balise doit être chargée dans tous les gabarits qui utilisent les traductions, même pour les gabarits qui étendent d’autres gabarits ayant déjà chargé la bibliothèque `i18n`.

Avertissement

Les chaînes traduites ne sont pas échappées lorsqu’elle sont introduites dans un gabarit. Cela permet d’inclure du HTML dans les traductions, par exemple pour des mises en évidence, mais de potentiels dangereux caractères seront également insérés tel quel.

### Balise de gabarit `translate`

La balise de gabarit `{% translate %}` traduit soit une chaîne constante (entourée de guillemets simples ou doubles) ou un contenu de variable :

    <title>{% translate "This is the title." %}</title>
    <title>{% translate myvar %}</title>

Si l’option `noop` est présente, l’accès à la variable se fait toujours mais la traduction est omise. C’est utile pour marquer du contenu qui devra être traduit à un moment ultérieur :

    <title>{% translate "myvar" noop %}</title>

En interne, cette technique de traduction fait appel à `gettext()`.

Dans le cas où une variable de gabarit (`myvar` ci-dessus) est passée à la balise, celle-ci résout en premier la variable en chaîne au moment de l’exécution puis consulte les catalogues de messages pour trouver une traduction.

Il n’est pas possible de mélanger du texte littéral et des variables dans `{% translate %}`. si vos traductions nécessitent des chaînes contenant des variables (substituants), utilisez plutôt `{% blocktranslate %}`.

Si vous aimeriez récupérer une chaîne traduite sans l’afficher, vous pouvez utiliser la syntaxe suivante :

    {% translate "This is the title" as the_title %}

    <title>{{ the_title }}</title>
    <meta name="description" content="{{ the_title }}">

En pratique, vous utiliserez cela pour obtenir une chaîne utilisée à plusieurs endroits dans un gabarit ou qui doit être passée comme paramètre à d’autres balises ou filtres de gabarit :

    {% translate "starting point" as start %}
    {% translate "end point" as end %}
    {% translate "La Grande Boucle" as race %}

    <h1>
      <a href="/" title="{% blocktranslate %}Back to '{{ race }}' homepage{% endblocktranslate %}">{{ race }}</a>
    </h1>
    <p>
    {% for stage in tour_stages %}
        {% cycle start end %}: {{ stage }}{% if forloop.counter|divisibleby:2 %}<br>{% else %}, {% endif %}
    {% endfor %}
    </p>

`{% translate %}` prend aussi en charge les marqueurs contextuels à l’aide du mot-clé `context`:

    {% translate "May" context "month name" %}

### Balise de gabarit `blocktranslate`

Contrairement à la balise `translate`, la balise `blocktranslate` permet de marquer comme chaînes à traduire des phrases complexes contenant à la fois du texte littéral et des variables par le moyen des substituants :

    {% blocktranslate %}This string will have {{ value }} inside.{% endblocktranslate %}

Pour traduire une expression de gabarit, par exemple un accès aux attributs d’un objet ou une utilisation de filtre de gabarit, il est nécessaire de lier l’expression à une variable locale pour l’utiliser ensuite dans le bloc à traduire. Exemple :

    {% blocktranslate with amount=article.price %}
    That will cost $ {{ amount }}.
    {% endblocktranslate %}

    {% blocktranslate with myvar=value|filter %}
    This will have {{ myvar }} inside.
    {% endblocktranslate %}

Vous pouvez utiliser plusieurs expressions à l’intérieur d’une seule balise `blocktranslate`:

    {% blocktranslate with book_t=book|title author_t=author|title %}
    This is {{ book_t }} by {{ author_t }}
    {% endblocktranslate %}

Note

L’ancien format plus verbeux est toujours accepté : {% blocktranslate with book|title as book_t and author|title as author_t %}`

D’autres balises de bloc (par exemple `{% for %}` ou `{% if %}`) ne sont pas autorisées à l’intérieur des balises `blocktranslate`.

Si la résolution d’un des paramètres du bloc échoue, `blocktranslate` se rabat sur la langue par défaut en désactivant temporairement la langue actuelle avec la fonction `deactivate_all()`.

Cette balise permet également l’utilisation de pluriels. Pour l’utiliser :

- Désignez et liez une valeur de compteur nommée `count`. Cette valeur sera celle utilisée pour choisir la bonne forme de pluriel.

- Indiquez à la fois les formes au singulier et au pluriel en les séparant par la balise `{% plural %}` à l’intérieur des balises `{% blocktranslate %}` et `{% endblocktranslate %}`.

Un exemple :

    {% blocktranslate count counter=list|length %}
    There is {{ counter }} {{ name }} object.
    {% plural %}
    There are {{ counter }} {{ name }} objects.
    {% endblocktranslate %}

Un exemple plus complexe :

    {% blocktranslate with amount=article.price count years=i.length %}
    That will cost $ {{ amount }} per year.
    {% plural %}
    That will cost $ {{ amount }} per {{ years }} years.
    {% endblocktranslate %}

Lorsque vous utilisez à la fois la fonction de pluriel et l’attribution de valeurs à des variables locales en plus de la valeur de compteur, il faut savoir qu’en interne, `blocktranslate` est converti en un appel `ngettext`. Cela signifie que les notes concernant les variables ngettext s’appliquent également.

La résolution inverse d’URL ne peut pas se faire à l’intérieur de `blocktranslate` et doit donc être effectuée au préalable :

    {% url 'path.to.view' arg arg2 as the_url %}
    {% blocktranslate %}
    This is a URL: {{ the_url }}
    {% endblocktranslate %}

Si vous aimeriez récupérer une chaîne traduite sans l’afficher, vous pouvez utiliser la syntaxe suivante :

    {% blocktranslate asvar the_title %}The title is {{ title }}.{% endblocktranslate %}
    <title>{{ the_title }}</title>
    <meta name="description" content="{{ the_title }}">

En pratique, vous utiliserez cela pour obtenir une chaîne utilisée à plusieurs endroits dans un gabarit ou qui doit être passée comme paramètre à d’autres balises ou filtres de gabarit.

`{% blocktranslate %}` prend aussi en charge les marqueurs contextuels à l’aide du mot-clé `context`:

    {% blocktranslate with name=user.username context "greeting" %}Hi {{ name }}{% endblocktranslate %}

Une autre fonctionnalité prise en charge par la balise `{% blocktranslate %}` est l’option `trimmed`. Cette option supprime les caractères de nouvelle ligne au début et à la fin du contenu de la balise `{% blocktranslate %}`, remplace les espaces blancs au début et à la fin d’une ligne et fusionne toutes les lignes en une seule via l’utilisation d’un espace pour les séparer. Ceci est très utile pour l’indentation du contenu d’une balise `{% blocktranslate %}` sans avoir les caractères d’indentation qui se retrouvent dans l’entrée correspondante du fichier `.po`, rendant le processus de traduction plus facile.

Par exemple, la balise `{% blocktranslate %}` suivante :

    {% blocktranslate trimmed %}
      First sentence.
      Second paragraph.
    {% endblocktranslate %}

donne comme résultat `"First sentence. Second paragraph."` dans le fichier `.po`, comparé à `"\n First sentence.\n Second paragraph.\n"` si l’option `trimmed` n’avait pas été utilisée.

### Texte littéral transmis aux balises et aux filtres

Vous pouvez traduire le texte littéral transmis comme paramètre à des balises et des filtres en utilisant la syntaxe familière `_()`:

    {% some_tag _("Page not found") value|yesno:_("yes,no") %}

Dans ce cas, aussi bien la balise que le filtre recevra la chaîne traduite, ils n’ont donc pas besoin de se préoccuper des traductions.

Note

Dans cet exemple, l’infrastructure de traduction recevra la chaîne `"yes,no"`, et non pas les chaînes individuelles `"yes"` et `"no"`. La chaîne à traduire devra contenir la virgule afin que le code d’analyse du filtre sache où séparer les paramètres. Par exemple, un traducteur allemand pourrait traduire la chaîne `"yes,no"` comme `"ja,nein"` (en préservant la virgule).

### Commentaires pour les traducteurs dans les gabarits

Tout comme avec le code Python, ces notes à l’intention des traducteurs peuvent être indiquées en utilisant des commentaires, soit avec la balise `comment`:

    {% comment %}Translators: View verb{% endcomment %}
    {% translate "View" %}

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

ou avec la syntaxe de commentaire sur une seule ligne `{#` … `#}`:

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

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

Note

Pour les curieux, voici les fragments correspondant dans le fichier `.po` résultant :

    #. 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 ""

### Changement de langue dans les gabarits

Si vous avez besoin de changer de langue à l’intérieur d’un gabarit, vous pouvez utiliser la balise de gabarit `language`:

    {% load i18n %}

    {% get_current_language as LANGUAGE_CODE %}
    <!-- Current language: {{ LANGUAGE_CODE }} -->
    <p>{% translate "Welcome to our page" %}</p>

    {% language 'en' %}
        {% get_current_language as LANGUAGE_CODE %}
        <!-- Current language: {{ LANGUAGE_CODE }} -->
        <p>{% translate "Welcome to our page" %}</p>
    {% endlanguage %}

Alors que la première occurrence de « Welcome to our page » utilise la langue active, la seconde sera toujours en anglais.

### Autres balises

Ces balises nécessitent aussi le chargement `{% load i18n %}`.

#### `get_available_languages`

`{% get_available_languages as LANGUAGES %}` renvoie une liste de tuples dont le premier élément est le code de langue et le second est le nom de la langue (traduit dans la langue actuellement active).

#### `get_current_language`

`{% get_current_language as LANGUAGE_CODE %}` renvoie la langue préférée de l’utilisateur actuel sous forme textuelle. Exemple : `en-us`. Voir Processus de découverte de la préférence de langue par Django.

#### `get_current_language_bidi`

`{% get_current_language_bidi as LANGUAGE_BIDI %}` renvoie la direction du texte de la langue actuelle. Si `True`, il s’agit d’une langue s’écrivant de droite à gauche, comme l’hébreu ou l’arabe. Si `False`, il s’agit d’une langue s’écrivant de gauche à droite, comme l’anglais, le français, l’allemand, etc.

#### Processeur de contexte `i18n`

Si vous activez le processeur de contexte `django.template.context_processors.i18n`, chaque `RequestContext` aura accès à `LANGUAGES`, `LANGUAGE_CODE` et `LANGUAGE_BIDI` tels que décrits ci-dessus.

#### `get_language_info`

Vous pouvez aussi récupérer toute information à propos des langues disponibles en utilisant des balises et des filtres intégrés. Pour obtenir des informations sur une seule langue, utilisez la balise `{% get_language_info %}`:

    {% get_language_info for LANGUAGE_CODE as lang %}
    {% get_language_info for "pl" as lang %}

Vous pouvez ensuite accéder à ces informations :

    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 }}

#### `get_language_info_list`

Vous pouvez aussi utiliser la balise de gabarit `{% get_language_info_list %}` pour obtenir les informations d’une liste de langues (par ex. les langues actives figurant dans `LANGUAGES`). Consultez la section sur la vue de redirection set_language pour trouver un exemple sur la manière d’afficher un sélecteur de langue avec `{% get_language_info_list %}`.

En plus du style liste de tuples de `LANGUAGES`, `{% get_language_info_list %}` accepte des listes de codes de langue. Si vous faites ceci dans votre vue :

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

vous pouvez faire une boucle sur ces langues dans le gabarit :

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

#### Filtres de gabarit

Il existe aussi quelques filtres pour votre confort :

- `{{ LANGUAGE_CODE|language_name }}` (« German »)

- `{{ LANGUAGE_CODE|language_name_local }}` (« Deutsch »)

- `{{ LANGUAGE_CODE|language_bidi }}` (False)

- `{{ LANGUAGE_CODE|language_name_translated }}` (« německy », lorsque la langue actuelle est le tchèque)

## Internationalisation : dans le code JavaScript

L’ajout de traductions en Javascript pose quelques problèmes :

- Le code JavaScript n’a pas d’accès à une implémentation de `gettext`.

- Le code JavaScript n’a pas accès à des fichiers `.po` ou `.mo`; ils doivent être fournis par le serveur.

- Les catalogues de traduction pour JavaScript doivent être aussi compacts que possible.

Django fournit une solution intégrée pour ces problèmes : il transpose les traductions dans JavaScript afin que vous puissiez appeler les fonctions de type `gettext` dans votre code JavaScript.

La solution principale à ces problèmes est la vue `JavaScriptCatalog` suivante, qui génère une bibliothèque de code JavaScript avec des fonctions qui simulent l’interface `gettext`, ainsi qu’un tableau de chaînes de traduction.

### La vue `JavaScriptCatalog`

class JavaScriptCatalog

  Une vue qui génère une bibliothèque de code JavaScript avec des fonctions qui simulent l’interface `gettext`, ainsi qu’un tableau de chaînes de traduction.

  Attributs

  domain

    Domaine de traduction contenant les chaînes à ajouter dans le résultat de la vue. Contient `'djangojs'` par défaut.

  packages

    Une liste de `noms d'applications` parmi les applications installées. Ces applications doivent contenir un dossier `locale`. Tous ces catalogues ainsi que ceux trouvés dans `LOCALE_PATHS` (qui sont toujours inclus) sont fusionnés en un seul catalogue. La valeur par défaut est `None`, ce qui signifie que toutes les traductions disponibles de toutes les applications dans `INSTALLED_APPS` sont présentes dans le résultat JavaScript.

  Exemple avec les valeurs par défaut :

      from django.views.i18n import JavaScriptCatalog

      urlpatterns = [
          path("jsi18n/", JavaScriptCatalog.as_view(), name="javascript-catalog"),
      ]

  Exemple avec des paquets personnalisés :

      urlpatterns = [
          path(
              "jsi18n/myapp/",
              JavaScriptCatalog.as_view(packages=["your.app.label"]),
              name="javascript-catalog",
          ),
      ]

  Si votre configuration d’URL racine utilise `i18n_patterns()`, `JavaScriptCatalog` doit également être enveloppé dans `i18n_patterns()` pour que le catalogue soit correctement généré.

  Exemple avec `i18n_patterns()`:

      from django.conf.urls.i18n import i18n_patterns

      urlpatterns = i18n_patterns(
          path("jsi18n/", JavaScriptCatalog.as_view(), name="javascript-catalog"),
      )

La priorité des traductions se fait par ordre d’apparition des modules dans le paramètre `packages`; ceux qui apparaissent en dernier ont la priorité sur les premiers. Ceci est important dans le cas où une même chaîne est traduite différemment dans plusieurs catalogues.

Si vous utilisez plus d’une vue `JavaScriptCatalog` sur un site et que plusieurs définissent les mêmes chaînes, les chaînes du catalogue chargé en dernier ont la priorité.

### Emploi du catalogue des traductions JavaScript

Pour utiliser le catalogue, insérez le script généré dynamiquement comme ceci :

    <script src="{% url 'javascript-catalog' %}"></script>

Nous utilisons la résolution inverse d’URL pour trouver l’URL de la vue du catalogue JavaScript. Lorsque le catalogue est chargé, votre code JavaScript peut utiliser les méthodes suivantes :

- `gettext`

- `ngettext`

- `interpolate`

- `get_format`

- `gettext_noop`

- `pgettext`

- `npgettext`

- `pluralidx`

#### `gettext`

La fonction `gettext` fonctionne de manière similaire à l’interface `gettext` standard accessible depuis le code Python :

    document.write(gettext("this is to be translated"))

#### `ngettext`

La fonction `ngettext` fournit une interface pour mettre au pluriel des mots et des phrases :

    const objectCount = 1 // or 0, or 2, or 3, ...
    const string = ngettext(
        'literal for the singular case',
        'literal for the plural case',
        objectCount
    );

#### `interpolate`

La fonction `interpolate` permet de remplir dynamiquement une chaîne de mise en forme. La syntaxe d’interpolation est empruntée à Python ; ainsi, la fonction `interpolate` prend en charge à la fois les paramètres nommés et les paramètres positionnels :

- Interpolation positionnelle : `obj` contient un objet Array JavaScript dont les valeurs d’éléments sont interpolées dans l’ordre d’apparition de leur substituant `fmt` correspondant. Par exemple :

      const formats = ngettext(
        'There is %s object. Remaining: %s',
        'There are %s objects. Remaining: %s',
        11
      );
      const string = interpolate(formats, [11, 20]);
      // string is 'There are 11 objects. Remaining: 20'

- Interpolation nommée : ce mode est sélectionné en passant `true` dans le paramètre booléen facultatif `named`. `obj` contient un objet JavaScript ou un tableau associatif. Par exemple :

      const data = {
        count: 10,
        total: 50
      };

      const formats = ngettext(
          'Total: %(total)s, there is %(count)s object',
          'there are %(count)s of a total of %(total)s objects',
          data.count
      );
      const string = interpolate(formats, data, true);

Il ne faut cependant pas abuser de l’interpolation de chaînes : c’est toujours du JavaScript, le code effectue donc des substitutions répétées par expression régulière. Ce n’est pas aussi rapide que l’interpolation de chaînes en Python, il s’agit donc de réserver cet usage pour les cas où c’est vraiment nécessaire (par exemple conjointement avec``ngettext`` pour générer des pluriels corrects).

#### `get_format`

La fonction `get_format` accède aux réglages de mise en forme régionalisée et peut obtenir la chaîne de format d’un nom de réglage donné :

    document.write(get_format('DATE_FORMAT'));
    // 'N j, Y'

Elle peut accéder aux réglages suivants :

- `DATE_FORMAT`

- `DATE_INPUT_FORMATS`

- `DATETIME_FORMAT`

- `DATETIME_INPUT_FORMATS`

- `DECIMAL_SEPARATOR`

- `FIRST_DAY_OF_WEEK`

- `MONTH_DAY_FORMAT`

- `NUMBER_GROUPING`

- `SHORT_DATE_FORMAT`

- `SHORT_DATETIME_FORMAT`

- `THOUSAND_SEPARATOR`

- `TIME_FORMAT`

- `TIME_INPUT_FORMATS`

- `YEAR_MONTH_FORMAT`

C’est bien utile pour conserver une cohérence de mise en forme en lien avec les valeurs produites par le code Python.

#### `gettext_noop`

Ceci émule la fonction `gettext`, mais ne fait rien d’autre que de renvoyer le contenu qui lui est transmis :

    document.write(gettext_noop("this will not be translated"))

C’est utile pour identifier des portions de code qui vont avoir besoin d’être traduites plus tard.

#### `pgettext`

The `pgettext` function behaves like the Python variant (`pgettext()`), providing a contextually translated word:

    document.write(pgettext("month name", "May"))

#### `npgettext`

The `npgettext` function also behaves like the Python variant (`npgettext()`), providing a pluralized contextually translated word:

    document.write(npgettext('group', 'party', 1));
    // party
    document.write(npgettext('group', 'party', 2));
    // parties

#### `pluralidx`

La fonction `pluralidx` fonctionne de manière semblable au filtre de gabarit `pluralize`, déterminant si un nombre `count` donné doit utiliser une forme plurielle d’un mot ou pas :

    document.write(pluralidx(0));
    // true
    document.write(pluralidx(1));
    // false
    document.write(pluralidx(2));
    // true

Dans le cas le plus simple, si aucune mise au pluriel n’est nécessaire, cette fonction renvoie `false` pour le nombre `1` et `true` pour tous les autres nombres.

Cependant, la mise au pluriel n’est pas si simple dans toutes les langues. Si la langue ne gère pas les pluriels, une valeur vide est renvoyée.

De plus, si les pluriels se basent sur des règles complexes, la vue du catalogue produit une expression conditionnelle. Son résultat aboutit soit à une valeur `true` (pluriel nécessaire), soit à une valeur `false` (pluriel non nécessaire).

### La vue `JSONCatalog`

class JSONCatalog

  Pour pouvoir utiliser une autre bibliothèque côté client pour la gestion des traductions, il peut être avantageux de profiter de la vue `JSONCatalog`. Le résultat est proche de `JavaScriptCatalog` mais le contenu est renvoyé sous forme de réponse JSON.

  Consultez la documentation de `JavaScriptCatalog` pour connaître les valeurs possibles et l’utilisation des attributs `domain` et `packages`.

  Le format de la réponse est le suivant :

      {
          "catalog": {
              # Translations catalog
          },
          "formats": {
              # Language formats for date, time, etc.
          },
          "plural": "..."  # Expression for plural forms, or null.
      }

### Note sur les performances

Les différentes vues JavaScript/JSON i18n génèrent leur catalogue à partir de fichiers `.mo` pour chaque requête. Comme le résultat est constant, au moins pour une version donnée du site, c’est une bonne occasion d’utiliser le cache.

Le cache côté serveur réduit la charge sur le processeur. Le décorateur `cache_page()` permet de le faire facilement. Pour provoquer l’invalidation du cache lorsque les traductions ont changé, ajoutez un préfixe de clé dépendant de la version, comme le montre l’exemple ci-dessous, ou faites correspondre la vue à une URL dépendant de la version :

    from django.views.decorators.cache import cache_page
    from django.views.i18n import JavaScriptCatalog

    # The value returned by get_version() must change when translations change.
    urlpatterns = [
        path(
            "jsi18n/",
            cache_page(86400, key_prefix="jsi18n-%s" % get_version())(
                JavaScriptCatalog.as_view()
            ),
            name="javascript-catalog",
        ),
    ]

Le cache côté client économise de la bande passante et accélère le chargement de votre site. Si vous utilisez les `ETags` (`ConditionalGetMiddleware`), il n’y a rien à faire de plus. Sinon, vous pouvez appliquer des décorateurs conditionnels. Dans l’exemple qui suit, le cache est invalidé chaque fois que vous relancez le serveur d’applications :

    from django.utils import timezone
    from django.views.decorators.http import last_modified
    from django.views.i18n import JavaScriptCatalog

    last_modified_date = timezone.now()

    urlpatterns = [
        path(
            "jsi18n/",
            last_modified(lambda req, **kw: last_modified_date)(
                JavaScriptCatalog.as_view()
            ),
            name="javascript-catalog",
        ),
    ]

Il est même possible de pré-générer le catalogue JavaScript dans le cadre de votre procédure de déploiement et le servir comme fichier statique. Cette technique radicale est implémentée dans django-statici18n.

## Internationalisation : dans les motifs d’URL

Django fournit deux mécanismes pour internationaliser les motifs d’URL :

- En ajoutant le préfixe de langue à la racine des motifs d’URL afin que `LocaleMiddleware` puisse détecter la langue à activer à partir de l’URL demandée.

- Making URL patterns themselves translatable via the `django.utils.translation.gettext_lazy()` function.

Avertissement

L’utilisation de chacune de ces techniques exige qu’une langue active soit définie pour chaque requête ; en d’autres termes, votre réglage `MIDDLEWARE` doit contenir `django.middleware.locale.LocaleMiddleware`.

### Préfixe de langue dans les motifs d’URL

i18n_patterns(*urls, prefix_default_language=True)

Cette fonction peut être utilisée dans une configuration d’URL racine et Django préfixe automatiquement tous les motifs d’URL définis par `i18n_patterns()` avec le code de langue actuellement actif.

En définissant `prefix_default_language` à `False`, le préfixe pour la langue par défaut (`LANGUAGE_CODE`) est enlevé. Cela peut être utile lors de l’ajout de traductions à un site existant afin que les URL actuelles ne changent pas.

Exemples de motifs d’URL :

    from django.conf.urls.i18n import i18n_patterns
    from django.urls import include, path

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

    urlpatterns = [
        path("sitemap.xml", sitemap, name="sitemap-xml"),
    ]

    news_patterns = (
        [
            path("", news_views.index, name="index"),
            path("category/<slug:slug>/", news_views.category, name="category"),
            path("<slug:slug>/", news_views.details, name="detail"),
        ],
        "news",
    )

    urlpatterns += i18n_patterns(
        path("about/", about_views.main, name="about"),
        path("news/", include(news_patterns, namespace="news")),
    )

Après la définition de ces motifs d’URL, Django ajoute automatiquement le préfixe de langue dans les motifs d’URL qui ont été ajoutés avec la fonction `i18n_patterns`. Exemple :

    >>> 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/'

Avec `prefix_default_language=False` et `LANGUAGE_CODE='en'`, les URL seront :

    >>> activate("en")
    >>> reverse("news:index")
    '/news/'

    >>> activate("nl")
    >>> reverse("news:index")
    '/nl/news/'

Avertissement

`i18n_patterns()` ne peut figurer que dans la configuration d’URL racine. Si vous essayez de l’utiliser dans une configuration d’URL incluse, vous obtiendrez une exception `ImproperlyConfigured`.

Avertissement

Assurez-vous de ne pas avoir de motifs d’URL non préfixés qui pourraient être confondus avec des motifs contenant le préfixe de langue automatique.

### Traduction de motifs d’URL

Les motifs d’URL peuvent aussi être signalés comme traduisibles en utilisant la fonction `gettext_lazy()`. Exemple :

    from django.conf.urls.i18n import i18n_patterns
    from django.urls import include, path
    from django.utils.translation import gettext_lazy as _

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

    urlpatterns = [
        path("sitemap.xml", sitemap, name="sitemap-xml"),
    ]

    news_patterns = (
        [
            path("", news_views.index, name="index"),
            path(_("category/<slug:slug>/"), news_views.category, name="category"),
            path("<slug:slug>/", news_views.details, name="detail"),
        ],
        "news",
    )

    urlpatterns += i18n_patterns(
        path(_("about/"), about_views.main, name="about"),
        path(_("news/"), include(news_patterns, namespace="news")),
    )

Après avoir effectué les traductions, la fonction `reverse()` renverra l’URL dans la langue active. Exemple :

    >>> 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/'

Avertissement

Dans la plupart des cas, il est recommandé de n’utiliser des URL traduites que dans des blocs de motifs avec préfixe de code de langue (utilisant `i18n_patterns()`), pour éviter l’éventualité qu’une URL mal traduite entre en conflit avec un motif d’URL non traduit.

### Résolution inverse dans les gabarits

Lors de la résolution d’URL régionalisées dans les gabarits, c’est toujours la langue active qui sera utilisée. Pour créer un lien vers une URL dans une autre langue, utilisez la balise de gabarit `language`. Elle active la langue indiquée à l’intérieur de la section de gabarit concernée :

    {% load i18n %}

    {% get_available_languages as languages %}

    {% translate "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 %}

La balise `language` n’accepte que le code de langue comme paramètre.

## Régionalisation : comment créer les fichiers de langues

Après avoir marqué les chaînes à traduire dans le code d’une application, il s’agit d’effectuer les traductions elles-mêmes. Voici comment ça fonctionne.

### Fichiers de messages

La première étape est de créer un fichier de messages pour la nouvelle langue. Il s’agit d’un simple fichier texte correspondant à une seule langue et contenant toutes les chaînes à traduire disponibles ainsi que leur traduction dans la langue cible. Les fichiers de messages possèdent l’extension de fichier `.po`.

Django est livré avec un outil, `django-admin makemessages`, qui automatise la création et la maintenance de ces fichiers.

Utilitaires gettext

La commande `makemessages` (ainsi que `compilemessages` présentée plus bas) utilise des commandes de la suite d’outils GNU gettext : `xgettext`, `msgfmt`, `msgmerge` et `msguniq`.

La version minimale prise en charge des utilitaires `gettext` est la version 0.19.

Pour créer ou mettre à jour un fichier de messages, exécutez cette commande :

    django-admin makemessages -l de

…où `de` est le nom de locale du fichier de messages que vous souhaitez créer. Par exemple, le portugais du Brésil est représenté par `pt_BR`, l’allemand d’Autriche par `de_AT` ou encore `id` pour l’indonésien.

Le script doit être lancé depuis l’un des deux endroits suivants :

- Le répertoire racine de votre projet Django (celui qui contient `manage.py`).

- Le répertoire racine de l’une de vos applications Django.

Le script parcourt l’arborescence des sources de votre projet ou de votre application et extrait toutes les chaînes marquées pour la traduction (consultez Processus de découverte des traductions par Django et assurez-vous que `LOCALE_PATHS` est correctement configuré). Il crée (ou met à jour) un fichier de messages dans le répertoire `locale/LANG/LC_MESSAGES`. Dans l’exemple avec `de`, le fichier équivaudra à `locale/de/LC_MESSAGES/django.po`.

Lorsque vous exécutez `makemessages` à partir du dossier racine de votre projet, les chaînes extraites sont automatiquement distribuées dans les fichiers de messages appropriés. C’est-à-dire qu’une chaîne extraite d’un fichier d’une application contenant un répertoire `locale` sera placée dans un fichier de messages sous ce répertoire. Une chaîne extraite d’un fichier d’une application sans répertoire `locale` sera placée dans un fichier de messages sous le répertoire apparaissant en premier dans `LOCALE_PATHS` ou générera une erreur si le réglage `LOCALE_PATHS` est vide.

Par défaut, `django-admin makemessages` examine chaque fichier ayant une extension `.html`, `.txt` ou `.py`. Dans le cas où vous souhaitez modifier cela, utilisez l’option `--extension` ou `-e` pour indiquer les extensions de fichiers à examiner :

    django-admin makemessages -l de -e txt

Séparez plusieurs extensions par une virgule ou utilisez plusieurs occurrences de `-e` ou `--extension`:

    django-admin makemessages -l de -e html,txt -e xml

Avertissement

Lors de la création de fichiers de messages à partir de code JavaScript, vous devez utiliser le domaine particulier `djangojs`, et non pas `-e js`.

Utilisation de gabarits Jinja2

`makemessages` ne comprend pas la syntaxe des gabarits Jinja2. Pour extraire les chaînes d’un projet contenant des gabarits Jinja2, utilisez plutôt l”extraction de messages de Babel.

Voici un exemple de fichier de configuration `babel.cfg`:

    # Extraction from Python source files
    [python: **.py]

    # Extraction from Jinja2 templates
    [jinja2: **.jinja]
    extensions = jinja2.ext.with_

Prenez soin de bien mentionner toutes les extensions que vous utilisez ! Sinon, Babel ne reconnaîtra pas les balises définies par ces extensions et ignorera complètement les gabarits Jinja2 contenant de telles balises.

Babel fournit des fonctionnalités similaires à `makemessages` et peut généralement le remplacer. Il ne dépend pas de `gettext`. Pour plus d’informations, lisez sa documentation au sujet de sa gestion des catalogues de messages.

Pas de gettext ?

Si les utilitaires `gettext` ne sont pas installés, `makemessages` crée des fichiers vides. Dans ce cas, installez les utilitaires `gettext` ou copiez le fichier de messages anglais (`locale/en/LC_MESSAGES/django.po`) s’il y en a un et utilisez-le comme point de départ, celui-ci étant un fichier de traductions vides.

Vous utilisez Windows ?

Si vous utilisez Windows et que vous avez besoin d’installer les utilitaires GNU gettext pour le fonctionnement de `makemessages`, consultez gettext et Windows pour plus d’informations.

Chaque fichier `.po` contient quelques métadonnées telles que les informations de contact du mainteneur de la traduction, mais la partie principale du fichier est composée d’une liste de messages, des correspondances entre les chaînes à traduire et les textes de la traduction dans la langue cible.

Par exemple, si votre application Django contient une chaîne à traduire pour le texte `"Welcome to my site."`, comme ceci :

    _("Welcome to my site.")

…alors `django-admin makemessages` créera un fichier `.po` contenant l’extrait (message) suivant :

    #: path/to/python/module.py:23
    msgid "Welcome to my site."
    msgstr ""

Une rapide explication :

- `msgid` est la chaîne à traduire apparaissant dans la source. N’y touchez pas.

- `msgstr` est l’emplacement devant recevoir la traduction dans la langue cible. Il est vide au début, il est donc de votre responsabilité d’y ajouter le contenu traduit. Prenez soin de ne pas enlever les guillemets autour de la traduction.

- Chaque message contient des indications utiles sous la forme d’une ligne de commentaire préfixée par `#` et située au-dessus de la ligne `msgid`; ce commentaire contient le nom de fichier et le numéro de ligne correspondant au fichier duquel cette chaîne à traduire a été extraite.

Les longs messages sont des cas spéciaux. La première expression juste après `msgstr` (ou `msgid`) est une chaîne vide. Le contenu lui-même est écrit dans les lignes suivantes sur plusieurs lignes qui seront concaténées au moment de la récupération de la traduction. N’oubliez donc pas les espaces en fin de ligne à l’intérieur du texte, sinon les lignes seront mises bout à bout sans espace !

Attention au codage des caractères

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.

Lignes « fuzzy »

`makemessages` génère parfois des éléments de traduction marqués comme « fuzzy » (approximatif), par exemple lorsque des traductions sont partiellement tirées d’autres chaînes précédemment traduites. Par défaut, ces chaînes approximatives ne sont pas incluses dans le résultat de `compilemessages`.

Pour réexaminer tout le code source et les gabarits à la recherche de nouvelles chaînes à traduire et pour mettre à jour tous les fichiers de messages pour toutes les langues, exécutez ceci :

    django-admin makemessages -a

### Compilation des fichiers de messages

Après avoir créé un fichier de messages et lors de chaque modification à ce fichier, il est nécessaire de le compiler dans un format plus efficace utilisable par `gettext`. Ceci se fait à l’aide de la commande `django-admin compilemessages`.

Cet outil parcourt tous les fichiers `.po` disponibles et crée des fichiers `.mo` qui sont des fichiers binaires optimisés pour `gettext`. Dans le même répertoire à partir duquel vous avez lancé `django-admin makemessages`, exécutez `django-admin compilemessages` comme ceci :

    django-admin compilemessages

C’est tout. Vos traductions sont prêtes à l’emploi.

Vous utilisez Windows ?

Si vous utilisez Windows et que vous avez besoin d’installer les utilitaires GNU gettext pour le fonctionnement de `django-admin compilemessages`, consultez gettext et Windows pour plus d’informations.

Fichiers `.po`: codage et présence du BOM

Django ne gère que les fichiers `.po` codés en UTF-8 et sans BOM (Byte Order Mark) ; si votre éditeur de texte ajoute par défaut de telles marques au début des fichiers, il vous faudra le reconfigurer.

### Dépannage : `gettext()` détecte `python-format` de façon erronée dans des chaînes contenant des caractères pourcent

Dans certains cas, tels que des chaînes avec un caractère pourcent suivi d’un espace et d’un marqueur de conversion de chaîne (par ex. `_("10% interest")`), `gettext()` marque faussement la chaîne avec `python-format`.

Si vous essayez de compiler des fichiers de messages contenant des chaînes faussement marquées, vous obtiendrez un message d’erreur du style `le nombre de spécifications de format dans 'msgid' et 'msgstr' ne correspondent pas` ou `'msgstr' n'est pas une chaîne de format Python valide, au contraire de 'msgid'`.

Pour contourner ce problème, vous pouvez échapper les caractères pourcent en ajoutant un second signe pourcent :

    from django.utils.translation import gettext as _

    output = _("10%% interest")

Ou il est aussi possible d’ajouter le marqueur `no-python-format` afin que tous les caractères pourcent soient considérés comme contenu littéral :

    # xgettext:no-python-format
    output = _("10% interest")

### Création de fichiers de messages à partir de code JavaScript

La création et la mise à jour des fichiers de messages se fait de la même manière que les autres fichiers de messages de Django, à l’aide de l’outil `django-admin makemessages`. La seule différence est que vous devez explicitement définir ce que le jargon `gettext` appelle un domaine, dans ce cas le domaine `djangojs`, en indiquant le paramètre `-d djangojs` comme ceci :

    django-admin makemessages -d djangojs -l de

Ceci va créer ou mettre à jour le fichier de messages JavaScript en allemand. Après la mise à jour des fichiers de messages, lancez `django-admin compilemessages` sur le même principe que pour les autres fichiers de messages de Django.

### `gettext` et Windows

Ceci n’est nécessaire que pour les personnes qui doivent extraire des messages à traduire ou compiler des fichiers de messages (`.po`). Le travail de traduction en soi requiert de modifier des fichiers de ce type, mais si vous souhaitez créer vos propres fichiers de messages ou que vous vouliez tester ou compiler un fichier de messages modifié, téléchargez un installeur binaire précompilé.

Il est aussi possible d’utiliser des binaires `gettext` obtenus par une autre source, tant que la commande `xgettext --version` fonctionne correctement. N’essayez pas d’utiliser les utilitaires de traduction de Django avec un paquet `gettext` si la commande `xgettext --version` saisie dans une invite de commande Windows produit une fenêtre contenant un message du genre «`xgettext.exe` a produit des erreurs et sera fermé par Windows».

### Personnalisation de la commande `makemessages`

Si vous souhaitez passer des paramètres supplémentaires à `xgettext`, vous devez créer une commande `makemessages` personnaliséeet surcharger son attribut `xgettext_options`:

    from django.core.management.commands import makemessages

    class Command(makemessages.Command):
        xgettext_options = makemessages.Command.xgettext_options + ["--keyword=mytrans"]

Si vous avez besoin de plus de souplesse, vous pouvez aussi ajouter un nouveau paramètre à votre commande `makemessages` personnalisée :

    from django.core.management.commands import makemessages

    class Command(makemessages.Command):
        def add_arguments(self, parser):
            super().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().handle(*args, **options)

## Divers

### La vue de redirection `set_language`

set_language(request)

Par commodité, Django fournit une vue, `django.views.i18n.set_language()`, qui définit la préférence de langue d’un utilisateur et redirige vers une URL donnée ou, par défaut, revient à la page précédente.

Activez la vue en ajoutant la ligne suivante dans votre configuration d’URL :

    path("i18n/", include("django.conf.urls.i18n")),

(Notez que cet exemple rend la vue disponible par `/i18n/setlang/`.)

Avertissement

Vérifiez que l’URL ci-dessus ne soit pas ajoutée au moyen de `i18n_patterns()`, car elle doit être elle-même indépendante de la langue pour fonctionner correctement.

La vue s’attend à être appelée par la méthode `POST`, avec un paramètre `language` défini dans la requête. La vue enregistre le choix de langue dans un cookie nommé par défaut `django_language` (le nom peut être modifié par le réglage `LANGUAGE_COOKIE_NAME`).

Après avoir défini le choix de langue, Django cherche un paramètre `next` dans les données `POST` ou `GET`. S’il y en a un et que Django le juge comme URL sûr (c.-à-d. qu’il ne pointe pas vers un hôte différent et qu’il utilise un protocole sûr), il redirige la requête vers cette URL. Sinon, Django se rabat sur la redirection vers l’URL de l’en-tête `Referer` ou, s’il n’est pas présent, vers `/`, en fonction de la nature de la requête :

- Si la requête accepte le contenu HTML (basé sur son en-tête `Accept`), la redirection est toujours effectuée.

- Si la requête n’accepte pas le HTML, la redirection ne s’effectue que si le paramètre `next` a été défini. Sinon, un code de statut 204 (Pas de contenu) est renvoyé.

Voici un exemple de code de gabarit HTML :

    {% 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{% endif %}>
                    {{ language.name_local }} ({{ language.code }})
                </option>
            {% endfor %}
        </select>
        <input type="submit" value="Go">
    </form>

Dans cet exemple, Django cherche l’URL de la page vers laquelle l’utilisateur sera redirigé dans la variable de contexte `redirect_to`.

### Définition explicite de la langue active

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 `django.utils.translation.activate()`. That applies to the current thread only. To persist the language for the entire session in a cookie, set the `LANGUAGE_COOKIE_NAME` cookie on the response:

    from django.conf import settings
    from django.http import HttpResponse
    from django.utils import translation

    user_language = "fr"
    translation.activate(user_language)
    response = HttpResponse(...)
    response.set_cookie(settings.LANGUAGE_COOKIE_NAME, user_language)

You would typically want to use both: `django.utils.translation.activate()` changes the language for this thread, and setting the cookie makes this preference persist in future requests.

### Traductions en dehors des vues et des gabarits

Django contient un ensemble d’outils d’internationalisation bien fourni pour être utilisé dans les vues et les gabarits, mais leur usage n’est pas limité au code spécifique à Django. Les mécanismes de traduction de Django peuvent être utilisés pour traduire n’importe quel texte vers toutes les langues prises en charge par Django (pour autant qu’un catalogue de traductions correspondant existe, bien sûr). Vous pouvez charger un catalogue de traductions, l’activer et traduire du texte dans la langue de votre choix, mais n’oubliez pas de restaurer la langue initiale, car l’activation d’un catalogue de traductions est valable pour tout un fil d’exécution (thread) et une telle modification affecte le code s’exécutant dans le même fil.

Par exemple :

    from django.utils import translation

    def welcome_translated(language):
        cur_language = translation.get_language()
        try:
            translation.activate(language)
            text = translation.gettext("welcome")
        finally:
            translation.activate(cur_language)
        return text

L’appel de cette fonction avec la valeur `'de'` produira `"Willkommen"`, quelle que soit la valeur de `LANGUAGE_CODE` ou la langue définie par un intergiciel.

Functions of particular interest are `django.utils.translation.get_language()` which returns the language used in the current thread, `django.utils.translation.activate()` which activates a translation catalog for the current thread, and `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 `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.gettext("welcome")

### Cookie de langue

Plusieurs réglages permettent d’ajuster les options du cookie de langue :

- `LANGUAGE_COOKIE_NAME`

- `LANGUAGE_COOKIE_AGE`

- `LANGUAGE_COOKIE_DOMAIN`

- `LANGUAGE_COOKIE_HTTPONLY`

- `LANGUAGE_COOKIE_PATH`

- `LANGUAGE_COOKIE_SAMESITE`

- `LANGUAGE_COOKIE_SECURE`

## Notes d’implémentation

### Spécialités des traductions Django

Le mécanisme de traduction de Django utilise le module standard `gettext` livré avec Python. Si vous connaissez `gettext`, vous allez peut-être constater ces particularités dans la manière dont Django gère les traductions :

- Le texte du domaine est `django` ou `djangojs`. Ce texte sert à distinguer les divers programmes qui stockent leurs données dans un endroit commun aux fichiers de messages (normalement `/usr/share/locale/`). Le domaine `django` est utilisé pour les chaînes de traduction du code Python et des gabarits et est chargé dans les catalogues de traductions globaux. Le domaine `djangojs` n’est utilisé que pour les catalogues de traductions JavaScript afin qu’ils restent aussi petits que possible.

- Django n’utilise pas directement `xgettext`, mais des adaptateurs Python autour de `xgettext` et `msgfmt`. Ceci essentiellement pour des raisons pratiques.

### Processus de découverte de la préférence de langue par Django

Après avoir préparé les traductions, ou si vous voulez utiliser les traductions qui proviennent de Django, activez les traductions pour votre application.

En arrière-plan, Django utilise un modèle très souple pour décider de la langue à activer, de manière globale, pour un utilisateur particulier ou les deux.

Pour configurer une préférence de langue générale du projet, définissez `LANGUAGE_CODE`. Django utilise cette langue comme traduction par défaut, c’est-à-dire le choix final si aucune autre traduction correspondante n’est découverte par l’une des méthodes employées par l’intergiciel de langue (voir ci-dessous).

Si vous souhaitez simplement faire fonctionner Django dans votre langue maternelle, la seule chose est faire est de définir `LANGUAGE_CODE` et de vérifier que les fichiers de messages correspondants ainsi que leur version compilée (`.mo`) existent.

Si vous voulez que chaque utilisateur puisse indiquer sa langue préférée, il est alors nécessaire d’utiliser `LocaleMiddleware`. Cet intergiciel active le choix de la langue en fonction des données de la requête. Il personnalise le contenu pour chaque utilisateur.

Pour utiliser `LocaleMiddleware`, ajoutez `'django.middleware.locale.LocaleMiddleware'` à votre réglage `MIDDLEWARE`. Comme les intergiciels sont sensibles à leur ordre d’apparition, suivez ces directives :

- Assurez vous que c’est un des premiers intergiciels installés.

- Il devrait figurer après `SessionMiddleware`, parce que `LocaleMiddleware` emploie des données de session. Et il devrait figurer avant `CommonMiddleware`, parce que `CommonMiddleware` a besoin d’une langue active pour la résolution de l’URL demandée.

- Si vous utilisez `CacheMiddleware`, placez-le avant `LocaleMiddleware`.

Par exemple, voici à quoi pourrait ressembler votre réglage `MIDDLEWARE`:

    MIDDLEWARE = [
        "django.contrib.sessions.middleware.SessionMiddleware",
        "django.middleware.locale.LocaleMiddleware",
        "django.middleware.common.CommonMiddleware",
    ]

(Pour plus d’informations sur les intergiciels, consultez la documentation sur les intergiciels.)

`LocaleMiddleware` essaie de déterminer la préférence de langue de l’utilisateur en suivant cet algorithme :

- 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 Internationalisation : dans les motifs d’URL for more information about the language prefix and how to internationalize URL patterns.

- S’il ne trouve rien, il cherche un cookie.

  Le nom de ce cookie est défini par le réglage `LANGUAGE_COOKIE_NAME` (la valeur par défaut est `django_language`).

- S’il ne trouve rien, il examine l’en-tête HTTP `Accept-Language`. Cet en-tête est envoyé par le navigateur et indique au serveur les langues préférées de l’utilisateur, triées par ordre de préférence. Django prend en compte chaque langue de cet en-tête jusqu’à ce qu’il en trouve une dont les traductions sont disponibles.

- S’il ne trouve rien, il utilise le réglage global `LANGUAGE_CODE`.

Notes :

- À chacun de ces endroits, la préférence de langue est supposée être dans le format de langue standard, sous forme textuelle. Par exemple, le portugais brésilien est représenté par `pt-br`.

- Si une langue de base est disponible mais que la variante indiquée ne l’est pas, Django utilise la langue de base. Par exemple, si un utilisateur indique `fr-be` (français de Belgique) mais que Django ne connaît que `fr`, il utilisera `fr`.

- Seules les langues présentes dans le réglage `LANGUAGES` peuvent être choisies. Si vous souhaitez restreindre le choix possible des langues à un sous-ensemble des langues disponibles (parce que votre application ne fournit pas toutes ces langues), définissez `LANGUAGES` à une liste de langues. Par exemple :

      LANGUAGES = [
          ("de", _("German")),
          ("en", _("English")),
      ]

  Cet exemple limite les langues disponibles lors de la sélection automatique à l’allemand et à l’anglais (et à toute variante, du genre `de-ch` ou `en-us`).

- Si vous définissez un réglage `LANGUAGES` personnalisé comme expliqué au point ci-dessus, il est possible de marquer les noms de langues comme chaînes à traduire, mais utilisez alors `gettext_lazy()` au lieu de `gettext()` pour éviter une importation circulaire.

  Voici un exemple de fichier de réglages :

      from django.utils.translation import gettext_lazy as _

      LANGUAGES = [
          ("de", _("German")),
          ("en", _("English")),
      ]

Lorsque `LocaleMiddleware` a déterminé la préférence de langue de l’utilisateur, il rend disponible cette préférence dans la variable `request.LANGUAGE_CODE` de chaque `HttpRequest`. Cette valeur est à votre disposition dans le code de vos vues. Voici un exemple :

    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.")

Remarquez qu’avec la traduction statique (sans intergiciel), la langue se trouve dans `settings.LANGUAGE_CODE` alors qu’avec la traduction dynamique (avec intergiciel), elle se trouve dans `request.LANGUAGE_CODE`.

### Processus de découverte des traductions par Django

Au moment de l’exécution, Django construit un catalogue consolidé en mémoire des chaînes de traduction. Pour faire cela, il cherche des traductions selon l’algorithme suivant concernant l’ordre dans lequel il examine les différents chemins de fichiers en vue du chargement des fichiers de messages compilés (`.mo`) et de la priorité d’éventuelles traductions différentes de la même chaîne :

1. Les répertoires énumérés dans `LOCALE_PATHS` ont la plus haute priorité, ceux apparaissant en premier ayant priorité sur les suivants.

2. Then, it looks for and uses if it exists a `locale` directory in each of the installed apps listed in `INSTALLED_APPS`. The ones appearing first have higher precedence than the ones appearing later.

3. Et finalement, la traduction de base fournie par Django dans django/conf/locale est utilisée en dernier recours.

Voir aussi

Les traductions de chaînes contenues dans des fichiers JavaScript sont parcourues par un algorithme semblable mais pas identique. Voir `JavaScriptCatalog` pour plus de détails.

Vous pouvez également placer des fichiers de formats personnalisés dans les répertoires `LOCALE_PATHS` si vous définissez aussi `FORMAT_MODULE_PATH`.

Dans tous les cas, le nom du répertoire contenant une traduction doit respecter le format de nommage avec la notation de nom de locale. Par ex. : `de`, `pt_BR`, `es_AR`, etc. Les chaînes non traduites des variantes territoriales de langues utilisent les traductions de la langue générique. Par exemple, les chaînes `pt_BR` non traduites utilisent les traductions `pt`.

De cette façon, vous pouvez écrire des applications incluant leurs propres traductions et il est possible de surcharger les traductions de base par celles de votre projet. Ou, vous pouvez construire un gros projet composé de plusieurs applications et placer toutes les traductions dans un seul gros fichier de messages commun spécifique au projet que vous mettez en place. C’est à vous de choisir.

Tous les dépôts de fichiers de messages sont structurés de la même façon. C’est-à-dire :

- Django parcourt tous les chemins mentionnés dans `LOCALE_PATHS` de votre fichier de réglages à la recherche de `<code_de_langue>/LC_MESSAGES/django.(po|mo)`

- `$CHEMIN_APP/locale/<code_de_langue>/LC_MESSAGES/django.(po|mo)`

- `$PYTHONPATH/django/conf/locale/<code_de_langue>/LC_MESSAGES/django.(po|mo)`

Pour créer des fichiers de messages, il faut utiliser l’outil `django-admin makemessages`. Et pour produire les fichiers binaires `.mo` utilisés par `gettext`, il faut utiliser `django-admin compilemessages`.

Vous pouvez aussi exécuter `django-admin compilemessages --settings=chemin.vers.settings` pour que le compilateur s’occupe de tous les répertoires énumérés dans le réglage `LOCALE_PATHS`.

### Utilisation d’une langue de base autre que l’anglais

Django part du principe général que les chaînes originales d’un projet traduisible sont écrites en anglais. Il est possible de choisir une autre langue, mais il faut garder à l’esprit un certain nombre de limites :

- `gettext` ne fournit que deux formes plurielles pour les messages originaux, ce qui signifie que vous devrez aussi fournir une traduction pour la langue de base afin d’inclure toutes les formes plurielles si les règles de pluriel de la langue de base diffèrent de l’anglais.

- Lorsqu’une variante d’anglais est activée et que les chaînes anglaises sont manquantes, la langue de repli ne sera pas la langue `LANGUAGE_CODE` du projet, mais dans la langue des chaînes originales. Par exemple, un utilisateur anglais visitant un site dont `LANGUAGE_CODE` est l’espagnol et dont les chaînes originales sont écrites en russe obtiendra du russe et non de l’espagnol.