
Flask i18n: zbuduj wielojęzyczną aplikację z Flask-Babel
Od podstaw gettext po wdrożenie produkcyjne: internacjonalizuj aplikację Flask za pomocą Flask-Babel, plików PO i automatycznych tłumaczeń AI.
Zainstaluj Flask-Babel
Flask-Babel to standardowe rozszerzenie internacjonalizacyjne dla Flaska. Integruje GNU gettext z Flaskiem i szablonami Jinja2, zapewniając gotowe funkcje tłumaczeniowe, wybór języka oraz obsługę stref czasowych.
pip install Flask-BabelSkonfiguruj Babel
Utwórz plik babel.cfg, aby wskazać narzędziu pybabel miejsca skanowania tekstów przeznaczonych do tłumaczenia, a następnie zainicjalizuj Flask-Babel z funkcją wybierającą język dla każdego żądania.
# 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)Oznacz teksty do tłumaczenia
Otocz każdy tekst widoczny dla użytkownika funkcją gettext() w kodzie Python oraz _() w szablonach Jinja2. Używaj lazy_gettext() dla tekstów zdefiniowanych podczas wczytywania modułu (takich jak etykiety formularzy i konfiguracja), które mają być przetłumaczone później podczas żądania.
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>Wyodrębnij wiadomości
Uruchom pybabel extract, aby przeskanować kod źródłowy i szablony w poszukiwaniu tekstów do tłumaczenia. Powstanie plik .pot (Portable Object Template). Następnie zainicjalizuj katalogi dla każdego języka docelowego albo zaktualizuj istniejące po zmianie tekstów źródłowych.
# 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 translationsPrzetłumacz pliki PO
Otwórz wygenerowane pliki .po i uzupełnij wartości msgstr dla każdego msgid. Pliki PO to zwykły tekst — możesz edytować je bezpośrednio, użyć edytora PO takiego jak Poedit albo zautomatyzować tłumaczenie za pomocą narzędzi AI.
# 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"Skompiluj tłumaczenia
Skompiluj pliki .po do binarnych plików .mo za pomocą pybabel compile. Flask-Babel podczas działania odczytuje pliki .mo — nie potrafi bezpośrednio odczytać plików .po. Po każdej aktualizacji tłumaczeń trzeba przeprowadzić ponowną kompilację.
# 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.Obsłuż liczbę mnogą i zmienne
Używaj ngettext() dla tekstów zależnych od liczby. Funkcja przyjmuje formę pojedynczą, formę mnogą oraz liczbę. Babel automatycznie stosuje właściwą regułę dla każdego języka — angielski ma 2 formy, rosyjski 3, arabski 6, a japoński 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 товаров в вашей корзине"Dodaj zmianę języka
Utwórz selektor języka, który zapisuje wybór użytkownika w sesji Flask. Zaktualizuj funkcję locale_selector tak, aby najpierw sprawdzała sesję, a następnie korzystała z wykrywania przez przeglądarkę.
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>Zautomatyzuj tłumaczenia
Po skonfigurowaniu Flask-Babel tłumacz pliki PO za pomocą AI. Zautomatyzuj cykl wyodrębniania, tłumaczenia i kompilowania w pipeline CI/CD, aby tłumaczenia pozostawały zsynchronizowane z kodem źródłowym.
# 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: inteligentny język rezerwowy z flask-babel-locale-chain
Domyślnie Flask-Babel przechodzi bezpośrednio do języka domyślnego, gdy preferowany język użytkownika jest niedostępny. Użytkownik pt-BR mający do dyspozycji tylko tłumaczenia pt-PT zobaczy angielski zamiast portugalskiego. flask-babel-locale-chain dodaje konfigurowalne łańcuchy rezerwowe, które sprawdzają powiązane języki w naturalnej kolejności.
# 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)Zautomatyzuj kontrolę jakości tłumaczeń
Typowe pułapki
Pominięcie kompilacji .po do .mo
Używanie gettext() na poziomie modułu
Wyodrębnianie pomija teksty
Błędy kodowania pliku PO
Zalecana struktura plików
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/Przetłumacz również:
Wypróbuj i18n Agent
Upuść tutaj plik tłumaczenia
JSON, YAML, PO, XML, CSV, Markdown, Properties
lub kliknij, aby go wybrać
Języki docelowe