Translations¶
Every user-visible string in the package goes through gettext — model field
labels and help texts, admin fieldset headings, banner copy, maintenance page
copy, and even the JavaScript unit labels, which are translated server-side
and injected into the script as variables.
Two catalogs ship with the package:
| Language | Status |
|---|---|
English (en) |
Source strings |
Polish (pl) |
Complete |
Turning it on¶
Translation is automatic once Django's i18n machinery is active. For
per-request language negotiation you need LocaleMiddleware:
USE_I18N = True
LANGUAGES = [("en", "English"), ("pl", "Polski")]
LANGUAGE_CODE = "en"
MIDDLEWARE = [
"django.contrib.sessions.middleware.SessionMiddleware",
# LocaleMiddleware must sit after SessionMiddleware and before Common.
"django.middleware.locale.LocaleMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django_countdown.middleware.CountdownBlockingMiddleware",
]
With that in place, a visitor sending Accept-Language: pl gets the Polish
maintenance page; everyone else gets English.
lang attribute on the maintenance page
blocked_base.html renders <html lang="{{ LANGUAGE_CODE|default:'en' }}">.
LANGUAGE_CODE only reaches the template context if
django.template.context_processors.i18n is registered — otherwise the
page always claims to be English, whatever language it actually renders
in. Add the processor if you serve more than one language:
Overriding the shipped wording¶
The packaged copy is deliberately generic — "We're sorry for the inconvenience. We're rolling out an important update." If it does not match your voice, you have two options.
Override the translation, which is the lighter touch. Catalogs in
LOCALE_PATHS take precedence over app catalogs, so you can restate any
string without forking a template:
Then edit the entry in locale/en/LC_MESSAGES/django.po:
This works even for English → English, which is the trick for rewording without touching markup.
Override the template, when the structure changes too — see Blocked page.
Adding a language to your project¶
If you translate the package for your own deployment only, the same
LOCALE_PATHS mechanism applies — run makemessages --locale de, fill in
the entries, compile.
The msgids to look for are the English source strings, spread across
models.py, admin.py and the four templates.
Contributing a language upstream¶
Translations are welcome in the package itself, where everyone benefits:
$ git clone https://github.com/iplweb/django-countdown.git
$ cd django-countdown/src/django_countdown
$ uv run django-admin makemessages --locale de
# edit locale/de/LC_MESSAGES/django.po
$ uv run django-admin compilemessages
Notes for a clean pull request:
- Commit both
django.poand the compileddjango.mo— the package ships compiled catalogs, andpackage-datainpyproject.tomlincludeslocale/**/*. makemessagesrewraps lines at 79 characters. If your diff shows unrelated reflow noise, that isxgettextnormalising the file; it is harmless.- Keep the placeholders intact. Several strings use named interpolation —
%(n)d days,{{ minutes }}inside{% blocktrans %}— and the substitution breaks if a name is changed. - Translate the JavaScript labels too — the duration units (
days,h,min,sec) and the maintenance page's status line, which is what visitors read while they wait for the site to come back. They are ordinary{% trans %}strings rendered into the script.
The example project¶
example/ is translated end to end and doubles as a working reference for
the setup above: LocaleMiddleware enabled, LOCALE_PATHS configured, and a
complete Polish catalog at
example/example_project/locale/pl/LC_MESSAGES/django.po.
Run it with Accept-Language: pl, or set LANGUAGE_CODE = "pl" in its
settings, to see both the app and the package render in Polish.