Skip to main content

Django i18n: Panduan Internasionalisasi & Terjemahan

Dari settings.py hingga produksi: konfigurasikan sistem terjemahan Django, tulis file PO, tangani bentuk jamak, dan otomatiskan penerjemahan dengan AI.

1

Instal & Ekstrak String

Framework i18n Django sudah tersedia—Anda hanya perlu mengaktifkannya. Instal django-locale-chain untuk fallback bahasa cerdas, lalu gunakan makemessages untuk mengekstrak string yang dapat diterjemahkan dari kode Python dan templat ke file PO.

Django menggunakan GNU gettext. Perintah makemessages memindai kode untuk panggilan gettext dan tag templat, lalu membuat file .po. Setelah diterjemahkan, compilemessages mengonversinya menjadi file biner .mo untuk pencarian cepat saat 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

Konfigurasikan Pengaturan & Middleware

Aktifkan internasionalisasi dalam settings.py dengan mengatur USE_I18N = True, menentukan daftar LANGUAGES yang didukung, dan menambahkan LocaleMiddleware ke stack MIDDLEWARE. LocaleMiddleware mendeteksi bahasa pengguna dari awalan URL, sesi, cookie, atau header 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 harus ditempatkan setelah SessionMiddleware (karena membaca sesi) dan sebelum CommonMiddleware. Jika urutannya salah, deteksi bahasa gagal tanpa pesan dan semua pengguna melihat LANGUAGE_CODE default.

Internasionalisasi URL

Gunakan i18n_patterns() untuk menambahkan kode bahasa aktif sebagai awalan URL secara otomatis. Ini memberi setiap bahasa namespace URL sendiri (/en/about/, /de/about/), yang lebih baik untuk SEO dan memungkinkan pengguna membagikan tautan spesifik bahasa.

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

Tandai String untuk Diterjemahkan

Django menyediakan dua fungsi terjemahan utama: gettext() (alias _()) untuk string yang dievaluasi saat permintaan dan gettext_lazy() untuk string yang dievaluasi saat impor. Dalam templat, gunakan tag {% trans %} dan {% blocktrans %}.

Dalam View (Kode 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}")

Dalam Templat

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>

Dalam Model & Formulir

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
Menggunakan gettext() alih-alih gettext_lazy() dalam bidang model, label formulir, atau atribut tingkat kelas menyebabkan string diterjemahkan sekali saat impor (saat server dimulai), bukan untuk setiap permintaan. Terjemahan akan terkunci dalam bahasa yang aktif selama impor—biasanya LANGUAGE_CODE default.

Format File PO

Setelah makemessages dijalankan, Django membuat file .po (Portable Object) untuk setiap bahasa. File ini berisi pasangan msgid/msgstr. Terjemahkan nilai msgstr, lalu jalankan compilemessages untuk membuat file biner .mo yang dibaca Django saat runtime.

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

Tangani Bentuk Jamak & Variabel

Django menggunakan ngettext() untuk bentuk jamak sesuai aturan bentuk jamak GNU gettext. Setiap bahasa menentukan jumlah bentuk jamak dan rumus untuk memilih bentuk yang benar. File PO menyatakannya dengan 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}
Jangan gunakan f-string Python atau .format() dalam panggilan gettext. Ekstraktor terjemahan tidak dapat mengurainya. Gunakan pemformatan %() gaya lama: _('Hello, %(name)s!') % {'name': name}. Ini juga memastikan penerjemah melihat placeholder variabel dalam editor PO.

Otomatiskan Kualitas Terjemahan

Temukan kunci hilang dan placeholder rusak sebelum dirilis dengan i18n-validate. Uji UI dengan terjemahan semu menggunakan i18n-pseudo sebelum terjemahan asli tersedia.

Kesalahan Umum

Kebingungan gettext() vs gettext_lazy()

Gunakan gettext_lazy() (diimpor sebagai _) untuk bidang model, label formulir, dan atribut tingkat kelas yang dievaluasi saat impor. Gunakan gettext() untuk fungsi view dan kode saat permintaan. Jika tertukar, terjemahan akan terkunci dalam bahasa saat server dimulai.

Lupa Menjalankan compilemessages

Django membaca file .mo (terkompilasi), bukan file .po. Jika Anda mengedit file .po dan tidak menjalankan 'python manage.py compilemessages', perubahan tidak akan tampil. Tambahkan compilemessages ke skrip deployment.

LOCALE_PATHS Belum Dikonfigurasi

Jika LOCALE_PATHS kosong atau menunjuk ke direktori salah, makemessages membuat file .po di tempat salah dan Django tidak dapat menemukannya saat runtime. Selalu atur LOCALE_PATHS = [BASE_DIR / 'locale'] dan pastikan direktorinya ada.

i18n_patterns Hilang dalam URL

Tanpa i18n_patterns(), URL tidak memiliki awalan bahasa dan Django hanya mengandalkan cookie/header untuk deteksi bahasa. Ini merugikan SEO (tidak ada URL spesifik bahasa) dan merusak pengalihan bahasa melalui URL. Bungkus pola URL yang menghadap pengguna dengan i18n_patterns().

Fallback Bahasa Cerdas dengan django-locale-chain

Sistem terjemahan Django langsung beralih ke LANGUAGE_CODE saat varian regional hilang. Pengguna pt-BR melihat bahasa Inggris meskipun Anda memiliki terjemahan pt-PT. django-locale-chain memperbaikinya dengan memasang rantai fallback gettext: pt-BR mencoba pt-PT, lalu pt, sebelum beralih ke bahasa default.

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

Struktur Proyek yang Disarankan

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

Coba i18n Agent Sekarang

Lepaskan file terjemahan Anda di sini

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

atau klik untuk menjelajahi

Bahasa target

Tidak perlu mendaftarEstimasi instan

Fallback Bahasa dengan django-locale-chain

Saat kunci terjemahan tidak ada dalam bahasa regional seperti pt-BR, Django langsung beralih ke bahasa templat alih-alih memeriksa bahasa induk pt terlebih dahulu.

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

Lihat Panduan Fallback Bahasa kami untuk daftar lengkap framework yang didukung dan 75 rantai bawaan. Learn more →

Pertanyaan yang Sering Diajukan