Skip to main content

Django i18n: handleiding voor internationalisatie en vertalingen

Van settings.py tot productie: configureer het vertaalsysteem van Django, schrijf PO-bestanden, verwerk meervoudsvormen en automatiseer vertalingen met AI.

1

Tekenreeksen installeren en extraheren

Het i18n-framework is ingebouwd in Django; je hoeft het alleen in te schakelen. Installeer django-locale-chain voor slimme localeterugval en gebruik vervolgens makemessages om vertaalbare tekenreeksen uit je Python-code en sjablonen naar PO-bestanden te extraheren.

Onder de motorkap gebruikt Django GNU gettext. De opdracht makemessages scant je code op gettext-aanroepen en sjabloontags en genereert vervolgens .po-bestanden. Na het vertalen zet compilemessages deze om in binaire .mo-bestanden voor snelle zoekacties tijdens runtime.
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

Instellingen en middleware configureren

Schakel internationalisatie in settings.py in door USE_I18N = True in te stellen, de lijst met ondersteunde LANGUAGES te definiëren en LocaleMiddleware aan je MIDDLEWARE-stack toe te voegen. LocaleMiddleware detecteert de taal van de gebruiker via het URL-voorvoegsel, de sessie, cookies of de Accept-Language-header.

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 moet na SessionMiddleware (het leest de sessie) en vóór CommonMiddleware staan. Bij een verkeerde volgorde mislukt taaldetectie zonder melding en zien alle gebruikers de standaard-LANGUAGE_CODE.

URL-internationalisatie

Gebruik i18n_patterns() om automatisch de actieve taalcode voor je URL's te plaatsen. Zo krijgt elke taal een eigen URL-naamruimte (/en/about/, /de/about/), wat beter is voor SEO en gebruikers taalspecifieke koppelingen laat delen.

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

Strings markeren voor vertaling

Django biedt twee belangrijke vertaalfuncties: gettext() (met alias _()) voor tekenreeksen die tijdens een verzoek worden geëvalueerd en gettext_lazy() voor tekenreeksen die tijdens het importeren worden geëvalueerd. Gebruik in sjablonen de tags {% trans %} en {% blocktrans %}.

In views (Python-code)

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

In sjablonen

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>

In modellen en formulieren

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
gettext() in plaats van gettext_lazy() gebruiken in modelvelden, formulierlabels of attributen op klasseniveau zorgt ervoor dat tekenreeksen één keer tijdens het importeren (bij het starten van de server) worden vertaald, niet per verzoek. De vertaling blijft staan in de taal die tijdens de import actief was, doorgaans de standaard-LANGUAGE_CODE.

PO-bestandsindeling

Na het uitvoeren van makemessages genereert Django voor elke taal .po-bestanden (Portable Object). Deze bevatten msgid/msgstr-paren. Vertaal de msgstr-waarden en voer daarna compilemessages uit om de binaire .mo-bestanden te genereren die Django tijdens runtime leest.

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

Meervoudsvormen en variabelen verwerken

Django gebruikt ngettext() voor meervoudsvormen volgens de regels van GNU gettext. Elke taal bepaalt hoeveel meervoudsvormen deze heeft en met welke formule de juiste vorm wordt gekozen. PO-bestanden leggen dit vast in de header 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}
Gebruik nooit Python-f-strings of .format() in gettext-aanroepen; de vertaalextractor kan deze niet verwerken. Gebruik de oude %()-notatie: _('Hello, %(name)s!') % {'name': name}. Zo zien vertalers de variabele placeholders ook in hun PO-editor.

Kwaliteitscontrole van vertalingen automatiseren

Vind ontbrekende sleutels en kapotte plaatsaanduidingen vóór de release met i18n-validate. Test je gebruikersinterface met pseudovertalingen uit i18n-pseudo voordat de echte vertalingen klaar zijn.

Veelvoorkomende valkuilen

Verwarring tussen gettext() en gettext_lazy()

Gebruik gettext_lazy() (geïmporteerd als _) voor modelvelden, formulierlabels en attributen op klasseniveau die tijdens de import worden geëvalueerd. Gebruik gettext() voor viewfuncties en code die tijdens een verzoek wordt uitgevoerd. Als je ze verwisselt, blijven vertalingen in de opstarttaal van de server staan.

compilemessages vergeten uit te voeren

Django leest gecompileerde .mo-bestanden, geen .po-bestanden. Als je een .po-bestand bewerkt en 'python manage.py compilemessages' niet uitvoert, verschijnen je wijzigingen niet. Voeg compilemessages toe aan je implementatiescript.

LOCALE_PATHS niet geconfigureerd

Als LOCALE_PATHS leeg is of naar de verkeerde map verwijst, maakt makemessages .po-bestanden op de verkeerde plek en kan Django ze tijdens runtime niet vinden. Stel altijd LOCALE_PATHS = [BASE_DIR / 'locale'] in en controleer of de map bestaat.

i18n_patterns ontbreekt in URL's

Zonder i18n_patterns() hebben URL's geen taalvoorvoegsel en vertrouwt Django voor taaldetectie alleen op cookies en headers. Dat schaadt SEO (geen taalspecifieke URL's) en maakt wisselen van taal via de URL onmogelijk. Omwikkel je gebruikersgerichte URL-patronen met i18n_patterns().

Slimme localeterugval met django-locale-chain

Het vertaalsysteem van Django valt bij een ontbrekende regionale variant rechtstreeks terug op LANGUAGE_CODE. Een gebruiker met pt-BR ziet Engels, zelfs als er vertalingen voor pt-PT zijn. django-locale-chain verhelpt dit met gettext-terugvalketens: pt-BR probeert eerst pt-PT, daarna pt en vervolgens de standaardtaal.

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

Aanbevolen projectstructuur

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

Probeer i18n Agent nu

Zet je vertaalbestand hier neer

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

of klik om een bestand te selecteren

Doeltalen

Geen registratie nodigDirecte prijsindicatie

Localeterugval met django-locale-chain

Wanneer een vertaalsleutel ontbreekt in een regionale locale zoals pt-BR, schakelt Django rechtstreeks over op de sjabloontaal in plaats van eerst de bovenliggende locale pt te controleren.

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',
    ...
]

Bekijk onze handleiding voor locale-fallbacks voor de volledige lijst met ondersteunde frameworks en 75 ingebouwde ketens. Learn more →

Veelgestelde vragen