diff --git a/django/views/i18n.py b/django/views/i18n.py index 2cf24d35407..91f797dce90 100644 --- a/django/views/i18n.py +++ b/django/views/i18n.py @@ -1,4 +1,3 @@ -import itertools import json import os import re @@ -273,26 +272,27 @@ class JavaScriptCatalog(View): def get_catalog(self): pdict = {} - num_plurals = self._num_plurals catalog = {} - trans_cat = self.translation._catalog - trans_fallback_cat = ( - self.translation._fallback._catalog if self.translation._fallback else {} - ) + translation = self.translation seen_keys = set() - for key, value in itertools.chain( - trans_cat.items(), trans_fallback_cat.items() - ): - if key == "" or key in seen_keys: - continue - if isinstance(key, str): - catalog[key] = value - elif isinstance(key, tuple): - msgid, cnt = key - pdict.setdefault(msgid, {})[cnt] = value + while True: + for key, value in translation._catalog.items(): + if key == "" or key in seen_keys: + continue + if isinstance(key, str): + catalog[key] = value + elif isinstance(key, tuple): + msgid, cnt = key + pdict.setdefault(msgid, {})[cnt] = value + else: + raise TypeError(key) + seen_keys.add(key) + if translation._fallback: + translation = translation._fallback else: - raise TypeError(key) - seen_keys.add(key) + break + + num_plurals = self._num_plurals for k, v in pdict.items(): catalog[k] = [v.get(i, "") for i in range(num_plurals)] return catalog diff --git a/tests/view_tests/custom_locale_path/es/LC_MESSAGES/djangojs.mo b/tests/view_tests/custom_locale_path/es/LC_MESSAGES/djangojs.mo new file mode 100644 index 00000000000..181b61fd9c8 Binary files /dev/null and b/tests/view_tests/custom_locale_path/es/LC_MESSAGES/djangojs.mo differ diff --git a/tests/view_tests/custom_locale_path/es/LC_MESSAGES/djangojs.po b/tests/view_tests/custom_locale_path/es/LC_MESSAGES/djangojs.po new file mode 100644 index 00000000000..12e0c367bad --- /dev/null +++ b/tests/view_tests/custom_locale_path/es/LC_MESSAGES/djangojs.po @@ -0,0 +1,20 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-09-15 16:45+0200\n" +"PO-Revision-Date: 2010-05-12 12:57-0300\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: media/js/translate.js:1 +msgid "custom_locale_path: this is to be translated" +msgstr "custom_locale_path: esto tiene que ser traducido" diff --git a/tests/view_tests/custom_locale_path/es_MX/LC_MESSAGES/djangojs.mo b/tests/view_tests/custom_locale_path/es_MX/LC_MESSAGES/djangojs.mo new file mode 100644 index 00000000000..045c5ec5832 Binary files /dev/null and b/tests/view_tests/custom_locale_path/es_MX/LC_MESSAGES/djangojs.mo differ diff --git a/tests/view_tests/custom_locale_path/es_MX/LC_MESSAGES/djangojs.po b/tests/view_tests/custom_locale_path/es_MX/LC_MESSAGES/djangojs.po new file mode 100644 index 00000000000..22c708a891a --- /dev/null +++ b/tests/view_tests/custom_locale_path/es_MX/LC_MESSAGES/djangojs.po @@ -0,0 +1,20 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-09-15 16:45+0200\n" +"PO-Revision-Date: 2010-05-12 12:57-0300\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: media/js/translate.js:1 +msgid "custom_locale_path: this is to be translated" +msgstr "" diff --git a/tests/view_tests/tests/test_i18n.py b/tests/view_tests/tests/test_i18n.py index 3aacded7c6d..aff920c2c43 100644 --- a/tests/view_tests/tests/test_i18n.py +++ b/tests/view_tests/tests/test_i18n.py @@ -344,6 +344,27 @@ class I18NViewTests(SimpleTestCase): self.assertContains(response, "il faut le traduire") self.assertNotContains(response, "Untranslated string") + def test_jsi18n_fallback_language_with_custom_locale_dir(self): + """ + The fallback language works when there are several levels of fallback + translation catalogs. + """ + locale_paths = [ + path.join( + path.dirname(path.dirname(path.abspath(__file__))), + "custom_locale_path", + ), + ] + with self.settings(LOCALE_PATHS=locale_paths), override("es_MX"): + response = self.client.get("/jsi18n/") + self.assertContains( + response, "custom_locale_path: esto tiene que ser traducido" + ) + response = self.client.get("/jsi18n_no_packages/") + self.assertContains( + response, "custom_locale_path: esto tiene que ser traducido" + ) + def test_i18n_fallback_language_plural(self): """ The fallback to a language with less plural forms maintains the real diff --git a/tests/view_tests/urls.py b/tests/view_tests/urls.py index cec156b5da4..c9a5c28573d 100644 --- a/tests/view_tests/urls.py +++ b/tests/view_tests/urls.py @@ -31,6 +31,7 @@ urlpatterns = [ # i18n views path("i18n/", include("django.conf.urls.i18n")), path("jsi18n/", i18n.JavaScriptCatalog.as_view(packages=["view_tests"])), + path("jsi18n_no_packages/", i18n.JavaScriptCatalog.as_view()), path("jsi18n/app1/", i18n.JavaScriptCatalog.as_view(packages=["view_tests.app1"])), path("jsi18n/app2/", i18n.JavaScriptCatalog.as_view(packages=["view_tests.app2"])), path("jsi18n/app5/", i18n.JavaScriptCatalog.as_view(packages=["view_tests.app5"])),