Skip to main content

Flask i18n: izveidojiet daudzvalodu lietotni ar Flask-Babel

No gettext pamatiem līdz izvietošanai produkcijas vidē: internacionalizējiet Flask lietotni ar Flask-Babel, PO failiem un automatizētu MI tulkošanu.

1

Instalēt Flask-Babel

Flask-Babel ir standarta Flask internacionalizācijas paplašinājums. Tas integrē GNU gettext ar Flask un Jinja2 veidnēm, uzreiz nodrošinot tulkošanas funkcijas, lokalizācijas izvēli un laika joslu atbalstu.

Flask-Babel aptver Babel (Python i18n bibliotēku) un integrē to Flask pieprasījuma dzīves ciklā. Tas nodrošina gettext(), ngettext() un lazy_gettext() funkcijas, kā arī automātisku lokalizācijas noteikšanu no pārlūka galvenēm.
Terminal
pip install Flask-Babel
2

Konfigurēt Babel

Izveidojiet babel.cfg failu, kas norāda pybabel, kur meklēt tulkojamas virknes, pēc tam inicializējiet Flask-Babel ar lokalizācijas izvēles funkciju, kas nosaka katram pieprasījumam apkalpojamo valodu.

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 tiek izsaukta katram pieprasījumam. Ja tā atgriež lokalizāciju, kurai nav kompilēta .mo faila, Flask-Babel klusām atkāpjas uz noklusējuma lokalizāciju. Kļūda netiek izmesta — virknes vienkārši parādās netulkotas.
3

Atzīmēt tulkojamas virknes

Katru lietotājiem paredzēto virkni Python kodā ietveriet ar gettext(), bet Jinja2 veidnēs — ar _(). Moduļa ielādes laikā definētām virknēm (piemēram, veidlapu etiķetēm un konfigurācijai), kas vēlāk jātulko pieprasījuma laikā, izmantojiet lazy_gettext().

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>
Jinja2 veidnēs izmantojiet _(), nevis gettext(): tas ir standarta gettext saīsinājums, kas uztur veidnes pārskatāmas. Flask-Babel automātiski reģistrē _() kā globālu Jinja2 funkciju.
4

Izvilkt ziņojumus

Palaidiet pybabel extract, lai skenētu pirmkodu un veidnes, meklējot tulkojamas virknes. Tas izveido .pot (Portable Object Template) failu. Tad inicializējiet katalogus katrai mērķa valodai vai atjauniniet esošos, kad mainās avota virknes.

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
Pēc pirmās izvilkšanas vienmēr palaidiet pybabel update, nevis init. Palaižot init esošā valodas direktorijā, tiek pārrakstīti visi esošie tulkojumi. Komanda update sapludina jaunas virknes, saglabājot esošos tulkojumus.
5

Tulkot PO failus

Atveriet ģenerētos .po failus un aizpildiet msgstr vērtības katram msgid. PO faili ir vienkāršs teksts: varat tos rediģēt tieši, izmantot tādu PO redaktoru kā Poedit vai automatizēt tulkošanu ar MI rīkiem.

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 failos ir konteksta komentāri (rindas, kas sākas ar #:), kas norāda, kur katra virkne tiek izmantota pirmkodā. Tulkotāji tos izmanto konteksta izpratnei. Saglabājiet tos — tos automātiski ģenerē pybabel extract.
6

Kompilēt tulkojumus

Kompilējiet .po failus bināros .mo failos ar pybabel compile. Izpildlaikā Flask-Babel nolasa .mo failus — tas nevar tieši nolasīt .po failus. Pēc katra tulkojumu atjauninājuma tie jākompilē no jauna.

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.
Ja pēc .po faila rediģēšanas tulkojumi neparādās, gandrīz noteikti aizmirsāt palaist pybabel compile. Tā ir visbiežākā Flask-Babel problēma. Pievienojiet kompilēšanas soli izvietošanas skriptam, lai no tās izvairītos produkcijas vidē.
7

Apstrādāt daudzskaitli un mainīgos

Daudzskaitlim jutīgām virknēm izmantojiet ngettext(). Tā pieņem vienskaitļa formu, daudzskaitļa formu un skaitu. Babel automātiski izmanto pareizo daudzskaitļa kārtulu katrai valodai: angļu valodā ir 2 formas, krievu — 3, arābu — 6, japāņu — 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 товаров в вашей корзине"
Daudzskaitļa loģikai nekad neizmantojiet if count == 1. Tādas valodas kā franču uzskata 0 par vienskaitli. Krievu un arābu valodā ir formas, kuru angļu valodā nav. Ļaujiet izvēli veikt ngettext() un Babel CLDR daudzskaitļa kārtulām.
8

Pievienot lokalizācijas pārslēgšanu

Izveidojiet valodas atlasītāju, kas glabā lietotāja izvēli Flask sesijā. Atjauniniet funkciju locale_selector, lai tā vispirms pārbaudītu sesiju un pēc tam atkāptos uz pārlūka noteikšanu.

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>
Pēc sesijas lokalizācijas maiņas izsauciet flask_babel.refresh(), lai piespiestu Flask-Babel atkārtoti nolasīt pašreizējā pieprasījuma lokalizāciju. Bez refresh() vecā lokalizācija saglabājas līdz nākamajam pieprasījumam.
9

Automatizēt tulkošanu

Kad Flask-Babel iestatīšana ir pabeigta, tulkojiet PO failus ar MI. Automatizējiet izvilkšanas, tulkošanas un kompilēšanas ciklu CI/CD konveijerā, lai tulkojumi paliktu sinhronizēti ar pirmkodu.

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
Tulkojiet pakāpeniski — pievienojot jaunas virknes un palaižot pybabel update, tulkojiet tikai jaunos netulkotos ierakstus (tukšas msgstr vērtības), nevis ģenerējiet visu no jauna. Tas saglabā cilvēku pārskatītos tulkojumus.

Papildus: vieda lokalizācijas atkāpšanās ar flask-babel-locale-chain

Pēc noklusējuma Flask-Babel uzreiz atkāpjas uz noklusējuma lokalizāciju, ja lietotāja vēlamā lokalizācija nav pieejama. pt-BR lietotājs, kam ir tikai pt-PT tulkojumi, redz angļu, nevis portugāļu valodu. flask-babel-locale-chain pievieno konfigurējamas atkāpšanās ķēdes, lai saistītās lokalizācijas dabiski pārietu cita citā.

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 ir atvērtā pirmkoda Python pakotne. Skatiet to GitHub adresē github.com/i18n-agent/flask-babel-locale-chain.

Automatizēt tulkojumu kvalitāti

Ar i18n-validate pirms izlaišanas atrodiet trūkstošās atslēgas un bojātos vietturus. Kamēr īstie tulkojumi vēl nav gatavi, pārbaudiet UI ar i18n-pseudo pseidotulkojumiem.

Biežākās kļūdas

Aizmirsts kompilēt .po par .mo

Flask-Babel nolasa kompilētus .mo failus, nevis .po failus. Ja pēc .po failu rediģēšanas tulkojumi neparādās, palaidiet pybabel compile -d translations. Pievienojiet šo soli izvietošanas skriptam.

gettext() lietošana moduļa līmenī

gettext() vajadzīgs pieprasījuma konteksts. Ja tulkotas virknes definējat moduļa līmenī (klases atribūtus, konstantes), tā vietā izmantojiet lazy_gettext(). Tas atliek tulkošanu, līdz virkne faktiski tiek atveidota pieprasījumā.

Izvilkšana izlaiž virknes

pybabel extract skenē tikai failus, kas atbilst babel.cfg modeļiem. Ja virknes netiek izvilktas, pārbaudiet, vai babel.cfg modeļi atbilst failu struktūrai, pievienojiet -k lazy_gettext slinko izsaukumu izvilkšanai un pārliecinieties, ka Jinja2 veidnes izmanto pareizo paplašinājumu (.html, .jinja2).

PO faila kodējuma kļūdas

PO failiem jābūt kodētiem UTF-8. Ja redzat UnicodeDecodeError, pārbaudiet .po faila galveni Content-Type: tajā jābūt charset=UTF-8. Daži redaktori saglabā citā kodējumā, tādēļ pēc rediģēšanas vienmēr pārbaudiet.

Ieteicamā failu struktūra

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/

Izmēģiniet i18n Agent tūlīt

Nometiet tulkošanas failu šeit

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

vai noklikšķiniet, lai izvēlētos

Mērķa valodas

Reģistrācija nav vajadzīgaTūlītēja tāme

Bieži uzdotie jautājumi par Flask i18n