summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorClaude Paroz <claude@2xlibre.net>2016-03-07 21:52:08 +0100
committerClaude Paroz <claude@2xlibre.net>2016-04-15 17:28:54 +0200
commitde40cfbe74642df1e94c131e1adaa3363173c0cf (patch)
tree447388e6b08d67f2c8c1f168d1ffc6df851beb03 /django
parent79a091820f46f00cba217a9e05daec855272c3ad (diff)
Fixed #19567 -- Added JavaScriptCatalog and JSONCatalog class-based views
Thanks Cristiano Coelho and Tim Graham for the reviews.
Diffstat (limited to 'django')
-rw-r--r--django/contrib/admin/sites.py10
-rw-r--r--django/views/i18n.py119
2 files changed, 121 insertions, 8 deletions
diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py
index d867aacbc1..dbc0472ab7 100644
--- a/django/contrib/admin/sites.py
+++ b/django/contrib/admin/sites.py
@@ -14,6 +14,7 @@ from django.utils.text import capfirst
from django.utils.translation import ugettext as _, ugettext_lazy
from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_protect
+from django.views.i18n import JavaScriptCatalog
system_check_errors = []
@@ -316,15 +317,8 @@ class AdminSite(object):
def i18n_javascript(self, request):
"""
Displays the i18n JavaScript that the Django admin requires.
-
- This takes into account the USE_I18N setting. If it's set to False, the
- generated JavaScript will be leaner and faster.
"""
- if settings.USE_I18N:
- from django.views.i18n import javascript_catalog
- else:
- from django.views.i18n import null_javascript_catalog as javascript_catalog
- return javascript_catalog(request, packages=['django.conf', 'django.contrib.admin'])
+ return JavaScriptCatalog.as_view(packages=['django.contrib.admin'])(request)
@never_cache
def logout(self, request, extra_context=None):
diff --git a/django/views/i18n.py b/django/views/i18n.py
index 12fa1f70f6..03c771bf2f 100644
--- a/django/views/i18n.py
+++ b/django/views/i18n.py
@@ -2,6 +2,7 @@ import importlib
import itertools
import json
import os
+import warnings
from django import http
from django.apps import apps
@@ -10,6 +11,7 @@ from django.template import Context, Engine
from django.urls import translate_url
from django.utils import six
from django.utils._os import upath
+from django.utils.deprecation import RemovedInDjango20Warning
from django.utils.encoding import smart_text
from django.utils.formats import get_format, get_format_modules
from django.utils.http import is_safe_url, urlunquote
@@ -17,6 +19,7 @@ from django.utils.translation import (
LANGUAGE_SESSION_KEY, check_for_language, get_language, to_locale,
)
from django.utils.translation.trans_real import DjangoTranslation
+from django.views.generic import View
DEFAULT_PACKAGES = ['django.conf']
LANGUAGE_QUERY_PARAMETER = 'language'
@@ -291,6 +294,10 @@ 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.
"""
+ warnings.warn(
+ "The javascript_catalog() view is deprecated in favor of the "
+ "JavaScriptCatalog view.", RemovedInDjango20Warning, stacklevel=2
+ )
locale = _get_locale(request)
packages = _parse_packages(packages)
catalog, plural = get_javascript_catalog(locale, domain, packages)
@@ -314,6 +321,10 @@ def json_catalog(request, domain='djangojs', packages=None):
"plural": '...' # Expression for plural forms, or null.
}
"""
+ warnings.warn(
+ "The json_catalog() view is deprecated in favor of the "
+ "JSONCatalog view.", RemovedInDjango20Warning, stacklevel=2
+ )
locale = _get_locale(request)
packages = _parse_packages(packages)
catalog, plural = get_javascript_catalog(locale, domain, packages)
@@ -323,3 +334,111 @@ def json_catalog(request, domain='djangojs', packages=None):
'plural': plural,
}
return http.JsonResponse(data)
+
+
+class JavaScriptCatalog(View):
+ """
+ Return the selected language catalog as a JavaScript library.
+
+ Receives the list of packages to check for translations in the `packages`
+ kwarg either from the extra dictionary passed to the url() function or as a
+ plus-sign delimited string from the request. Default is 'django.conf'.
+
+ You can override the gettext domain for this view, but usually you don't
+ want to do that as JavaScript messages go to the djangojs domain. This
+ might be needed if you deliver your JavaScript source from Django templates.
+ """
+ domain = 'djangojs'
+ packages = None
+
+ def get(self, request, *args, **kwargs):
+ locale = get_language()
+ domain = kwargs.get('domain', self.domain)
+ # If packages are not provided, default to all installed packages, as
+ # DjangoTranslation without localedirs harvests them all.
+ packages = kwargs.get('packages', '').split('+') or self.packages
+ paths = self.get_paths(packages) if packages else None
+ self.translation = DjangoTranslation(locale, domain=domain, localedirs=paths)
+ context = self.get_context_data(**kwargs)
+ return self.render_to_response(context)
+
+ def get_paths(self, packages):
+ allowable_packages = dict((app_config.name, app_config) for app_config in apps.get_app_configs())
+ app_configs = [allowable_packages[p] for p in packages if p in allowable_packages]
+ # paths of requested packages
+ return [os.path.join(app.path, 'locale') for app in app_configs]
+
+ def get_plural(self):
+ plural = None
+ if '' in self.translation._catalog:
+ for line in self.translation._catalog[''].split('\n'):
+ if line.startswith('Plural-Forms:'):
+ plural = line.split(':', 1)[1].strip()
+ if plural is not None:
+ # This should be a compiled function of a typical plural-form:
+ # Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 :
+ # n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;
+ plural = [el.strip() for el in plural.split(';') if el.strip().startswith('plural=')][0].split('=', 1)[1]
+ return plural
+
+ def get_catalog(self):
+ pdict = {}
+ maxcnts = {}
+ catalog = {}
+ trans_cat = self.translation._catalog
+ trans_fallback_cat = self.translation._fallback._catalog if self.translation._fallback else {}
+ for key, value in itertools.chain(six.iteritems(trans_cat), six.iteritems(trans_fallback_cat)):
+ if key == '' or key in catalog:
+ continue
+ if isinstance(key, six.string_types):
+ catalog[key] = value
+ elif isinstance(key, tuple):
+ msgid = key[0]
+ cnt = key[1]
+ maxcnts[msgid] = max(cnt, maxcnts.get(msgid, 0))
+ pdict.setdefault(msgid, {})[cnt] = value
+ else:
+ raise TypeError(key)
+ for k, v in pdict.items():
+ catalog[k] = [v.get(i, '') for i in range(maxcnts[msgid] + 1)]
+ return catalog
+
+ def get_context_data(self, **kwargs):
+ return {
+ 'catalog': self.get_catalog(),
+ 'formats': get_formats(),
+ 'plural': self.get_plural(),
+ }
+
+ def render_to_response(self, context, **response_kwargs):
+ def indent(s):
+ return s.replace('\n', '\n ')
+
+ template = Engine().from_string(js_catalog_template)
+ context['catalog_str'] = indent(
+ json.dumps(context['catalog'], sort_keys=True, indent=2)
+ ) if context['catalog'] else None
+ context['formats_str'] = indent(json.dumps(context['formats'], sort_keys=True, indent=2))
+
+ return http.HttpResponse(template.render(Context(context)), 'text/javascript')
+
+
+class JSONCatalog(JavaScriptCatalog):
+ """
+ Return the selected language catalog as a JSON object.
+
+ Receives the same parameters as JavaScriptCatalog and 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.
+ }
+ """
+ def render_to_response(self, context, **response_kwargs):
+ return http.JsonResponse(context)