A Django museum-guide site deploys an updated German .po file, yet several reworded exhibit texts still appear in English — what do you check, and in what order?
answer
- is there a fresh .mo?
- flags on the changed entries
- does msgid still match the code?
- processes cache catalogs
basics
~20 sCheck that compilemessages produced a fresh .mo and that it was deployed, that the changed entries are not marked fuzzy, that each msgid still matches the current source string, that the catalog sits in a searched locale directory, and that workers were restarted.
solid answer
~40 sWork from the file to the process. First, is there a `.mo` newer than the `.po`, and did it reach the servers? Django never reads `.po`. Second, look at the changed entries. Reworded source strings often come back from `makemessages` flagged `#, fuzzy` with the old translation pre-filled, and `compilemessages` leaves fuzzy entries out unless you pass `--use-fuzzy`, so the English source shows. Third, confirm the `msgid` matches the code exactly. A changed source string makes the old entry obsolete (`#~`), and whitespace or placeholder differences are enough to miss. Fourth, check the catalog is in a searched location (`LOCALE_PATHS` or an installed app's `locale/de/LC_MESSAGES/`) and not shadowed by a higher-precedence catalog. Finally, restart the workers: each process caches loaded catalogs, and only the development autoreloader clears that cache on `.mo` changes.
code
bash · 10 lines# Which reworded entries are still fuzzy or obsolete?
grep -n -A3 '#, fuzzy' exhibits/locale/de/LC_MESSAGES/django.po
grep -c '^#~ msgid' exhibits/locale/de/LC_MESSAGES/django.po
# Re-extract, then compile (fails on format errors)
python manage.py makemessages -l de
python manage.py compilemessages -l de
# Only if the fuzzy guesses were reviewed as acceptable:
python manage.py compilemessages -l de --use-fuzzygo deeper
Recall that .po edits only take effect after compilemessages produces a new .mo.
Explain fuzzy and obsolete entries, why reworded strings become fuzzy, and what --use-fuzzy changes.
Diagnose partial translation by working from file to process, and put fuzzy and format checks into CI.
Define the translation release process so source rewording, translator review and deployment stay in step.
## Read the symptom first "Several reworded texts are English, the rest of the German site is fine" is a narrow symptom. It suggests that: - the German catalog **is** being found and loaded, since most strings work; - German **is** the active language for those pages, so language selection is not the problem; - something about **those particular entries** keeps them out of the compiled catalog, or they no longer match what the code asks for. If *everything* were English, you would look first at language activation, `USE_I18N`, or a catalog that was never found at all. ## Step 1: is there a current `.mo`, and did it ship? Django's runtime reads only binary `.mo` files. Check: 1. `compilemessages` ran **after** the `.po` edit. It skips files whose `.mo` is already at least as new as the `.po`, and reports them as "already compiled and up to date". 2. It succeeded. `msgfmt --check-format` fails on placeholder mismatches, such as a German `msgstr` missing `%(room)s`, and the command then ends with "compilemessages generated one or more errors". A `.po` saved with a BOM is also rejected. 3. The new `.mo` is actually in the deployed artefact, whether committed or built in CI. ## Step 2: are the changed entries fuzzy? When a curator rewords a source string, for example "Oil on canvas" becoming "Oil paint on canvas", the next `makemessages` run can't find the old msgid. `msgmerge` then often creates the new entry **pre-filled with the old translation and flagged `#, fuzzy`**, so that a translator can review it. By default `compilemessages` **excludes fuzzy entries**. They fall back to the source text (English, on a site whose `LANGUAGE_CODE` is English), which is exactly the "a few strings in English" pattern. Fixes, in order of preference: - have the translator review each fuzzy entry and delete the `fuzzy` flag; - only if the guesses are acceptable, compile with `compilemessages --use-fuzzy`. ## Step 3: does the `msgid` still match the code? Lookups are exact string matches: - If the code changed **after** the last `makemessages`, the catalog has no entry for the new wording. Re-run `makemessages --all` and translate the new entries. - The old entry is now **obsolete**, commented out with `#~`. Translating an obsolete entry has no effect. `--no-obsolete` removes them to avoid confusion. - Whitespace matters. A `{% blocktranslate %}` without `trimmed` carries newlines and indentation into the msgid, so re-indenting a template silently changes the msgid. - Placeholders are part of the msgid: `%(name)s` versus `%(title)s` is a different string. ## Step 4: is the catalog where Django looks, and not shadowed? - The path must be `<base>/de/LC_MESSAGES/django.mo`, using a locale name (`de`, `pt_BR`), under a directory in `LOCALE_PATHS` or an installed app's `locale/`. - A higher-precedence catalog can translate the same msgid differently, for example an old override in `LOCALE_PATHS` that still carries the previous wording. `LOCALE_PATHS` wins over apps, and earlier apps in `INSTALLED_APPS` win over later ones. - JavaScript strings live in `djangojs.mo`. If the untranslated texts come from `.js` files, check that domain instead. ## Step 5: did the processes reload? Each worker loads catalogs on first use and **caches** them for its lifetime. The development server's autoreloader watches `.mo` files and clears that cache on change, which is why "it works locally". Production application servers don't do this, so new catalogs need a worker restart or reload. ## A checklist to run | Check | Evidence of the problem | |---|---| | `.mo` newer than `.po`, present on the server | `.mo` missing, older, or not in the artefact | | `compilemessages` exit status | "generated one or more errors", format mismatch or BOM | | entry flags | `#, fuzzy` on the reworded entries | | msgid equals the source string | only an obsolete `#~` entry, or a whitespace or placeholder mismatch | | catalog location and precedence | wrong directory name, or a higher-ranked catalog with a different `msgstr` | | worker restart | pages correct on a fresh worker but not on old ones | Automating steps 1–3 in CI turns most of this into a failed build instead of a customer report. Run `compilemessages` in the pipeline so format errors fail the build, and fail on any remaining fuzzy entries in the release languages.
- Why does the problem disappear on a developer's machine running runserver?The development server's autoreloader watches `.mo` files in `locale/`, the apps' `locale/` directories and `LOCALE_PATHS`, and clears Django's translation cache when one changes. So a local recompile shows up immediately. Production workers keep the catalogs they loaded until they are restarted. The local compile may also have used `--use-fuzzy` or picked up a newer `.po` than the one that was deployed.
- How would you stop fuzzy entries from reaching production unnoticed?Make the release pipeline run `makemessages` and `compilemessages` for the release languages and fail if any `#, fuzzy` flag or empty `msgstr` remains, for example with a gettext statistics check such as `msgfmt --statistics`. Pair that with a rule that translators clear fuzzy flags as part of review. Then the gap shows as a failed build, not as English text in the German exhibit pages.
saying these in an interview costs you the question
- Fuzzy entries are compiled and shown, just marked for review.
- Translating the #~ obsolete entry fixes the reworded text.
- Django rereads .po files on every request, so no restart is needed.
- If most German strings work, the .mo must be up to date for all of them.
- compilemessages always recompiles every .po file.