mirror of
https://github.com/django/django.git
synced 2025-10-24 06:06:09 +00:00
Fixed #19567 -- Added JavaScriptCatalog and JSONCatalog class-based views
Thanks Cristiano Coelho and Tim Graham for the reviews.
This commit is contained in:
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user