Introduction
Aperçu
L’internationalisation et la régionalisation sont des étapes essentielles dans le développement d’applications web modernes, visant à adapter le contenu et les formats pour un public mondial. Ces concepts permettent à une application unique de répondre aux besoins de différents utilisateurs, en leur offrant une expérience localisée dans leur langue et selon leurs conventions culturelles.
Django intègre des fonctionnalités robustes pour gérer la traduction des textes, la mise en forme des dates, heures, et nombres, ainsi que la gestion des fuseaux horaires.
En résumé, Django facilite deux aspects principaux :
-
Permettre aux développeurs et aux créateurs de gabarits de marquer les éléments de leur application à traduire ou à formater selon les paramètres locaux.
-
Appliquer ces paramètres pour adapter dynamiquement l’application en fonction des préférences des utilisateurs.
La traduction dépend de la langue cible, tandis que la mise en forme est souvent déterminée par les conventions du pays. Ces informations sont généralement transmises par les navigateurs via l’en-tête Accept-Language.
Définitions
Les termes « internationalisation » et « régionalisation » sont souvent confondus. Voici une définition simplifiée :
Internationalisation
Régionalisation
Terminologie importante
Voici quelques termes clés à connaître pour travailler avec l’internationalisation dans Django :
-
Nom de locale
Une chaîne indiquant soit une langue (par exemple fr), soit une combinaison langue-pays (par exemple fr_CA). Les langues sont en minuscules, les pays en majuscules (ex. : es_MX, en_US).
-
Code de langue
Le format utilisé dans l’en-tête Accept-Language des navigateurs pour indiquer les langues acceptées. Exemple : fr, en-us.
-
Fichier de messages
Fichiers .po contenant les chaînes à traduire et leurs équivalents traduits.
-
Chaîne à traduire
Texte marqué dans l’application comme nécessitant une traduction.
-
Format de fichier
Modules Python définissant les formats locaux pour les dates, heures, nombres, etc.
Mise en œuvre de l’internationalisation et de la régionalisation dans un projet concret
Prérequis
Système d’exploitation Ubuntu, ou tout autres systèmes d’exploitation Linux, Python 3, GNU gettext.
Pour la l’installation de Python 3 si ce n’est pas encore le cas : Dans un terminal utilisez la commande sudo apt update puis sudo apt install python3
Pour GNU gettext utiliser toujours dans un terminal la commande sudo apt install gettext.
Initialisation
Après avoir créé un environnement virtuel(optionnel) utiliser la commande, on installe django puis on crée un projet et en suite une application.
Configuration
Après l’initialisation du projet passons donc à la configuration du projet, et nous commençons par se rassurer que l’application que nous avons créée est bien dans la liste des applications enregistrer au travers de la constante INSTALLED_APPS.
Ensuite nous passons à l’active l’internationalisation et la régionalisation, puis nous choisissons la langue par défaut et on met la liste des langues pris en charge dans le projet et enfin on précise le chemin vers les fichiers de traduction :
USE_I18N = True
USE_L10N = True
LANGUAGE_CODE = 'en'
LANGUAGES = [
('en', _('English')),
('fr', _('French')),
]
LOCALE_PATHS = [
os.path.join(BASE_DIR, 'locale'),
]
En dernier position, on ajoute l’intergiciel (middleware) django.middleware.locale.LocaleMiddleware, qui joue un rôle crucial dans l’internationalisation et la régionalisation, surtout en détectant et en configurant la langue active de l'application pour chaque requête en fonction des préférences utilisateur (sessions, cookies ou en-têtes HTTP) ou des informations dans l'URL, afin de présenter le contenu et les formats adaptés à la langue et à la culture locales.
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
Mise en œuvre
Dans un projet Django, il existe plusieurs endroits où l'on peut gérer la traduction afin de rendre l'application accessible dans différentes langues. Ces endroits incluent les fichiers Python, tels que les vues et les modèles, les gabarits (templates), et même le code JavaScript pour la traduction côté client.
I. Traduction dans les fichiers Python (.py)
Le module django.utils.translation fournit plusieurs fonctions qui facilitent la traduction du texte dans les fichiers Python de votre projet. Ces fonctions permettent de marquer les chaînes de caractères pour qu'elles soient traduites dans les fichiers de messages (.po). Voici un aperçu de ces fonctions, avec des exemples simples pour mieux comprendre leur utilisation :
1. gettext
Utilisée pour traduire une chaîne simple.
Exemple :
from django.utils.translation import gettext as _
message = _("Welcome to our platform!")
2. ngettext
Utilisée pour les chaînes dépendantes du nombre (pluriel/singulier).
Exemple :
from django.utils.translation import ngettext
message = ngettext(
"You have one new message.",
"You have {count} new messages.",
count=3
)
3. pgettext
Ajoute un contexte à une chaîne pour éviter les ambiguïtés dans la traduction.
Exemple :
from django.utils.translation import pgettext
message = pgettext("salutation", "Hello")
4. npgettext
Combine la gestion du pluriel et le contexte.
Exemple :
from django.utils.translation import npgettext
message = npgettext(
"email notification",
"You have one new email.",
"You have {count} new emails.",
count=2
)
5. gettext_noop
Marque une chaîne pour la traduction, mais ne la traduit pas immédiatement. Utile pour les chaînes stockées dans la base de données ou les constantes.
Exemple :
from django.utils.translation import gettext_noop
GREETING = gettext_noop("Welcome back!")
6. Fonctions "lazy"
Les versions différées (gettext_lazy, ngettext_lazy, etc.) sont utilisées lorsque la traduction doit être retardée jusqu'à ce que la chaîne soit réellement affichée, par exemple dans les modèles ou les formulaires.
Exemple :
from django.utils.translation import gettext_lazy as _
class MyModel(models.Model):
title = models.CharField(max_length=100, verbose_name=_("Title"))
II. Traduction dans les gabarits (templates)
Django offre un support complet pour l'internationalisation directement dans les gabarits HTML, grâce à des balises et filtres spécialement conçus. Cela permet de traduire efficacement le contenu visible par les utilisateurs dans différentes langues. Pour utiliser ces outils, il est nécessaire d'ajouter {% load i18n %} au début du gabarit. Cette balise doit être incluse dans chaque fichier où des traductions sont nécessaires, même si le gabarit hérite d’un autre qui a déjà chargé la bibliothèque i18n.
La balise {% translate %} est utilisée pour traduire une chaîne statique ou une variable.
<p>{% translate "Hello, World!" %}</p>
<p>{% translate greeting_variable %}</p>
Pour marquer un texte comme nécessitant une traduction ultérieure, utilisez l'option noop.
<p>{% translate "This text will be translated later" noop %}</p>
{% translate "Site title" as site_title %}
<title>{{ site_title }}</title>
Pour des textes plus complexes qui incluent des variables ou des substitutions, on utilise {% blocktranslate %}. Cette balise permet de combiner texte et logique de traduction.
{% blocktranslate %}
Hello, {{ username }}! Welcome to our platform.
{% endblocktranslate %}
Pour gérer les pluriels, la balise accepte l'option count et une balise {% plural %} pour définir les formes singulière et plurielle.
{% blocktranslate count item_count=items|length %}
There is {{ item_count }} item in your cart.
{% plural %}
There are {{ item_count }} items in your cart.
{% endblocktranslate %}
Pour traduire des expressions plus complexes, vous pouvez utiliser l'option with pour définir des variables locales.
{% blocktranslate with price=product.price %}
This product costs ${{ price }}.
{% endblocktranslate %}
Cette option permet de supprimer les espaces ou sauts de ligne inutiles pour améliorer la lisibilité et simplifier la traduction dans les fichiers .po.
{% blocktranslate trimmed %}
This is the first line.
This is the second line.
{% endblocktranslate %}
Pour transmettre une chaîne traduite à une balise ou un filtre, utilisez la fonction _().
{% some_tag _("Page not found") %}
Cela traduit automatiquement la chaîne avant de l’envoyer à la balise ou au filtre.
III. Commentaire pour les traducteurs
Dans le code Python de Django, on peut ajouter des commentaires pour les traducteurs en utilisant # Translators: avant les chaînes à traduire. Ces commentaires aident à préciser le contexte, comme l'emplacement du texte, et sont inclus dans les fichiers .po pour guider les traducteurs.
# Translators: This message appears on the home page only
output = gettext("Welcome to my site.")
Dans les gabarits, des commentaires sont aussi possibles avec {% comment %} ou {# … #} pour fournir des précisions avant les balises de traduction comme {% translate %}. Ces notes apparaissent également dans les fichiers .po.
{# Translators: Label of a button that triggers search #}
<button type="submit">{% translate "Go" %}</button>
IV. Traduction côté client (JavaScript)
La traduction côté client dans un projet Django se fait principalement via l'utilisation de la vue JavaScriptCatalog. Cette vue génère un fichier JavaScript contenant des fonctions qui simulent l'interface de traduction de gettext pour le côté client, permettant ainsi d'effectuer des traductions directement dans le code JavaScript exécuté sur le navigateur de l'utilisateur.
Django propose la vue JavaScriptCatalog pour intégrer la traduction côté client. Cette vue génère un fichier JavaScript contenant un ensemble de fonctions et un tableau de chaînes traduites. Vous pouvez inclure ce fichier dans vos pages HTML de manière dynamique. Voici un exemple de configuration :
from django.views.i18n import JavaScriptCatalog
from django.urls import path
urlpatterns = [
path("jsi18n/", JavaScriptCatalog.as_view(), name="javascript-catalog"),
]
La vue JavaScriptCatalog vous permet de générer un fichier contenant les traductions de votre projet en utilisant les mêmes chaînes présentes dans vos fichiers .po ou .mo, mais adaptées pour être utilisées en JavaScript.
Pour utiliser les traductions dans vos fichiers JavaScript, il suffit d'inclure le fichier généré par la vue JavaScriptCatalog dans vos pages HTML :
<script src="{% url 'javascript-catalog' %}"></script>
Cela permet à votre code JavaScript d'utiliser des fonctions de traduction telles que gettext, ngettext, interpolate, et bien d'autres, de manière similaire à la traduction côté serveur.
Voici une liste des principales fonctions disponibles après l'intégration du fichier JavaScript des traductions :
-
gettext : Permet de traduire une chaîne de texte simple.
document.write(gettext("Welcome to the site!"));
-
ngettext : Utilisée pour la traduction de chaînes avec pluriels, en fonction d'un nombre donné.
const itemsCount = 2;
const string = ngettext('There is one item', 'There are multiple items', itemsCount);
-
interpolate : Permet d'insérer des variables dans une chaîne de format.
const message = interpolate("You have %(count)s new messages", {count: 5});
-
get_format : Permet d'accéder à des formats de date, heure, ou de nombre régionalisés.
const dateFormat = get_format('DATE_FORMAT');
Voici les autres réglages possibles :
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
-
gettext_noop : Fonction utilisée pour marquer des chaînes qui seront traduites plus tard.
document.write(gettext_noop("This will not be translated immediately."));
-
pgettext : Permet de traduire une chaîne en prenant en compte le contexte.
document.write(pgettext("month name", "January"));
-
npgettext : Version plurielle de pgettext, utile pour les chaînes de texte pluriels avec contexte.
document.write(npgettext('group', 'party', 1));
document.write(npgettext('group', 'party', 2));
-
pluralidx : Détermine la nécessité d'un pluriel en fonction du nombre donné.
document.write(pluralidx(1)); // false
document.write(pluralidx(2)); // true
V. Internationalisation dans les motifs d'URL
Django propose deux mécanismes pour gérer l'internationalisation des URL :
-
Ajout du préfixe de langue : Utilise LocaleMiddleware pour détecter la langue à partir de l'URL demandée.
-
Traduction des motifs d'URL : Utilisation de gettext_lazy() pour rendre les motifs eux-mêmes traduisibles.
Avertissement
L'usage de ces mécanismes requiert que LocaleMiddleware soit inclus dans le réglage MIDDLEWARE de votre projet pour garantir qu'une langue active soit définie pour chaque requête.
La fonction i18n_patterns() permet de préfixer automatiquement les motifs d'URL avec le code de la langue active. Le paramètre prefix_default_language=False
permet d'éviter le préfixe pour la langue par défaut.
from django.conf.urls.i18n import i18n_patterns
from django.urls import include, path
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"),
]
urlpatterns += i18n_patterns(
path("about/", about_views.main, name="about"),
path("news/", include(news_patterns, namespace="news")),
)
from django.urls import reverse
from django.utils.translation import activate
activate("en")
reverse("news:index") # '/en/news/'
activate("nl")
reverse("news:detail", kwargs={"slug": "news-slug"}) # '/nl/news/news-slug/'
reverse("news:index") # '/news/'
Avertissements
-
Limitation : i18n_patterns() doit être utilisé uniquement dans la configuration d'URL racine.
-
Précaution : Evitez d'avoir des motifs d'URL non préfixés qui pourraient être confondus avec ceux utilisant le préfixe de langue.
VI. Régionalisation : Comment créer les fichiers de langues
Pour gérer la traduction des chaînes marquées dans le code Django, il est nécessaire de créer des fichiers de messages pour chaque langue cible. Ces fichiers contiennent toutes les chaînes à traduire et leurs traductions. Les fichiers de messages ont l'extension .po.
Django propose la commande makemessages pour générer et mettre à jour ces fichiers de messages de manière automatisée. Par exemple, pour créer ou mettre à jour un fichier de messages pour l'allemand, vous pouvez utiliser la commande suivante :
python manage.py makemessages -l de
Cette commande génère ou met à jour un fichier .po dans le répertoire locale/de/LC_MESSAGES/django.po. Le répertoire doit être présent sous locale/LANG/LC_MESSAGES, où LANG représente le code de la langue (par exemple, de pour l'allemand).
Lorsque vous lancez makemessages depuis le répertoire racine de votre projet ou d'une de vos applications Django, il parcourt l'arborescence des sources du projet pour extraire les chaînes marquées à traduire. Il crée ou met à jour le fichier .po correspondant dans les répertoires appropriés.
Par défaut, makemessages examine les fichiers avec les extensions .html, .txt et .py. Si vous souhaitez spécifier d'autres extensions, utilisez l'option --extension ou -e :
python manage.py makemessages -l de -e txt
L'option -d permet de spécifier le domaine du fichier de traduction, qui détermine le contexte d'extraction des chaînes. Par défaut, le domaine est django, mais si vous travaillez avec des fichiers JavaScript, vous devez utiliser djangojs comme domaine :
python manage.py makemessages -l de -d djangojs
Cela garantit que les traductions de JavaScript seront extraites sous le bon domaine.
Lorsque vous créez des fichiers de messages à partir du code JavaScript, il est nécessaire d'utiliser le domaine djangojs plutôt que d'utiliser l'extension -e js. Cela garantit que les traductions seront correctement extraites et associées.
Si les utilitaires gettext ne sont pas installés, makemessages génère des fichiers vides. Dans ce cas, il est recommandé d'installer les utilitaires gettext ou d'utiliser un fichier .po vide comme point de départ.
Pour les projets utilisant des gabarits Jinja2, il est préférable d'utiliser Babel pour extraire les chaînes, car makemessages ne prend pas en charge la syntaxe spécifique des gabarits Jinja2.
Aperçu d’un fichier de traduction :
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-01-10 11:36+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: LearnI18N/settings.py:96
msgid "English"
msgstr "Anglais"
#: LearnI18N/settings.py:97
msgid "French"
msgstr "Français"
#: home/apps.py:8
msgid "Home App"
msgstr "Application Home"
#: home/models.py:14
#, python-format
msgid "This greeting has %(count)s word."
msgid_plural "This greeting has %(count)s words."
msgstr[0] "Cette salutation a %(count)s mot."
msgstr[1] "Cette salutation a %(count)s mots."
Conclusion
L'internationalisation et la localisation dans Django offrent des outils puissants pour rendre vos applications accessibles à un public mondial. En suivant les bonnes pratiques présentées dans cet article, vous pouvez adapter votre interface utilisateur, vos motifs d’URL et vos fichiers de traduction pour répondre aux besoins de différents utilisateurs et cultures.
Pour mieux comprendre et appliquer ces concepts, vous pouvez explorer un exemple de projet disponible sur GitHub. Ce dépôt fournit un code prêt à l’emploi pour expérimenter les fonctionnalités i18n et l10n dans Django.