# Обработка многоязычности в Django: как избежать трагедии «Polish» → «польский язык» (Контекстные маркеры)
Когда вы действительно поддерживаете многоязычность (i18n), вы, вероятно, сталкивались с такими ситуациями:
* Кнопка помечена как **“Polish”** (смысл «отшлифовать»), но переводится как **“польский язык”**
* В компоненте выбора даты **“May”** переводится как имя человека в некоторых языках
* В меню “Book” переводится только как «книга», но не как «забронировать».
Причина проста.
> Компьютер не понимает «контекст». Если строки одинаковы, он считает их одинаковыми.
В этой статье мы покажем, как использовать **Контекстные маркеры** в Django, чтобы оставить исходный код неизменным, но при этом привязать к одной и той же строке разные переводы.
---
## Почему это происходит? Базовый принцип gettext {#sec-aff43a618865}
Система перевода Django использует GNU gettext. Он работает по очень простым правилам:
* Исходная строка → `msgid`
* Перевод → `msgstr`
* Если `msgid` одинаковы, то используется один и тот же `msgstr`
```po
# django.po
msgid "Polish"
msgstr "польский язык"
```
После такой записи, где бы вы ни использовали «Polish», будет применён один и тот же перевод.
Таким образом, два UI‑элемента не различаются:
* «Polish» = глагол «отшлифовать»
* «Polish» = название языка/страны
Чтобы избежать этого, иногда меняют исходный код:
```html
{% trans "Polish (verb)" %}
{% trans "Polish (language)" %}
```
Но это приводит к тому, что видимая строка становится странной, а переиспользование становится трудным.
Наша цель:
* Оставить исходную строку как `"Polish"`
* Добавить отдельное описание того, в каком контексте она используется
Для этого используется **Контекстный маркер**.
---
## Решение в шаблонах: `{% translate %}` + `context` {#sec-e28151952ba2}
В шаблонах Django можно добавить context к тегу {% translate %} (или к старому {% trans %}):
### 1) Исходный код (конфликт) {#sec-40afc992a96b}
```html
{% load i18n %}
{% translate "Polish" %}
```
В `.po` файле обе строки имеют `msgid "Polish"`, поэтому переводится только один вариант.
### 2) Улучшенный код (с контекстом) {#sec-4fa05f04b9d3}
```html
{% load i18n %}
{% translate "Polish" context "language name" %}
```
Важно:
* Текст после `context` не виден пользователю.
* Это мета‑информация для переводчика.
* Описывайте контекст коротко и ясно.
* `"verb: to refine UI"`
* `"language name"`
* `"menu label"`
* `"button text"`
### 3) Можно использовать и в `{% blocktranslate %}` {#sec-c2a9f27dea01}
```html
{% load i18n %}
{% blocktranslate context "greeting message" with username=user.username %}
Hello {{ username }}
{% endblocktranslate %}
```
Таким образом, одна и та же строка может использоваться в разных контекстах.
---
## Решение в Python‑коде: `pgettext` {#sec-3a9a008d3f99}
В коде (views, models, forms) вместо `gettext` используют функции `pgettext` и его варианты.
* `pgettext(context, message)`
* `pgettext_lazy(context, message)` – ленивый вызов
* `npgettext(context, singular, plural, number)` – множественное число + контекст
### 1) Пример {#sec-27f89a09cea2}
```python
from django.utils.translation import pgettext
def my_view(request):
# 1. Название месяца "May"
month = pgettext("month name", "May")
# 2. Имя человека "May"
person = pgettext("person name", "May")
# 3. Модальный глагол "may"
verb = pgettext("auxiliary verb", "may")
```
### 2) Использование `pgettext_lazy` в модели {#sec-11832a96275e}
```python
from django.db import models
from django.utils.translation import pgettext_lazy
class Order(models.Model):
type = models.CharField(
verbose_name=pgettext_lazy("order model field", "Order type"),
max_length=20,
)
STATUS_CHOICES = [
("open", pgettext_lazy("order status", "Open")),
("opened", pgettext_lazy("log action", "Open")),
]
status = models.CharField(
max_length=20,
choices=STATUS_CHOICES,
)
```
### 3) Множественное число: `npgettext` {#sec-ed84f3cf8d15}
```python
from django.utils.translation import npgettext
def get_notification(count):
return npgettext(
"user notification",
"You have %(count)d message",
"You have %(count)d messages",
count
) % {"count": count}
```
---
## Как выглядит `.po` файл? {#sec-fb610023c81a}
После добавления контекста, при выполнении `python manage.py makemessages -l ko` в `.po` появляется поле `msgctxt`.
```po
# django.po
# 1) "Polish" = глагол «отшлифовать»
msgctxt "verb: to refine UI"
msgid "Polish"
msgstr "отшлифовать"
# 2) "Polish" = название языка/страны
msgctxt "language name"
msgid "Polish"
msgstr "польский язык"
```
`msgid` одинаковый, но `msgctxt` различается, поэтому переводчики видят два разных элемента.
---
## Как писать хороший контекст? {#sec-df37b35ab408}
Контекст не виден пользователю, но является важной подсказкой для переводчика.
### 1) Описывайте роль {#sec-9dfe29490c86}
* `"button label"`
* `"menu item"`
* `"tooltip"`
* `"error message"`
* `"form field label"`
### 2) Добавляйте доменную концепцию {#sec-135dce4b280b}
* `"File"` – `"file menu item"`, `"uploaded file object"`
* `"Order"` – `"e-commerce order"`, `"sorting order"`
### 3) Не включайте перевод в контекст {#sec-9197b1a38f27}
Контекст должен быть на исходном языке (обычно английском) и описывать концепцию.
---
## Когда использовать контекстные маркеры? {#sec-58e84d2a2b4c}
Если вы сталкиваетесь с одним из следующих случаев, обязательно применяйте контекст:
1. Короткие строки (1–2 слова) – кнопки, вкладки, пункты меню.
2. Строки, используемые в разных значениях в UI – `"Open"`, `"Book"`, `"Order"`.
3. Переводчики работают без доступа к экрану – внешние платформы, файлы `.po`.
---
## Итоги {#sec-8cc5759d0812}

* Django по умолчанию сопоставляет один перевод с одним `msgid`.
* Поэтому омонимия часто приводит к конфликтам.
* Не меняйте исходный текст. Вместо этого:
* В шаблонах используйте `{% translate "…" context "…" %}`.
* В Python‑коде – `pgettext`, `pgettext_lazy`, `npgettext`.
* Это добавит `msgctxt` в `.po`, позволяя иметь разные переводы для одной строки.
Таким образом, вы сохраняете читаемость кода и повышаете качество и поддерживаемость переводов.
---
**Похожие статьи**
- [Разница между gettext и gettext_lazy в Django: когда использовать](/ko/whitedec/2026/1/5/django-gettext-vs-gettext-lazy/)
- [Проблемы с gettext_lazy в JSON‑ключах и их решение](/ko/whitedec/2025/4/26/gettext-lazy-json-key-problem-solution/)