'django translation not working for me

i cant force django to translate strings, choosen language from request is correct, but i have no translated strings.

maybe here could be problem: when i run shell, returned language_code is not equal as i have in settings.py

gettext = lambda s: s
USE_I18N = True
USE_L10N = True
LANGUAGE_CODE = 'en'

LANGUAGES = (
   ('cs', gettext('Czech')),
   ('en', gettext('English')),
)

>>> from django.utils import translation
>>> 
>>> translation.get_language()
'en-us'
>>> from django.conf import settings
>>> settings.LANGUAGE_CODE
'en'

my message file is in locale/cs/LC_MESSAGES/django.{mo,po} i have this content there (translated abbr of week short names):

msgid "Mo Tu We Th Fr Sa Su"
msgstr "Po Út St Čt Pá So Ne"

in view, when i am getting right value 'cs'

print get_language() 
'cs'

and next line is ugettext

ugettext('Mo Tu We Th Fr Sa Su')
'Mo Tu We Th Fr Sa Su'

but i am expecting

'Po Út St Čt Pá So Ne'

can somebody tell me where could be problem? :o)



Solution 1:[1]

It is possible that translation is in .po file however is missing in .mo file. Try recompile messages by running

./manage.py compilemessages 

Solution 2:[2]

I tracked the code whole night and found some clues. Mainly in trans_real.py and gettext.py. You can put a breakpoint in "translation" function.

  1. The translation code are executed only once when app starts. So you need to reload the uwsgi to load the new version.
  2. Po file is never used. gettext.py use "mo" file only.(in my laptop). So you have to compile messages after changing.
  3. The "-" in the language code will be converted to "_" in locale path. For example "zh-CN" will be converted to "zh_CN". That's why the translation file cannot be found. I think it will be better if just using two characters as the language code. And the case sensitive should be considered in Linux system.

Solution 3:[3]

1. Make sure you have a correct-translated .mo file:

Execute msgunfmt ./locale/jp/LC_MESSAGES/django.mo to decompile .mo file:

Example output:

msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <[email protected]>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

msgid "world"
msgstr "????"

2. Make sure Python's builtin gettext worked

Execute python under project root, enter the Python shell, then execute:

import gettext
assert gettext.translation("django", "locale", ['jp']).gettext("world") == "????"

3. Make sure LOCALE_PATHS is correctly set

Execute python manage.py under project root, enter the Django shell, then execute:

from django.conf import settings
assert len(settings.LOCALE_PATHS) > 0

4. Make sure Django's gettext worked under corresponding language context

Enter Django shell, Execute:

from django.utils import translation
with translation.override("jp"):
    assert translation.gettext("world") == "????"

5. Make sure you have a standard language code, and django supports it

Enter Django shell, Execute:

from django.utils import translation
assert 'ja' in translation.trans_real.get_languages()
assert 'zh-hans' in translation.trans_real.get_languages()
assert 'zh-hant' in translation.trans_real.get_languages()
assert 'zh-cn' not in translation.trans_real.get_languages()

6. Make sure you have the correct language context

Server code:

from django.contrib import admin
from django.http import JsonResponse
from django.urls import path
from django.utils.translation import gettext_lazy, get_language


def home(_):
    return JsonResponse(dict(
        language=get_language(),
        hello=gettext_lazy("world")
    ), json_dumps_params=dict(ensure_ascii=False, indent=4))


urlpatterns = [
    path('admin/', admin.site.urls),
    path('', home)
]

Test it:

curl 'http://localhost:8000' -H 'Accept-Language: ja'

Example response:

{
    "language": "ja",
    "hello": "????"
}

The required config but not in default Django project:

  1. LOCALE_PATHS The locale path is required for django to find .mo files
  2. MIDDLEWARE The LocaleMiddleware is required for web project to detect context's language
BASE_DIR = Path(__file__).resolve().parent.parent
LOCALE_PATHS = (
    BASE_DIR / "locale", # Here
)
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.locale.LocaleMiddleware', # And here
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

Another note:

Do not execute mkdir locale to create locale folder manually, python manage.py makemessages will create locale folder if your settings worked.

The correct i18n steps for a new Django project:

  1. Set LOCALE_PATHS
  2. Set LocaleMiddleware
  3. python manage.py makemessages
  4. python manage.py compilemessages
  5. python manage.py runserver to see if it worked.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 mark
Solution 3