summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Kolosov <m17.admin@gmail.com>2015-06-05 15:10:28 +0100
committerTim Graham <timograham@gmail.com>2015-08-05 09:05:21 -0400
commit244404227e8a1c5e241658ef0df789a28ed3bbc6 (patch)
tree749c5e86ee0894161e9328e7673561eebaf3a2de
parente8cd65f8297928d3fa7ad3d338953a4423028713 (diff)
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.
-rw-r--r--django/views/i18n.py59
-rw-r--r--docs/releases/1.9.txt4
-rw-r--r--docs/topics/i18n/translation.txt46
-rw-r--r--tests/view_tests/tests/test_i18n.py29
-rw-r--r--tests/view_tests/urls.py1
5 files changed, 128 insertions, 11 deletions
diff --git a/django/views/i18n.py b/django/views/i18n.py
index 49bcfead4d..68039bcf2a 100644
--- a/django/views/i18n.py
+++ b/django/views/i18n.py
@@ -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):
"""
@@ -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:
@@ -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 = []
@@ -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.,
@@ -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())
+ locale = _get_locale(request)
+ packages = _parse_packages(packages)
+ catalog, plural = get_javascript_catalog(locale, domain, packages)
+ return render_javascript_catalog(catalog, plural)
- 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('+')
+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)
diff --git a/docs/releases/1.9.txt b/docs/releases/1.9.txt
index 6f2611f267..1f1183d9a7 100644
--- a/docs/releases/1.9.txt
+++ b/docs/releases/1.9.txt
@@ -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`.
diff --git a/docs/topics/i18n/translation.txt b/docs/topics/i18n/translation.txt
index d976ddf2ef..974f6d31ce 100644
--- a/docs/topics/i18n/translation.txt
+++ b/docs/topics/i18n/translation.txt
@@ -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
-------------------
diff --git a/tests/view_tests/tests/test_i18n.py b/tests/view_tests/tests/test_i18n.py
index 886c10f5fc..a10a3bc7e1 100644
--- a/tests/view_tests/tests/test_i18n.py
+++ b/tests/view_tests/tests/test_i18n.py
@@ -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):
@@ -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
diff --git a/tests/view_tests/urls.py b/tests/view_tests/urls.py
index 4215a4b26a..e8f2855c98 100644
--- a/tests/view_tests/urls.py
+++ b/tests/view_tests/urls.py
@@ -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}),