Skip to main content

Flask i18n: Δημιουργήστε μια πολύγλωσση εφαρμογή με Flask-Babel

Από τις βασικές αρχές του gettext έως την ανάπτυξη στην παραγωγή: διεθνοποιήστε την εφαρμογή Flask με το Flask-Babel, αρχεία PO και αυτοματοποιημένη μετάφραση με AI.

1

Εγκατάσταση του Flask-Babel

Το Flask-Babel είναι η τυπική επέκταση διεθνοποίησης για το Flask. Ενσωματώνει το GNU gettext στο Flask και στα πρότυπα Jinja2, παρέχοντας εξαρχής συναρτήσεις μετάφρασης, επιλογή locale και υποστήριξη ζώνης ώρας.

Το Flask-Babel περιβάλλει το Babel, τη βιβλιοθήκη i18n της Python, και το ενσωματώνει στον κύκλο ζωής των αιτημάτων του Flask. Σας παρέχει τις συναρτήσεις gettext(), ngettext() και lazy_gettext(), καθώς και αυτόματη ανίχνευση locale από τις κεφαλίδες του προγράμματος περιήγησης.
Terminal
pip install Flask-Babel
2

Ρύθμιση του Babel

Δημιουργήστε ένα αρχείο babel.cfg για να υποδείξετε στο pybabel πού θα αναζητήσει μεταφράσιμες συμβολοσειρές και, στη συνέχεια, αρχικοποιήστε το Flask-Babel με μια συνάρτηση επιλογής locale που καθορίζει ποια γλώσσα θα παρέχεται σε κάθε αίτημα.

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 καλείται σε κάθε αίτημα. Αν επιστρέψει ένα locale που δεν διαθέτει μεταγλωττισμένο αρχείο .mo, το Flask-Babel επιστρέφει αθόρυβα στο προεπιλεγμένο locale. Δεν εμφανίζεται κανένα σφάλμα — οι συμβολοσειρές απλώς παραμένουν αμετάφραστες.
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>
Χρησιμοποιήστε _() αντί για gettext() στα πρότυπα Jinja2 — είναι η τυπική συντομογραφία του 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. Προσθέστε το βήμα μεταγλώττισης στο script διάθεσής σας, ώστε να το αποφεύγετε στην παραγωγή.
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

Προσθήκη επιλογής locale

Δημιουργήστε έναν επιλογέα γλώσσας που αποθηκεύει την επιλογή του χρήστη στη συνεδρία 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>
Μετά την αλλαγή του locale της συνεδρίας, καλέστε τη flask_babel.refresh() για να εξαναγκάσετε το Flask-Babel να διαβάσει ξανά το locale για το τρέχον αίτημα. Χωρίς τη refresh(), το παλιό locale παραμένει μέχρι το επόμενο αίτημα.
9

Αυτοματοποίηση μεταφράσεων

Αφού ολοκληρώσετε τη ρύθμιση του Flask-Babel, μεταφράστε τα αρχεία PO με AI. Αυτοματοποιήστε τον κύκλο εξαγωγής, μετάφρασης και μεταγλώττισης στο pipeline CI/CD σας, ώστε οι μεταφράσεις να παραμένουν συγχρονισμένες με τον πηγαίο κώδικα.

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.

Αυτοματοποιήστε τον έλεγχο ποιότητας των μεταφράσεων

Εντοπίστε κλειδιά που λείπουν και λανθασμένα placeholders πριν φτάσουν στην παραγωγή με το i18n-validate. Δοκιμάστε τη διεπαφή χρήστη με ψευδομεταφράσεις μέσω του i18n-pseudo, πριν φτάσουν οι πραγματικές μεταφράσεις.

Συνηθισμένες παγίδες

Παράλειψη μεταγλώττισης από .po σε .mo

Το Flask-Babel διαβάζει μεταγλωττισμένα αρχεία .mo και όχι αρχεία .po. Αν οι μεταφράσεις δεν εμφανίζονται μετά την επεξεργασία των αρχείων .po, εκτελέστε pybabel compile -d translations. Προσθέστε αυτό το βήμα στο script διάθεσής σας.

Χρήση της gettext() σε επίπεδο μονάδας

Η gettext() απαιτεί περιβάλλον αιτήματος. Αν ορίζετε μεταφρασμένες συμβολοσειρές σε επίπεδο μονάδας, όπως χαρακτηριστικά κλάσης ή σταθερές, χρησιμοποιήστε τη lazy_gettext(). Αναβάλλει τη μετάφραση μέχρι την πραγματική απόδοση της συμβολοσειράς μέσα σε ένα αίτημα.

Η εξαγωγή παραλείπει συμβολοσειρές

Το pybabel extract σαρώνει μόνο αρχεία που αντιστοιχούν στα μοτίβα του babel.cfg. Αν δεν εξάγονται συμβολοσειρές: ελέγξτε ότι τα μοτίβα του babel.cfg αντιστοιχούν στη δομή αρχείων σας, προσθέστε -k lazy_gettext για την εξαγωγή lazy κλήσεων και βεβαιωθείτε ότι τα πρότυπα 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