আপনি স্ট্রিংগুলি অনুবাদের জন্য চিহ্নিত করেছেন, .po ফাইল তৈরি করেছেন, compilemessages চালিয়েছেন, এবং আপনার অ্যাপ এখনও ইংরেজি দেখাচ্ছে। আপনি একা নন। Django-র i18n ফ্রেমওয়ার্ক শক্তিশালী, কিন্তু এতে এমন ধারালো প্রান্ত রয়েছে যা অভিজ্ঞ ডেভেলপারদেরও ধরে ফেলে।
এই গাইডটি Django অনুবাদ নীরবে ব্যর্থ হওয়ার ১০টি সবচেয়ে সাধারণ কারণ কভার করে, প্রতিটির জন্য সুনির্দিষ্ট লক্ষণ এবং সমাধান সহ।
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-ভিত্তিক ভাষা স্যুইচিং ব্যবহার করেন তাহলে আপনার URL কনফিগারেশনে django.conf.urls.i18n অন্তর্ভুক্ত আছে কিনা নিশ্চিত করুন:
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
এটি কোনো API কল বা পরিবর্তন না করে আপনার .po ফাইলের সমস্ত অনুবাদিত নয় এমন এন্ট্রি দেখায়।
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) সোর্সের সাথে হুবহু মেলে। প্লেসহোল্ডার হ্যান্ডলিং ১০০% টেস্ট কভারেজ দ্বারা আচ্ছাদিত, তাই অনুবাদ থেকে ফরম্যাট স্ট্রিং এরর কম্পাইল টাইমে ধরা পড়ার পরিবর্তে টুল স্তরেই নির্মূল হয়।
আপনি যদি ম্যানুয়ালি অনুবাদ করেন বা প্লেসহোল্ডার হ্যান্ডল করে না এমন টুল দিয়ে, আপনার .po ফাইলগুলি এই দিয়ে যাচাই করুন:
msgfmt --check-format locale/de/LC_MESSAGES/django.po
এটি gettext-এর ফরম্যাট স্ট্রিং ভ্যালিডেশন চালায় এবং যেকোনো অমিল রিপোর্ট করে।
সব একসাথে: একটি প্রতিরক্ষামূলক ওয়ার্কফ্লো
এই সমস্যাগুলির বেশিরভাগ একটি মূল কারণ ভাগ করে: ম্যানুয়াল ধাপ যা ভুলে যাওয়া সহজ। এখানে একটি ওয়ার্কফ্লো যা সমস্ত ১০টি সমস্যা প্রতিরোধ করে:
# 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
ধাপ ৪ আপনার 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 ফ্ল্যাগ, এবং ফরম্যাট স্ট্রিং সমস্যা) তা ধরে ফেলে।