diff options
| author | Claude Paroz <claude@2xlibre.net> | 2016-03-07 21:52:08 +0100 |
|---|---|---|
| committer | Claude Paroz <claude@2xlibre.net> | 2016-04-15 17:28:54 +0200 |
| commit | de40cfbe74642df1e94c131e1adaa3363173c0cf (patch) | |
| tree | 447388e6b08d67f2c8c1f168d1ffc6df851beb03 /django | |
| parent | 79a091820f46f00cba217a9e05daec855272c3ad (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.py | 10 | ||||
| -rw-r--r-- | django/views/i18n.py | 119 |
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) |
