1
0
mirror of https://github.com/django/django.git synced 2025-06-06 12:09:11 +00:00

Fixed #22404 -- Added a view that exposes i18n catalog as a JSON

Added django.views.i18n.json_catalog() view, which returns a JSON
response containing translations, formats, and a plural expression
for the specified language.
This commit is contained in:
Sergey Kolosov 2015-06-05 15:10:28 +01:00 committed by Tim Graham
parent e8cd65f829
commit 244404227e
5 changed files with 130 additions and 13 deletions

View File

@ -17,6 +17,9 @@ from django.utils.translation import (
LANGUAGE_SESSION_KEY, check_for_language, get_language, to_locale, LANGUAGE_SESSION_KEY, check_for_language, get_language, to_locale,
) )
DEFAULT_PACKAGES = ['django.conf']
LANGUAGE_QUERY_PARAMETER = 'language'
def set_language(request): def set_language(request):
""" """
@ -36,7 +39,7 @@ def set_language(request):
next = '/' next = '/'
response = http.HttpResponseRedirect(next) response = http.HttpResponseRedirect(next)
if request.method == 'POST': 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): if lang_code and check_for_language(lang_code):
next_trans = translate_url(next, lang_code) next_trans = translate_url(next, lang_code)
if next_trans != next: if next_trans != next:
@ -199,7 +202,7 @@ def get_javascript_catalog(locale, domain, packages):
default_locale = to_locale(settings.LANGUAGE_CODE) default_locale = to_locale(settings.LANGUAGE_CODE)
app_configs = apps.get_app_configs() app_configs = apps.get_app_configs()
allowable_packages = set(app_config.name for app_config in 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] packages = [p for p in packages if p in allowable_packages]
t = {} t = {}
paths = [] paths = []
@ -284,6 +287,21 @@ def get_javascript_catalog(locale, domain, packages):
return catalog, plural 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): def null_javascript_catalog(request, domain=None, packages=None):
""" """
Returns "identity" versions of the JavaScript i18n functions -- i.e., Returns "identity" versions of the JavaScript i18n functions -- i.e.,
@ -305,16 +323,35 @@ def javascript_catalog(request, domain='djangojs', packages=None):
go to the djangojs domain. But this might be needed if you go to the djangojs domain. But this might be needed if you
deliver your JavaScript source from Django templates. deliver your JavaScript source from Django templates.
""" """
locale = to_locale(get_language()) locale = _get_locale(request)
packages = _parse_packages(packages)
if request.GET and 'language' in request.GET:
if check_for_language(request.GET['language']):
locale = to_locale(request.GET['language'])
if packages is None:
packages = ['django.conf']
if isinstance(packages, six.string_types):
packages = packages.split('+')
catalog, plural = get_javascript_catalog(locale, domain, packages) catalog, plural = get_javascript_catalog(locale, domain, packages)
return render_javascript_catalog(catalog, plural) return render_javascript_catalog(catalog, plural)
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)
data = {
'catalog': catalog,
'formats': get_formats(),
'plural': plural,
}
return http.JsonResponse(data)

View File

@ -357,6 +357,10 @@ Internationalization
for languages which can be written in different scripts, for example Latin for languages which can be written in different scripts, for example Latin
and Cyrillic (e.g. ``be@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 * Added the ``name_translated`` attribute to the object returned by the
:ttag:`get_language_info` template tag. Also added a corresponding template :ttag:`get_language_info` template tag. Also added a corresponding template
filter: :tfilter:`language_name_translated`. filter: :tfilter:`language_name_translated`.

View File

@ -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`` will render a conditional expression. This will evaluate to either a ``true``
(should pluralize) or ``false`` (should **not** pluralize) value. (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 Note on performance
------------------- -------------------

View File

@ -105,6 +105,20 @@ class I18NTests(TestCase):
# Message with context (msgctxt) # Message with context (msgctxt)
self.assertContains(response, '"month name\\u0004May": "mai"', 1) 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') @override_settings(ROOT_URLCONF='view_tests.urls')
class JsI18NTests(SimpleTestCase): class JsI18NTests(SimpleTestCase):
@ -127,6 +141,21 @@ class JsI18NTests(SimpleTestCase):
response = self.client.get('/jsi18n/') response = self.client.get('/jsi18n/')
self.assertNotContains(response, 'esto tiene que ser traducido') 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): def test_jsi18n_fallback_language(self):
""" """
Let's make sure that the fallback language is still working properly Let's make sure that the fallback language is still working properly

View File

@ -84,6 +84,7 @@ urlpatterns = [
url(r'^jsi18n_admin/$', i18n.javascript_catalog, js_info_dict_admin), url(r'^jsi18n_admin/$', i18n.javascript_catalog, js_info_dict_admin),
url(r'^jsi18n_template/$', views.jsi18n), url(r'^jsi18n_template/$', views.jsi18n),
url(r'^jsi18n_multi_catalogs/$', views.jsi18n_multi_catalogs), url(r'^jsi18n_multi_catalogs/$', views.jsi18n_multi_catalogs),
url(r'^jsoni18n/$', i18n.json_catalog, js_info_dict),
# Static views # Static views
url(r'^site_media/(?P<path>.*)$', static.serve, {'document_root': media_dir}), url(r'^site_media/(?P<path>.*)$', static.serve, {'document_root': media_dir}),