# Обработка многоязычности в 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} ![Размышляющий разработчик](/media/editor_temp/6/14112b82-d049-4b32-9275-02429e9305c0.png) * 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/)