I created this test app to troubleshoot a problem we are having with Django's JavaScriptCatalog
.
-
LOCALE_PATHS
set toPROJECT_ROOT/locale
. -
Translations for all apps stored under
LOCALE_PATHS
. -
JavaScriptCatalog
configured withoutpackages
:path('jsi18n/', JavaScriptCatalog.as_view(), name='javascript-catalog')
-
Translations in the project (
django
anddjangojs
domains):es_MX
andes
(demonstrate the problem).de_BE
andde
(don't demonstrate the problem).
-
Test view with four strings, each one translated in a specific
po
file:strings = { "en - Not translated": _("en - Not translated"), "de - Not translated": _("de - Not translated"), "de_BE - Not translated": _("de_BE - Not translated"), "es - Not translated": _("es - Not translated"), "es_MX - Not translated": _("es_MX - Not translated"), }
Translations from a region-specific language code (es-mx
) first fall back to their base language code
(es
) and then to the default language.
When the language is set to es-mx
, strings that are not translated in es_MX
appear in the JavaScript
catalog with their translations from the default language (set by LANGUAGE_CODE
).
Note: This can happen with (some) other locale codes. I am using es-mx
as an example.
- Set up virtualenv:
python3 -m venv .venv; . .venv/bin/activate
- Install requirements
pip install -r requirements.txt
- Compile messages:
python manage.py compilemessages
- Run server:
python manage.py runserver
-
Activate
es-mx
in the test view (edit code @testapp/views.py
). -
First, visit http://127.0.0.1:8000/test/
Notice:
# Falls back to es/django.po: "es - Not translated": "es - Translated @ es/django.po" # From es_MX/django.po "es_MX - Not translated": "es_MX - Translated @ es_MX/django.po"
-
Then, visit http://127.0.0.1:8000/jsi18n/
Notice:
# From es_MX/djangojs.po "es_MX - Not translated": "es_MX - Translated @ es_MX/djangojs.po" # "es - Not translated" not present in the catalog <-- This is the issue # It should have been pulled from es/djangojs.po
JavaScriptCatalog
uses the first level of fallback
from the translations
(link to code):
trans_fallback_cat = (
self.translation._fallback._catalog if self.translation._fallback else {}
)
However, in the case of es_MX
there is a deeper chain of fallback
objects:
>>> from django.utils.translation.trans_real import DjangoTranslation
>>> dt = DjangoTranslation('es-mx', domain='djangojs')
>>> strings = dict(dt._catalog.items())
# es_MX translation present in _catalog as expected:
>>> strings.get('es_MX - Not translated')
# Fallback es translation not present in first _fallback:
>>> dt._fallback._catalog.get('es - Not translated')
# Fallback es translation present in second _fallback:
>>> dt._fallback._fallback._catalog.get('es - Not translated')
The reason for this is that the first level of fallback comes from another app, the admin in this specific case.
-
Repeat the test with
de-be
. It works as expected. -
Remove or rename the Django admin's files
es/LC_MESSAGES/djangojs*
and test withes-mx
. It works as expected. -
Configure the catalog like this:
JavaScriptCatalog.as_view(packages=['testapp'])
and test withes-mx
. It works as expected.