Skip to main content

Flask-i18n: Mehrsprachige App mit Flask-Babel erstellen

Von gettext-Grundlagen bis zur Produktionsbereitstellung: Internationalisieren Sie Ihre Flask-App mit Flask-Babel, PO-Dateien und automatisierter KI-Übersetzung.

1

Flask-Babel installieren

Flask-Babel ist die Standarderweiterung zur Internationalisierung für Flask. Sie integriert GNU gettext in Flask und Jinja2-Vorlagen und bietet Übersetzungsfunktionen, Locale-Auswahl und Zeitzonenunterstützung.

Flask-Babel umschließt Babel, die Python-i18n-Bibliothek, und integriert sie in den Anfragelebenszyklus von Flask. Sie erhalten die Funktionen gettext(), ngettext() und lazy_gettext() sowie automatische Locale-Erkennung aus Browser-Headern.
Terminal
pip install Flask-Babel
2

Babel konfigurieren

Erstellen Sie eine Datei babel.cfg, die pybabel mitteilt, wo nach übersetzbaren Zeichenfolgen gesucht werden soll. Initialisieren Sie Flask-Babel anschließend mit einer Locale-Auswahlfunktion, die für jede Anfrage die bereitzustellende Sprache bestimmt.

babel.cfg
# babel.cfg — tells pybabel where to find translatable strings
[python: **.py]
[jinja2: **/templates/**.html]
extensions=jinja2.ext.autoescape,jinja2.ext.with_
app.py
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)
Die Funktion locale_selector wird bei jeder Anfrage aufgerufen. Gibt sie eine Locale ohne kompilierte .mo-Datei zurück, fällt Flask-Babel unbemerkt auf die Standard-Locale zurück. Es wird kein Fehler ausgelöst – Zeichenfolgen erscheinen lediglich nicht übersetzt.
3

Zeichenfolgen zur Übersetzung kennzeichnen

Umschließen Sie jede sichtbare Zeichenfolge im Python-Code mit gettext() und in Jinja2-Vorlagen mit _(). Verwenden Sie lazy_gettext() für beim Laden des Moduls definierte Zeichenfolgen, etwa Formularbezeichnungen und Konfiguration, die später zur Anfragezeit übersetzt werden müssen.

app.py
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'))
templates/index.html
{# 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>
Verwenden Sie in Jinja2-Vorlagen _() statt gettext(). Dies ist die übliche gettext-Kurzform und hält Ihre Vorlagen übersichtlich. Flask-Babel registriert _() automatisch als globale Jinja2-Funktion.
4

Nachrichten extrahieren

Führen Sie pybabel extract aus, um Ihren Quellcode und Ihre Vorlagen nach übersetzbaren Zeichenfolgen zu durchsuchen. Dadurch entsteht eine .pot-Datei (Portable Object Template). Initialisieren Sie anschließend Kataloge für jede Zielsprache oder aktualisieren Sie vorhandene Kataloge, wenn sich Ausgangszeichenfolgen ändern.

Terminal
# 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 translations
Führen Sie nach der ersten Extraktion stets pybabel update und nicht init aus. init in einem bestehenden Sprachverzeichnis überschreibt alle vorhandenen Übersetzungen. update führt neue Zeichenfolgen zusammen und erhält bestehende Übersetzungen.
5

PO-Dateien übersetzen

Öffnen Sie die erzeugten .po-Dateien und tragen Sie für jede msgid die msgstr-Werte ein. PO-Dateien sind Nur-Text: Sie können sie direkt bearbeiten, einen PO-Editor wie Poedit verwenden oder die Übersetzung mit KI-Werkzeugen automatisieren.

translations/de/LC_MESSAGES/messages.po
# 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"
PO-Dateien enthalten Kontextkommentare (Zeilen, die mit #: beginnen), die die Verwendung jeder Zeichenfolge in Ihrem Quellcode zeigen. Übersetzungsfachkräfte nutzen sie zum Verständnis des Kontexts. Behalten Sie sie bei – pybabel extract erzeugt sie automatisch.
6

Übersetzungen kompilieren

Kompilieren Sie Ihre .po-Dateien mit pybabel compile in binäre .mo-Dateien. Flask-Babel liest zur Laufzeit .mo-Dateien und kann .po-Dateien nicht direkt lesen. Nach jeder Übersetzungsaktualisierung müssen Sie erneut kompilieren.

Terminal
# 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.
Wenn Übersetzungen nach der Bearbeitung einer .po-Datei nicht erscheinen, haben Sie fast sicher pybabel compile nicht ausgeführt. Dies ist das häufigste Flask-Babel-Problem. Fügen Sie den Kompilierungsschritt Ihrem Bereitstellungsskript hinzu, um es in der Produktion zu vermeiden.
7

Pluralformen und Variablen verarbeiten

Verwenden Sie ngettext() für pluralabhängige Zeichenfolgen. Die Funktion erhält eine Singularform, eine Pluralform und die Anzahl. Babel verwendet automatisch die richtige Pluralregel jeder Sprache – Englisch hat zwei Formen, Russisch drei, Arabisch sechs und Japanisch eine.

app.py
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)
Plural forms in .po files
# 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 товаров в вашей корзине"
Verwenden Sie für die Plurallogik niemals if count == 1. Sprachen wie Französisch behandeln 0 als Singular. Russisch und Arabisch besitzen Formen, die es im Englischen nicht gibt. Überlassen Sie die Auswahl ngettext() und den CLDR-Pluralregeln von Babel.
8

Locale-Wechsel hinzufügen

Erstellen Sie eine Sprachauswahl, die die Auswahl in der Flask-Sitzung speichert. Aktualisieren Sie Ihre Funktion locale_selector so, dass sie zuerst die Sitzung prüft und anschließend auf die Browsererkennung zurückfällt.

app.py
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']
    )
templates/components/language_switcher.html
{# 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>
Rufen Sie nach einer Änderung der Sitzungs-Locale flask_babel.refresh() auf, damit Flask-Babel die Locale für die aktuelle Anfrage erneut liest. Ohne refresh() bleibt die alte Locale bis zur nächsten Anfrage bestehen.
9

Übersetzungen automatisieren

Wenn Ihre Flask-Babel-Einrichtung abgeschlossen ist, übersetzen Sie Ihre PO-Dateien mit KI. Automatisieren Sie den Zyklus aus Extrahieren, Übersetzen und Kompilieren in Ihrer CI/CD-Pipeline, damit Übersetzungen mit Ihrem Quellcode synchron bleiben.

Terminal
# 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 translations
Übersetzen Sie schrittweise: Wenn Sie neue Zeichenfolgen hinzufügen und pybabel update ausführen, übersetzen Sie nur neue, noch nicht übersetzte Einträge mit leeren msgstr-Werten, statt alles neu zu erzeugen. So bleiben von Menschen geprüfte Übersetzungen erhalten.

Bonus: intelligenter Locale-Fallback mit flask-babel-locale-chain

Standardmäßig wechselt Flask-Babel direkt zur Standard-Locale, wenn die bevorzugte Locale nicht verfügbar ist. Eine Person mit pt-BR sieht bei ausschließlich vorhandenen pt-PT-Übersetzungen Englisch statt Portugiesisch. flask-babel-locale-chain ergänzt konfigurierbare Fallback-Ketten, damit verwandte Locales natürlich aufeinander zurückfallen.

app.py
# 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)
flask-babel-locale-chain ist ein quelloffenes Python-Paket. Sie finden es auf GitHub unter github.com/i18n-agent/flask-babel-locale-chain.

Übersetzungsqualität automatisieren

Erkennen Sie mit i18n-validate fehlende Schlüssel und beschädigte Platzhalter vor der Veröffentlichung. Testen Sie Ihre Benutzeroberfläche mit i18n-pseudo und Pseudoübersetzungen, bevor echte Übersetzungen vorliegen.

Häufige Fallstricke

Kompilierung von .po zu .mo vergessen

Flask-Babel liest kompilierte .mo-Dateien, nicht .po-Dateien. Wenn Übersetzungen nach der Bearbeitung von .po-Dateien nicht erscheinen, führen Sie pybabel compile -d translations aus. Fügen Sie diesen Schritt Ihrem Bereitstellungsskript hinzu.

gettext() auf Modulebene verwenden

gettext() benötigt einen Anfragekontext. Verwenden Sie lazy_gettext(), wenn Sie übersetzte Zeichenfolgen auf Modulebene definieren, etwa Klassenattribute oder Konstanten. Die Funktion verzögert die Übersetzung, bis die Zeichenfolge tatsächlich innerhalb einer Anfrage gerendert wird.

Extraktion übersieht Zeichenfolgen

pybabel extract durchsucht nur Dateien, die den Mustern in babel.cfg entsprechen. Wenn Zeichenfolgen nicht extrahiert werden: Prüfen Sie, ob die babel.cfg-Muster zu Ihrer Dateistruktur passen, ergänzen Sie -k lazy_gettext für verzögerte Aufrufe und stellen Sie sicher, dass Jinja2-Vorlagen die richtige Erweiterung verwenden (.html, .jinja2).

Kodierungsfehler in PO-Dateien

PO-Dateien müssen UTF-8-kodiert sein. Wenn UnicodeDecodeError erscheint, prüfen Sie den Content-Type-Header Ihrer .po-Datei: Er sollte charset=UTF-8 enthalten. Einige Editoren speichern mit anderen Kodierungen – prüfen Sie dies stets nach der Bearbeitung.

Empfohlene Dateistruktur

Project Structure
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/

i18n Agent jetzt testen

Legen Sie Ihre Übersetzungsdatei hier ab

JSON, YAML, PO, XML, CSV, Markdown, Properties

oder zum Auswählen klicken

Zielsprachen

Keine Registrierung erforderlichSofortiges Angebot

Häufig gestellte Fragen zu Flask-i18n