return a path to a builtin template avoid calling this function at the module level or in a classdefinition because file may not exist e g in frozen environments redirect to a given url while setting the chosen language in the session if enabled and in a cookie the url and the language code need to be specified in the request parameters since this view changes how the user will see the rest of the site it must only be accessed as a post request if called as a get request it will redirect to the page in the request the next parameter without changing any state return all formats strings required for i18n to work formatsettings dateformat datetimeformat timeformat yearmonthformat monthdayformat shortdateformat shortdatetimeformat firstdayofweek decimalseparator thousandseparator numbergrouping dateinputformats timeinputformats datetimeinputformats return attr getformatattr for attr in formatsettings class javascriptcatalogview domain djangojs packages none def getself request args kwargs locale getlanguage domain kwargs getdomain self domain if packages are not provided default to all installed packages as djangotranslation without localedirs harvests them all packages kwargs getpackages packages packages split if packages else self packages paths self getpathspackages if packages else none self translation djangotranslationlocale domaindomain localedirspaths context self getcontextdatakwargs return self rendertoresponsecontext def getpathsself packages allowablepackages appconfig name appconfig for appconfig in apps getappconfigs appconfigs allowablepackagesp for p in packages if p in allowablepackages if lenappconfigs lenpackages excluded p for p in packages if p not in allowablepackages raise valueerror invalid packages provided to javascriptcatalog s joinexcluded paths of requested packages return os path joinapp path locale for app in appconfigs property def numpluralsself match re searchrnpluralssd self pluralstring or if match return intmatch1 return 2 property def pluralstringself if in self translation catalog for line in self translation catalog splitn if line startswithpluralforms return line split 11 strip return none def getpluralself plural self pluralstring if plural is not none this should be a compiled function of a typical pluralform pluralforms nplurals3 pluraln101 n100 11 0 n102 n104 n10010 n10020 1 2 plural el strip for el in plural split if el strip startswithplural 0 split 11 return plural def getcatalogself pdict catalog translation self translation seenkeys set while true for key value in translation catalog items if key or key in seenkeys continue if isinstancekey str catalogkey value elif isinstancekey tuple msgid cnt key pdict setdefaultmsgid cnt value else raise typeerrorkey seenkeys addkey if translation fallback translation translation fallback else break numplurals self numplurals for k v in pdict items catalogk v geti for i in rangenumplurals return catalog def getcontextdataself kwargs return catalog self getcatalog formats getformats plural self getplural def rendertoresponseself context responsekwargs def indents return s replacen n with builtintemplatepathi18ncatalog js openencodingutf8 as fh template engine fromstringfh read contextcatalogstr indentjson dumpscontextcatalog sortkeystrue indent2 if contextcatalog else none contextformatsstr indent json dumpscontextformats sortkeystrue indent2 return httpresponse template rendercontextcontext textjavascript charsetutf8 class jsoncatalogjavascriptcatalog translations catalog language formats for date time etc def rendertoresponseself context responsekwargs return jsonresponsecontext return a path to a builtin template avoid calling this function at the module level or in a class definition because __file__ may not exist e g in frozen environments redirect to a given url while setting the chosen language in the session if enabled and in a cookie the url and the language code need to be specified in the request parameters since this view changes how the user will see the rest of the site it must only be accessed as a post request if called as a get request it will redirect to the page in the request the next parameter without changing any state return all formats strings required for i18n to work return the selected language catalog as a javascript library receive the list of packages to check for translations in the packages kwarg either from the extra dictionary passed to the path 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 if packages are not provided default to all installed packages as djangotranslation without localedirs harvests them all paths of requested packages return the number of plurals for this catalog language or 2 if no plural string is available return the plural string including nplurals for this catalog language or none if no plural string is available 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 return the selected language catalog as a json object receive the same parameters as javascriptcatalog and return 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
import json
import os
import re
from pathlib import Path

from django.apps import apps
from django.conf import settings
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
from django.template import Context, Engine
from django.urls import translate_url
from django.utils.formats import get_format
from django.utils.http import url_has_allowed_host_and_scheme
from django.utils.translation import check_for_language, get_language
from django.utils.translation.trans_real import DjangoTranslation
from django.views.generic import View

LANGUAGE_QUERY_PARAMETER = "language"


def builtin_template_path(name):
    
    return Path(__file__).parent / "templates" / name


def set_language(request):
    
    next_url = request.POST.get("next", request.GET.get("next"))
    if (
        next_url or request.accepts("text/html")
    ) and not url_has_allowed_host_and_scheme(
        url=next_url,
        allowed_hosts={request.get_host()},
        require_https=request.is_secure(),
    ):
        next_url = request.META.get("HTTP_REFERER")
        if not url_has_allowed_host_and_scheme(
            url=next_url,
            allowed_hosts={request.get_host()},
            require_https=request.is_secure(),
        ):
            next_url = "/"
    response = HttpResponseRedirect(next_url) if next_url else HttpResponse(status=204)
    if request.method == "POST":
        lang_code = request.POST.get(LANGUAGE_QUERY_PARAMETER)
        if lang_code and check_for_language(lang_code):
            if next_url:
                next_trans = translate_url(next_url, lang_code)
                if next_trans != next_url:
                    response = HttpResponseRedirect(next_trans)
            response.set_cookie(
                settings.LANGUAGE_COOKIE_NAME,
                lang_code,
                max_age=settings.LANGUAGE_COOKIE_AGE,
                path=settings.LANGUAGE_COOKIE_PATH,
                domain=settings.LANGUAGE_COOKIE_DOMAIN,
                secure=settings.LANGUAGE_COOKIE_SECURE,
                httponly=settings.LANGUAGE_COOKIE_HTTPONLY,
                samesite=settings.LANGUAGE_COOKIE_SAMESITE,
            )
    return response


def get_formats():
    
    FORMAT_SETTINGS = (
        "DATE_FORMAT",
        "DATETIME_FORMAT",
        "TIME_FORMAT",
        "YEAR_MONTH_FORMAT",
        "MONTH_DAY_FORMAT",
        "SHORT_DATE_FORMAT",
        "SHORT_DATETIME_FORMAT",
        "FIRST_DAY_OF_WEEK",
        "DECIMAL_SEPARATOR",
        "THOUSAND_SEPARATOR",
        "NUMBER_GROUPING",
        "DATE_INPUT_FORMATS",
        "TIME_INPUT_FORMATS",
        "DATETIME_INPUT_FORMATS",
    )
    return {attr: get_format(attr) for attr in FORMAT_SETTINGS}


class JavaScriptCatalog(View):
    

    domain = "djangojs"
    packages = None

    def get(self, request, *args, **kwargs):
        locale = get_language()
        domain = kwargs.get("domain", self.domain)
        
        
        packages = kwargs.get("packages", "")
        packages = packages.split("+") if packages else 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 = {
            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
        ]
        if len(app_configs) < len(packages):
            excluded = [p for p in packages if p not in allowable_packages]
            raise ValueError(
                "Invalid package(s) provided to JavaScriptCatalog: %s"
                % ",".join(excluded)
            )
        
        return [os.path.join(app.path, "locale") for app in app_configs]

    @property
    def _num_plurals(self):
        
        match = re.search(r"nplurals=\s*(\d+)", self._plural_string or "")
        if match:
            return int(match[1])
        return 2

    @property
    def _plural_string(self):
        
        if "" in self.translation._catalog:
            for line in self.translation._catalog[""].split("\n"):
                if line.startswith("Plural-Forms:"):
                    return line.split(":", 1)[1].strip()
        return None

    def get_plural(self):
        plural = self._plural_string
        if plural is not None:
            
            
            
            plural = [
                el.strip()
                for el in plural.split(";")
                if el.strip().startswith("plural=")
            ][0].split("=", 1)[1]
        return plural

    def get_catalog(self):
        pdict = {}
        catalog = {}
        translation = self.translation
        seen_keys = set()
        while True:
            for key, value in translation._catalog.items():
                if key == "" or key in seen_keys:
                    continue
                if isinstance(key, str):
                    catalog[key] = value
                elif isinstance(key, tuple):
                    msgid, cnt = key
                    pdict.setdefault(msgid, {})[cnt] = value
                else:
                    raise TypeError(key)
                seen_keys.add(key)
            if translation._fallback:
                translation = translation._fallback
            else:
                break

        num_plurals = self._num_plurals
        for k, v in pdict.items():
            catalog[k] = [v.get(i, "") for i in range(num_plurals)]
        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  ")

        with builtin_template_path("i18n_catalog.js").open(encoding="utf-8") as fh:
            template = Engine().from_string(fh.read())
        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 HttpResponse(
            template.render(Context(context)), 'text/javascript; charset="utf-8"'
        )


class JSONCatalog(JavaScriptCatalog):
    

    def render_to_response(self, context, **response_kwargs):
        return JsonResponse(context)