web/lib/django/views/i18n.py
changeset 38 77b6da96e6f1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/lib/django/views/i18n.py	Wed Jun 02 18:57:35 2010 +0200
@@ -0,0 +1,265 @@
+import os
+import gettext as gettext_module
+
+from django import http
+from django.conf import settings
+from django.utils import importlib
+from django.utils.translation import check_for_language, activate, to_locale, get_language
+from django.utils.text import javascript_quote
+from django.utils.encoding import smart_unicode
+from django.utils.formats import get_format_modules
+
+def set_language(request):
+    """
+    Redirect to a given url while setting the chosen language in the
+    session or 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.
+    """
+    next = request.REQUEST.get('next', None)
+    if not next:
+        next = request.META.get('HTTP_REFERER', None)
+    if not next:
+        next = '/'
+    response = http.HttpResponseRedirect(next)
+    if request.method == 'POST':
+        lang_code = request.POST.get('language', None)
+        if lang_code and check_for_language(lang_code):
+            if hasattr(request, 'session'):
+                request.session['django_language'] = lang_code
+            else:
+                response.set_cookie(settings.LANGUAGE_COOKIE_NAME, lang_code)
+    return response
+
+def get_formats():
+    """
+    Returns all formats strings required for i18n to work
+    """
+    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'
+    )
+    result = {}
+    for module in [settings] + get_format_modules(reverse=True):
+        for attr in FORMAT_SETTINGS:
+            try:
+                result[attr] = getattr(module, attr)
+            except AttributeError:
+                pass
+    src = []
+    for k, v in result.items():
+        if isinstance(v, (basestring, int)):
+            src.append("formats['%s'] = '%s';\n" % (javascript_quote(k), javascript_quote(smart_unicode(v))))
+        elif isinstance(v, (tuple, list)):
+            v = [javascript_quote(smart_unicode(value)) for value in v]
+            src.append("formats['%s'] = ['%s'];\n" % (javascript_quote(k), "', '".join(v)))
+    return ''.join(src)
+
+NullSource = """
+/* gettext identity library */
+
+function gettext(msgid) { return msgid; }
+function ngettext(singular, plural, count) { return (count == 1) ? singular : plural; }
+function gettext_noop(msgid) { return msgid; }
+"""
+
+LibHead = """
+/* gettext library */
+
+var catalog = new Array();
+"""
+
+LibFoot = """
+
+function gettext(msgid) {
+  var value = catalog[msgid];
+  if (typeof(value) == 'undefined') {
+    return msgid;
+  } else {
+    return (typeof(value) == 'string') ? value : value[0];
+  }
+}
+
+function ngettext(singular, plural, count) {
+  value = catalog[singular];
+  if (typeof(value) == 'undefined') {
+    return (count == 1) ? singular : plural;
+  } else {
+    return value[pluralidx(count)];
+  }
+}
+
+function gettext_noop(msgid) { return msgid; }
+
+"""
+
+LibFormatHead = """
+/* formatting library */
+
+var formats = new Array();
+
+"""
+
+LibFormatFoot = """
+function get_format(format_type) {
+    var value = formats[format_type];
+    if (typeof(value) == 'undefined') {
+      return msgid;
+    } else {
+      return value;
+    }
+}
+"""
+
+SimplePlural = """
+function pluralidx(count) { return (count == 1) ? 0 : 1; }
+"""
+
+InterPolate = r"""
+function interpolate(fmt, obj, named) {
+  if (named) {
+    return fmt.replace(/%\(\w+\)s/g, function(match){return String(obj[match.slice(2,-2)])});
+  } else {
+    return fmt.replace(/%s/g, function(match){return String(obj.shift())});
+  }
+}
+"""
+
+PluralIdx = r"""
+function pluralidx(n) {
+  var v=%s;
+  if (typeof(v) == 'boolean') {
+    return v ? 1 : 0;
+  } else {
+    return v;
+  }
+}
+"""
+
+def null_javascript_catalog(request, domain=None, packages=None):
+    """
+    Returns "identity" versions of the JavaScript i18n functions -- i.e.,
+    versions that don't actually do anything.
+    """
+    src = [NullSource, InterPolate, LibFormatHead, get_formats(), LibFormatFoot]
+    return http.HttpResponse(''.join(src), 'text/javascript')
+
+def javascript_catalog(request, domain='djangojs', packages=None):
+    """
+    Returns the selected language catalog as a javascript library.
+
+    Receives the list of packages to check for translations in the
+    packages parameter either from an infodict or as a +-delimited
+    string from the request. Default is 'django.conf'.
+
+    Additionally 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. But this might be needed if you
+    deliver your JavaScript source from Django templates.
+    """
+    if request.GET:
+        if 'language' in request.GET:
+            if check_for_language(request.GET['language']):
+                activate(request.GET['language'])
+    if packages is None:
+        packages = ['django.conf']
+    if isinstance(packages, basestring):
+        packages = packages.split('+')
+    packages = [p for p in packages if p == 'django.conf' or p in settings.INSTALLED_APPS]
+    default_locale = to_locale(settings.LANGUAGE_CODE)
+    locale = to_locale(get_language())
+    t = {}
+    paths = []
+    en_catalog_missing = False
+    # first load all english languages files for defaults
+    for package in packages:
+        p = importlib.import_module(package)
+        path = os.path.join(os.path.dirname(p.__file__), 'locale')
+        paths.append(path)
+        try:
+            catalog = gettext_module.translation(domain, path, ['en'])
+            t.update(catalog._catalog)
+        except IOError:
+            # 'en' catalog was missing.
+            if locale.startswith('en'):
+                # If 'en' is the selected language this would cause issues
+                # later on if default_locale is something other than 'en'.
+                en_catalog_missing = True
+            # Otherwise it is harmless.
+            pass
+    # next load the settings.LANGUAGE_CODE translations if it isn't english
+    if default_locale != 'en':
+        for path in paths:
+            try:
+                catalog = gettext_module.translation(domain, path, [default_locale])
+            except IOError:
+                catalog = None
+            if catalog is not None:
+                t.update(catalog._catalog)
+    # last load the currently selected language, if it isn't identical to the default.
+    if locale != default_locale:
+        # If the flag en_catalog_missing has been set, the currently
+        # selected language is English but it doesn't have a translation
+        # catalog (presumably due to being the language translated from).
+        # If that is the case, a wrong language catalog might have been
+        # loaded in the previous step. It needs to be discarded.
+        if en_catalog_missing:
+            t = {}
+        else:
+            locale_t = {}
+            for path in paths:
+                try:
+                    catalog = gettext_module.translation(domain, path, [locale])
+                except IOError:
+                    catalog = None
+                if catalog is not None:
+                    locale_t.update(catalog._catalog)
+            if locale_t:
+                t = locale_t
+    src = [LibHead]
+    plural = None
+    if '' in t:
+        for l in t[''].split('\n'):
+            if l.startswith('Plural-Forms:'):
+                plural = l.split(':',1)[1].strip()
+    if plural is not None:
+        # this should actually 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]
+        src.append(PluralIdx % plural)
+    else:
+        src.append(SimplePlural)
+    csrc = []
+    pdict = {}
+    for k, v in t.items():
+        if k == '':
+            continue
+        if isinstance(k, basestring):
+            csrc.append("catalog['%s'] = '%s';\n" % (javascript_quote(k), javascript_quote(v)))
+        elif isinstance(k, tuple):
+            if k[0] not in pdict:
+                pdict[k[0]] = k[1]
+            else:
+                pdict[k[0]] = max(k[1], pdict[k[0]])
+            csrc.append("catalog['%s'][%d] = '%s';\n" % (javascript_quote(k[0]), k[1], javascript_quote(v)))
+        else:
+            raise TypeError(k)
+    csrc.sort()
+    for k, v in pdict.items():
+        src.append("catalog['%s'] = [%s];\n" % (javascript_quote(k), ','.join(["''"]*(v+1))))
+    src.extend(csrc)
+    src.append(LibFoot)
+    src.append(InterPolate)
+    src.append(LibFormatHead)
+    src.append(get_formats())
+    src.append(LibFormatFoot)
+    src = ''.join(src)
+    return http.HttpResponse(src, 'text/javascript')
+