Minarkahan mo ang mga string para sa pagsasalin, nag-generate ng .po file, nag-run ng compilemessages, at nagpapakita pa rin ng English ang iyong app. Hindi ka nag-iisa. Ang i18n framework ng Django ay makapangyarihan, ngunit may mga matalas na gilid na nakakahuli kahit sa mga bihasang developer.
Saklaw ng gabay na ito ang 10 pinakakaraniwang dahilan kung bakit tahimik na nabibigo ang mga pagsasalin ng Django, kasama ang eksaktong mga sintomas at ayos para sa bawat isa.
1. Nakalimutang Mag-run ng compilemessages Pagkatapos Mag-edit ng .po File
Nag-edit ka ng .po file (manual o gamit ang isang tool), ngunit hindi kailanman lumalabas ang naisaling teksto. Patuloy na nagpapakita ang app ng orihinal na mga English string.
Hindi binabasa ng Django ang .po file sa runtime. Binabasa nito ang compiled na .mo (machine object) binary file sa halip. Kung mag-edit ka ng .po file nang hindi nagco-compile ulit, walang alam ang Django na may nagbago.
Mag-run ng compilemessages pagkatapos ng bawat pagbabago sa .po file:
python manage.py compilemessages
Kung ino-automate mo ang iyong mga pagsasalin gamit ang TranslateBot, idagdag ang compilemessages bilang huling hakbang sa iyong workflow:
python manage.py makemessages -a --no-obsolete
python manage.py translate
python manage.py compilemessages
2. Nawawalang {% load i18n %} sa mga Template
Gumagamit ka ng {% trans "Hello" %} sa isang template, ngunit naglalabas ang Django ng TemplateSyntaxError. O mas malala, tahimik na walang ginagawa ang tag kung may misconfigured na template engine ka.
Ang {% trans %} at {% blocktrans %} tag ay nasa i18n template tag library ng Django. Kung hindi ilo-load, hindi sila makikilala ng template engine.
Magdagdag ng {% load i18n %} sa itaas ng bawat template na gumagamit ng translation tag:
{% load i18n %}
<h1>{% trans "Welcome to our site" %}</h1>
<p>{% blocktrans with name=user.name %}Hello, {{ name }}!{% endblocktrans %}</p>
Ito ay per-template na kinakailangan. Kahit na nag-load ng i18n ang isang parent template, kailangan ng mga child template na gumagamit ng translation tag ang sarili nilang {% load i18n %} na deklarasyon.
3. Wala ang LocaleMiddleware sa MIDDLEWARE o Nasa Maling Posisyon
Palaging nagse-serve ang Django ng content sa default na wika anuman ang Accept-Language header ng browser, URL prefix, o session setting.
Tinutukoy ng LocaleMiddleware ang aktibong wika para sa bawat request. Kung wala ito, ang default ng Django ay LANGUAGE_CODE at binabalewala ang lahat ng mekanismo sa pagpili ng wika. Mahalaga rin ang posisyon nito sa middleware stack, dahil kailangan nito ng access sa session data at URL resolution.
Idagdag ang LocaleMiddleware sa iyong MIDDLEWARE setting, pagkatapos ng SessionMiddleware at 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",
]
Tiyakin din na kasama ang django.conf.urls.i18n sa iyong URL configuration kung gumagamit ka ng URL-based na pagpapalit ng wika:
from django.conf.urls.i18n import i18n_patterns
urlpatterns = i18n_patterns(
path("", include("myapp.urls")),
)
4. Hindi Magkatugma ang Language Code (hal. pt-br vs pt_BR)
Umiiral ang mga pagsasalin sa iyong .po file, matagumpay ang compilemessages, ngunit binabalewala ng Django ang pagsasalin para sa ilang locale.
Inaasahan ng Django na ang mga locale directory ay susunod sa format na <language>_<COUNTRY> na may underscore separator at uppercase na country code. Halimbawa, pt_BR para sa Brazilian Portuguese. Kung ang iyong directory ay pinangalanang pt-br, pt-BR, o ptBR, hindi ito mahahanap ng Django. Ganoon din sa LANGUAGES setting: ang mga code doon ay gumagamit ng mga hyphen (pt-br), ngunit ang filesystem ay gumagamit ng mga underscore (pt_BR).
Tiyaking tumutugma ang iyong directory structure sa mga inaasahan ng Django:
locale/
pt_BR/
LC_MESSAGES/
django.po
django.mo
At sa iyong settings, gamitin ang hyphenated na form:
LANGUAGES = [
("en", "English"),
("pt-br", "Brazilian Portuguese"),
("zh-hans", "Simplified Chinese"),
]
Kapag nagra-run ng makemessages, gamitin ang underscore form para sa locale flag:
python manage.py makemessages -l pt_BR
5. Mga Fuzzy Entry na Tahimik na Nilalaktawan sa Compilation
May pagsasalin sa .po file, ngunit nagpapakita ang Django ng orihinal na English string sa runtime para sa partikular na entry na iyon. Ito ay partikular na nakakabigo dahil nandoon mismo ang pagsasalin sa file.
Kapag natukoy ng makemessages ng Django na bahagyang nagbago ang isang source string, minamarkahan nito ang umiiral na pagsasalin bilang "fuzzy" (ibig sabihin ay isang hula na nangangailangan ng human review). Nilalaktawan ng compilemessages command ang lahat ng fuzzy entry, tinatrato silang hindi naisalin. Kaya mukhang naisalin ang entry sa .po file, ngunit ganap itong hindi kasama sa .mo file.
Ganito ang hitsura ng fuzzy entry:
#, fuzzy
msgid "Welcome to our website!"
msgstr "Welkom op onze website!"
I-review ang pagsasalin, i-update ang msgstr kung kinakailangan, pagkatapos alisin ang #, fuzzy flag:
msgid "Welcome to our website!"
msgstr "Welkom op onze website!"
Pagkatapos ay mag-compile ulit:
python manage.py compilemessages
Sa mas malaking proyekto, naiipon ang mga fuzzy entry at madaling makaligtaan. Awtomatikong nakakahuli ang check_translations command ng TranslateBot ng mga ito:
python manage.py check_translations
locale/nl/LC_MESSAGES/django.po: 0 untranslated, 3 fuzzy
CommandError: Translation check failed
Idagdag ang check_translations --makemessages sa iyong CI pipeline at hindi ka na kailanman magpa-deploy ng fuzzy entry.
6. Hindi Na-configure ang LOCALE_PATHS o Nakaturo sa Maling Directory
Gumagawa ng .po file ang makemessages sa isang lokasyon, ngunit hinahanap sila ng Django sa ibang lugar. Umiiral ang mga pagsasalin sa disk ngunit hindi kailanman nilo-load.
Hinahanap ng Django ang mga translation file sa partikular na pagkakasunod: mga LOCALE_PATHS directory muna, pagkatapos ang locale/ directory ng bawat app, at sa huli ang locale/ directory ng proyekto. Kung hindi nakatakda ang LOCALE_PATHS o nakaturo sa maling path, maaaring hindi kailanman mahanap ng Django ang iyong mga .po file.
Itakda ang LOCALE_PATHS sa iyong settings sa isang absolute path:
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
LOCALE_PATHS = [
BASE_DIR / "locale",
]
I-verify na umiiral ang directory at naglalaman ng inaasahang structure:
locale/
de/
LC_MESSAGES/
django.po
django.mo
nl/
LC_MESSAGES/
django.po
django.mo
Isang karaniwang pagkakamali ay ang pagtatakda ng LOCALE_PATHS sa locale/ (relative) sa halip na absolute path. Hindi nire-resolve ng Django ang mga relative path mula sa root ng iyong proyekto. Depende ito sa working directory ng proseso, na madalas ay hindi ang inaasahan mo.
7. Lumang Pagsasalin ang Inise-serve ng Cache
Nag-update at nag-compile ka ng mga pagsasalin, ngunit patuloy na lumalabas ang lumang teksto. Naaayos ito kapag ni-restart ang server.
Ang translation catalog ng Django ay nilo-load nang isang beses bawat proseso. Sa production, ang mga WSGI/ASGI server gaya ng Gunicorn o Uvicorn ay pinapanatiling buhay ang mga worker process sa mahabang panahon. Maaaring nagbago na ang .mo file sa disk, ngunit ang tumatakbong proseso ay mayroon pa ring lumang pagsasalin sa memory. Bukod pa rito, kung gumagamit ka ng cache framework ng Django o reverse proxy gaya ng Nginx o Cloudflare, ang mga cached na response ay magse-serve ng lumang content hanggang mag-expire ang mga ito.
I-restart ang iyong application server pagkatapos mag-deploy ng bagong pagsasalin:
# Gunicorn
kill -HUP $(cat /tmp/gunicorn.pid)
# Systemd
sudo systemctl restart myapp
# Docker
docker compose restart web
Para sa cache framework ng Django, i-clear ang cache pagkatapos mag-update ng mga pagsasalin:
from django.core.cache import cache
cache.clear()
Sa development, awtomatikong nagre-reload ang runserver kapag nagbago ang mga Python file ngunit hindi nimo-monitor ang mga .mo file. Kailangan mong manual itong i-restart pagkatapos mag-run ng compilemessages.
8. Mga String na Hindi Nakabalot sa gettext (_() o {% trans %})
Hindi ine-extract ng makemessages ang ilang string, kaya hindi sila kailanman lumalabas sa iyong .po file at hindi kailanman naisasalin. Ito ang pinakapangunahing isyu at pinakamadaling makalimutan sa isang malaking codebase.
Ginagamit ng makemessages command ng Django ang xgettext para i-scan ang iyong source code para sa mga translation marker. Kung ang isang string ay hindi nakabalot sa gettext() (karaniwang ina-alias bilang _()), gettext_lazy(), {% trans %}, o {% blocktrans %}, ito ay hindi nakikita ng extraction process.
Balutin ang bawat user-facing na string:
# 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>
Gamitin ang --dry-run flag ng TranslateBot para i-preview kung aling mga string ang kasalukuyang hindi naisalin, para mahuli mo ang mga string na napalampas sa extraction:
python manage.py translate --target-lang de --dry-run
Pinapakita nito ang lahat ng hindi naisaling entry sa iyong mga .po file nang hindi gumagawa ng anumang API call o pagbabago.
9. Ang mga f-string ay Hindi Maisasalin (Limitasyon ng Django)
Binabalot mo ang isang f-string sa _() at nakakakuha ka ng syntax error, o nag-e-extract ang makemessages ng sira/hindi kumpletong string na hindi maisasalin.
Ang mga Python f-string ay sine-evaluate sa runtime. Ang xgettext extraction tool ay nag-pa-parse ng source code nang statically, kaya hindi nito mae-evaluate ang mga Python expression sa loob ng {} braces. Ibig sabihin ang _(f"Hello, {name}") ay ine-extract bilang string na naglalaman ng literal na {name} expression (o ganap na nabibigong ma-extract), at ang resultang .po entry ay hindi kailanman tutugma sa runtime string.
Gamitin sa halip ang % formatting ng Django o .format() na may named placeholder:
# 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)
Hindi ito limitasyon ng TranslateBot o tooling. Ito ay fundamental sa kung paano gumagana ang gettext. Ang source string ay kailangang maging static literal para ma-extract at ma-look up sa runtime.
Prinopreserba ng TranslateBot ang lahat ng placeholder format na ito (%(name)s, {0}, %s, HTML tag) sa panahon ng pagsasalin, kaya nananatiling ganap na gumagana ang mga naisaling string.
10. Mga Placeholder Format String Error na Nagsi-sira ng .po Compilation
Nabibigo ang compilemessages na may error, o ang .po file ay may #, python-format flag mismatch, at tahimik na ibinaba ang entry.
Kapag ang isang source string ay naglalaman ng Python format placeholder gaya ng %(name)s, minamarkahan ng Django ang .po entry ng #, python-format. Kung ang pagsasalin ay may iba't ibang placeholder (isang typo gaya ng %(nome)s, nawawalang placeholder, o sobra) maaaring tanggihan ng gettext tool ang entry o mabigo ang compilemessages. Karaniwang nangyayari ito sa manual na pagsasalin o sa mga AI translation tool na hindi nauunawaan ang placeholder semantics.
Ganito ang hitsura ng sirang entry:
#, python-format
msgid "Hello, %(name)s! You have %(count)d new messages."
msgstr "Hallo, %(naam)s! Je hebt %(count)d nieuwe berichten."
Dito ang %(naam)s ay dapat na %(name)s. Kailangang eksaktong tumugma ang mga placeholder sa source.
Tiyaking naglalaman ang mga naisaling string ng eksaktong parehong placeholder gaya ng source. Tingnan ang mga typo, nawawalang placeholder, at sobrang placeholder.
Ito ay isang lugar kung saan nagbibigay ang TranslateBot ng tunay na bentahe. Ang placeholder preservation logic nito ay tinitiyak na ang lahat ng format string (%(name)s, {0}, %s) sa naisaling output ay eksaktong tumutugma sa source. Ang placeholder handling ay saklaw ng 100% test coverage, kaya ang format string error mula sa pagsasalin ay inaalis sa tool level sa halip na mahuli sa compile time.
Kung manu-mano kang nagsasalin o gumagamit ng tool na hindi nagha-handle ng placeholder, i-validate ang iyong mga .po file gamit ang:
msgfmt --check-format locale/de/LC_MESSAGES/django.po
Pinapatakbo nito ang format string validation ng gettext at nire-report ang anumang mismatch.
Pagsasama-sama ng Lahat: Isang Defensive na Workflow
Karamihan sa mga isyung ito ay may iisang root cause: mga manual na hakbang na madaling makalimutan. Narito ang isang workflow na pumipigil sa lahat ng 10 problema:
# 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
Idagdag ang hakbang 4 sa iyong CI pipeline at ang mga hindi naisaling string, fuzzy entry, at format error ay magfa-fail sa build bago makarating sa production.
Talahanayan ng Mabilis na Sanggunian
| Dahilan | Sintomas | Isang Linyang Ayos |
|---|---|---|
Walang compilemessages |
Umiiral ang pagsasalin pero hindi lumalabas | python manage.py compilemessages |
Nawawalang {% load i18n %} |
TemplateSyntaxError sa {% trans %} |
Magdagdag ng {% load i18n %} sa template |
| Nawawalang LocaleMiddleware | Palaging default ang wika sa English | Idagdag ang django.middleware.locale.LocaleMiddleware sa MIDDLEWARE |
| Hindi magkatugma ang language code | Hindi nahanap ang locale directory | Gamitin ang pt_BR (underscore) para sa directory, pt-br (hyphen) para sa settings |
| Nilaktawan ang fuzzy entry | Nasa .po ang pagsasalin pero wala sa app |
Alisin ang #, fuzzy flag pagkatapos i-review |
| Maling LOCALE_PATHS | Umiiral ang .po file pero binabalewala ng Django |
Itakda ang LOCALE_PATHS sa absolute path |
| Naka-cache na pagsasalin | Lumang teksto ang lumalabas pagkatapos ng update | I-restart ang application server |
| String wala sa gettext | String nawawala sa .po file |
Balutin sa _() o {% trans %} |
| f-string sa gettext | Sirang extraction o runtime mismatch | Palitan ng % o .format() placeholder |
| Hindi magkatugma ang placeholder | Nabigo ang compilemessages o ibinaba ang entry |
Eksaktong itugma ang placeholder sa pagitan ng source at pagsasalin |
Karamihan sa mga problemang ito ay nawawala kapag ino-automate mo ang translate step at nagpapatupad ng mga check sa CI. Ang translate command ng TranslateBot ay nagha-handle ng placeholder preservation at incremental translation, habang ang check_translations ay nakakahuli ng anumang nakalusot (hindi naisaling entry, fuzzy flag, at format string issue) bago makarating sa production.