
Flask i18n : créez une application multilingue avec Flask-Babel
Des bases de gettext au déploiement en production : internationalisez votre application Flask avec Flask-Babel, les fichiers PO et la traduction automatisée par IA.
Installer Flask-Babel
Flask-Babel est l'extension d'internationalisation standard pour Flask. Elle intègre GNU gettext à Flask et aux modèles Jinja2, en fournissant nativement des fonctions de traduction, la sélection de la locale et la prise en charge des fuseaux horaires.
pip install Flask-BabelConfigurer Babel
Créez un fichier babel.cfg pour indiquer à pybabel où rechercher les chaînes traduisibles, puis initialisez Flask-Babel avec une fonction de sélection de la locale qui détermine quelle langue servir pour chaque requête.
# babel.cfg — tells pybabel where to find translatable strings
[python: **.py]
[jinja2: **/templates/**.html]
extensions=jinja2.ext.autoescape,jinja2.ext.with_from flask import Flask, request
from flask_babel import Babel
app = Flask(__name__)
app.config['BABEL_DEFAULT_LOCALE'] = 'en'
app.config['BABEL_DEFAULT_TIMEZONE'] = 'UTC'
# Directory where translations live (default: "translations")
app.config['BABEL_TRANSLATION_DIRECTORIES'] = 'translations'
def get_locale():
# 1. Check URL parameter or session
# 2. Fall back to browser Accept-Language header
return request.accept_languages.best_match(['en', 'de', 'ja', 'es', 'fr'])
babel = Babel(app, locale_selector=get_locale)Marquer les chaînes à traduire
Encadrez chaque chaîne destinée à l'utilisateur avec gettext() dans le code Python et _() dans les modèles Jinja2. Utilisez lazy_gettext() pour les chaînes définies au chargement du module (comme les libellés de formulaires et la configuration) qui doivent être traduites plus tard, au moment de la requête.
from flask_babel import gettext, ngettext, lazy_gettext
# In views — gettext() for immediate translation
@app.route('/')
def index():
flash(gettext('Your profile has been updated.'))
return render_template('index.html',
title=gettext('Home'))
# In forms/config — lazy_gettext() for deferred translation
class LoginForm(FlaskForm):
username = StringField(lazy_gettext('Username'))
password = PasswordField(lazy_gettext('Password'))
submit = SubmitField(lazy_gettext('Sign In')){# In Jinja2 templates, use _() shorthand for gettext #}
<h1>{{ _('Welcome to our app') }}</h1>
<p>{{ _('Hello, %(name)s!', name=user.name) }}</p>
<footer>
{{ _('Copyright %(year)s Example Corp.', year=2026) }}
</footer>Extraire les messages
Exécutez pybabel extract pour analyser votre code source et vos modèles à la recherche de chaînes traduisibles. Cela crée un fichier .pot (Portable Object Template). Initialisez ensuite les catalogues pour chaque langue cible, ou mettez à jour les catalogues existants lorsque les chaînes source changent.
# Extract translatable strings from source code
pybabel extract -F babel.cfg -k lazy_gettext -o messages.pot .
# Initialize a new language (first time only)
pybabel init -i messages.pot -d translations -l de
pybabel init -i messages.pot -d translations -l ja
pybabel init -i messages.pot -d translations -l es
# Update existing catalogs when source strings change
pybabel update -i messages.pot -d translationsTraduire des fichiers PO
Ouvrez les fichiers .po générés et renseignez la valeur msgstr pour chaque msgid. Les fichiers PO sont du texte brut — vous pouvez les modifier directement, utiliser un éditeur PO comme Poedit, ou automatiser la traduction avec des outils d'IA.
# translations/de/LC_MESSAGES/messages.po
msgid "Welcome to our app"
msgstr "Willkommen in unserer App"
msgid "Hello, %(name)s!"
msgstr "Hallo, %(name)s!"
msgid "Your profile has been updated."
msgstr "Ihr Profil wurde aktualisiert."
msgid "Username"
msgstr "Benutzername"
msgid "Password"
msgstr "Passwort"
msgid "Sign In"
msgstr "Anmelden"Compiler les traductions
Compilez vos fichiers .po en fichiers binaires .mo à l'aide de pybabel compile. Flask-Babel lit les fichiers .mo à l'exécution — il ne peut pas lire directement les fichiers .po. Vous devez recompiler après chaque mise à jour de traduction.
# Compile .po files to binary .mo files (required at runtime)
pybabel compile -d translations
# Flask-Babel reads .mo files, not .po files.
# You MUST compile after every translation update.Gérer les pluriels et les variables
Utilisez ngettext() pour les chaînes sensibles au pluriel. Elle prend une forme singulière, une forme plurielle et le nombre. Babel applique automatiquement la règle de pluriel correcte pour chaque langue — l'anglais compte 2 formes, le russe 3, l'arabe 6 et le japonais 1.
from flask_babel import ngettext
@app.route('/cart')
def cart():
count = len(session.get('cart_items', []))
message = ngettext(
'%(num)d item in your cart', # singular
'%(num)d items in your cart', # plural
count # determines which form
)
return render_template('cart.html', message=message)# English: 2 forms (nplurals=2)
msgid "%(num)d item in your cart"
msgid_plural "%(num)d items in your cart"
msgstr[0] "%(num)d item in your cart"
msgstr[1] "%(num)d items in your cart"
# German: 2 forms (nplurals=2)
msgid "%(num)d item in your cart"
msgid_plural "%(num)d items in your cart"
msgstr[0] "%(num)d Artikel in Ihrem Warenkorb"
msgstr[1] "%(num)d Artikel in Ihrem Warenkorb"
# Japanese: 1 form (nplurals=1)
msgid "%(num)d item in your cart"
msgid_plural "%(num)d items in your cart"
msgstr[0] "カートに%(num)d個の商品があります"
# Russian: 3 forms (nplurals=3)
msgid "%(num)d item in your cart"
msgid_plural "%(num)d items in your cart"
msgstr[0] "%(num)d товар в вашей корзине"
msgstr[1] "%(num)d товара в вашей корзине"
msgstr[2] "%(num)d товаров в вашей корзине"Ajouter le changement de locale
Créez un sélecteur de langue qui stocke le choix de l'utilisateur dans la session Flask. Mettez à jour votre fonction locale_selector pour qu'elle vérifie d'abord la session, puis se replie sur la détection du navigateur.
from flask import session, redirect, url_for, request
from flask_babel import refresh
@app.route('/set-language/<lang>')
def set_language(lang):
session['lang'] = lang
refresh() # Force Flask-Babel to re-read the locale
return redirect(request.referrer or url_for('index'))
# Update get_locale to check session first
def get_locale():
# 1. Explicit user choice (stored in session)
if 'lang' in session:
return session['lang']
# 2. Browser Accept-Language header
return request.accept_languages.best_match(
['en', 'de', 'ja', 'es', 'fr']
){# Language switcher component #}
<nav class="language-switcher">
{% for lang, name in [('en','English'),('de','Deutsch'),
('ja','日本語'),('es','Español'),
('fr','Français')] %}
<a href="{{ url_for('set_language', lang=lang) }}"
class="{{ 'active' if get_locale() == lang }}">
{{ name }}
</a>
{% endfor %}
</nav>Automatiser les traductions
Une fois votre configuration Flask-Babel terminée, traduisez vos fichiers PO à l'aide de l'IA. Automatisez le cycle extraction-traduction-compilation dans votre pipeline CI/CD pour maintenir vos traductions synchronisées avec votre code source.
# Translate your PO files with AI directly from your IDE
# or use the CLI in CI/CD:
npx i18n-agent translate translations/de/LC_MESSAGES/messages.po \
--source-lang en --target-lang de
# Bulk translate all languages at once:
npx i18n-agent translate messages.pot --lang de,ja,es,fr
# Then compile:
pybabel compile -d translationsBonus : repli intelligent de locale avec flask-babel-locale-chain
Par défaut, Flask-Babel se replie directement sur la locale par défaut lorsque la locale préférée de l'utilisateur n'est pas disponible. Un utilisateur pt-BR ne disposant que de traductions pt-PT voit l'anglais s'afficher au lieu du portugais. flask-babel-locale-chain ajoute des chaînes de repli configurables afin que les locales apparentées s'enchaînent naturellement.
# pip install flask-babel-locale-chain
from flask_babel_locale_chain import LocaleChain
# Define fallback chains: pt-BR falls back to pt before en
locale_chain = LocaleChain({
'pt-BR': ['pt-BR', 'pt', 'en'],
'pt-PT': ['pt-PT', 'pt', 'en'],
'zh-Hant': ['zh-Hant', 'zh-Hans', 'en'],
'en-GB': ['en-GB', 'en', 'en-US'],
})
def get_locale():
requested = request.accept_languages.best_match(
['en', 'pt-BR', 'pt', 'zh-Hant', 'zh-Hans']
)
# Returns the best available locale from the chain
return locale_chain.resolve(requested)Automatiser la qualité des traductions
Pièges courants
Oubli de compiler les .po en .mo
Utilisation de gettext() au niveau du module
L'extraction manque des chaînes
Erreurs d'encodage des fichiers PO
Structure de fichiers recommandée
my-flask-app/
├── app.py # Flask app with Babel config
├── babel.cfg # Extraction config
├── messages.pot # Template (extracted strings)
├── translations/
│ ├── de/
│ │ └── LC_MESSAGES/
│ │ ├── messages.po # German translations (editable)
│ │ └── messages.mo # Compiled binary (generated)
│ ├── ja/
│ │ └── LC_MESSAGES/
│ │ ├── messages.po
│ │ └── messages.mo
│ └── es/
│ └── LC_MESSAGES/
│ ├── messages.po
│ └── messages.mo
├── templates/
│ ├── base.html
│ ├── index.html
│ └── components/
│ └── language_switcher.html
├── requirements.txt
└── venv/Essayez i18n Agent maintenant
Déposez votre fichier de traduction ici
JSON, YAML, PO, XML, CSV, Markdown, Properties
ou cliquez pour parcourir
Langues cibles