Loading django/views/i18n.py +49 −12 Original line number Diff line number Diff line Loading @@ -17,6 +17,9 @@ from django.utils.translation import ( LANGUAGE_SESSION_KEY, check_for_language, get_language, to_locale, ) DEFAULT_PACKAGES = ['django.conf'] LANGUAGE_QUERY_PARAMETER = 'language' def set_language(request): """ Loading @@ -36,7 +39,7 @@ def set_language(request): next = '/' response = http.HttpResponseRedirect(next) if request.method == 'POST': lang_code = request.POST.get('language') lang_code = request.POST.get(LANGUAGE_QUERY_PARAMETER) if lang_code and check_for_language(lang_code): next_trans = translate_url(next, lang_code) if next_trans != next: Loading Loading @@ -199,7 +202,7 @@ def get_javascript_catalog(locale, domain, packages): default_locale = to_locale(settings.LANGUAGE_CODE) app_configs = apps.get_app_configs() allowable_packages = set(app_config.name for app_config in app_configs) allowable_packages.add('django.conf') allowable_packages.update(DEFAULT_PACKAGES) packages = [p for p in packages if p in allowable_packages] t = {} paths = [] Loading Loading @@ -284,6 +287,21 @@ def get_javascript_catalog(locale, domain, packages): return catalog, plural def _get_locale(request): language = request.GET.get(LANGUAGE_QUERY_PARAMETER) if not (language and check_for_language(language)): language = get_language() return to_locale(language) def _parse_packages(packages): if packages is None: packages = list(DEFAULT_PACKAGES) elif isinstance(packages, six.string_types): packages = packages.split('+') return packages def null_javascript_catalog(request, domain=None, packages=None): """ Returns "identity" versions of the JavaScript i18n functions -- i.e., Loading @@ -305,16 +323,35 @@ def javascript_catalog(request, domain='djangojs', packages=None): go to the djangojs domain. But this might be needed if you deliver your JavaScript source from Django templates. """ locale = to_locale(get_language()) if request.GET and 'language' in request.GET: if check_for_language(request.GET['language']): locale = to_locale(request.GET['language']) locale = _get_locale(request) packages = _parse_packages(packages) catalog, plural = get_javascript_catalog(locale, domain, packages) return render_javascript_catalog(catalog, plural) if packages is None: packages = ['django.conf'] if isinstance(packages, six.string_types): packages = packages.split('+') def json_catalog(request, domain='djangojs', packages=None): """ Return the selected language catalog as a JSON object. Receives the same parameters as javascript_catalog(), but returns a response with a JSON object of the following format: { "catalog": { # Translations catalog }, "formats": { # Language formats for date, time, etc. }, "plural": '...' # Expression for plural forms, or null. } """ locale = _get_locale(request) packages = _parse_packages(packages) catalog, plural = get_javascript_catalog(locale, domain, packages) return render_javascript_catalog(catalog, plural) data = { 'catalog': catalog, 'formats': get_formats(), 'plural': plural, } return http.JsonResponse(data) docs/releases/1.9.txt +4 −0 Original line number Diff line number Diff line Loading @@ -357,6 +357,10 @@ Internationalization for languages which can be written in different scripts, for example Latin and Cyrillic (e.g. ``be@latin``). * Added the :func:`django.views.i18n.json_catalog` view to help build a custom client-side i18n library upon Django translations. It returns a JSON object containing a translations catalog, formatting settings, and a plural rule. * Added the ``name_translated`` attribute to the object returned by the :ttag:`get_language_info` template tag. Also added a corresponding template filter: :tfilter:`language_name_translated`. Loading docs/topics/i18n/translation.txt +46 −0 Original line number Diff line number Diff line Loading @@ -1213,6 +1213,52 @@ Additionally, if there are complex rules around pluralization, the catalog view will render a conditional expression. This will evaluate to either a ``true`` (should pluralize) or ``false`` (should **not** pluralize) value. The ``json_catalog`` view ------------------------- .. versionadded:: 1.9 .. function:: json_catalog(request, domain='djangojs', packages=None) In order to use another client-side library to handle translations, you may want to take advantage of the ``json_catalog()`` view. It's similar to :meth:`~django.views.i18n.javascript_catalog` but returns a JSON response. The JSON object contains i18n formatting settings (those available for `get_format`_), a plural rule (as a ``plural`` part of a GNU gettext ``Plural-Forms`` expression), and translation strings. The translation strings are taken from applications or Django's own translations, according to what is specified either via ``urlpatterns`` arguments or as request parameters. Paths listed in :setting:`LOCALE_PATHS` are also included. The view is hooked up to your application and configured in the same fashion as :meth:`~django.views.i18n.javascript_catalog` (namely, the ``domain`` and ``packages`` arguments behave identically):: from django.views.i18n import json_catalog js_info_dict = { 'packages': ('your.app.package',), } urlpatterns = [ url(r'^jsoni18n/$', json_catalog, js_info_dict), ] The response format is as follows: .. code-block:: json { "catalog": { # Translations catalog }, "formats": { # Language formats for date, time, etc. }, "plural": "..." # Expression for plural forms, or null. } Note on performance ------------------- Loading tests/view_tests/tests/test_i18n.py +29 −0 Original line number Diff line number Diff line Loading @@ -105,6 +105,20 @@ class I18NTests(TestCase): # Message with context (msgctxt) self.assertContains(response, '"month name\\u0004May": "mai"', 1) def test_jsoni18n(self): """ The json_catalog returns the language catalog and settings as JSON. """ with override('de'): response = self.client.get('/jsoni18n/') data = json.loads(response.content.decode('utf-8')) self.assertIn('catalog', data) self.assertIn('formats', data) self.assertIn('plural', data) self.assertEqual(data['catalog']['month name\x04May'], 'Mai') self.assertIn('DATETIME_FORMAT', data['formats']) self.assertEqual(data['plural'], '(n != 1)') @override_settings(ROOT_URLCONF='view_tests.urls') class JsI18NTests(SimpleTestCase): Loading @@ -127,6 +141,21 @@ class JsI18NTests(SimpleTestCase): response = self.client.get('/jsi18n/') self.assertNotContains(response, 'esto tiene que ser traducido') def test_jsoni18n_with_missing_en_files(self): """ Same as above for the json_catalog view. Here we also check for the expected JSON format. """ with self.settings(LANGUAGE_CODE='es'), override('en-us'): response = self.client.get('/jsoni18n/') data = json.loads(response.content.decode('utf-8')) self.assertIn('catalog', data) self.assertIn('formats', data) self.assertIn('plural', data) self.assertEqual(data['catalog'], {}) self.assertIn('DATETIME_FORMAT', data['formats']) self.assertIsNone(data['plural']) def test_jsi18n_fallback_language(self): """ Let's make sure that the fallback language is still working properly Loading tests/view_tests/urls.py +1 −0 Original line number Diff line number Diff line Loading @@ -84,6 +84,7 @@ urlpatterns = [ 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), url(r'^jsoni18n/$', i18n.json_catalog, js_info_dict), # Static views url(r'^site_media/(?P<path>.*)$', static.serve, {'document_root': media_dir}), Loading Loading
django/views/i18n.py +49 −12 Original line number Diff line number Diff line Loading @@ -17,6 +17,9 @@ from django.utils.translation import ( LANGUAGE_SESSION_KEY, check_for_language, get_language, to_locale, ) DEFAULT_PACKAGES = ['django.conf'] LANGUAGE_QUERY_PARAMETER = 'language' def set_language(request): """ Loading @@ -36,7 +39,7 @@ def set_language(request): next = '/' response = http.HttpResponseRedirect(next) if request.method == 'POST': lang_code = request.POST.get('language') lang_code = request.POST.get(LANGUAGE_QUERY_PARAMETER) if lang_code and check_for_language(lang_code): next_trans = translate_url(next, lang_code) if next_trans != next: Loading Loading @@ -199,7 +202,7 @@ def get_javascript_catalog(locale, domain, packages): default_locale = to_locale(settings.LANGUAGE_CODE) app_configs = apps.get_app_configs() allowable_packages = set(app_config.name for app_config in app_configs) allowable_packages.add('django.conf') allowable_packages.update(DEFAULT_PACKAGES) packages = [p for p in packages if p in allowable_packages] t = {} paths = [] Loading Loading @@ -284,6 +287,21 @@ def get_javascript_catalog(locale, domain, packages): return catalog, plural def _get_locale(request): language = request.GET.get(LANGUAGE_QUERY_PARAMETER) if not (language and check_for_language(language)): language = get_language() return to_locale(language) def _parse_packages(packages): if packages is None: packages = list(DEFAULT_PACKAGES) elif isinstance(packages, six.string_types): packages = packages.split('+') return packages def null_javascript_catalog(request, domain=None, packages=None): """ Returns "identity" versions of the JavaScript i18n functions -- i.e., Loading @@ -305,16 +323,35 @@ def javascript_catalog(request, domain='djangojs', packages=None): go to the djangojs domain. But this might be needed if you deliver your JavaScript source from Django templates. """ locale = to_locale(get_language()) if request.GET and 'language' in request.GET: if check_for_language(request.GET['language']): locale = to_locale(request.GET['language']) locale = _get_locale(request) packages = _parse_packages(packages) catalog, plural = get_javascript_catalog(locale, domain, packages) return render_javascript_catalog(catalog, plural) if packages is None: packages = ['django.conf'] if isinstance(packages, six.string_types): packages = packages.split('+') def json_catalog(request, domain='djangojs', packages=None): """ Return the selected language catalog as a JSON object. Receives the same parameters as javascript_catalog(), but returns a response with a JSON object of the following format: { "catalog": { # Translations catalog }, "formats": { # Language formats for date, time, etc. }, "plural": '...' # Expression for plural forms, or null. } """ locale = _get_locale(request) packages = _parse_packages(packages) catalog, plural = get_javascript_catalog(locale, domain, packages) return render_javascript_catalog(catalog, plural) data = { 'catalog': catalog, 'formats': get_formats(), 'plural': plural, } return http.JsonResponse(data)
docs/releases/1.9.txt +4 −0 Original line number Diff line number Diff line Loading @@ -357,6 +357,10 @@ Internationalization for languages which can be written in different scripts, for example Latin and Cyrillic (e.g. ``be@latin``). * Added the :func:`django.views.i18n.json_catalog` view to help build a custom client-side i18n library upon Django translations. It returns a JSON object containing a translations catalog, formatting settings, and a plural rule. * Added the ``name_translated`` attribute to the object returned by the :ttag:`get_language_info` template tag. Also added a corresponding template filter: :tfilter:`language_name_translated`. Loading
docs/topics/i18n/translation.txt +46 −0 Original line number Diff line number Diff line Loading @@ -1213,6 +1213,52 @@ Additionally, if there are complex rules around pluralization, the catalog view will render a conditional expression. This will evaluate to either a ``true`` (should pluralize) or ``false`` (should **not** pluralize) value. The ``json_catalog`` view ------------------------- .. versionadded:: 1.9 .. function:: json_catalog(request, domain='djangojs', packages=None) In order to use another client-side library to handle translations, you may want to take advantage of the ``json_catalog()`` view. It's similar to :meth:`~django.views.i18n.javascript_catalog` but returns a JSON response. The JSON object contains i18n formatting settings (those available for `get_format`_), a plural rule (as a ``plural`` part of a GNU gettext ``Plural-Forms`` expression), and translation strings. The translation strings are taken from applications or Django's own translations, according to what is specified either via ``urlpatterns`` arguments or as request parameters. Paths listed in :setting:`LOCALE_PATHS` are also included. The view is hooked up to your application and configured in the same fashion as :meth:`~django.views.i18n.javascript_catalog` (namely, the ``domain`` and ``packages`` arguments behave identically):: from django.views.i18n import json_catalog js_info_dict = { 'packages': ('your.app.package',), } urlpatterns = [ url(r'^jsoni18n/$', json_catalog, js_info_dict), ] The response format is as follows: .. code-block:: json { "catalog": { # Translations catalog }, "formats": { # Language formats for date, time, etc. }, "plural": "..." # Expression for plural forms, or null. } Note on performance ------------------- Loading
tests/view_tests/tests/test_i18n.py +29 −0 Original line number Diff line number Diff line Loading @@ -105,6 +105,20 @@ class I18NTests(TestCase): # Message with context (msgctxt) self.assertContains(response, '"month name\\u0004May": "mai"', 1) def test_jsoni18n(self): """ The json_catalog returns the language catalog and settings as JSON. """ with override('de'): response = self.client.get('/jsoni18n/') data = json.loads(response.content.decode('utf-8')) self.assertIn('catalog', data) self.assertIn('formats', data) self.assertIn('plural', data) self.assertEqual(data['catalog']['month name\x04May'], 'Mai') self.assertIn('DATETIME_FORMAT', data['formats']) self.assertEqual(data['plural'], '(n != 1)') @override_settings(ROOT_URLCONF='view_tests.urls') class JsI18NTests(SimpleTestCase): Loading @@ -127,6 +141,21 @@ class JsI18NTests(SimpleTestCase): response = self.client.get('/jsi18n/') self.assertNotContains(response, 'esto tiene que ser traducido') def test_jsoni18n_with_missing_en_files(self): """ Same as above for the json_catalog view. Here we also check for the expected JSON format. """ with self.settings(LANGUAGE_CODE='es'), override('en-us'): response = self.client.get('/jsoni18n/') data = json.loads(response.content.decode('utf-8')) self.assertIn('catalog', data) self.assertIn('formats', data) self.assertIn('plural', data) self.assertEqual(data['catalog'], {}) self.assertIn('DATETIME_FORMAT', data['formats']) self.assertIsNone(data['plural']) def test_jsi18n_fallback_language(self): """ Let's make sure that the fallback language is still working properly Loading
tests/view_tests/urls.py +1 −0 Original line number Diff line number Diff line Loading @@ -84,6 +84,7 @@ urlpatterns = [ 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), url(r'^jsoni18n/$', i18n.json_catalog, js_info_dict), # Static views url(r'^site_media/(?P<path>.*)$', static.serve, {'document_root': media_dir}), Loading