Skip to main content

i18n di Django: guida all'internazionalizzazione e alla traduzione

Da settings.py alla produzione: configuri il sistema di traduzione di Django, scriva file PO, gestisca i plurali e automatizzi le traduzioni con l'IA.

1

Installare ed estrarre le stringhe

Il framework i18n di Django è integrato: deve soltanto abilitarlo. Installi django-locale-chain per fallback intelligenti tra lingue, quindi usi makemessages per estrarre le stringhe traducibili dal codice Python e dai modelli in file PO.

Django usa GNU gettext internamente. Il comando makemessages esamina il codice alla ricerca di chiamate gettext e tag dei modelli, quindi genera file .po. Dopo la traduzione, compilemessages li converte in file binari .mo per una ricerca rapida durante l'esecuzione.
Terminal
pip install django-locale-chain
Terminal
# Extract all translatable strings from Python and template files
python manage.py makemessages -l de -l ja -l es -l fr

# After translating .po files, compile to .mo (binary)
python manage.py compilemessages

# Project structure after running makemessages:
# locale/
# ├── de/
# │   └── LC_MESSAGES/
# │       ├── django.po    <-- translate this
# │       └── django.mo    <-- compiled (auto-generated)
# ├── ja/
# │   └── LC_MESSAGES/
# │       ├── django.po
# │       └── django.mo
# └── es/
#     └── LC_MESSAGES/
#         ├── django.po
#         └── django.mo
2

Configurare impostazioni e middleware

Abiliti l'internazionalizzazione in settings.py impostando USE_I18N = True, definendo l'elenco LANGUAGES supportato e aggiungendo LocaleMiddleware allo stack MIDDLEWARE. LocaleMiddleware rileva la lingua dell'utente dal prefisso URL, dalla sessione, dai cookie o dall'intestazione Accept-Language.

settings.py
# settings.py

from django.utils.translation import gettext_lazy as _

# Default language
LANGUAGE_CODE = 'en'

# Enable i18n
USE_I18N = True
USE_L10N = True

# Languages your site supports
LANGUAGES = [
    ('en', _('English')),
    ('de', _('German')),
    ('ja', _('Japanese')),
    ('es', _('Spanish')),
    ('fr', _('French')),
    ('pt-br', _('Brazilian Portuguese')),
]

# Where Django looks for .po files
LOCALE_PATHS = [
    BASE_DIR / 'locale',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.locale.LocaleMiddleware',       # <-- enables i18n
    'locale_chain.middleware.LocaleChainMiddleware',    # <-- smart fallbacks
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
LocaleMiddleware deve trovarsi dopo SessionMiddleware, perché legge la sessione, e prima di CommonMiddleware. Se è posizionato nell'ordine errato, il rilevamento della lingua non riesce senza segnalazioni e tutti gli utenti vedono il LANGUAGE_CODE predefinito.

Internazionalizzazione degli URL

Usi i18n_patterns() per anteporre automaticamente agli URL il codice della lingua attiva. Ogni lingua riceve così un proprio spazio dei nomi URL (/en/about/, /de/about/), che migliora la SEO e consente agli utenti di condividere link specifici per lingua.

urls.py
# urls.py
from django.conf.urls.i18n import i18n_patterns
from django.urls import path, include

urlpatterns = [
    # Non-localized URLs (API, admin, etc.)
    path('api/', include('api.urls')),
]

urlpatterns += i18n_patterns(
    # These get prefixed with the language code: /en/about/, /de/about/
    path('', include('myapp.urls')),
    path('admin/', admin.site.urls),
    prefix_default_language=False,  # Skip prefix for default language
)
3

Contrassegnare le stringhe per la traduzione

Django offre due funzioni di traduzione principali: gettext() (con alias _()) per le stringhe valutate al momento della richiesta e gettext_lazy() per quelle valutate al momento dell'importazione. Nei modelli, usi i tag {% trans %} e {% blocktrans %}.

Nelle viste (codice Python)

views.py
from django.utils.translation import gettext as _
from django.utils.translation import ngettext
from django.http import HttpResponse

def greeting_view(request):
    # Simple translation
    welcome = _("Welcome to our site")

    # Translation with variables (Python string formatting)
    user_greeting = _("Hello, %(name)s!") % {"name": request.user.username}

    # Plurals
    count = request.user.order_set.count()
    order_text = ngettext(
        "You have %(count)d order.",
        "You have %(count)d orders.",
        count,
    ) % {"count": count}

    return HttpResponse(f"{welcome}<br>{user_greeting}<br>{order_text}")

Nei modelli

templates/myapp/index.html
{# Load the i18n template tags #}
{% load i18n %}

{# Simple translation #}
<h1>{% trans "Welcome to our site" %}</h1>

{# Translation with variables #}
{% blocktrans with name=user.username %}
  Hello, {{ name }}!
{% endblocktrans %}

{# Plurals in templates #}
{% blocktrans count count=order_count %}
  You have {{ count }} order.
{% plural %}
  You have {{ count }} orders.
{% endblocktrans %}

{# Mark strings as translatable but don't output them (for attributes, etc.) #}
{% trans "Submit" as submit_label %}
<button type="submit">{{ submit_label }}</button>

Nei modelli di dati e nei moduli

models.py
from django.db import models
from django.utils.translation import gettext_lazy as _

class Product(models.Model):
    name = models.CharField(_("product name"), max_length=200)
    description = models.TextField(_("description"), blank=True)

    class Meta:
        verbose_name = _("product")
        verbose_name_plural = _("products")

    def __str__(self):
        return self.name

# IMPORTANT: Use gettext_lazy (_) for anything evaluated at import time:
# - Model field labels, verbose_name, help_text
# - Form field labels
# - Class-level attributes
# Use gettext for anything evaluated at request time:
# - View functions, template tags
L'uso di gettext() anziché gettext_lazy() nei campi dei modelli di dati, nelle etichette dei moduli o negli attributi a livello di classe fa sì che le stringhe vengano tradotte una volta al momento dell'importazione, quando si avvia il server, anziché per ogni richiesta. La traduzione rimane bloccata nella lingua attiva durante l'importazione, generalmente il LANGUAGE_CODE predefinito.

Formato dei file PO

Dopo l'esecuzione di makemessages, Django genera file .po (Portable Object) per ogni lingua. Questi file contengono coppie msgid/msgstr. Traduca i valori msgstr, quindi esegua compilemessages per generare i file binari .mo letti da Django durante l'esecuzione.

locale/de/LC_MESSAGES/django.po
# locale/de/LC_MESSAGES/django.po

msgid "Welcome to our site"
msgstr "Willkommen auf unserer Seite"

msgid "Hello, %(name)s!"
msgstr "Hallo, %(name)s!"

#, python-format
msgid "You have %(count)d order."
msgid_plural "You have %(count)d orders."
msgstr[0] "Sie haben %(count)d Bestellung."
msgstr[1] "Sie haben %(count)d Bestellungen."

msgid "product name"
msgstr "Produktname"

msgid "description"
msgstr "Beschreibung"

msgid "product"
msgstr "Produkt"

msgid "products"
msgstr "Produkte"

msgid "Submit"
msgstr "Absenden"
4

Gestire plurali e variabili

Django usa ngettext() per la gestione dei plurali, seguendo le regole di GNU gettext. Ogni lingua definisce il numero di forme plurali e la formula per selezionare quella corretta. I file PO dichiarano queste informazioni con un'intestazione Plural-Forms.

Plural forms by language
# English: 2 forms (singular, plural)
msgid "%(count)d item"
msgid_plural "%(count)d items"
msgstr[0] "%(count)d item"
msgstr[1] "%(count)d items"

# German: 2 forms (singular, plural)
msgstr[0] "%(count)d Artikel"
msgstr[1] "%(count)d Artikel"

# Russian: 3 forms (one, few, many)
msgstr[0] "%(count)d товар"        # 1 item
msgstr[1] "%(count)d товара"       # 2-4 items
msgstr[2] "%(count)d товаров"      # 5+ items

# Arabic: 6 forms (zero, one, two, few, many, other)
msgstr[0] "لا عناصر"               # 0
msgstr[1] "عنصر واحد"              # 1
msgstr[2] "عنصران"                 # 2
msgstr[3] "%(count)d عناصر"        # 3-10
msgstr[4] "%(count)d عنصرًا"       # 11-99
msgstr[5] "%(count)d عنصر"         # 100+

# Japanese: 1 form (no plural distinction)
msgstr[0] "%(count)d個のアイテム"

# In Python code, always use ngettext:
from django.utils.translation import ngettext

msg = ngettext(
    "%(count)d item",
    "%(count)d items",
    count,
) % {"count": count}
Non usi mai le f-string Python o .format() nelle chiamate gettext. Il programma di estrazione delle traduzioni non è in grado di analizzarle. Usi la formattazione tradizionale %(): _('Hello, %(name)s!') % {'name': name}. In questo modo, i traduttori vedono anche i segnaposto delle variabili nei propri editor PO.

Automatizzare la qualità

Con i18n-validate, rilevi chiavi mancanti e segnaposto non validi prima del rilascio. Testi l'interfaccia con le pseudotraduzioni di i18n-pseudo prima che arrivino le traduzioni reali.

Problemi comuni

Confusione tra gettext() e gettext_lazy()

Usi gettext_lazy() (importato come _) per i campi dei modelli di dati, le etichette dei moduli e ogni attributo a livello di classe valutato al momento dell'importazione. Usi gettext() per le funzioni delle viste e il codice eseguito al momento della richiesta. Confonderli blocca le traduzioni nella lingua attiva all'avvio del server.

Mancata esecuzione di compilemessages

Django legge i file .mo (compilati), non i file .po. Se modifica un file .po e non esegue 'python manage.py compilemessages', i cambiamenti non appariranno. Aggiunga compilemessages allo script di distribuzione.

LOCALE_PATHS non configurato

Se LOCALE_PATHS è vuoto o indica la directory sbagliata, makemessages crea i file .po nella posizione errata e Django non riesce a trovarli durante l'esecuzione. Imposti sempre LOCALE_PATHS = [BASE_DIR / 'locale'] e verifichi che la directory esista.

i18n_patterns mancante negli URL

Senza i18n_patterns(), gli URL non hanno un prefisso linguistico e Django si affida unicamente ai cookie o alle intestazioni per rilevare la lingua. Questo penalizza la SEO, perché mancano URL specifici per lingua, e impedisce il cambio di lingua tramite URL. Racchiuda i pattern URL rivolti agli utenti con i18n_patterns().

Fallback intelligenti tra lingue con django-locale-chain

Il sistema di traduzione di Django passa direttamente a LANGUAGE_CODE quando manca una variante regionale. Un utente pt-BR vede l'inglese anche se sono disponibili traduzioni pt-PT. django-locale-chain risolve il problema installando catene di fallback gettext: pt-BR prova pt-PT e poi pt, prima di passare alla lingua predefinita.

settings.py
# settings.py -- Smart fallback with django-locale-chain
# pip install django-locale-chain

MIDDLEWARE = [
    # ...
    'django.middleware.locale.LocaleMiddleware',
    'locale_chain.middleware.LocaleChainMiddleware',  # after LocaleMiddleware
    # ...
]

# That's it! 75 built-in fallback chains are now active:
# pt-BR user → tries pt-PT → tries pt → falls back to LANGUAGE_CODE
# es-MX user → tries es-419 → tries es → falls back to LANGUAGE_CODE
# fr-CA user → tries fr → falls back to LANGUAGE_CODE

# Optional: customize specific chains
LOCALE_FALLBACK_CHAINS = {
    "pt-BR": ["pt-PT", "pt"],
    "es-MX": ["es-419", "es"],
    "fr-CA": ["fr"],
}

# Or configure programmatically in AppConfig.ready():
from locale_chain import configure

class MyAppConfig(AppConfig):
    name = "myapp"

    def ready(self):
        configure(overrides={"zh-Hant-HK": ["zh-Hant-TW", "zh-Hant"]})

Struttura del progetto consigliata

Project Structure
myproject/
├── myproject/
│   ├── settings.py          # i18n config, MIDDLEWARE, LANGUAGES
│   ├── urls.py              # i18n_patterns for URL prefixing
│   └── wsgi.py
├── myapp/
│   ├── models.py            # gettext_lazy for field labels
│   ├── views.py             # gettext for request-time strings
│   └── templates/
│       └── myapp/
│           └── index.html   # {% load i18n %}, {% trans %}, {% blocktrans %}
├── locale/                  # Created by makemessages
│   ├── de/
│   │   └── LC_MESSAGES/
│   │       ├── django.po    # German translations
│   │       └── django.mo    # Compiled binary
│   ├── ja/
│   │   └── LC_MESSAGES/
│   │       ├── django.po
│   │       └── django.mo
│   └── es/
│       └── LC_MESSAGES/
│           ├── django.po
│           └── django.mo
├── manage.py
└── requirements.txt

Provi subito i18n Agent

Trascinare qui il file di traduzione

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

oppure fare clic per selezionarlo

Lingue di destinazione

Nessuna registrazione richiestaPreventivo immediato

Fallback della lingua con django-locale-chain

Quando manca una chiave di traduzione in una lingua regionale come pt-BR, Django passa direttamente alla lingua del modello anziché controllare prima la lingua principale pt.

Terminal
pip install django-locale-chain
Configuration
# settings.py
LOCALE_CHAINS = {
    'pt-BR': ['pt', 'es', 'en'],
    'zh-Hant-HK': ['zh-Hant', 'zh', 'en'],
}

MIDDLEWARE = [
    ...
    'django_locale_chain.middleware.LocaleChainMiddleware',
    ...
]

Consultare la Guida al fallback delle lingue per l'elenco completo dei framework supportati e delle 75 catene integrate. Learn more →

Domande frequenti