Fixed #22106 -- Allowed using more than one instance of javascript_catalog per project.

This commit is contained in:
Moritz Sichert 2014-02-23 15:10:31 +01:00 committed by Tim Graham
parent 556eb67701
commit 6bb2175ed6
7 changed files with 147 additions and 73 deletions

View File

@ -94,87 +94,87 @@ js_catalog_template = r"""
django.pluralidx = function (count) { return (count == 1) ? 0 : 1; };
{% endif %}
{% if catalog_str %}
/* gettext library */
django.catalog = {{ catalog_str }};
django.gettext = function (msgid) {
var value = django.catalog[msgid];
if (typeof(value) == 'undefined') {
return msgid;
} else {
return (typeof(value) == 'string') ? value : value[0];
}
};
django.ngettext = function (singular, plural, count) {
var value = django.catalog[singular];
if (typeof(value) == 'undefined') {
return (count == 1) ? singular : plural;
} else {
return value[django.pluralidx(count)];
}
};
django.gettext_noop = function (msgid) { return msgid; };
django.pgettext = function (context, msgid) {
var value = django.gettext(context + '\x04' + msgid);
if (value.indexOf('\x04') != -1) {
value = msgid;
}
return value;
};
django.npgettext = function (context, singular, plural, count) {
var value = django.ngettext(context + '\x04' + singular, context + '\x04' + plural, count);
if (value.indexOf('\x04') != -1) {
value = django.ngettext(singular, plural, count);
}
return value;
};
{% else %}
/* gettext identity library */
django.gettext = function (msgid) { return msgid; };
django.ngettext = function (singular, plural, count) { return (count == 1) ? singular : plural; };
django.gettext_noop = function (msgid) { return msgid; };
django.pgettext = function (context, msgid) { return msgid; };
django.npgettext = function (context, singular, plural, count) { return (count == 1) ? singular : plural; };
django.catalog = django.catalog || {};
{% if catalog_str %}
var newcatalog = {{ catalog_str }};
for (var key in newcatalog) {
django.catalog[key] = newcatalog[key];
}
{% endif %}
django.interpolate = function (fmt, obj, named) {
if (named) {
return fmt.replace(/%\(\w+\)s/g, function(match){return String(obj[match.slice(2,-2)])});
} else {
return fmt.replace(/%s/g, function(match){return String(obj.shift())});
}
};
if (!django.jsi18n_initialized) {
django.gettext = function (msgid) {
var value = django.catalog[msgid];
if (typeof(value) == 'undefined') {
return msgid;
} else {
return (typeof(value) == 'string') ? value : value[0];
}
};
django.ngettext = function (singular, plural, count) {
var value = django.catalog[singular];
if (typeof(value) == 'undefined') {
return (count == 1) ? singular : plural;
} else {
return value[django.pluralidx(count)];
}
};
/* formatting library */
django.gettext_noop = function (msgid) { return msgid; };
django.formats = {{ formats_str }};
django.get_format = function (format_type) {
var value = django.formats[format_type];
if (typeof(value) == 'undefined') {
return format_type;
} else {
django.pgettext = function (context, msgid) {
var value = django.gettext(context + '\x04' + msgid);
if (value.indexOf('\x04') != -1) {
value = msgid;
}
return value;
}
};
};
/* add to global namespace */
globals.pluralidx = django.pluralidx;
globals.gettext = django.gettext;
globals.ngettext = django.ngettext;
globals.gettext_noop = django.gettext_noop;
globals.pgettext = django.pgettext;
globals.npgettext = django.npgettext;
globals.interpolate = django.interpolate;
globals.get_format = django.get_format;
django.npgettext = function (context, singular, plural, count) {
var value = django.ngettext(context + '\x04' + singular, context + '\x04' + plural, count);
if (value.indexOf('\x04') != -1) {
value = django.ngettext(singular, plural, count);
}
return value;
};
django.interpolate = function (fmt, obj, named) {
if (named) {
return fmt.replace(/%\(\w+\)s/g, function(match){return String(obj[match.slice(2,-2)])});
} else {
return fmt.replace(/%s/g, function(match){return String(obj.shift())});
}
};
/* formatting library */
django.formats = {{ formats_str }};
django.get_format = function (format_type) {
var value = django.formats[format_type];
if (typeof(value) == 'undefined') {
return format_type;
} else {
return value;
}
};
/* add to global namespace */
globals.pluralidx = django.pluralidx;
globals.gettext = django.gettext;
globals.ngettext = django.ngettext;
globals.gettext_noop = django.gettext_noop;
globals.pgettext = django.pgettext;
globals.npgettext = django.npgettext;
globals.interpolate = django.interpolate;
globals.get_format = django.get_format;
django.jsi18n_initialized = true;
}
}(this));
{% endautoescape %}

View File

@ -143,6 +143,9 @@ Internationalization
* The :func:`django.views.i18n.set_language` view now properly redirects to
:ref:`translated URLs <url-internationalization>`, when available.
* The :func:`django.views.i18n.javascript_catalog` view now works correctly
if used multiple times with different configurations on the same page.
Management Commands
^^^^^^^^^^^^^^^^^^^

View File

@ -952,6 +952,31 @@ different apps and this changes often and you don't want to pull in one big
catalog file. As a security measure, these values can only be either
``django.conf`` or any package from the :setting:`INSTALLED_APPS` setting.
You can also split the catalogs in multiple URLs and load them as you need in
your sites::
js_info_dict_app = {
'packages': ('your.app.package',),
}
js_info_dict_other_app = {
'packages': ('your.other.app.package',),
}
urlpatterns = [
url(r'^jsi18n/app/$', javascript_catalog, js_info_dict_app),
url(r'^jsi18n/other_app/$', javascript_catalog, js_info_dict_other_app),
]
If you use more than one ``javascript_catalog`` on a site and some of them
define the same strings, the strings in the catalog that was loaded last take
precedence.
.. versionchanged:: 1.9
Before Django 1.9, the catalogs completely overwrote each other and you
could only use one at a time.
The JavaScript translations found in the paths listed in the
:setting:`LOCALE_PATHS` setting are also always included. To keep consistency
with the translations lookup order algorithm used for Python and templates, the

View File

@ -0,0 +1,17 @@
<html>
<head>
<script type="text/javascript" src="/jsi18n/app1/"></script>
<script type="text/javascript" src="/jsi18n/app2/"></script>
<body>
<p id="app1string">
<script type="text/javascript">
document.write(gettext('this app1 string is to be translated'))
</script>
</p>
<p id="app2string">
<script type="text/javascript">
document.write(gettext('this app2 string is to be translated'))
</script>
</p>
</body>
</html>

View File

@ -1,4 +1,6 @@
# -*- coding:utf-8 -*-
from __future__ import unicode_literals
import gettext
import json
import os
@ -100,7 +102,7 @@ class I18NTests(TestCase):
self.assertContains(response, json.dumps(trans_txt), 1)
if lang_code == 'fr':
# Message with context (msgctxt)
self.assertContains(response, r'"month name\u0004May": "mai"', 1)
self.assertContains(response, '"month name\\u0004May": "mai"', 1)
@override_settings(ROOT_URLCONF='view_tests.urls')
@ -270,6 +272,16 @@ class JavascriptI18nTests(LiveServerTestCase):
elem = self.selenium.find_element_by_id("npgettext_plur")
self.assertEqual(elem.text, "455 Resultate")
@modify_settings(INSTALLED_APPS={'append': ['view_tests.app1', 'view_tests.app2']})
@override_settings(LANGUAGE_CODE='fr')
def test_multiple_catalogs(self):
self.selenium.get('%s%s' % (self.live_server_url, '/jsi18n_multi_catalogs/'))
elem = self.selenium.find_element_by_id('app1string')
self.assertEqual(elem.text, 'il faut traduire cette chaîne de caractères de app1')
elem = self.selenium.find_element_by_id('app2string')
self.assertEqual(elem.text, 'il faut traduire cette chaîne de caractères de app2')
class JavascriptI18nChromeTests(JavascriptI18nTests):
webdriver_class = 'selenium.webdriver.chrome.webdriver.WebDriver'

View File

@ -38,6 +38,16 @@ js_info_dict_admin = {
'packages': ('django.contrib.admin', 'view_tests'),
}
js_info_dict_app1 = {
'domain': 'djangojs',
'packages': ('view_tests.app1',),
}
js_info_dict_app2 = {
'domain': 'djangojs',
'packages': ('view_tests.app2',),
}
js_info_dict_app5 = {
'domain': 'djangojs',
'packages': ('view_tests.app5',),
@ -64,12 +74,15 @@ urlpatterns = [
# i18n views
url(r'^i18n/', include('django.conf.urls.i18n')),
url(r'^jsi18n/$', i18n.javascript_catalog, js_info_dict),
url(r'^jsi18n/app1/$', i18n.javascript_catalog, js_info_dict_app1),
url(r'^jsi18n/app2/$', i18n.javascript_catalog, js_info_dict_app2),
url(r'^jsi18n/app5/$', i18n.javascript_catalog, js_info_dict_app5),
url(r'^jsi18n_english_translation/$', i18n.javascript_catalog, js_info_dict_english_translation),
url(r'^jsi18n_multi_packages1/$', i18n.javascript_catalog, js_info_dict_multi_packages1),
url(r'^jsi18n_multi_packages2/$', i18n.javascript_catalog, js_info_dict_multi_packages2),
url(r'^jsi18n_admin/$', i18n.javascript_catalog, js_info_dict_admin),
url(r'^jsi18n_template/$', views.jsi18n),
url(r'^jsi18n_multi_catalogs/$', views.jsi18n_multi_catalogs),
# Static views
url(r'^site_media/(?P<path>.*)$', static.serve, {'document_root': media_dir}),

View File

@ -82,6 +82,10 @@ def jsi18n(request):
return render_to_response('jsi18n.html')
def jsi18n_multi_catalogs(request):
return render_to_response('jsi18n-multi-catalogs.html')
def raises_template_does_not_exist(request, path='i_dont_exist.html'):
# We need to inspect the HTML generated by the fancy 500 debug view but
# the test client ignores it, so we send it explicitly.