Skip to main content

Flask i18n: направите вишејезичну апликацију помоћу Flask-Babel

Од основа gettext система до постављања у продукцију: интернационализујте Flask апликацију помоћу Flask-Babel, PO датотека и аутоматизованог AI превођења.

1

Инсталирајте Flask-Babel

Flask-Babel је стандардни додатак за Flask интернационализацију. Интегрише GNU gettext са Flask и Jinja2 шаблонима и одмах пружа функције за превођење, избор локала и подршку за временске зоне.

Flask-Babel обухвата Babel (Python i18n библиотеку) и интегрише га са животним циклусом Flask захтева. Пружа функције gettext(), ngettext() и lazy_gettext(), као и аутоматско откривање локала из заглавља прегледача.
Terminal
pip install Flask-Babel
2

Подесите Babel

Направите датотеку babel.cfg да алатки pybabel наведете где да тражи преводиве текстове, а затим иницијализујте Flask-Babel функцијом за избор локала која одређује језик за сваки захтев.

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)
Функција locale_selector позива се при сваком захтеву. Ако врати локал који нема компајлирану .mo датотеку, Flask-Babel неприметно прелази на подразумевани локал. Не баца се грешка — текстови само изгледају непреведено.
3

Означите текстове за превођење

Обухватите сваки текст намењен кориснику функцијом gettext() у Python коду и _() у Jinja2 шаблонима. Користите 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 шаблонима користите _() уместо gettext() — то је стандардна gettext скраћеница која одржава шаблоне чистим. Flask-Babel аутоматски региструје _() као Jinja2 глобалну функцију.
4

Издвојте поруке

Покрените pybabel extract да скенирате изворни код и шаблоне у потрази за преводивим текстовима. Тиме се прави .pot (Portable Object Template) датотека. Затим иницијализујте каталоге за сваки циљни језик или ажурирајте постојеће када се изворни текстови промене.

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
После првог издвајања увек покрените pybabel update (не init). Покретање init команде над постојећим директоријумом језика замењује све постојеће преводе. Команда update спаја нове текстове уз очување постојећих превода.
5

Преведите PO датотеке

Отворите генерисане .po датотеке и попуните вредности msgstr за сваки msgid. PO датотеке су обичан текст — можете директно да их уређујете, користите PO уређивач као што је Poedit или аутоматизујете превођење помоћу AI алатки.

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 датотеке садрже коментаре контекста (редове који почињу са #:) који показују где се сваки текст користи у изворном коду. Преводиоци их користе да разумеју контекст. Сачувајте их — pybabel extract их генерише аутоматски.
6

Компајлирајте преводе

Компајлирајте .po датотеке у бинарне .mo датотеке помоћу pybabel compile команде. Flask-Babel током извршавања чита .mo датотеке — не може директно да чита .po датотеке. Морате поново да их компајлирате после сваког ажурирања превода.

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.
Ако се преводи не појаве након уређивања .po датотеке, готово сигурно сте заборавили да покренете pybabel compile. То је најчешћи проблем са Flask-Babel системом. Додајте корак компајлирања у скрипту за постављање да га избегнете у продукцији.
7

Обрадите множину и променљиве

Користите ngettext() за текстове осетљиве на множину. Функција прима облик једнине, облик множине и број. Babel аутоматски користи исправно правило множине за сваки језик — енглески има 2 облика, руски 3, арапски 6, а јапански 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 товаров в вашей корзине"
Никада не користите if count == 1 за логику множине. Језици попут француског третирају 0 као једнину. Руски и арапски имају облике које енглески нема. Препустите избор функцији ngettext() и CLDR правилима множине библиотеке Babel.
8

Додајте промену локала

Направите бирач језика који чува избор корисника у Flask сесији. Ажурирајте функцију locale_selector тако да прво провери сесију, па пређе на откривање из прегледача.

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>
Након промене локала сесије позовите flask_babel.refresh() да приморате Flask-Babel да поново прочита локал за тренутни захтев. Без refresh(), стари локал остаје до следећег захтева.
9

Аутоматизујте преводе

Када завршите подешавање Flask-Babel система, преведите PO датотеке помоћу AI технологије. Аутоматизујте циклус издвајања, превођења и компајлирања у CI/CD pipeline систему да преводи остану усклађени са изворним кодом.

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
Преводите постепено — када додате нове текстове и покренете pybabel update, преведите само нове непреведене ставке (празне вредности msgstr) уместо поновног генерисања свега. Тиме се чувају преводи које су људи прегледали.

Додатно: паметни резервни локали уз flask-babel-locale-chain

Flask-Babel подразумевано одмах прелази на подразумевани локал када жељени локал корисника није доступан. Корисник локала pt-BR који има само преводе за pt-PT види енглески уместо португалског. flask-babel-locale-chain додаје подесиве ланце резервних локала како би се сродни локали природно надовезивали.

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 је Python пакет отвореног кода. Погледајте га на GitHub платформи на адреси github.com/i18n-agent/flask-babel-locale-chain.

Аутоматизујте квалитет превода

Помоћу i18n-validate алатке откријте недостајуће кључеве и неисправне чуваре места пре испоруке. Тестирајте кориснички интерфејс псеудопреводима помоћу i18n-pseudo алатке пре него што стигну прави преводи.

Уобичајене замке

Заборављено компајлирање .po у .mo

Flask-Babel чита компајлиране .mo датотеке, а не .po датотеке. Ако се преводи не појаве након уређивања .po датотека, покрените pybabel compile -d translations. Додајте овај корак у скрипту за постављање.

Употреба gettext() на нивоу модула

gettext() захтева контекст захтева. Ако дефинишете преведене текстове на нивоу модула (атрибуте класа, константе), користите lazy_gettext(). Он одлаже превођење док се текст заиста не прикаже у захтеву.

Издвајање пропушта текстове

pybabel extract скенира само датотеке које одговарају обрасцима у babel.cfg. Ако текстови нису издвојени: проверите да ли babel.cfg обрасци одговарају структури датотека, додајте -k lazy_gettext да издвојите лење позиве и проверите да ли Jinja2 шаблони користе исправан наставак (.html, .jinja2).

Грешке кодирања PO датотеке

PO датотеке морају да буду кодиране као UTF-8. Ако видите UnicodeDecodeError, проверите Content-Type заглавље у .po датотеци: треба да садржи charset=UTF-8. Неки уређивачи чувају датотеке у другом кодирању — увек проверите након уређивања.

Препоручена структура датотека

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/

Испробајте i18n Agent сада

Пустите датотеку за превођење овде

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

или кликните за избор

Циљни језици

Регистрација није потребнаТренутна процена

Честа питања о Flask i18n систему