
Flask i18n: створіть багатомовний застосунок із Flask-Babel
Від основ gettext до розгортання у виробничому середовищі: інтернаціоналізуйте свій застосунок Flask за допомогою Flask-Babel, файлів PO та автоматизованого перекладу на основі ШІ.
Установити Flask-Babel
Flask-Babel — стандартне розширення для інтернаціоналізації Flask. Воно інтегрує GNU gettext із шаблонами Flask і Jinja2 та одразу надає функції перекладу, вибір локалі й підтримку часових поясів.
pip install Flask-BabelНалаштувати Babel
Створіть файл babel.cfg, щоб указати pybabel, де шукати рядки для перекладу, а потім ініціалізуйте Flask-Babel за допомогою функції вибору локалі, яка визначає мову для кожного запиту.
# 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)Позначити рядки для перекладу
Огорніть кожен видимий користувачу рядок викликом gettext() у коді Python і _() у шаблонах Jinja2. Використовуйте lazy_gettext() для рядків, визначених під час завантаження модуля, наприклад підписів полів форм і конфігурації, які потрібно перекласти пізніше під час обробки запиту.
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>Видобути повідомлення
Запустіть pybabel extract, щоб просканувати вихідний код і шаблони на наявність рядків для перекладу. Команда створить файл .pot (Portable Object Template). Потім ініціалізуйте каталоги для кожної цільової мови або оновіть наявні каталоги після зміни вихідних рядків.
# 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Перекласти файли PO
Відкрийте створені файли .po та заповніть значення msgstr для кожного msgid. Файли PO є звичайними текстовими файлами: Ви можете редагувати їх безпосередньо, скористатися редактором PO на зразок Poedit або автоматизувати переклад за допомогою інструментів ШІ.
# 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 у двійкові файли .mo за допомогою pybabel compile. Flask-Babel читає файли .mo під час виконання й не може читати файли .po безпосередньо. Після кожного оновлення перекладів потрібно повторно виконувати компіляцію.
# 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.Опрацювати форми множини та змінні
Використовуйте ngettext() для рядків, форма яких залежить від кількості. Функція приймає форму однини, форму множини та кількість. Babel автоматично застосовує правильне правило множини для кожної мови: англійська має 2 форми, російська — 3, арабська — 6, а японська — 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 товаров в вашей корзине"Додати перемикання локалі
Створіть селектор мови, який зберігатиме вибір користувача в сеансі Flask. Оновіть функцію locale_selector, щоб вона спочатку перевіряла сеанс, а потім використовувала визначення мови браузером як резервний варіант.
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>Автоматизувати переклад
Після завершення налаштування Flask-Babel перекладіть файли PO за допомогою ШІ. Автоматизуйте цикл видобування, перекладу та компіляції у своєму pipeline CI/CD, щоб переклади залишалися синхронізованими з вихідним кодом.
# 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Додатково: розумні резервні локалі з flask-babel-locale-chain
За замовчуванням Flask-Babel одразу переходить до стандартної локалі, коли бажана локаль користувача недоступна. Користувач із pt-BR, коли наявні лише переклади pt-PT, побачить англійський текст замість португальського. flask-babel-locale-chain додає налаштовувані ланцюжки резервного вибору, щоб споріднені локалі використовувалися послідовно.
# 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)Автоматизувати контроль якості перекладу
Поширені помилки
Забули скомпілювати .po у .mo
Використання gettext() на рівні модуля
Під час видобування пропущено рядки
Помилки кодування файлів PO
Рекомендована структура файлів
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
або натисніть, щоб вибрати
Цільові мови