
Django i18n: Hướng dẫn quốc tế hóa và dịch thuật
Từ settings.py đến môi trường thực tế: cấu hình hệ thống dịch của Django, viết tệp PO, xử lý dạng số nhiều và tự động dịch bằng AI.
Cài đặt và trích xuất chuỗi
Django tích hợp sẵn framework i18n, bạn chỉ cần bật lên. Cài đặt django-locale-chain để dùng cơ chế dự phòng locale thông minh, rồi dùng makemessages để trích xuất các chuỗi có thể dịch từ mã Python và mẫu vào tệp PO.
pip install django-locale-chain# 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.moCấu hình cài đặt và middleware
Bật quốc tế hóa trong settings.py bằng cách đặt USE_I18N = True, xác định danh sách LANGUAGES được hỗ trợ và thêm LocaleMiddleware vào ngăn xếp MIDDLEWARE. LocaleMiddleware phát hiện ngôn ngữ của người dùng qua tiền tố URL, phiên, cookie hoặc tiêu đề Accept-Language.
# 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',
]Quốc tế hóa URL
Dùng i18n_patterns() để tự động thêm mã ngôn ngữ đang hoạt động vào đầu URL. Mỗi ngôn ngữ sẽ có không gian tên URL riêng (/en/about/, /de/about/), giúp SEO tốt hơn và cho phép người dùng chia sẻ liên kết theo ngôn ngữ.
# 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
)Đánh dấu chuỗi cần dịch
Django cung cấp hai hàm dịch chính: gettext() (bí danh _()) cho chuỗi được đánh giá khi xử lý yêu cầu và gettext_lazy() cho chuỗi được đánh giá khi import. Trong mẫu, dùng các thẻ {% trans %} và {% blocktrans %}.
Trong view (mã Python)
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}")Trong mẫu
{# 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>Trong model và biểu mẫu
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Định dạng tệp PO
Sau khi chạy makemessages, Django tạo tệp .po (Portable Object) cho từng ngôn ngữ. Các tệp này chứa cặp msgid/msgstr. Dịch giá trị msgstr, rồi chạy compilemessages để tạo tệp nhị phân .mo mà Django đọc khi chạy.
# 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"Xử lý dạng số nhiều và biến
Django dùng ngettext() cho dạng số nhiều theo quy tắc số nhiều của GNU gettext. Mỗi ngôn ngữ xác định số dạng số nhiều và công thức chọn dạng phù hợp. Tệp PO khai báo thông tin này bằng tiêu đề Plural-Forms.
# 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}Tự động kiểm soát chất lượng bản dịch
Lỗi thường gặp
Nhầm lẫn giữa gettext() và gettext_lazy()
Quên chạy compilemessages
Chưa cấu hình LOCALE_PATHS
URL thiếu i18n_patterns
Dự phòng locale thông minh với django-locale-chain
Hệ thống dịch của Django chuyển thẳng về LANGUAGE_CODE khi thiếu một biến thể khu vực. Người dùng pt-BR sẽ thấy tiếng Anh ngay cả khi bạn có bản dịch pt-PT. django-locale-chain khắc phục bằng chuỗi dự phòng gettext: pt-BR thử pt-PT, rồi pt trước khi trở về ngôn ngữ mặc định.
# 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"]})Cấu trúc dự án đề xuất
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.txtDùng thử i18n Agent ngay
Thả tệp bản dịch của bạn vào đây
JSON, YAML, PO, XML, CSV, Markdown, Properties
hoặc nhấp để duyệt
Ngôn ngữ đích
Dự phòng locale với django-locale-chain
Khi thiếu khóa dịch trong một locale khu vực như pt-BR, Django chuyển thẳng về ngôn ngữ mẫu thay vì kiểm tra locale cha pt trước.
pip install django-locale-chain# settings.py
LOCALE_CHAINS = {
'pt-BR': ['pt', 'es', 'en'],
'zh-Hant-HK': ['zh-Hant', 'zh', 'en'],
}
MIDDLEWARE = [
...
'django_locale_chain.middleware.LocaleChainMiddleware',
...
]Xem Hướng dẫn dự phòng locale để biết danh sách đầy đủ các framework được hỗ trợ và 75 chuỗi tích hợp sẵn. Learn more →