Skip to main content

Django i18n: ghid de internaționalizare și traducere

De la settings.py până în producție: configurați sistemul de traducere Django, scrieți fișiere PO, gestionați formele de plural și automatizați traducerile cu IA.

1

Instalați și extrageți șirurile

Cadrul i18n din Django este integrat -- trebuie doar să îl activați. Instalați django-locale-chain pentru mecanisme inteligente de rezervă între setările regionale, apoi utilizați makemessages pentru a extrage șirurile traductibile din codul Python și din șabloane în fișiere PO.

Django utilizează GNU gettext în fundal. Comanda makemessages scanează codul pentru apeluri gettext și etichete de șablon, apoi generează fișiere .po. După traducere, compilemessages le transformă în fișiere binare .mo pentru căutări rapide în timpul execuției.
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

Configurați setările și middleware-ul

Activați internaționalizarea în settings.py setând USE_I18N = True, definind lista LANGUAGES acceptată și adăugând LocaleMiddleware în stiva MIDDLEWARE. LocaleMiddleware detectează limba utilizatorului din prefixul URL-ului, din sesiune, dintr-un cookie sau din antetul 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 trebuie să apară după SessionMiddleware (deoarece citește sesiunea) și înainte de CommonMiddleware. Dacă este plasat în ordinea greșită, detectarea limbii eșuează fără niciun avertisment, iar toți utilizatorii văd valoarea LANGUAGE_CODE implicită.

Internaționalizarea URL-urilor

Utilizați i18n_patterns() pentru a adăuga automat la URL-uri prefixul codului limbii active. Astfel, fiecare limbă primește propriul spațiu de nume URL (/en/about/, /de/about/), ceea ce este mai avantajos pentru SEO și le permite utilizatorilor să distribuie linkuri specifice unei limbi.

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

Marcați șirurile pentru traducere

Django oferă două funcții principale de traducere: gettext() (cu aliasul _()) pentru șirurile evaluate la prelucrarea solicitării și gettext_lazy() pentru șirurile evaluate la import. În șabloane, utilizați etichetele {% trans %} și {% blocktrans %}.

În vizualizări (cod 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}")

În șabloane

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>

În modele și formulare

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
Utilizarea gettext() în loc de gettext_lazy() în câmpurile modelelor, etichetele formularelor sau atributele de clasă face ca șirurile să fie traduse o singură dată, la import (când pornește serverul), nu pentru fiecare solicitare. Traducerea va rămâne blocată în limba activă în timpul importului -- de obicei, valoarea LANGUAGE_CODE implicită.

Formatul fișierelor PO

După executarea makemessages, Django generează pentru fiecare limbă fișiere .po (Portable Object). Acestea conțin perechi msgid/msgstr. Traduceți valorile msgstr, apoi executați compilemessages pentru a genera fișierele binare .mo pe care Django le citește în timpul execuției.

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

Gestionați formele de plural și variabilele

Django utilizează ngettext() pentru pluralizare, conform regulilor de plural din GNU gettext. Fiecare limbă definește numărul formelor de plural și formula pentru selectarea formei corecte. Fișierele PO declară aceste informații printr-un antet 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}
Nu utilizați niciodată șiruri f Python sau .format() în apelurile gettext. Instrumentul de extragere a traducerilor nu le poate analiza. Utilizați formatarea clasică %(): _('Hello, %(name)s!') % {'name': name}. Astfel, traducătorii vor vedea și substituenții variabilelor în editoarele lor PO.

Automatizați verificarea calității traducerilor

Detectați cheile lipsă și substituenții nevalizi înainte de lansare cu i18n-validate. Testați interfața cu pseudotraduceri folosind i18n-pseudo înainte de sosirea traducerilor reale.

Capcane frecvente

Confuzia dintre gettext() și gettext_lazy()

Utilizați gettext_lazy() (importată ca _) pentru câmpurile modelelor, etichetele formularelor și orice atribut de clasă evaluat la import. Utilizați gettext() pentru funcțiile vizualizărilor și codul executat la prelucrarea solicitărilor. Confundarea lor blochează traducerile în limba activă la pornirea serverului.

Ați uitat să executați compilemessages

Django citește fișiere .mo (compilate), nu fișiere .po. Dacă editați un fișier .po și nu executați 'python manage.py compilemessages', modificările nu vor apărea. Adăugați compilemessages în scriptul de implementare.

LOCALE_PATHS nu este configurat

Dacă LOCALE_PATHS este gol sau indică un director greșit, makemessages creează fișierele .po într-un loc greșit, iar Django nu le poate găsi în timpul execuției. Setați întotdeauna LOCALE_PATHS = [BASE_DIR / 'locale'] și verificați dacă directorul există.

i18n_patterns lipsește din URL-uri

Fără i18n_patterns(), URL-urile nu au prefix de limbă, iar Django se bazează exclusiv pe cookie-uri și antete pentru detectarea limbii. Acest lucru afectează negativ SEO (nu există URL-uri specifice limbilor) și împiedică schimbarea limbii prin URL. Includeți tiparele URL destinate utilizatorilor în i18n_patterns().

Mecanisme inteligente de rezervă cu django-locale-chain

Sistemul de traducere Django revine direct la LANGUAGE_CODE când lipsește o variantă regională. Un utilizator pt-BR vede engleză chiar dacă aveți traduceri pt-PT. django-locale-chain remediază acest comportament instalând lanțuri de rezervă gettext: pentru pt-BR se încearcă pt-PT, apoi pt, înainte de a se reveni la limba implicită.

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"]})

Structura recomandată a proiectului

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

Încercați acum i18n Agent

Plasați aici fișierul de traducere

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

sau faceți clic pentru a-l selecta

Limbi țintă

Nu este necesară înregistrareaEstimare instantanee

Rezerva pentru setările regionale cu django-locale-chain

Când lipsește o cheie de traducere într-o setare regională precum pt-BR, Django trece direct la limba șablonului, fără să verifice mai întâi setarea regională părinte 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',
    ...
]

Consultați Ghidul nostru privind setările regionale de rezervă pentru lista completă a cadrelor acceptate și a celor 75 de lanțuri predefinite. Learn more →

Întrebări frecvente