Internationalisation (i18n)¶
Supported Languages¶
| Code | Language | Status |
|---|---|---|
en |
English | Default, active |
it |
Italiano | Active |
es |
Español | Planned |
de |
Deutsch | Planned |
All translations are done manually by Alex.
How Django i18n Works End-to-End¶
1. Mark strings in code/templates
↓
2. uv run python manage.py makemessages -l it
→ scans all templates and Python files
→ creates/updates locale/it/LC_MESSAGES/django.po
↓
3. Edit django.po — fill in msgstr translations manually
↓
4. uv run python manage.py compilemessages
→ compiles django.po → django.mo (binary, read at runtime)
↓
5. LocaleMiddleware detects language from:
URL prefix → session → Accept-Language header → LANGUAGE_CODE default
Settings¶
# config/settings/base.py
LANGUAGE_CODE = 'en-us'
USE_I18N = True
LANGUAGES = [
('en', 'English'),
('it', 'Italiano'),
# ('es', 'Español'), # uncomment when translations are ready
# ('de', 'Deutsch'),
]
LOCALE_PATHS = [BASE_DIR / 'locale']
Middleware (order matters — see Settings):
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.locale.LocaleMiddleware", # after Session, before Common
"django.middleware.common.CommonMiddleware",
Template Usage¶
{% load i18n %}
<!-- Short string -->
<h1>{% trans "About Me" %}</h1>
<!-- String with a variable -->
{% blocktrans with name=user.name %}
Hello, {{ name }}. Welcome to my site.
{% endblocktrans %}
Language Switcher Component¶
<!-- templates/components/_lang_switcher.html -->
{% load i18n %}
<form action="{% url 'set_language' %}" method="post">
{% csrf_token %}
<input name="next" type="hidden" value="{{ request.path }}">
<select name="language" onchange="this.form.submit()">
{% get_available_languages as languages %}
{% for code, name in languages %}
<option value="{{ code }}"
{% if code == LANGUAGE_CODE %}selected{% endif %}>
{{ name }}
</option>
{% endfor %}
</select>
</form>
The set_language URL is provided by path('i18n/', include('django.conf.urls.i18n'))
in config/urls.py.
Workflow Commands¶
# Extract all translatable strings → .po files (run after adding new {% trans %} tags)
uv run python manage.py makemessages -l it
uv run python manage.py makemessages -l es
# Compile .po → .mo after editing translations
uv run python manage.py compilemessages
# Both languages at once
uv run python manage.py makemessages --all
Note
The compiled .mo files should be committed to the repository so production
deployments don't need to run compilemessages. The .po source files are
the ones you edit; .mo files are derived artefacts.