ब्लॉग पर वापस जाएं

Django अनुवाद क्यों टूटते हैं (और 10 सबसे सामान्य कारणों को कैसे ठीक करें)

2026-02-04 11 मिनट पढ़ने का समय
Django अनुवाद क्यों टूटते हैं (और 10 सबसे सामान्य कारणों को कैसे ठीक करें)

आपने अनुवाद के लिए स्ट्रिंग्स को मार्क किया, .po फ़ाइलें जनरेट कीं, compilemessages चलाया, और आपकी ऐप अभी भी अंग्रेज़ी दिखा रही है। आप अकेले नहीं हैं। Django का i18n फ्रेमवर्क शक्तिशाली है, लेकिन इसमें ऐसी तीखी धारें हैं जो अनुभवी डेवलपर्स को भी पकड़ लेती हैं।

यह गाइड Django अनुवादों के चुपचाप विफल होने के 10 सबसे आम कारणों को कवर करती है, साथ ही प्रत्येक के लिए सटीक लक्षण और समाधान।

1. .po फ़ाइलें संपादित करने के बाद compilemessages चलाना भूलना

आपने एक .po फ़ाइल संपादित की (मैन्युअली या किसी टूल से), लेकिन अनुवादित टेक्स्ट कभी दिखाई नहीं देता। ऐप मूल अंग्रेज़ी स्ट्रिंग्स दिखाती रहती है।

Django रनटाइम पर .po फ़ाइलें नहीं पढ़ता। यह संकलित .mo (मशीन ऑब्जेक्ट) बाइनरी फ़ाइलें पढ़ता है। यदि आप .po फ़ाइल को बिना रीकंपाइल किए संपादित करते हैं, तो Django को कोई पता नहीं चलता कि कुछ बदला है।

हर .po फ़ाइल बदलाव के बाद compilemessages चलाएं:

python manage.py compilemessages

यदि आप TranslateBot से अपने अनुवाद स्वचालित करते हैं, तो अपने वर्कफ़्लो के अंतिम चरण के रूप में compilemessages जोड़ें:

python manage.py makemessages -a --no-obsolete
python manage.py translate
python manage.py compilemessages

2. टेम्पलेट्स में {% load i18n %} गायब

आप एक टेम्पलेट में {% trans "Hello" %} का उपयोग करते हैं, लेकिन Django एक TemplateSyntaxError उठाता है। या इससे भी बुरा, यदि आपका टेम्पलेट इंजन गलत तरीके से कॉन्फ़िगर किया गया है तो टैग चुपचाप कुछ नहीं करता।

{% trans %} और {% blocktrans %} टैग Django की i18n टेम्पलेट टैग लाइब्रेरी में हैं। इसे लोड किए बिना, टेम्पलेट इंजन उन्हें पहचान नहीं पाता।

हर उस टेम्पलेट के शीर्ष पर {% load i18n %} जोड़ें जो अनुवाद टैग का उपयोग करता है:

{% load i18n %}

<h1>{% trans "Welcome to our site" %}</h1>
<p>{% blocktrans with name=user.name %}Hello, {{ name }}!{% endblocktrans %}</p>

यह एक प्रति-टेम्पलेट आवश्यकता है। भले ही एक पैरेंट टेम्पलेट i18n लोड करता हो, अनुवाद टैग का उपयोग करने वाले चाइल्ड टेम्पलेट्स को अपनी स्वयं की {% load i18n %} घोषणा की आवश्यकता होती है।

3. LocaleMiddleware MIDDLEWARE में नहीं है या गलत स्थिति में है

ब्राउज़र के Accept-Language हेडर, URL प्रीफ़िक्स, या सेशन सेटिंग्स की परवाह किए बिना Django हमेशा डिफ़ॉल्ट भाषा में कंटेंट परोसता है।

LocaleMiddleware प्रत्येक अनुरोध के लिए सक्रिय भाषा निर्धारित करता है। इसके बिना, Django LANGUAGE_CODE पर डिफ़ॉल्ट हो जाता है और सभी भाषा-चयन तंत्रों को अनदेखा करता है। मिडलवेयर स्टैक में इसकी स्थिति भी मायने रखती है, क्योंकि इसे सेशन डेटा और URL रिज़ॉल्यूशन तक पहुंच की आवश्यकता होती है।

अपनी MIDDLEWARE सेटिंग में LocaleMiddleware जोड़ें, SessionMiddleware और CommonMiddleware के बाद:

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.locale.LocaleMiddleware",  # Must be after SessionMiddleware
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
]

यदि आप URL-आधारित भाषा स्विचिंग का उपयोग करते हैं तो यह भी सुनिश्चित करें कि django.conf.urls.i18n आपकी URL कॉन्फ़िगरेशन में शामिल है:

from django.conf.urls.i18n import i18n_patterns

urlpatterns = i18n_patterns(
    path("", include("myapp.urls")),
)

4. भाषा कोड बेमेल (जैसे pt-br बनाम pt_BR)

अनुवाद आपकी .po फ़ाइलों में मौजूद हैं, compilemessages सफल होता है, लेकिन Django कुछ लोकेल के लिए अनुवाद को अनदेखा करता है।

Django अपेक्षा करता है कि लोकेल डायरेक्ट्रीज़ <language>_<COUNTRY> प्रारूप का पालन करें, अंडरस्कोर विभाजक और अपरकेस कंट्री कोड के साथ। उदाहरण के लिए, ब्राज़ीलियन पुर्तगाली के लिए pt_BR। यदि आपकी डायरेक्ट्री का नाम pt-br, pt-BR, या ptBR है, तो Django इसे नहीं खोज पाएगा। यही बात LANGUAGES सेटिंग पर भी लागू होती है: वहां के कोड हाइफ़न (pt-br) का उपयोग करते हैं, लेकिन फ़ाइलसिस्टम अंडरस्कोर (pt_BR) का उपयोग करता है।

सुनिश्चित करें कि आपकी डायरेक्ट्री संरचना Django की अपेक्षाओं से मेल खाती है:

locale/
    pt_BR/
        LC_MESSAGES/
            django.po
            django.mo

और अपनी सेटिंग्स में, हाइफ़नेटेड फ़ॉर्म का उपयोग करें:

LANGUAGES = [
    ("en", "English"),
    ("pt-br", "Brazilian Portuguese"),
    ("zh-hans", "Simplified Chinese"),
]

makemessages चलाते समय, लोकेल फ़्लैग के लिए अंडरस्कोर फ़ॉर्म का उपयोग करें:

python manage.py makemessages -l pt_BR

5. Fuzzy प्रविष्टियां संकलन के दौरान चुपचाप छोड़ दी जाती हैं

अनुवाद .po फ़ाइल में मौजूद है, लेकिन Django उस विशिष्ट प्रविष्टि के लिए रनटाइम पर मूल अंग्रेज़ी स्ट्रिंग दिखाता है। यह विशेष रूप से निराशाजनक है क्योंकि अनुवाद वहीं फ़ाइल में है।

जब Django का makemessages पता लगाता है कि एक सोर्स स्ट्रिंग में थोड़ा बदलाव हुआ है, तो यह मौजूदा अनुवाद को "fuzzy" (अर्थात यह एक अनुमान है जिसे मानवीय समीक्षा की आवश्यकता है) के रूप में चिह्नित करता है। compilemessages कमांड सभी fuzzy प्रविष्टियों को छोड़ देता है, उन्हें अनअनुवादित मानते हुए। इसलिए प्रविष्टि .po फ़ाइल में अनुवादित दिखती है, लेकिन .mo फ़ाइल इसे पूरी तरह बाहर कर देती है।

एक fuzzy प्रविष्टि इस तरह दिखती है:

#, fuzzy
msgid "Welcome to our website!"
msgstr "Welkom op onze website!"

अनुवाद की समीक्षा करें, आवश्यकता होने पर msgstr अपडेट करें, फिर #, fuzzy फ़्लैग हटाएं:

msgid "Welcome to our website!"
msgstr "Welkom op onze website!"

फिर पुनः संकलित करें:

python manage.py compilemessages

बड़ी परियोजनाओं में, fuzzy प्रविष्टियां जमा होती जाती हैं और उन्हें चूकना आसान है। TranslateBot का check_translations कमांड इन्हें स्वचालित रूप से पकड़ता है:

python manage.py check_translations
locale/nl/LC_MESSAGES/django.po: 0 untranslated, 3 fuzzy
CommandError: Translation check failed

इसे अपनी CI पाइपलाइन में check_translations --makemessages के साथ जोड़ें और आप कभी भी fuzzy प्रविष्टि शिप नहीं करेंगे।

6. LOCALE_PATHS कॉन्फ़िगर नहीं है या गलत डायरेक्ट्री की ओर इशारा करता है

makemessages एक स्थान पर .po फ़ाइलें बनाता है, लेकिन Django उन्हें कहीं और खोजता है। अनुवाद डिस्क पर मौजूद हैं लेकिन कभी लोड नहीं होते।

Django एक विशिष्ट क्रम में अनुवाद फ़ाइलें खोजता है: पहले LOCALE_PATHS डायरेक्ट्रीज़, फिर प्रत्येक ऐप की locale/ डायरेक्ट्री, और अंत में प्रोजेक्ट की locale/ डायरेक्ट्री। यदि LOCALE_PATHS सेट नहीं है या गलत पथ की ओर इशारा करता है, तो Django आपकी .po फ़ाइलें कभी नहीं खोज सकता।

अपनी सेटिंग्स में LOCALE_PATHS को एक एब्सोल्यूट पथ पर सेट करें:

from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent

LOCALE_PATHS = [
    BASE_DIR / "locale",
]

सत्यापित करें कि डायरेक्ट्री मौजूद है और अपेक्षित संरचना रखती है:

locale/
    de/
        LC_MESSAGES/
            django.po
            django.mo
    nl/
        LC_MESSAGES/
            django.po
            django.mo

एक सामान्य गलती LOCALE_PATHS को locale/ (रिलेटिव) पर सेट करना है, एब्सोल्यूट पथ के बजाय। Django आपकी प्रोजेक्ट रूट से रिलेटिव पथों को हल नहीं करता। यह प्रोसेस की वर्किंग डायरेक्ट्री पर निर्भर करता है, जो अक्सर वह नहीं होती जिसकी आप अपेक्षा करते हैं।

7. कैश पुराने अनुवाद परोस रहा है

आपने अनुवाद अपडेट और संकलित किए, लेकिन पुराना टेक्स्ट दिखाई देता रहता है। सर्वर को पुनः आरंभ करने से यह ठीक हो जाता है।

Django का अनुवाद कैटलॉग प्रति प्रोसेस एक बार लोड होता है। प्रोडक्शन में, Gunicorn या Uvicorn जैसे WSGI/ASGI सर्वर वर्कर प्रोसेस को लंबे समय तक जीवित रखते हैं। .mo फ़ाइल डिस्क पर बदल चुकी हो सकती है, लेकिन चल रहे प्रोसेस में अभी भी पुराने अनुवाद मेमोरी में हैं। इसके अलावा, यदि आप Django के कैश फ्रेमवर्क या Nginx या Cloudflare जैसे रिवर्स प्रॉक्सी का उपयोग करते हैं, तो कैश्ड रिस्पॉन्स एक्सपायर होने तक पुराना कंटेंट परोसेंगे।

नए अनुवाद तैनात करने के बाद अपना एप्लिकेशन सर्वर पुनः आरंभ करें:

# Gunicorn
kill -HUP $(cat /tmp/gunicorn.pid)

# Systemd
sudo systemctl restart myapp

# Docker
docker compose restart web

Django के कैश फ्रेमवर्क के लिए, अनुवाद अपडेट करने के बाद कैश साफ़ करें:

from django.core.cache import cache
cache.clear()

डेवलपमेंट में, runserver Python फ़ाइलें बदलने पर ऑटो-रीलोड करता है लेकिन .mo फ़ाइलें नहीं देखता। compilemessages चलाने के बाद आपको इसे मैन्युअली पुनः आरंभ करना होगा।

8. स्ट्रिंग्स gettext (_() या {% trans %}) में रैप नहीं हैं

makemessages कुछ स्ट्रिंग्स को एक्सट्रैक्ट नहीं करता, इसलिए वे कभी आपकी .po फ़ाइलों में नहीं दिखती और कभी अनुवादित नहीं होतीं। यह सबसे बुनियादी समस्या है और बड़े कोडबेस में सबसे आसानी से अनदेखी होने वाली भी।

Django का makemessages कमांड अनुवाद मार्कर के लिए आपके सोर्स कोड को स्कैन करने के लिए xgettext का उपयोग करता है। यदि कोई स्ट्रिंग gettext() (आमतौर पर _() के रूप में उपनामित), gettext_lazy(), {% trans %}, या {% blocktrans %} में रैप नहीं है, तो यह एक्सट्रैक्शन प्रक्रिया के लिए अदृश्य है।

हर उपयोगकर्ता-सामना करने वाली स्ट्रिंग को रैप करें:

# Python code
from django.utils.translation import gettext_lazy as _

class Article(models.Model):
    class Meta:
        verbose_name = _("article")
        verbose_name_plural = _("articles")

# Views
from django.utils.translation import gettext as _

def my_view(request):
    message = _("Your changes have been saved.")
    return HttpResponse(message)
<!-- Templates -->
{% load i18n %}
<h1>{% trans "Welcome" %}</h1>
<p>{% blocktrans with count=items|length %}You have {{ count }} items.{% endblocktrans %}</p>

TranslateBot के --dry-run फ़्लैग का उपयोग करके वर्तमान में अनअनुवादित स्ट्रिंग्स का पूर्वावलोकन करें, ताकि आप एक्सट्रैक्शन के दौरान छूटी हुई स्ट्रिंग्स पकड़ सकें:

python manage.py translate --target-lang de --dry-run

यह आपकी .po फ़ाइलों में सभी अनअनुवादित प्रविष्टियों को बिना कोई API कॉल या बदलाव किए दिखाता है।

9. f-string का अनुवाद नहीं किया जा सकता (Django सीमा)

आप _() में एक f-string रैप करते हैं और या तो सिंटैक्स एरर मिलता है, या makemessages एक टूटी/आंशिक स्ट्रिंग एक्सट्रैक्ट करता है जिसका अनुवाद नहीं किया जा सकता।

Python f-string रनटाइम पर इवैल्यूएट होती है। xgettext एक्सट्रैक्शन टूल सोर्स कोड को स्टैटिकली पार्स करता है, इसलिए यह {} ब्रेसेज़ के अंदर Python एक्सप्रेशन को इवैल्यूएट नहीं कर सकता। इसका मतलब है कि _(f"Hello, {name}") एक ऐसी स्ट्रिंग के रूप में एक्सट्रैक्ट होती है जिसमें एक लिटरल {name} एक्सप्रेशन है (या पूरी तरह एक्सट्रैक्ट होने में विफल), और परिणामी .po प्रविष्टि कभी रनटाइम स्ट्रिंग से मेल नहीं खाएगी।

इसके बजाय Django के % फ़ॉर्मेटिंग या नामित प्लेसहोल्डर्स के साथ .format() का उपयोग करें:

# Wrong -- f-string cannot be extracted
message = _(f"Hello, {user.name}! You have {count} new messages.")

# Correct -- named placeholders
message = _("Hello, %(name)s! You have %(count)d new messages.") % {
    "name": user.name,
    "count": count,
}

# Also correct -- .format() with positional args
message = _("Hello, {0}! You have {1} new messages.").format(user.name, count)

यह TranslateBot या टूलिंग की सीमा नहीं है। यह gettext के काम करने के तरीके के लिए मौलिक है। सोर्स स्ट्रिंग एक स्टैटिक लिटरल होनी चाहिए ताकि इसे रनटाइम पर एक्सट्रैक्ट और लुकअप किया जा सके।

TranslateBot अनुवाद के दौरान इन सभी प्लेसहोल्डर फ़ॉर्मेट (%(name)s, {0}, %s, HTML टैग) को संरक्षित रखता है, इसलिए अनुवादित स्ट्रिंग्स पूरी तरह कार्यात्मक रहती हैं।

10. प्लेसहोल्डर फ़ॉर्मेट स्ट्रिंग एरर .po संकलन को तोड़ रहे हैं

compilemessages एक एरर के साथ विफल होता है, या .po फ़ाइल में #, python-format फ़्लैग बेमेल है, और प्रविष्टि चुपचाप हटा दी जाती है।

जब एक सोर्स स्ट्रिंग में %(name)s जैसे Python फ़ॉर्मेट प्लेसहोल्डर होते हैं, तो Django .po प्रविष्टि को #, python-format से चिह्नित करता है। यदि अनुवाद में अलग प्लेसहोल्डर हैं (एक टाइपो जैसे %(nome)s, एक गायब प्लेसहोल्डर, या एक अतिरिक्त) तो gettext टूल प्रविष्टि को अस्वीकार कर सकते हैं या compilemessages विफल हो सकता है। यह आमतौर पर मैन्युअल अनुवादों या ऐसे AI अनुवाद टूल्स के साथ होता है जो प्लेसहोल्डर सेमांटिक्स को नहीं समझते।

एक टूटी प्रविष्टि इस तरह दिखती है:

#, python-format
msgid "Hello, %(name)s! You have %(count)d new messages."
msgstr "Hallo, %(naam)s! Je hebt %(count)d nieuwe berichten."

यहां %(naam)s को %(name)s होना चाहिए। प्लेसहोल्डर्स को सोर्स से बिल्कुल मेल खाना चाहिए।

सुनिश्चित करें कि अनुवादित स्ट्रिंग्स में सोर्स के समान सटीक प्लेसहोल्डर हों। टाइपो, गायब प्लेसहोल्डर, और अतिरिक्त प्लेसहोल्डर की जांच करें।

यह एक ऐसा क्षेत्र है जहां TranslateBot वास्तविक लाभ प्रदान करता है। इसका प्लेसहोल्डर संरक्षण तर्क सुनिश्चित करता है कि अनुवादित आउटपुट में सभी फ़ॉर्मेट स्ट्रिंग्स (%(name)s, {0}, %s) सोर्स से बिल्कुल मेल खाती हैं। प्लेसहोल्डर हैंडलिंग 100% टेस्ट कवरेज द्वारा कवर है, इसलिए अनुवाद से फ़ॉर्मेट स्ट्रिंग एरर कंपाइल समय पर पकड़े जाने के बजाय टूल स्तर पर ही समाप्त हो जाते हैं।

यदि आप मैन्युअली अनुवाद कर रहे हैं या ऐसे टूल के साथ जो प्लेसहोल्डर नहीं संभालता, तो अपनी .po फ़ाइलों को इसके साथ सत्यापित करें:

msgfmt --check-format locale/de/LC_MESSAGES/django.po

यह gettext की फ़ॉर्मेट स्ट्रिंग सत्यापन चलाता है और किसी भी बेमेल की रिपोर्ट करता है।

सब कुछ एक साथ: एक रक्षात्मक वर्कफ़्लो

इनमें से अधिकांश समस्याओं का एक मूल कारण साझा है: मैन्युअल चरण जो भूलने में आसान हैं। यहां एक वर्कफ़्लो है जो सभी 10 समस्याओं को रोकता है:

# 1. Extract strings (catches #8 -- any new gettext-wrapped strings)
python manage.py makemessages -a --no-obsolete

# 2. Translate (catches #1, #5, #8, #9, #10 -- handles untranslated,
#    fuzzy, and placeholder issues automatically)
python manage.py translate

# 3. Compile (catches #1 -- generates .mo files)
python manage.py compilemessages

# 4. Verify in CI (catches everything that slipped through)
python manage.py check_translations --makemessages

चरण 4 को अपनी CI पाइपलाइन में जोड़ें और अनअनुवादित स्ट्रिंग्स, fuzzy प्रविष्टियां, और फ़ॉर्मेट एरर प्रोडक्शन तक पहुंचने से पहले बिल्ड को विफल कर देंगे।

त्वरित संदर्भ तालिका

कारण लक्षण एक-पंक्ति समाधान
compilemessages नहीं चलाया अनुवाद मौजूद हैं लेकिन दिखाई नहीं देते python manage.py compilemessages
{% load i18n %} गायब {% trans %} पर TemplateSyntaxError टेम्पलेट में {% load i18n %} जोड़ें
LocaleMiddleware गायब भाषा हमेशा अंग्रेज़ी पर डिफ़ॉल्ट MIDDLEWARE में django.middleware.locale.LocaleMiddleware जोड़ें
भाषा कोड बेमेल लोकेल डायरेक्ट्री नहीं मिली डायरेक्ट्रीज़ के लिए pt_BR (अंडरस्कोर), सेटिंग्स के लिए pt-br (हाइफ़न) उपयोग करें
Fuzzy प्रविष्टियां छोड़ी गईं अनुवाद .po में है लेकिन ऐप में नहीं समीक्षा के बाद #, fuzzy फ़्लैग हटाएं
गलत LOCALE_PATHS .po फ़ाइलें मौजूद हैं लेकिन Django उन्हें अनदेखा करता है LOCALE_PATHS को एब्सोल्यूट पथ पर सेट करें
कैश्ड अनुवाद अपडेट के बाद पुराना टेक्स्ट दिखाई देता है एप्लिकेशन सर्वर पुनः आरंभ करें
स्ट्रिंग gettext में नहीं स्ट्रिंग .po फ़ाइलों से गायब _() या {% trans %} में रैप करें
gettext में f-string टूटा एक्सट्रैक्शन या रनटाइम बेमेल % या .format() प्लेसहोल्डर से बदलें
प्लेसहोल्डर बेमेल compilemessages विफल या प्रविष्टि हटाई गई सोर्स और अनुवाद के बीच प्लेसहोल्डर बिल्कुल मिलाएं

जब आप अनुवाद चरण को स्वचालित करते हैं और CI में जांच लागू करते हैं तो इनमें से अधिकांश समस्याएं गायब हो जाती हैं। TranslateBot का translate कमांड प्लेसहोल्डर संरक्षण और इंक्रीमेंटल अनुवाद संभालता है, जबकि check_translations प्रोडक्शन तक पहुंचने से पहले किसी भी चीज़ को पकड़ता है जो छूट जाती है (अनअनुवादित प्रविष्टियां, fuzzy फ़्लैग, और फ़ॉर्मेट स्ट्रिंग समस्याएं)।

.po फ़ाइलें मैन्युअल रूप से संपादित करना बंद करें

TranslateBot AI के साथ Django अनुवाद को स्वचालित करता है। एक कमांड, सभी भाषाएं, प्रति अनुवाद बेहद कम लागत।