Skip to main content

Django i18n : คู่มือการรองรับหลายภาษาและการแปล

ตั้งแต่ settings.py จนถึงระบบจริง กำหนดค่าระบบแปลของ Django เขียนไฟล์ PO จัดการพหูพจน์ แล้วทำให้การแปลเป็นอัตโนมัติด้วย AI

1

ติดตั้งและแยกข้อความ

เฟรมเวิร์ก i18n ของ Django มีในตัว เพียงเปิดใช้งาน ติดตั้ง django-locale-chain เพื่อใช้ภาษาสำรองอัจฉริยะ แล้วใช้ makemessages แยกข้อความที่แปลได้จากโค้ด Python กับเทมเพลตลงในไฟล์ PO

Django ใช้ GNU gettext เบื้องหลัง คำสั่ง makemessages สแกนโค้ดเพื่อหาการเรียก gettext และแท็กเทมเพลต แล้วสร้างไฟล์ .po หลังแปล compilemessages จะแปลงเป็นไฟล์ไบนารี .mo เพื่อให้ค้นหาได้รวดเร็วขณะรัน
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

กำหนดค่าการตั้งค่าและมิดเดิลแวร์

เปิดใช้การรองรับหลายภาษาใน settings.py โดยตั้ง USE_I18N = True กำหนดรายการ LANGUAGES ที่รองรับ และเพิ่ม LocaleMiddleware ลงในสแต็ก MIDDLEWARE LocaleMiddleware ตรวจหาภาษาของผู้ใช้จากคำนำหน้า URL เซสชัน คุกกี้ หรือส่วนหัว 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 ต้องอยู่หลัง SessionMiddleware เพราะอ่านเซสชัน และอยู่ก่อน CommonMiddleware หากวางผิดลำดับ การตรวจหาภาษาจะล้มเหลวโดยไม่แจ้งเตือนและผู้ใช้ทั้งหมดจะเห็น LANGUAGE_CODE เริ่มต้น

การทำให้ URL รองรับหลายภาษา

ใช้ i18n_patterns() เติมรหัสภาษาปัจจุบันเป็นคำนำหน้า URL โดยอัตโนมัติ แต่ละภาษาจึงมีเนมสเปซ URL ของตัวเอง (/en/about/, /de/about/) ซึ่งดีกว่าสำหรับ SEO และช่วยให้ผู้ใช้แชร์ลิงก์เฉพาะภาษาได้

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

ทำเครื่องหมายข้อความสำหรับแปล

Django มีฟังก์ชันแปลหลักสองแบบ ได้แก่ gettext() ซึ่งมีชื่อย่อ _() สำหรับข้อความที่ประเมินตอนรับคำขอ และ gettext_lazy() สำหรับข้อความที่ประเมินตอนนำเข้า ในเทมเพลตให้ใช้แท็ก {% trans %} และ {% blocktrans %}

ในมุมมอง (โค้ด 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}")

ในเทมเพลต

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>

ในโมเดลและแบบฟอร์ม

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() แทน gettext_lazy() ในช่องโมเดล ป้ายแบบฟอร์ม หรือแอตทริบิวต์ระดับคลาส ทำให้แปลข้อความครั้งเดียวตอนนำเข้า ซึ่งเป็นตอนเซิร์ฟเวอร์เริ่มทำงาน แทนการแปลรายคำขอ คำแปลจะค้างอยู่ในภาษาที่เปิดใช้ตอนนำเข้า ซึ่งมักเป็น LANGUAGE_CODE เริ่มต้น

รูปแบบไฟล์ PO

หลังรัน makemessages Django จะสร้างไฟล์ .po (Portable Object) สำหรับแต่ละภาษา ซึ่งมีคู่ msgid/msgstr ให้แปลค่า msgstr แล้วรัน compilemessages เพื่อสร้างไฟล์ไบนารี .mo ที่ Django อ่านขณะรัน

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

จัดการพหูพจน์และตัวแปร

Django ใช้ ngettext() สำหรับพหูพจน์ตามกฎพหูพจน์ของ GNU gettext แต่ละภาษากำหนดจำนวนรูปพหูพจน์และสูตรเลือกรูปแบบที่ถูกต้อง ไฟล์ PO ประกาศข้อมูลนี้ด้วยส่วนหัว 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}
อย่าใช้ f-string ของ Python หรือ .format() ภายในการเรียก gettext ตัวแยกคำแปลแยกวิเคราะห์ไม่ได้ ให้ใช้การจัดรูปแบบ %() แบบเดิม : _('Hello, %(name)s!') % {'name': name} วิธีนี้ยังทำให้นักแปลเห็นตัวยึดตำแหน่งตัวแปรในเครื่องมือแก้ไข PO

ทำให้คุณภาพการแปลเป็นอัตโนมัติ

ใช้ i18n-validate จับคีย์ที่หายไปและตัวยึดตำแหน่งเสียหายก่อนส่งขึ้นใช้งาน แล้วทดสอบ UI ด้วยคำแปลจำลองผ่าน i18n-pseudo ก่อนคำแปลจริงจะมาถึง

ข้อผิดพลาดที่พบบ่อย

สับสนระหว่าง gettext() กับ gettext_lazy()

ใช้ gettext_lazy() ซึ่งนำเข้าเป็น _ สำหรับช่องโมเดล ป้ายแบบฟอร์ม และแอตทริบิวต์ระดับคลาสที่ประเมินตอนนำเข้า ใช้ gettext() สำหรับฟังก์ชันมุมมองและโค้ดตอนรับคำขอ การสลับกันทำให้คำแปลค้างในภาษาตอนเซิร์ฟเวอร์เริ่มทำงาน

ลืมรัน compilemessages

Django อ่านไฟล์ .mo ที่คอมไพล์แล้ว ไม่ใช่ไฟล์ .po หากแก้ไฟล์ .po แต่ไม่รัน 'python manage.py compilemessages' การเปลี่ยนแปลงจะไม่แสดง ให้เพิ่ม compilemessages ลงในสคริปต์ดีพลอย

ไม่ได้กำหนดค่า LOCALE_PATHS

หาก LOCALE_PATHS ว่างหรือชี้ผิดไดเรกทอรี makemessages จะสร้างไฟล์ .po ผิดที่และ Django หาไม่พบขณะรัน ให้ตั้ง LOCALE_PATHS = [BASE_DIR / 'locale'] และตรวจว่าไดเรกทอรีมีอยู่เสมอ

ไม่มี i18n_patterns ใน URL

หากไม่มี i18n_patterns() URL จะไม่มีคำนำหน้าภาษาและ Django ต้องพึ่งคุกกี้/ส่วนหัวเพื่อตรวจหาภาษาเท่านั้น วิธีนี้ส่งผลเสียต่อ SEO เพราะไม่มี URL เฉพาะภาษา และทำให้การสลับภาษาผ่าน URL ใช้งานไม่ได้ ให้ครอบรูปแบบ URL ที่ผู้ใช้เห็นด้วย i18n_patterns()

การใช้ภาษาสำรองอัจฉริยะด้วย django-locale-chain

ระบบแปลของ Django จะถอยไปใช้ LANGUAGE_CODE ทันทีเมื่อไม่มีรูปแบบภาษาตามภูมิภาค ผู้ใช้ pt-BR จึงเห็นภาษาอังกฤษแม้มีคำแปล pt-PT django-locale-chain แก้ปัญหานี้ด้วยการติดตั้งลำดับสำรอง gettext โดย pt-BR จะลอง pt-PT แล้ว pt ก่อนถอยไปใช้ภาษาเริ่มต้น

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

โครงสร้างโปรเจกต์ที่แนะนำ

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

แปลรายการต่อไปนี้ได้ด้วย :

ลองใช้ i18n Agent ตอนนี้

ลากและวางไฟล์แปลของคุณที่นี่

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

หรือคลิกเพื่อเลือกไฟล์

ภาษาเป้าหมาย

ไม่ต้องลงทะเบียนประเมินราคาได้ทันที

การใช้ภาษาสำรองด้วย django-locale-chain

เมื่อไม่มีคีย์คำแปลในภาษาตามภูมิภาคอย่าง pt-BR Django จะข้ามไปใช้ภาษาเทมเพลตทันทีแทนที่จะตรวจภาษาหลัก 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',
    ...
]

ดูคู่มือการใช้ภาษาสำรองของเราสำหรับรายการเฟรมเวิร์กที่รองรับทั้งหมดและลำดับสำเร็จรูป 75 รายการ Learn more →

คำถามที่พบบ่อย