mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Fixed #22106 -- Allowed using more than one instance of javascript_catalog per project.
This commit is contained in:
		
				
					committed by
					
						 Tim Graham
						Tim Graham
					
				
			
			
				
	
			
			
			
						parent
						
							556eb67701
						
					
				
				
					commit
					6bb2175ed6
				
			| @@ -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 %} | ||||
|   | ||||
| @@ -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 | ||||
| ^^^^^^^^^^^^^^^^^^^ | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
							
								
								
									
										17
									
								
								tests/view_tests/templates/jsi18n-multi-catalogs.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								tests/view_tests/templates/jsi18n-multi-catalogs.html
									
									
									
									
									
										Normal 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> | ||||
| @@ -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' | ||||
|   | ||||
| @@ -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}), | ||||
|   | ||||
| @@ -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. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user