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:
parent
e8cd65f829
commit
244404227e
@ -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)
|
||||||
|
@ -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`.
|
||||||
|
@ -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
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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}),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user