
Flask i18n: bouw een meertalige app met Flask-Babel
Van de basis van gettext tot implementatie in productie: internationaliseer je Flask-app met Flask-Babel, PO-bestanden en geautomatiseerde AI-vertalingen.
Flask-Babel installeren
Flask-Babel is de standaardextensie voor internationalisatie in Flask. De extensie integreert GNU gettext met Flask- en Jinja2-sjablonen en biedt direct vertaalfuncties, taalselectie en ondersteuning voor tijdzones.
pip install Flask-BabelBabel configureren
Maak een bestand babel.cfg om pybabel te vertellen waar het naar vertaalbare tekenreeksen moet zoeken. Initialiseer Flask-Babel vervolgens met een taalselectiefunctie die bepaalt welke taal voor elke aanvraag wordt aangeboden.
# 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)Strings markeren voor vertaling
Omsluit elke voor gebruikers zichtbare tekenreeks in Python-code met gettext() en in Jinja2-sjablonen met _(). Gebruik lazy_gettext() voor tekenreeksen die bij het laden van een module worden gedefinieerd (zoals formulierlabels en configuratie) en later tijdens een aanvraag moeten worden vertaald.
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>Berichten extraheren
Voer pybabel extract uit om je broncode en sjablonen op vertaalbare tekenreeksen te doorzoeken. Hiermee wordt een .pot-bestand (Portable Object Template) gemaakt. Initialiseer vervolgens catalogi voor elke doeltaal of werk bestaande catalogi bij wanneer brontekenreeksen veranderen.
# 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 translationsPO-bestanden vertalen
Open de gegenereerde .po-bestanden en vul voor elke msgid de msgstr-waarden in. PO-bestanden zijn platte tekst: je kunt ze rechtstreeks bewerken, een PO-editor zoals Poedit gebruiken of de vertaling met AI-tools automatiseren.
# 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"Vertalingen compileren
Compileer je .po-bestanden met pybabel compile naar binaire .mo-bestanden. Flask-Babel leest tijdens runtime .mo-bestanden en kan .po-bestanden niet rechtstreeks lezen. Na elke vertaalupdate moet je opnieuw compileren.
# 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.Meervoudsvormen en variabelen verwerken
Gebruik ngettext() voor tekenreeksen die gevoelig zijn voor meervoudsvormen. De functie neemt een enkelvoudsvorm, een meervoudsvorm en het aantal aan. Babel gebruikt automatisch de juiste meervoudsregel voor elke taal: Engels heeft 2 vormen, Russisch 3, Arabisch 6 en Japans 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 товаров в вашей корзине"Een taalschakelaar toevoegen
Bouw een taalselector die de keuze van de gebruiker in de Flask-sessie opslaat. Werk je functie locale_selector bij om eerst de sessie te controleren en daarna terug te vallen op browserherkenning.
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>Vertalingen automatiseren
Nu je Flask-Babel-configuratie compleet is, kun je je PO-bestanden met AI vertalen. Automatiseer de cyclus extraheren-vertalen-compileren in je CI/CD-pipeline om vertalingen met je broncode gesynchroniseerd te houden.
# 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: slimme locale-fallback met flask-babel-locale-chain
Standaard valt Flask-Babel rechtstreeks terug op de standaardtaal wanneer de voorkeurstaal van de gebruiker niet beschikbaar is. Een gebruiker met pt-BR en alleen pt-PT-vertalingen ziet Engels in plaats van Portugees. flask-babel-locale-chain voegt configureerbare terugvalketens toe, zodat verwante talen op natuurlijke wijze op elkaar terugvallen.
# 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)Kwaliteitscontrole van vertalingen automatiseren
Veelvoorkomende valkuilen
Vergeten .po naar .mo te compileren
gettext() op moduleniveau gebruiken
Tekenreeksen ontbreken na extractie
Coderingsfouten in PO-bestanden
Aanbevolen bestandsstructuur
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/Probeer i18n Agent nu
Zet je vertaalbestand hier neer
JSON, YAML, PO, XML, CSV, Markdown, Properties
of klik om een bestand te selecteren
Doeltalen