Skip to main content

Flask i18n: Bumuo ng Multi-Language App gamit ang Flask-Babel

Mula sa mga batayan ng gettext hanggang production deployment: i-internationalize ang inyong Flask app gamit ang Flask-Babel, PO files, at automated AI translation.

1

I-install ang Flask-Babel

Ang Flask-Babel ang standard na internationalization extension para sa Flask. Ini-integrate nito ang GNU gettext sa Flask at mga template ng Jinja2, at nagbibigay ng translation functions, pagpili ng locale, at timezone support out of the box.

Binalot ng Flask-Babel ang Babel (ang Python i18n library) at ini-integrate ito sa request lifecycle ng Flask. Nagbibigay ito sa inyo ng gettext(), ngettext(), at lazy_gettext() functions, pati awtomatikong locale detection mula sa mga browser header.
Terminal
pip install Flask-Babel
2

I-configure ang Babel

Gumawa ng babel.cfg file para sabihan ang pybabel kung saan mag-scan ng mga translatable string, pagkatapos ay i-initialize ang Flask-Babel gamit ang isang locale selector function na tumutukoy kung aling wika ang ihahain sa bawat request.

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)
Tinatawag ang locale_selector function sa bawat request. Kapag nagbalik ito ng locale na walang compiled .mo file, tahimik na magfa-fallback ang Flask-Babel sa default locale. Walang error na itinatapon — lalabas lang na hindi isinalin ang mga string.
3

Markahan ang Mga String para sa Pagsasalin

Balutin ang bawat user-facing string sa gettext() sa Python code at _() sa mga template ng Jinja2. Gamitin ang lazy_gettext() para sa mga string na dinefine sa module load time (tulad ng mga label ng form at configuration) na kailangang maisalin sa request time.

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>
Gumamit ng _() sa mga template ng Jinja2 sa halip na gettext() — ito ang standard na gettext shorthand at pinananatiling malinis ang inyong mga template. Awtomatikong nire-register ng Flask-Babel ang _() bilang Jinja2 global.
4

I-extract ang Mga Mensahe

Patakbuhin ang pybabel extract para i-scan ang inyong source code at mga template para sa mga translatable string. Gumagawa ito ng .pot (Portable Object Template) file. Pagkatapos, i-initialize ang mga catalog para sa bawat target language, o i-update ang mga umiiral kapag nagbago ang mga source string.

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
Laging patakbuhin ang pybabel update (hindi init) pagkatapos ng unang extraction. Ang pagtakbo ng init sa umiiral na language directory ay nag-o-overwrite ng lahat ng umiiral na pagsasalin. Mina-merge ng update command ang mga bagong string habang pinapanatili ang mga umiiral na pagsasalin.
5

Isalin ang Mga PO File

Buksan ang mga nagawang .po file at punan ang mga value ng msgstr para sa bawat msgid. Plain text ang mga PO file — maaari ninyo itong i-edit nang direkta, gumamit ng PO editor tulad ng Poedit, o i-automate ang pagsasalin gamit ang mga AI tool.

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"
Kasama sa mga PO file ang mga comment sa konteksto (mga linyang nagsisimula sa #:) na nagpapakita kung saan ginagamit ang bawat string sa inyong source code. Ginagamit ito ng mga tagasalin para maunawaan ang konteksto. Panatilihin ang mga ito — awtomatikong ginagawa ng pybabel extract.
6

I-compile ang Mga Pagsasalin

I-compile ang inyong mga .po file sa binary .mo file gamit ang pybabel compile. Binabasa ng Flask-Babel ang mga .mo file sa runtime — hindi nito direktang mababasa ang mga .po file. Kailangan ninyong mag-recompile pagkatapos ng bawat pag-update ng pagsasalin.

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.
Kapag hindi lumalabas ang mga pagsasalin pagkatapos i-edit ang isang .po file, halos tiyak na nakalimutan ninyong patakbuhin ang pybabel compile. Ito ang pinakakaraniwang isyu sa Flask-Babel. Idagdag ang compile step sa inyong deployment script upang maiwasan ito sa production.
7

Hawakan ang Mga Plural at Variable

Gumamit ng ngettext() para sa mga string na sensitibo sa plural. Tumatanggap ito ng singular form, plural form, at count. Awtomatikong ginagamit ng Babel ang tamang plural rule para sa bawat wika — may 2 anyo ang English, 3 ang Russian, 6 ang Arabic, at 1 ang Japanese.

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 товаров в вашей корзине"
Huwag kailanman gumamit ng if count == 1 para sa plural logic. Sa ilang wika tulad ng French, itinuturing na singular ang 0. May mga anyo ang Russian at Arabic na wala sa English. Hayaan ang ngettext() at ang CLDR plural rules ng Babel ang humawak sa pagpili.
8

Magdagdag ng Pagpapalit ng Locale

Bumuo ng language selector na nag-iimbak ng pagpili ng user sa Flask session. I-update ang inyong locale_selector function para suriin muna ang session, saka mag-fallback sa browser detection.

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>
Pagkatapos baguhin ang session locale, tawagin ang flask_babel.refresh() para pilitin ang Flask-Babel na muling basahin ang locale para sa kasalukuyang request. Kapag walang refresh(), mananatili ang lumang locale hanggang sa susunod na request.
9

I-automate ang Mga Pagsasalin

Kapag kumpleto na ang inyong Flask-Babel setup, isalin ang inyong mga PO file gamit ang AI. I-automate ang extract-translate-compile cycle sa inyong CI/CD pipeline upang panatilihing naka-sync ang mga pagsasalin sa inyong source code.

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
Isalin nang paunti-unti — kapag nagdagdag kayo ng mga bagong string at nagpatakbo ng pybabel update, isalin lang ang mga bagong untranslated entry (mga empty msgstr value) sa halip na i-regenerate ang lahat. Pinapanatili nito ang mga pagsasaling na-review na ng tao.

Bonus: Smart Locale Fallback gamit ang flask-babel-locale-chain

Bilang default, diretsong nagfa-fallback ang Flask-Babel sa default locale kapag hindi available ang preferred locale ng user. Ang pt-BR user na mayroon lang pt-PT translations ay makakakita ng English sa halip na Portuguese. Nagdaragdag ang flask-babel-locale-chain ng mga nako-configure na fallback chain para natural na mag-cascade ang mga magkakaugnay na locale.

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)
Ang flask-babel-locale-chain ay isang open-source na Python package. Tingnan ito sa GitHub sa github.com/i18n-agent/flask-babel-locale-chain.

I-automate ang Kalidad ng Pagsasalin

Matukoy ang mga nawawalang key at sirang placeholder bago maipadala sa release gamit ang i18n-validate. Subukan ang inyong UI gamit ang pseudo-translations sa pamamagitan ng i18n-pseudo bago pa dumating ang mga tunay na pagsasalin.

Mga Karaniwang Pagkakamali

Nakalimutang I-compile ang .po sa .mo

Binabasa ng Flask-Babel ang mga compiled .mo file, hindi ang mga .po file. Kapag hindi lumalabas ang mga pagsasalin pagkatapos i-edit ang mga .po file, patakbuhin ang pybabel compile -d translations. Idagdag ang hakbang na ito sa inyong deployment script.

Paggamit ng gettext() sa Module Level

Nangangailangan ng request context ang gettext(). Kung dinefine ninyo ang mga isinaling string sa module level (class attributes, constants), gumamit ng lazy_gettext() sa halip. Ipinagpapaliban nito ang pagsasalin hanggang sa aktuwal na i-render ang string sa isang request.

Hindi Nai-e-extract ang Mga String

Nagsa-scan lamang ang pybabel extract ng mga file na tumutugma sa mga pattern sa babel.cfg. Kung hindi nae-extract ang mga string: tiyaking tumutugma ang mga pattern sa inyong babel.cfg sa inyong file structure, idagdag ang -k lazy_gettext para ma-extract ang mga lazy call, at tiyaking gumagamit ang mga Jinja2 template ng tamang extension (.html, .jinja2).

Mga Error sa Encoding ng PO File

Dapat naka-encode sa UTF-8 ang mga PO file. Kung makakita kayo ng UnicodeDecodeError, suriin ang header na Content-Type sa inyong .po file: dapat nakalagay ang charset=UTF-8. May ilang editor na nagsa-save gamit ang ibang encoding — laging i-verify pagkatapos mag-edit.

Inirerekomendang File Structure

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/

Subukan ang i18n Agent Ngayon

I-drop dito ang inyong translation file

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

o i-click para mag-browse

Mga target language

Hindi kailangan ang signupInstant na estimate

FAQ sa Flask i18n