Localization with Flask-Babel
Localization with Flask-Babel
Flask-Babel makes it straightforward to translate your app into multiple languages using the industry-standard GNU gettext system.
Installation
pip install Flask-Babel
Add to requirements.txt:
Flask-Babel==4.0.0
Setup
# app.py
from flask import Flask, request, session
from flask_babel import Babel
app = Flask(__name__)
app.config["BABEL_DEFAULT_LOCALE"] = "en"
app.config["BABEL_SUPPORTED_LOCALES"] = ["en", "fr", "es"]
def get_locale():
"""Determine locale from session, then browser preference."""
if "lang" in session:
return session["lang"]
# Auto-detect from browser Accept-Language header
return request.accept_languages.best_match(["en", "fr", "es"])
babel = Babel(app, locale_selector=get_locale)
Marking Strings for Translation
In Python files, wrap strings with _():
from flask_babel import _
@app.route("/")
def home():
greeting = _("Welcome to Flask Vibe")
return render_template("home.html", greeting=greeting)
In Jinja2 templates, use {{ _("...") }}:
<!-- templates/home.html -->
<h1>{{ _("Welcome to Flask Vibe") }}</h1>
<p>{{ _("Build apps you can understand.") }}</p>
<button>{{ _("Get Started") }}</button>
For strings with variables:
# Python
message = _("Hello, %(name)s!", name=user["name"])
<!-- Template -->
<p>{{ _("%(count)s tutorials available", count=tutorial_count) }}</p>
Configuration File
Create babel.cfg in your project root:
[python: **.py]
encoding = utf-8
[jinja2: **/templates/**.html]
encoding = utf-8
Extracting Strings
Run this to scan your code and templates for translatable strings:
pybabel extract -F babel.cfg -o messages.pot .
This creates messages.pot — a template with all strings found.
Creating Translation Files
For each language you support:
# First time — creates the directory structure
pybabel init -i messages.pot -d translations -l fr
pybabel init -i messages.pot -d translations -l es
This creates:
translations/
├── fr/
│ └── LC_MESSAGES/
│ └── messages.po
└── es/
└── LC_MESSAGES/
└── messages.po
Editing Translations
Open translations/fr/LC_MESSAGES/messages.po and fill in translations:
#: templates/home.html:3
msgid "Welcome to Flask Vibe"
msgstr "Bienvenue sur Flask Vibe"
#: templates/home.html:4
msgid "Build apps you can understand."
msgstr "Construisez des applications que vous comprenez."
#: templates/home.html:5
msgid "Get Started"
msgstr "Commencer"
Compiling Translations
After editing .po files, compile them to binary .mo files:
pybabel compile -d translations
Run this every time you update translations.
Workflow for Adding New Strings
When you add new translatable strings to your code:
# 1. Re-extract (updates messages.pot)
pybabel extract -F babel.cfg -o messages.pot .
# 2. Update existing .po files with new strings
pybabel update -i messages.pot -d translations
# 3. Edit the .po files to add translations
# 4. Recompile
pybabel compile -d translations
Language Switcher Route
@app.route("/set-language/<lang>")
def set_language(lang):
supported = ["en", "fr", "es"]
if lang in supported:
session["lang"] = lang
return redirect(request.referrer or "/")
<!-- In your navbar -->
<div class="language-switcher">
<a href="/set-language/en">EN</a>
<a href="/set-language/fr">FR</a>
<a href="/set-language/es">ES</a>
</div>
Formatting Dates and Numbers
Flask-Babel also localizes dates and numbers:
from flask_babel import format_date, format_number, format_currency
# In a route
formatted_date = format_date(post["created_at"], format="long")
# English: "January 15, 2026"
# French: "15 janvier 2026"
<!-- In templates -->
{{ post.created_at | datetimeformat }}
Project Structure
myapp/
├── babel.cfg
├── messages.pot # Auto-generated, commit to git
└── translations/
├── fr/
│ └── LC_MESSAGES/
│ ├── messages.po # Commit to git
│ └── messages.mo # Auto-generated, add to .gitignore
└── es/
└── LC_MESSAGES/
├── messages.po
└── messages.mo
Was this helpful?
Get More Flask Vibe Tutorials
Join 1,000+ developers getting weekly Flask tips and AI-friendly code patterns.
No spam. Unsubscribe anytime.