
Flask i18n: Buat Aplikasi Multibahasa dengan Flask-Babel
Dari dasar gettext hingga deployment produksi: internasionalisasikan aplikasi Flask dengan Flask-Babel, file PO, dan penerjemahan AI otomatis.
Instal Flask-Babel
Flask-Babel adalah ekstensi internasionalisasi standar untuk Flask. Ekstensi ini mengintegrasikan GNU gettext dengan templat Flask dan Jinja2 serta langsung menyediakan fungsi terjemahan, pemilihan bahasa, dan dukungan zona waktu.
pip install Flask-BabelKonfigurasikan Babel
Buat file babel.cfg untuk memberi tahu pybabel tempat memindai string yang dapat diterjemahkan, lalu inisialisasi Flask-Babel dengan fungsi pemilih bahasa yang menentukan bahasa bagi setiap permintaan.
# 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)Tandai String untuk Diterjemahkan
Bungkus setiap string yang terlihat pengguna dengan gettext() dalam kode Python dan _() dalam templat Jinja2. Gunakan lazy_gettext() untuk string yang ditentukan saat modul dimuat (seperti label formulir dan konfigurasi) dan perlu diterjemahkan kemudian saat permintaan.
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>Ekstrak Pesan
Jalankan pybabel extract untuk memindai kode sumber dan templat bagi string yang dapat diterjemahkan. Ini membuat file .pot (Portable Object Template). Lalu inisialisasi katalog untuk setiap bahasa target atau perbarui katalog yang ada saat string sumber berubah.
# 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 translationsTerjemahkan File-file PO
Buka file .po yang dibuat dan isi nilai msgstr untuk setiap msgid. File PO berupa teks biasa—Anda dapat mengeditnya langsung, menggunakan editor PO seperti Poedit, atau mengotomatiskan penerjemahan dengan alat 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"Kompilasi Terjemahan
Kompilasi file .po menjadi file biner .mo menggunakan pybabel compile. Flask-Babel membaca file .mo saat runtime—tidak dapat membaca file .po secara langsung. Anda harus mengompilasi ulang setelah setiap pembaruan terjemahan.
# 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.Tangani Bentuk Jamak dan Variabel
Gunakan ngettext() untuk string yang peka terhadap bentuk jamak. Fungsi menerima bentuk tunggal, bentuk jamak, dan jumlah. Babel menggunakan aturan bentuk jamak yang benar untuk setiap bahasa secara otomatis—Inggris memiliki 2 bentuk, Rusia 3, Arab 6, dan Jepang 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 товаров в вашей корзине"Tambahkan Pengalihan Bahasa
Buat pemilih bahasa yang menyimpan pilihan pengguna dalam sesi Flask. Perbarui fungsi locale_selector untuk memeriksa sesi terlebih dahulu, lalu beralih ke deteksi browser.
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>Otomatiskan Penerjemahan
Setelah penyiapan Flask-Babel selesai, terjemahkan file PO dengan AI. Otomatiskan siklus ekstrak-terjemahkan-kompilasi dalam pipeline CI/CD agar terjemahan tetap sinkron dengan kode sumber.
# 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: Fallback Bahasa Cerdas dengan flask-babel-locale-chain
Secara default, Flask-Babel langsung beralih ke bahasa default saat bahasa pilihan pengguna tidak tersedia. Pengguna pt-BR yang hanya memiliki terjemahan pt-PT melihat bahasa Inggris, bukan Portugis. flask-babel-locale-chain menambahkan rantai fallback yang dapat dikonfigurasi agar bahasa terkait beralih secara alami.
# 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)Otomatiskan Kualitas Terjemahan
Kesalahan Umum
Lupa Mengompilasi .po menjadi .mo
Menggunakan gettext() di Tingkat Modul
Ekstraksi Melewatkan String
Kesalahan Pengodean File PO
Struktur File yang Disarankan
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/Coba i18n Agent Sekarang
Lepaskan file terjemahan Anda di sini
JSON, YAML, PO, XML, CSV, Markdown, Properties
atau klik untuk menjelajahi
Bahasa target