Internationalize routes#
Problem#
The page tree should serve several languages, each under its own URL prefix such as /en/ and /de/, and the in-page text must follow the active language.
Solution#
The file router mounts as a single include() in the root URLconf.
Wrap that include in i18n_patterns() so Django prepends the language prefix to every routed URL.
Add LocaleMiddleware so the prefix sets the active language, and translate template text with the usual Django i18n tags.
Walkthrough#
Enable i18n in settings#
Turn on translation, list the offered languages, and point at a locale directory.
USE_I18N = True
LANGUAGE_CODE = "en"
LANGUAGES = [
("en", "English"),
("de", "Deutsch"),
]
LOCALE_PATHS = [BASE_DIR / "locale"]
Add the locale middleware#
Place LocaleMiddleware after the session middleware and before CommonMiddleware.
It reads the URL prefix and sets the active language for the request.
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.locale.LocaleMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
Mount the router under a language prefix#
Wrap the file router include in i18n_patterns().
Every routed URL now resolves under /en/ and /de/, and the prefix-free URLs redirect to the active language.
from django.conf.urls.i18n import i18n_patterns
from django.urls import include, path
urlpatterns = i18n_patterns(
path("", include("next.urls")),
)
Keep URLs that must not carry a prefix, such as a health check, in a plain urlpatterns list outside the i18n_patterns call.
Translate in-page text#
Mark template strings with the standard Django i18n tags.
Load the i18n tag library at the top of the template.
The examples below use a separate shop project rather than the Notes project shown elsewhere in the documentation.
{% load i18n %}
<h1>{% translate "Welcome to the shop" %}</h1>
<p>{% blocktranslate %}Browse the catalog below.{% endblocktranslate %}</p>
Translate text built in Python with gettext().
A @context callable returns the already-translated string.
from django.utils.translation import gettext as _
from next import context
@context("heading")
def heading() -> str:
"""Return the localized page heading."""
return _("Featured products")
Compile the catalogs#
Extract the marked strings and compile the binary catalogs.
uv run python manage.py makemessages -l de
uv run python manage.py compilemessages
Edit locale/de/LC_MESSAGES/django.po with the German strings before compiling.
Verification#
Start the server and request the same route under two prefixes.
uv run python manage.py runserver
Visiting /en/ renders the English text and /de/ renders the German text.
A request to the prefix-free URL redirects to the language picked from the request.
Once the router include is wrapped in i18n_patterns, page_reverse returns a URL carrying the active language prefix.
URL names are unchanged.
page_reverse reverses the same next:page_<segments> name, and i18n_patterns applies the active prefix at resolve time.
See also#
See also
File router for how the router include is mounted. Reverse URLs for building prefixed URLs from code.