Skip to main content

Flask i18n: izradite višejezičnu aplikaciju uz Flask-Babel

Od osnova sustava gettext do objave u produkciji: internacionalizirajte svoju Flask aplikaciju uz Flask-Babel, PO datoteke i automatizirano prevođenje s pomoću AI-ja.

1

Instalirajte Flask-Babel

Flask-Babel standardno je proširenje za internacionalizaciju Flaska. Integrira GNU gettext s predlošcima Flask i Jinja2 te odmah nudi funkcije za prevođenje, odabir lokalne postavke i podršku za vremenske zone.

Flask-Babel obavija Babel (Pythonovu biblioteku i18n) i integrira ga sa životnim ciklusom Flaskova zahtjeva. Nudi funkcije gettext(), ngettext() i lazy_gettext() te automatsko prepoznavanje lokalne postavke iz zaglavlja preglednika.
Terminal
pip install Flask-Babel
2

Konfigurirajte Babel

Izradite datoteku babel.cfg kako biste alatu pybabel zadali gdje tražiti prevodive tekstove, a zatim inicijalizirajte Flask-Babel funkcijom za odabir lokalne postavke koja određuje jezik svakog zahtjeva.

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)
Funkcija locale_selector poziva se za svaki zahtjev. Ako vrati lokalnu postavku bez prevedene .mo datoteke, Flask-Babel tiho se vraća na zadanu postavku. Pogreška se ne izbacuje — tekstovi samo ostaju neprevedeni.
3

Označite tekstove za prijevod

Svaki tekst namijenjen korisniku obuhvatite funkcijom gettext() u Python kôdu i _() u Jinja2 predlošcima. lazy_gettext() upotrebljavajte za tekstove definirane pri učitavanju modula (poput oznaka obrazaca i konfiguracije) koji se trebaju prevesti kasnije, pri obradi zahtjeva.

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>
U Jinja2 predlošcima upotrebljavajte _() umjesto gettext() — to je standardna kratica sustava gettext koja predloške održava preglednima. Flask-Babel automatski registrira _() kao globalnu funkciju Jinja2.
4

Izdvojte poruke

Pokrenite pybabel extract kako biste pregledali izvorni kôd i predloške u potrazi za prevodivim tekstovima. Time nastaje .pot datoteka (Portable Object Template). Zatim inicijalizirajte kataloge za svaki ciljni jezik ili ažurirajte postojeće kada se izvorni tekstovi promijene.

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
Nakon prvog izdvajanja uvijek pokrenite pybabel update, a ne init. Pokretanje naredbe init nad postojećom mapom jezika prepisuje sve postojeće prijevode. Naredba update spaja nove tekstove i čuva postojeće prijevode.
5

Prevedite PO datoteke

Otvorite generirane .po datoteke i ispunite vrijednost msgstr za svaki msgid. PO datoteke običan su tekst — možete ih izravno uređivati, upotrijebiti uređivač PO datoteka poput Poedita ili automatizirati prevođenje AI alatima.

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 datoteke sadržavaju kontekstne komentare (retke koji počinju s #:) i pokazuju gdje se pojedini tekst upotrebljava u izvornom kôdu. Prevoditelji ih upotrebljavaju za razumijevanje konteksta. Sačuvajte ih — pybabel extract generira ih automatski.
6

Prevedite .po datoteke u .mo

Naredbom pybabel compile prevedite .po datoteke u binarne .mo datoteke. Flask-Babel tijekom izvođenja čita .mo datoteke i ne može izravno čitati .po datoteke. Nakon svakog ažuriranja prijevoda morate ih ponovno prevesti.

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.
Ako se prijevodi ne pojave nakon uređivanja .po datoteke, gotovo sigurno niste pokrenuli pybabel compile. To je najčešći problem s Flask-Babelom. Dodajte korak prevođenja u skriptu za objavu kako biste ga izbjegli u produkciji.
7

Obradite množinu i varijable

Upotrebljavajte ngettext() za tekstove ovisne o množini. Funkcija prima oblik jednine, oblik množine i broj. Babel automatski primjenjuje odgovarajuće pravilo množine za svaki jezik — engleski ima 2 oblika, ruski 3, arapski 6, a japanski 1.

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 товаров в вашей корзине"
Nikada nemojte upotrebljavati if count == 1 za logiku množine. Jezici poput francuskog broj 0 smatraju jedninom, a ruski i arapski imaju oblike kojih u engleskom nema. Odabir prepustite funkciji ngettext() i pravilima množine CLDR biblioteke Babel.
8

Dodajte promjenu lokalne postavke

Izradite birač jezika koji korisnikov odabir sprema u Flaskovu sesiju. Ažurirajte funkciju locale_selector tako da prvo provjeri sesiju, a zatim se vrati na prepoznavanje iz preglednika.

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>
Nakon promjene lokalne postavke u sesiji pozovite flask_babel.refresh() kako bi Flask-Babel ponovno pročitao postavku trenutačnog zahtjeva. Bez refresh() stara postavka ostaje do sljedećeg zahtjeva.
9

Automatizirajte prevođenje

Nakon dovršetka postavljanja Flask-Babela prevedite PO datoteke s pomoću AI-ja. Automatizirajte ciklus izdvajanja, prevođenja i izrade .mo datoteka u CI/CD pipelineu kako bi prijevodi ostali usklađeni s izvornim kôdom.

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
Prevodite postupno — kada dodate nove tekstove i pokrenete pybabel update, prevedite samo nove neprevedene stavke (prazne vrijednosti msgstr) umjesto ponovnog generiranja svega. Tako ćete sačuvati prijevode koje su pregledali ljudi.

Dodatno: pametne zamjenske lokalne postavke uz flask-babel-locale-chain

Flask-Babel prema zadanim postavkama odmah prelazi na zadanu lokalnu postavku kada korisnikova željena postavka nije dostupna. Korisnik s postavkom pt-BR i samo prijevodima za pt-PT vidi engleski umjesto portugalskog. flask-babel-locale-chain dodaje podesive zamjenske lance kako bi se srodne postavke prirodno nadovezivale.

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 Pythonov je paket otvorenog kôda. Pogledajte ga na GitHubu na adresi github.com/i18n-agent/flask-babel-locale-chain.

Automatizirajte provjeru kvalitete prijevoda

Alatom i18n-validate otkrijte ključeve koji nedostaju i neispravna rezervirana mjesta prije objave. Korisničko sučelje testirajte pseudoprijevodima uz i18n-pseudo prije nego što stignu stvarni prijevodi.

Uobičajene zamke

.po datoteke nisu prevedene u .mo

Flask-Babel čita prevedene .mo datoteke, a ne .po datoteke. Ako se prijevodi ne pojave nakon uređivanja .po datoteka, pokrenite pybabel compile -d translations. Dodajte taj korak u skriptu za objavu.

Upotreba gettext() na razini modula

gettext() zahtijeva kontekst zahtjeva. Ako prevedene tekstove definirate na razini modula (atribute klasa, konstante), upotrijebite lazy_gettext(). On odgađa prevođenje dok se tekst stvarno ne iscrta unutar zahtjeva.

Izdvajanje propušta tekstove

pybabel extract pregledava samo datoteke koje odgovaraju obrascima u babel.cfg. Ako tekstovi nisu izdvojeni, provjerite odgovaraju li obrasci iz babel.cfg strukturi datoteka, dodajte -k lazy_gettext za izdvajanje odgođenih poziva i provjerite upotrebljavaju li Jinja2 predlošci ispravan nastavak (.html, .jinja2).

Pogreške kodiranja PO datoteke

PO datoteke moraju biti kodirane kao UTF-8. Ako vidite UnicodeDecodeError, provjerite zaglavlje Content-Type u .po datoteci: treba sadržavati charset=UTF-8. Neki uređivači spremaju datoteke u drugom kodiranju — uvijek provjerite nakon uređivanja.

Preporučena struktura datoteka

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/

Isprobajte i18n Agent odmah

Povucite datoteku za prijevod ovdje

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

ili kliknite za odabir

Ciljni jezici

Registracija nije potrebnaProcjena odmah

Česta pitanja o Flask i18n-u