Skip to main content

Django i18n: ръководство за интернационализация и превод

От settings.py до производствената среда: конфигурирайте системата за превод на Django, създавайте PO файлове, обработвайте множествено число и автоматизирайте преводите с ИИ.

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

Конфигурирайте настройките и middleware

Активирайте интернационализацията в 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}
Никога не използвайте Python f-strings или .format() в извиквания на gettext. Инструментът за извличане на преводи не може да ги анализира. Използвайте старото форматиране с %(): _('Hello, %(name)s!') % {'name': name}. Така преводачите ще виждат заместващите параметри и в своите редактори на PO файлове.

Автоматизирайте контрола на качеството на преводите

Откривайте липсващи ключове и повредени заместващи параметри с i18n-validate, преди да достигнат до потребителите. Тествайте интерфейса си с псевдопреводи чрез 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 →

Често задавани въпроси