# General Translation Knowledge Base: Come si traduce un'app Flask o FastAPI?
URL: https://generaltranslation.com/it/kb/translate-python-app.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: Contrassegna le stringhe visibili agli utenti, determina l'impostazione regionale di ogni richiesta (di solito da Accept-Language) e carica le traduzioni corrispondenti. Le app Flask in genere usano Flask-Babel con cataloghi gettext, mentre le app FastAPI integrano Babel tramite un middleware. La CLI di General Translation popola questi cataloghi, mentre i suoi SDK sperimentali gt-flask e gt-fastapi traducono le stringhe inline di t() senza bisogno di cataloghi.
Category: Internazionalizzazione
Last updated: 2026-10-28
Authors: General Translation Team
Published: 2026-10-28

Né Flask né FastAPI offrono un supporto nativo alla traduzione, quindi servono tre cose: un modo per contrassegnare le stringhe visibili agli utenti, un modo per determinare la lingua di ogni richiesta e le traduzioni per ogni lingua supportata. La soluzione consolidata sono i cataloghi gettext di Babel; un approccio più recente mantiene le stringhe inline e carica le traduzioni in formato JSON. In entrambi i casi, la lingua è legata alla richiesta, non al processo, perché un unico server risponde contemporaneamente a utenti che parlano lingue diverse.

## Di cosa ha bisogno un&#39;app web Python tradotta

* **Stringhe contrassegnate.** Anziché restituire una stringa semplice, il codice chiama una funzione come `gettext()` o [`t()`](/docs/python/reference/functions/t), e i modelli usano `_()` o `{% trans %}`. È proprio il contrassegno a permettere a uno strumento di individuare ogni stringa e cercarne la traduzione.
* **Un&#39;impostazione regionale per ogni richiesta.** Di solito proviene dall&#39;intestazione `Accept-Language` e può essere sostituita da un prefisso nell&#39;URL, da un cookie o da un&#39;impostazione dell&#39;account. Flask la conserva nel contesto della richiesta; FastAPI gestisce le richieste in modo concorrente su un unico event loop, quindi l&#39;impostazione regionale va memorizzata in una `contextvars.ContextVar` che ogni richiesta imposta.
* **Traduzioni per ogni impostazione regionale.** gettext carica cataloghi `.mo` compilati; gli SDK di General Translation caricano un file JSON per ogni impostazione regionale. In entrambi i casi, se manca una traduzione si usa il testo di origine senza generare errori: le stringhe non tradotte compaiono quindi in inglese, senza mandare in crash l&#39;applicazione.

## Opzione 1: cataloghi gettext con Babel

[Babel](https://babel.pocoo.org/en/latest/cmdline.html) estrae le stringhe contrassegnate in un template POT, crea e aggiorna un catalogo PO per ogni lingua e li compila in file `.mo`. Formatta inoltre date, numeri e valute per qualsiasi impostazione regionale.

### Flask con Flask-Babel

[Flask-Babel](https://python-babel.github.io/flask-babel/) (4.0.0) integra Babel con le richieste e i modelli Jinja di Flask. Basta fornirgli un `locale_selector` che restituisca l&#39;impostazione regionale di ciascuna richiesta:

```python title="app.py"
from flask import Flask, render_template, request
from flask_babel import Babel, gettext, ngettext


def select_locale():
    # Corrispondenza migliore tra l'header Accept-Language e le lingue supportate dall'app
    return request.accept_languages.best_match(["en", "es", "pl"])


app = Flask(__name__)
babel = Babel(app, locale_selector=select_locale)


@app.route("/inbox/<int:count>")
def inbox(count):
    return {
        "title": gettext("Inbox"),
        # ngettext seleziona la forma plurale in base al locale attivo
        "unread": ngettext("You have %(num)d unread message.", "You have %(num)d unread messages.", count),
    }


@app.route("/")
def home():
    return render_template("home.html", name="Alice")
```

Nei modelli, le stringhe vengono contrassegnate con `_()`, ad esempio `{{ _("Welcome back, %(name)s!", name=name) }}`. Un file `babel.cfg` indica a `pybabel` dove cercare, mentre la gestione dei cataloghi è affidata a quattro comandi:

```bash
# babel.cfg contiene [python: **.py] e [jinja2: **/templates/**.html]
pybabel extract -F babel.cfg -o messages.pot .
pybabel init -i messages.pot -d translations -l es    # una volta per ogni lingua
pybabel update -i messages.pot -d translations        # dopo ogni extract, per tutte le lingue
pybabel compile -d translations
```

Con il catalogo spagnolo generato dalla CLI di General Translation, una richiesta con `Accept-Language: es-MX,es;q=0.9` ha restituito &quot;Bandeja de entrada&quot; e &quot;Tienes 5 mensajes sin leer.&quot; Con il catalogo polacco creato ma vuoto, una richiesta in polacco ha restituito tutte le stringhe in inglese, senza alcun avviso.

### FastAPI con Babel

FastAPI non offre un equivalente ufficiale di Flask-Babel. Esistono pacchetti di terze parti come [fastapi-babel](https://pypi.org/project/fastapi-babel/) e [starlette-babel](https://pypi.org/project/starlette-babel/), oppure puoi caricare tu stesso i cataloghi di Babel in un middleware. Questa versione legge la stessa directory `translations` e conserva il catalogo di ciascuna richiesta in una `ContextVar`:

```python title="main.py"
from contextvars import ContextVar
from functools import lru_cache

from babel.core import negotiate_locale
from babel.support import Translations
from fastapi import FastAPI, Request

SUPPORTED = ["en", "es", "pl"]
current = ContextVar("translations")


@lru_cache
def load(locale: str) -> Translations:
    # Legge translations/<locale>/LC_MESSAGES/messages.mo, compilato da pybabel
    return Translations.load("translations", [locale])


def _(message: str) -> str:
    return current.get().gettext(message)


def preferred_locales(header: str) -> list[str]:
    """Accept-Language tags, most preferred first, without q=0 entries."""
    weighted = []
    for part in header.split(","):
        tag, sep, param = part.strip().partition(";")
        try:
            q = float(param.strip().removeprefix("q=")) if sep else 1.0
        except ValueError:
            q = 0.0
        # q=0 significa «non accettabile», quindi lo si salta
        if tag and q > 0:
            weighted.append((q, tag.replace("-", "_")))
    # sorted() è stabile, quindi i tag con peso uguale mantengono l'ordine dell'header
    return [tag for q, tag in sorted(weighted, key=lambda pair: -pair[0])]


app = FastAPI()


@app.middleware("http")
async def select_locale(request: Request, call_next):
    preferred = preferred_locales(request.headers.get("accept-language", ""))
    current.set(load(negotiate_locale(preferred, SUPPORTED) or "en"))
    return await call_next(request)


@app.get("/inbox/{count}")
def inbox(count: int):
    t = current.get()
    return {
        "title": _("Inbox"),
        "unread": t.ngettext("You have %(num)d unread message.", "You have %(num)d unread messages.", count) % {"num": count},
    }
```

Eseguendo l&#39;app con Uvicorn, `Accept-Language: es-MX,es;q=0.9,en;q=0.8` ha restituito le stringhe in spagnolo, `de,en;q=0.5` ha ripiegato sull&#39;inglese, `en;q=0.5,es` ha restituito lo spagnolo e `es;q=0,en` ha restituito l&#39;inglese. `negotiate_locale` associa `es_MX` a `es`. `preferred_locales` ordina i tag in base al rispettivo peso `q` e scarta quelli con `q=0`, che indica una lingua non accettata dall&#39;utente.

### Cosa resta a carico tuo con Babel

Babel gestisce i cataloghi, ma non scrive le traduzioni. Ogni stringa nuova o modificata resta in attesa che qualcuno compili il relativo `msgstr` in ciascuna lingua, incluse tutte e tre le voci del plurale del polacco, e poi che si eseguano `pybabel compile` e una nuova distribuzione. (Consulta [Come si traducono i file PO e POT?](/kb/translate-po-files) per il formato e per i controlli che individuano le voci non valide, e [General Translation vs. Babel e Flask-Babel](/kb/general-translation-vs-flask-babel) per sapere cosa copre ciascuno strumento.) Le app Django seguono lo stesso pattern con i propri comandi. (Consulta [General Translation vs. il framework di traduzione di Django](/kb/general-translation-vs-django-i18n).)

## Scegliere l&#39;impostazione regionale per ogni richiesta

`Accept-Language` elenca le lingue dell&#39;utente con pesi `q` facoltativi: il tuo compito è farle corrispondere alle lingue che supporti, con un valore predefinito per tutti gli altri casi. Sia `best_match()` di Werkzeug su `request.accept_languages` sia `negotiate_locale` di Babel ripiegano da un tag regionale come `es-MX` a `es`. Fai in modo che una scelta esplicita abbia la precedenza sull&#39;intestazione: un prefisso URL `/es/` per le pagine indicizzate dai motori di ricerca, oppure un cookie impostato dal selettore della lingua. (Consulta [Come rilevo la lingua del browser di un utente?](/kb/detect-user-language) per l&#39;ordine completo dei segnali.) Nei server Node.js lo stesso problema si risolve con `AsyncLocalStorage`. (Consulta [Come traduco un&#39;app Node.js?](/kb/translate-node-app).)

## Confronto tra gli approcci

| Approccio                                         | Come scrivi le stringhe                                             | Impostazione regionale per richiesta       | Chi scrive le traduzioni                       | Stato                                                                |
| ------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------ | ---------------------------------------------- | -------------------------------------------------------------------- |
| Flask-Babel con cataloghi PO                      | `gettext`, `ngettext`, Jinja `_()`                                  | `locale_selector`                          | Tu o i traduttori, catalogo per catalogo       | Stabile                                                              |
| Babel in un middleware FastAPI                    | `gettext` tramite una `ContextVar`                                  | Il tuo middleware                          | Tu o i traduttori, catalogo per catalogo       | Stabile, ma da assemblare tu                                         |
| API di traduzione automatica per richiesta        | Stringhe grezze inviate a runtime                                   | Passi tu la lingua di destinazione         | L&#39;API, a ogni richiesta                    | Aggiunge latenza e costi a ogni chiamata, salvo uso di cache         |
| Cataloghi Babel più la CLI di General Translation | Come con Babel                                                      | Come con Babel                             | General Translation, prima della distribuzione | La CLI è stabile; Babel resta invariato                              |
| `gt-flask` o `gt-fastapi` di General Translation  | [`t()`](/docs/python/reference/functions/t) inline con messaggi ICU | Corrispondenza `Accept-Language` integrata | General Translation, prima della distribuzione | Sperimentale (0.1.0); le traduzioni caricate scadono dopo 60 secondi |

Usa Flask-Babel, o Babel in un middleware FastAPI, se disponi già di cataloghi o traduttori. Aggiungi la CLI di General Translation a questa configurazione se vuoi che, a ogni modifica, ogni catalogo venga completato in tutte le lingue, senza passare da un ciclo di traduzione. Prova `gt-flask` o `gt-fastapi` su un prototipo in cui preferisci mantenere le stringhe inline senza file di catalogo, fissando la versione. Chiama un&#39;API di traduzione automatica al momento della richiesta solo per i contenuti che esistono soltanto a runtime, come i post degli utenti.

## Come General Translation traduce le app Flask e FastAPI

### Mantieni Babel e traduci i cataloghi

La CLI di General Translation, `gt`, legge un catalogo PO in inglese e scrive i cataloghi delle altre lingue nelle posizioni in cui Babel si aspetta di trovarli. Crea il catalogo inglese una sola volta con `pybabel init -l en` e aggiornalo dopo ogni estrazione con `pybabel update -l en`, quindi indicalo in `gt.config.json`:

```json title="gt.config.json"
{
  "defaultLocale": "en",
  "locales": ["es", "pl"],
  "files": {
    "pot": {
      "include": ["translations/[locale]/LC_MESSAGES/messages.po"],
      "transformationFormat": "PO"
    }
  }
}
```

Verificato con `gt` 2.26.11, la CLI associa il catalogo inglese in `translations/en/LC_MESSAGES` alle cartelle `es` e `pl` che si trovano allo stesso livello. Esegui [`npx gt translate`](/docs/cli/reference/commands/translate), quindi `pybabel compile -d translations`. General Translation compila tutte le forme plurali per ciascuna lingua di destinazione e verifica i segnaposto come `%(num)d` in ogni forma plurale. Un Glossario e dei prompt personalizzati in un [gruppo di contesto](/docs/platform/dashboard/guides/defining-context-for-translations) mantengono la terminologia coerente con gli altri tuoi progetti. La CLI gira su Node.js, quindi Node deve essere installato sulla macchina che la esegue; la tua app continua a caricare i cataloghi compilati senza alcuna dipendenza Runtime da General Translation.

### Oppure usa gt-flask e gt-fastapi

Gli SDK Python di General Translation, `gt-flask` e `gt-fastapi` (0.1.0 su PyPI, dal repository [gt-python](https://github.com/generaltranslation/gt-python)), sono sperimentali e potrebbero cambiare da un rilascio all&#39;altro. Richiedono Python 3.10 o versioni successive. Le stringhe restano nel codice come messaggi ICU passati a [`t`](/docs/python/reference/functions/t), con i valori forniti come argomenti con nome, mentre [`initialize_gt`](/docs/python/reference/functions/initialize-gt) configura la corrispondenza basata su `Accept-Language`. Questa app Flask carica le traduzioni da file JSON locali:

```python title="app.py"
import json
from pathlib import Path

from flask import Flask
from gt_flask import initialize_gt, t, get_locale


def load_translations(locale: str) -> dict[str, str]:
    # File che la CLI gt scrive nel percorso files.gt.output
    path = Path("translations") / f"{locale}.json"
    return json.loads(path.read_text()) if path.exists() else {}


app = Flask(__name__)
# Con Flask, passa qui le impostazioni regionali in modo che le traduzioni vengano caricate all'avvio
initialize_gt(app, default_locale="en", locales=["es", "fr"], load_translations=load_translations)


@app.route("/")
def index():
    return {
        "message": t("Hello, {name}!", name="Alice"),
        "unread": t("{count, plural, one {You have # unread message.} other {You have # unread messages.}}", count=3),
        "locale": get_locale(),
    }
```

Per FastAPI, importa da `gt_fastapi`, crea `FastAPI()` e usa `@app.get`; in questo caso [`initialize_gt`](/docs/python/reference/functions/initialize-gt) aggiunge un middleware HTTP e carica le traduzioni durante il lifespan dell&#39;app, quindi bastano le impostazioni regionali definite in `gt.config.json`. Con il file `translations/es.json` generato dalla CLI di General Translation, entrambe le app hanno risposto così:

```text title="Output"
Accept-Language: es-MX,es;q=0.9   {"locale": "es", "message": "¡Hola, Alice!", "unread": "Tienes 3 mensajes sin leer."}
Accept-Language: de                {"locale": "en", "message": "Hello, Alice!", "unread": "You have 3 unread messages."}
```

È la CLI a generare questi file. Aggiungi `gt-flask` o `gt-fastapi` a `requirements.txt` o `pyproject.toml` affinché la CLI lo rilevi, quindi imposta il percorso di output in `gt.config.json`:

```json title="gt.config.json"
{
  "defaultLocale": "en",
  "locales": ["es", "fr"],
  "files": { "gt": { "output": "translations/[locale].json" } }
}
```

[`npx gt translate`](/docs/cli/reference/commands/translate) analizza i tuoi file `.py` alla ricerca di chiamate a [`t()`](/docs/python/reference/functions/t) (ignorando ambienti virtuali, test e migrazioni), le traduce e scrive un file per ogni impostazione regionale. In questa app, [`npx gt validate`](/docs/cli/reference/commands/validate) ha rilevato 2 voci traducibili per `gt-flask` senza chiamare l&#39;API. Per caricare invece le traduzioni dalla CDN di General Translation, imposta `projectId` e `cacheUrl` nella config (vedi [Scegli come vengono distribuite le traduzioni](/docs/python/guides/configuring#delivery)). Gli SDK non richiedono alcuna chiave API; con i file locali non ci sono chiamate di rete a runtime.

Nella versione 0.1.0, [`t()`](/docs/python/reference/functions/t) legge le traduzioni da una cache in memoria che scade dopo 60 secondi e che non viene ricaricata in quel percorso di lettura: di conseguenza, un server che resta in esecuzione a lungo restituisce le stringhe di origine dalla scadenza della cache fino alla successiva esecuzione del loader. (Vedi le [note sul precaricamento](/docs/python/guides/configuring#preload).) Finché un rilascio non cambierà questo comportamento, usa per il traffico di produzione cataloghi Babel popolati dalla CLI e limita l&#39;uso di `gt-flask` e `gt-fastapi` ai prototipi.

## Domande frequenti

### gt-flask sostituisce la formattazione di date e valute di Flask-Babel?

No. Nella versione 0.1.0, `gt-flask` e `gt-fastapi` esportano helper per la traduzione e per l&#39;impostazione regionale ([`t`](/docs/python/reference/functions/t), [`get_locale`](/docs/python/reference/functions/get-locale), [`declare_var`](/docs/python/reference/functions/declare-var) e alcuni altri), quindi per la formattazione in base all&#39;impostazione regionale continua a usare `format_date` e `format_currency` di Babel.

### Come posso permettere agli utenti di scegliere una lingua invece di usare Accept-Language?

Con Flask-Babel, leggi un cookie o un segmento dell&#39;URL nel tuo `locale_selector`. Con gli SDK di General Translation, passa una callback [`get_locale`](/docs/python/reference/functions/get-locale) a [`initialize_gt`](/docs/python/reference/functions/initialize-gt): questa sostituisce la corrispondenza basata sull&#39;intestazione, quindi spetta a te restituire un&#39;impostazione regionale supportata. (Vedi [Rilevare le impostazioni locali della richiesta](/docs/python/guides/detecting-locale).)

### Come si traducono i contenuti del database o i post degli utenti?

I cataloghi e [`t()`](/docs/python/reference/functions/t) gestiscono le stringhe presenti nel codice. I contenuti che esistono solo a runtime richiedono invece una chiamata di traduzione nel momento in cui vengono creati o richiesti, ad esempio tramite l&#39;endpoint [Translate content](/docs/platform/openapi/reference/translation/translate-content) di General Translation (`POST /v2/projects/{projectId}/translate`), salvando poi i risultati insieme all&#39;originale.

## Punti chiave

* Flask e FastAPI richiedono tre aggiunte: stringhe contrassegnate, un&#39;impostazione regionale per ogni richiesta e traduzioni per ogni impostazione regionale; FastAPI memorizza l&#39;impostazione regionale in una `ContextVar`.
* In Flask se ne occupa Flask-Babel, con un `locale_selector` e i comandi `pybabel extract`, [`init`](/docs/cli/reference/commands/init), `update` e `compile`; FastAPI carica le `Translations` di Babel in un middleware.
* Se un catalogo è vuoto o mancante, si torna silenziosamente all&#39;inglese: il lavoro che resta da fare è quindi compilare ogni `msgstr`.
* La CLI di General Translation compila i cataloghi PO di Babel con `"pot"` e `"transformationFormat": "PO"`, compresi gli slot per i plurali, mentre la tua app continua a caricare i cataloghi compilati.
* I pacchetti sperimentali `gt-flask` e `gt-fastapi` 0.1.0 di General Translation traducono le stringhe inline di [`t()`](/docs/python/reference/functions/t) partendo dai file JSON generati dalla CLI; con Flask, passa `locales` a [`initialize_gt`](/docs/python/reference/functions/initialize-gt) e tieni conto della scadenza della cache dopo 60 secondi prima di gestire traffico in produzione.

## Related questions

- [General Translation, Babel e Flask-Babel a confronto: quale scegliere?](https://generaltranslation.com/it/kb/general-translation-vs-flask-babel.mdx): Babel formatta date, numeri e valute per qualsiasi impostazione regionale e gestisce i cataloghi gettext, mentre Flask-Babel lo integra con Flask, ma nessuno dei due traduce le tue stringhe. La CLI di General Translation popola i file PO di Babel per ogni lingua, forme plurali incluse, così puoi continuare a usare Flask-Babel e smettere di tradurre i cataloghi a mano.
- [Come si traducono i file PO e POT?](https://generaltranslation.com/it/kb/translate-po-files.mdx): Compila ogni msgstr, incluse tutte le forme plurali richieste dall'intestazione Plural-Forms, e lascia invariati msgid, msgctxt e i segnaposto. Esegui msgfmt --check per verificare intestazioni, numero di forme plurali e segnaposto nelle voci con un flag di formato, e controlla a parte che nessun msgstr sia vuoto. La CLI di General Translation genera un file PO tradotto per ogni lingua a partire dal tuo template POT.
- [Come si rileva la lingua del browser di un utente?](https://generaltranslation.com/it/kb/detect-user-language.mdx): Leggi l'elenco ordinato delle lingue del browser (navigator.languages in JavaScript o l'intestazione Accept-Language sul server), ordinato per q-value, e confrontalo con le impostazioni regionali supportate, prevedendo un fallback. Safari e Chrome in incognito restituiscono una sola lingua, mentre la geolocalizzazione tramite IP rivela la posizione, non la lingua.
- [Come si traduce un'app Node.js?](https://generaltranslation.com/it/kb/translate-node-app.mdx): Rileva la lingua di ogni richiesta, di solito dall'intestazione Accept-Language, e restituisci ogni stringa in quella lingua. gt-node di General Translation associa l'impostazione regionale a ogni richiesta con withGT e traduce le stringhe con getGT, a partire dalle traduzioni che npx gt translate --publish genera prima della distribuzione.
- [General Translation e il framework di traduzione di Django: servono entrambi?](https://generaltranslation.com/it/kb/general-translation-vs-django-i18n.mdx): Usa insieme il framework di traduzione di Django e General Translation. Django si occupa dell'estrazione dei messaggi, della selezione della lingua e del rendering. General Translation compila i suoi file PO, forme plurali comprese, con la terminologia del tuo prodotto e la revisione nella Dashboard. I tuoi template e i modelli di URL restano invariati, mentre le traduzioni si aggiornano di pari passo con il codice.

## Previous and next questions

- Previous question: [Come si localizza un'app Android con strings.xml?](https://generaltranslation.com/it/kb/localize-android-app.mdx)
- Next question: [Come si traducono i contenuti in Payload CMS?](https://generaltranslation.com/it/kb/translate-payload-content.mdx)

## Sitemap

See the [Knowledge Base sitemap](https://generaltranslation.com/it/kb/sitemap.md) for every article.
