What's the correct way to set up Django translation?
Question:
I’ve got an issue with translations not working on Django 1.6. I’ve added this to my settings.py
:
LANGUAGE_CODE = 'en-us'
ugettext = lambda s: s
LANGUAGES = (
('en', ugettext('English')),
('de', ugettext('German')),
)
Also added middlewares:
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
As well as to my *.py files whenever I’m using a string which shall be l10nd:
from django.utils.translation import ugettext_lazy as _
My templates start with:
{% extends "base.html" %}
{% load i18n %}
And inside the template, I used the trans
placeholder. E.g.
<h1>{% trans "Register a tank" %}</h1>
I have provided translations in locale/de/LC_MESSAGES/django.po
:
msgid "Register a tank"
msgstr "Einen neuen Tank anmelden"
My browser is set to request German content first:
Browser settings
What did I miss?
P.S. The project I’m currently fuzzy around is hosted on GitHub: https://github.com/frlan/blankspot
Answers:
Please set translated string
in django.po
and then use python manage.py compilemessages
for e.g
#: path/to/python/module.py:23
msgid "Welcome to my site."
msgstr "put appropriate translated string here"
Suggestion-: You can use django-rosetta
package to add translated string
from UI interface. It is easy to add T-string from django-admin. https://github.com/mbi/django-rosetta
You need to enable the LocaleMiddleware
in your settings, to tell Django to do language detection based on the browser-settings. Changing your language preferences effectly sets the Accept-Language
header. You might need to check in an incognito window, because other means of language detection have a higher priority, such as the user’s session and the django_language
cookie.
Add LOCALE_PATHS
to settings.py
and set it as below:
import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
LOCALE_PATHS = (
os.path.join(BASE_DIR, 'locale'),
)
Note that LOCALE_PATHS
must be a tuple (look at the comma at the end of the path).
Now based on LOCALE_PATHS
, the locale
folder should be in the root of your project.
And be sure that you run the commands django-admin.py makemessages -l de
and django-admin.py compilemessages
from the root of your project.
djPrj
|
+---> djPrj
|
+---> djApp
|
+---> locale
|
+---> templates
Also rearrange your MIDDLEWARE_CLASSES
to be LocaleMiddleware
after SessionMiddleware
and before CommonMiddleware
as mentioned here:
MIDDLEWARE_CLASSES = (
'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',
)
Restart your service (python manage.py runserver
) and check again.
Just to ensure that your localization is applied to your Django admin page with the default django.mo
file of Django, do the following test:
First in main urls.py
of project replace patterns
with i18n_patterns
:
from django.conf.urls.i18n import i18n_patterns
urlpatterns = i18n_patterns('',
url(r'^admin/', include(admin.site.urls)),
# ...
)
Now go to the admin page with a de
prefix, like: http://127.0.0.1:8000/de/admin/
And the admin page should be shown in German.
OK, are you able to see the admin page of Django in German?
Also check your view with the de
prefix too.
According to your project code, some sentences are not in trans
blocks. Put them as:
{% trans "your sentence" %}
Also you must use ugettext_lazy
instead of ugettext
in your code for views and models (Read here and here.)
Replace this:
from django.utils.translation import ugettext as _
with:
from django.utils.translation import ugettext_lazy as _
And now everything will work.
Check the cookies and the session — according to How Django Discovers Language Preference, the process is this:
- language prefix in the URL, when using i18n_patterns in URLconf
- _language key in the current user’s session
- django_language cookie (or as specified by settings.LANGUAGE_COOKIE_NAME)
- Accept-Language HTTP header (this is what your browser setting sends)
- settings.LANGUAGE_CODE
Since your browser settings are set to prefer ‘de’, I suspect the LocaleMiddleware must decide otherwise in one of the previous steps 1. – 3.
In my case, I used en-gb as the parameter to run
django-admin.py makemessages -l en-gb
Instead, it should be en_GB.
django-admin.py makemessages -l en_GB
For example below, you can translate English to French.
First, create locale
folder just under django-project
as shown below. *I use Django 4.2.1 and you can see my answer explaining how to set templates:
django-project
|-core
| |-settings.py
| └-urls.py
|-my_app1
| |-views.py
| |-urls.py
| |-models.py
| |_admin.py
| └-apps.py
|-my_app2
|-templates
| └-index.html
└-locale # Here
Then, set core/settings.py
as shown below. *LocaleMiddleware
must be between SessionMiddleware
and CommonMiddleware
. 'en-us'
is set to LANGUAGE_CODE
by default in core/settings.py
:
# "core/settings.py"
MIDDLEWARE = [
...
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware', # Here
'django.middleware.common.CommonMiddleware',
...
]
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
from django.utils.translation import gettext_lazy as _
LANGUAGES = ( # Here
('en', _('English')),
('fr', _('French'))
)
And, Hello
and World
are translated and outputted on console and index.html
is rendered in test()
in my_app1/views.py
as shown below:
# "my_app1/views.py"
from django.shortcuts import render
from django.utils.translation import gettext as _
def test(request):
print(_("Hello"), _("World")) # Here
return render(request, 'index.html')
And, test()
path is set to urlpatterns
in my_app1/urls.py
as shown below:
# "my_app1/urls.py"
from django.urls import path
from . import views
app_name = "my_app1"
urlpatterns = [
path('', views.test, name="test") # Here
]
And, admin
and my_app1
paths are set to urlpatterns
with i18n_patterns() in core/urls.py
as shown below. *You can see my question and my answer explaining prefix_default_language=False with i18n_patterns()
:
# "core/urls.py"
from django.contrib import admin
from django.urls import path, include
from django.conf.urls.i18n import i18n_patterns
urlpatterns = i18n_patterns(
path('admin/', admin.site.urls), # Here
path('my_app1/', include('my_app1.urls')) # Here
)
And, 3 tags are set in templates/index.py
as shown below. *For the translation in Django Templates, {% load i18n %}
is needed and {% translate %}
or {% trans %}
can translate:
# "templates/index.py"
{% load i18n %}
{% translate "Hello" %} {% trans "World" %}
And, name
field and person
model are translated in my_app1/models.py
as shown below:
# "my_app1/models.py"
from django.db import models
from django.utils.translation import gettext_lazy as _
class Person(models.Model):
first_name = models.CharField(
max_length=20, verbose_name=_("first name") # Here
)
last_name = models.CharField(
max_length=20, verbose_name=_("last name") # Here
)
class Meta:
verbose_name = _('person') # Here
verbose_name_plural = _('persons') # Here
And, this is my_app1/admin.py
below:
# "my_app1/admin.py"
from django.contrib import admin
from .models import Person
@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
pass
And, my_app1
is translated in my_app1/apps.py
as shown below:
# "my_app1/apps.py"
from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class MyApp1Config(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'my_app1'
verbose_name = _('my_app1') # Here
Now, run the command below. *You can see my answer explaining the command below:
django-admin makemessages -l fr
Then, django.po
is created in locale/fr/LC_MESSAGES/
as shown below:
django-project
|-core
| |-settings.py
| └-urls.py
|-my_app1
| |-views.py
| |-urls.py
| |-models.py
| |_admin.py
| └-apps.py
|-my_app2
|-templates
| └-index.html
└-locale
└-fr
└-LC_MESSAGES
└-django.po # Here
Then, add Bonjour
, Monde
, Anglais
and Français
to each msgstr ""
respectively in locale/fr/LC_MESSAGES/django.po
as shown below:
# "locale/fr/LC_MESSAGES/django.po"
...
#: .coresettings.py:140
msgid "English"
msgstr "Anglais" # Here
#: .coresettings.py:141
msgid "French"
msgstr "Français" # Here
#: .my_app1apps.py:7
msgid "my_app1"
msgstr "mon_app1" # Here
#: .my_app1models.py:6
msgid "first name"
msgstr "prénom"
#: .my_app1models.py:7
msgid "last name"
msgstr "nom de famille"
#: .my_app1models.py:13
msgid "person"
msgstr "personne"
#: .my_app1models.py:14
msgid "persons"
msgstr "personnes"
#: .my_app1views.py:5 .templatesindex.html:3
msgid "Hello"
msgstr "Bonjour" # Here
#: .my_app1views.py:5 .templatesindex.html:3
msgid "World"
msgstr "Monde" # Here
...
Then, run the command below to compile django.po
to django.mo
. *You can see my answer explaining the command below:
django-admin compilemessages
Then, django.po
is compiled to django.mo
in locale/fr/LC_MESSAGES/
as shown below:
django-project
|-core
| |-settings.py
| └-urls.py
|-my_app1
| |-views.py
| |-urls.py
| |-models.py
| |_admin.py
| └-apps.py
|-my_app2
|-templates
| └-index.html
└-locale
└-fr
└-LC_MESSAGES
|-django.po
└-django.mo # Here
Now, http://localhost:8000/en/my_app1/
can show Hello World
as shown below:
And, Hello World
below is outputted on console:
Hello World
And, http://localhost:8000/fr/my_app1/
can show Bonjour Monde
as shown below:
And, Bonjour Monde
below is outputted on console:
Bonjour Monde
And, http://localhost:8000/en/admin/my_app1/person/add/
can show the English version of Django Admin as shown below:
And, http://localhost:8000/fr/admin/my_app1/person/add/
can show the French version of Django Admin as shown below:
I’ve got an issue with translations not working on Django 1.6. I’ve added this to my settings.py
:
LANGUAGE_CODE = 'en-us'
ugettext = lambda s: s
LANGUAGES = (
('en', ugettext('English')),
('de', ugettext('German')),
)
Also added middlewares:
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
As well as to my *.py files whenever I’m using a string which shall be l10nd:
from django.utils.translation import ugettext_lazy as _
My templates start with:
{% extends "base.html" %}
{% load i18n %}
And inside the template, I used the trans
placeholder. E.g.
<h1>{% trans "Register a tank" %}</h1>
I have provided translations in locale/de/LC_MESSAGES/django.po
:
msgid "Register a tank"
msgstr "Einen neuen Tank anmelden"
My browser is set to request German content first:
Browser settings
What did I miss?
P.S. The project I’m currently fuzzy around is hosted on GitHub: https://github.com/frlan/blankspot
Please set translated string
in django.po
and then use python manage.py compilemessages
for e.g
#: path/to/python/module.py:23
msgid "Welcome to my site."
msgstr "put appropriate translated string here"
Suggestion-: You can use django-rosetta
package to add translated string
from UI interface. It is easy to add T-string from django-admin. https://github.com/mbi/django-rosetta
You need to enable the LocaleMiddleware
in your settings, to tell Django to do language detection based on the browser-settings. Changing your language preferences effectly sets the Accept-Language
header. You might need to check in an incognito window, because other means of language detection have a higher priority, such as the user’s session and the django_language
cookie.
Add LOCALE_PATHS
to settings.py
and set it as below:
import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
LOCALE_PATHS = (
os.path.join(BASE_DIR, 'locale'),
)
Note that LOCALE_PATHS
must be a tuple (look at the comma at the end of the path).
Now based on LOCALE_PATHS
, the locale
folder should be in the root of your project.
And be sure that you run the commands django-admin.py makemessages -l de
and django-admin.py compilemessages
from the root of your project.
djPrj
|
+---> djPrj
|
+---> djApp
|
+---> locale
|
+---> templates
Also rearrange your MIDDLEWARE_CLASSES
to be LocaleMiddleware
after SessionMiddleware
and before CommonMiddleware
as mentioned here:
MIDDLEWARE_CLASSES = (
'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',
)
Restart your service (python manage.py runserver
) and check again.
Just to ensure that your localization is applied to your Django admin page with the default django.mo
file of Django, do the following test:
First in main urls.py
of project replace patterns
with i18n_patterns
:
from django.conf.urls.i18n import i18n_patterns
urlpatterns = i18n_patterns('',
url(r'^admin/', include(admin.site.urls)),
# ...
)
Now go to the admin page with a de
prefix, like: http://127.0.0.1:8000/de/admin/
And the admin page should be shown in German.
OK, are you able to see the admin page of Django in German?
Also check your view with the de
prefix too.
According to your project code, some sentences are not in trans
blocks. Put them as:
{% trans "your sentence" %}
Also you must use ugettext_lazy
instead of ugettext
in your code for views and models (Read here and here.)
Replace this:
from django.utils.translation import ugettext as _
with:
from django.utils.translation import ugettext_lazy as _
And now everything will work.
Check the cookies and the session — according to How Django Discovers Language Preference, the process is this:
- language prefix in the URL, when using i18n_patterns in URLconf
- _language key in the current user’s session
- django_language cookie (or as specified by settings.LANGUAGE_COOKIE_NAME)
- Accept-Language HTTP header (this is what your browser setting sends)
- settings.LANGUAGE_CODE
Since your browser settings are set to prefer ‘de’, I suspect the LocaleMiddleware must decide otherwise in one of the previous steps 1. – 3.
In my case, I used en-gb as the parameter to run
django-admin.py makemessages -l en-gb
Instead, it should be en_GB.
django-admin.py makemessages -l en_GB
For example below, you can translate English to French.
First, create locale
folder just under django-project
as shown below. *I use Django 4.2.1 and you can see my answer explaining how to set templates:
django-project
|-core
| |-settings.py
| └-urls.py
|-my_app1
| |-views.py
| |-urls.py
| |-models.py
| |_admin.py
| └-apps.py
|-my_app2
|-templates
| └-index.html
└-locale # Here
Then, set core/settings.py
as shown below. *LocaleMiddleware
must be between SessionMiddleware
and CommonMiddleware
. 'en-us'
is set to LANGUAGE_CODE
by default in core/settings.py
:
# "core/settings.py"
MIDDLEWARE = [
...
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware', # Here
'django.middleware.common.CommonMiddleware',
...
]
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
from django.utils.translation import gettext_lazy as _
LANGUAGES = ( # Here
('en', _('English')),
('fr', _('French'))
)
And, Hello
and World
are translated and outputted on console and index.html
is rendered in test()
in my_app1/views.py
as shown below:
# "my_app1/views.py"
from django.shortcuts import render
from django.utils.translation import gettext as _
def test(request):
print(_("Hello"), _("World")) # Here
return render(request, 'index.html')
And, test()
path is set to urlpatterns
in my_app1/urls.py
as shown below:
# "my_app1/urls.py"
from django.urls import path
from . import views
app_name = "my_app1"
urlpatterns = [
path('', views.test, name="test") # Here
]
And, admin
and my_app1
paths are set to urlpatterns
with i18n_patterns() in core/urls.py
as shown below. *You can see my question and my answer explaining prefix_default_language=False with i18n_patterns()
:
# "core/urls.py"
from django.contrib import admin
from django.urls import path, include
from django.conf.urls.i18n import i18n_patterns
urlpatterns = i18n_patterns(
path('admin/', admin.site.urls), # Here
path('my_app1/', include('my_app1.urls')) # Here
)
And, 3 tags are set in templates/index.py
as shown below. *For the translation in Django Templates, {% load i18n %}
is needed and {% translate %}
or {% trans %}
can translate:
# "templates/index.py"
{% load i18n %}
{% translate "Hello" %} {% trans "World" %}
And, name
field and person
model are translated in my_app1/models.py
as shown below:
# "my_app1/models.py"
from django.db import models
from django.utils.translation import gettext_lazy as _
class Person(models.Model):
first_name = models.CharField(
max_length=20, verbose_name=_("first name") # Here
)
last_name = models.CharField(
max_length=20, verbose_name=_("last name") # Here
)
class Meta:
verbose_name = _('person') # Here
verbose_name_plural = _('persons') # Here
And, this is my_app1/admin.py
below:
# "my_app1/admin.py"
from django.contrib import admin
from .models import Person
@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
pass
And, my_app1
is translated in my_app1/apps.py
as shown below:
# "my_app1/apps.py"
from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class MyApp1Config(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'my_app1'
verbose_name = _('my_app1') # Here
Now, run the command below. *You can see my answer explaining the command below:
django-admin makemessages -l fr
Then, django.po
is created in locale/fr/LC_MESSAGES/
as shown below:
django-project
|-core
| |-settings.py
| └-urls.py
|-my_app1
| |-views.py
| |-urls.py
| |-models.py
| |_admin.py
| └-apps.py
|-my_app2
|-templates
| └-index.html
└-locale
└-fr
└-LC_MESSAGES
└-django.po # Here
Then, add Bonjour
, Monde
, Anglais
and Français
to each msgstr ""
respectively in locale/fr/LC_MESSAGES/django.po
as shown below:
# "locale/fr/LC_MESSAGES/django.po"
...
#: .coresettings.py:140
msgid "English"
msgstr "Anglais" # Here
#: .coresettings.py:141
msgid "French"
msgstr "Français" # Here
#: .my_app1apps.py:7
msgid "my_app1"
msgstr "mon_app1" # Here
#: .my_app1models.py:6
msgid "first name"
msgstr "prénom"
#: .my_app1models.py:7
msgid "last name"
msgstr "nom de famille"
#: .my_app1models.py:13
msgid "person"
msgstr "personne"
#: .my_app1models.py:14
msgid "persons"
msgstr "personnes"
#: .my_app1views.py:5 .templatesindex.html:3
msgid "Hello"
msgstr "Bonjour" # Here
#: .my_app1views.py:5 .templatesindex.html:3
msgid "World"
msgstr "Monde" # Here
...
Then, run the command below to compile django.po
to django.mo
. *You can see my answer explaining the command below:
django-admin compilemessages
Then, django.po
is compiled to django.mo
in locale/fr/LC_MESSAGES/
as shown below:
django-project
|-core
| |-settings.py
| └-urls.py
|-my_app1
| |-views.py
| |-urls.py
| |-models.py
| |_admin.py
| └-apps.py
|-my_app2
|-templates
| └-index.html
└-locale
└-fr
└-LC_MESSAGES
|-django.po
└-django.mo # Here
Now, http://localhost:8000/en/my_app1/
can show Hello World
as shown below:
And, Hello World
below is outputted on console:
Hello World
And, http://localhost:8000/fr/my_app1/
can show Bonjour Monde
as shown below:
And, Bonjour Monde
below is outputted on console:
Bonjour Monde
And, http://localhost:8000/en/admin/my_app1/person/add/
can show the English version of Django Admin as shown below:
And, http://localhost:8000/fr/admin/my_app1/person/add/
can show the French version of Django Admin as shown below: