Ticket #150 (closed task: fixed)

Opened 4 years ago

Last modified 4 years ago

Use Aquarium code for parsing user's preferred language settings

Reported by: jjinux Owned by: bbangert
Priority: normal Milestone: 0.9.5
Component: I18N & Unicode Version: 0.9.3
Severity: normal Keywords: i18n
Cc: ianb@…, dds

Description (last modified by bbangert) (diff)

Here are some chunks of the appropriate code:

    def initGetText(self):

        """Initialize gettext.

        Use the class API so that "_" is not globally installed.

        Add the following to ``self._ctx``:  ``translation``, ``_``,
        ``gettext``, ``ngettext``.  If ``properties.USE_GETTEXT`` is False,
        ``gettext.NullTranslations``.  That way, your code will work even if
        you use ``_()`` with gettext turned off.

        """

        # It's unnecessary to cache translation instances.  The gettext module
        # already does that.

        ctx = self._ctx
        if properties.USE_GETTEXT:
            from gettext import translation
            ctx.translation = translation(
                languages=self.getLanguagePreferences(),
                *properties.GETTEXT_ARGS, **properties.GETTEXT_KWARGS)
        else:
            from gettext import NullTranslations
            ctx.translation = NullTranslations()
        ctx.gettext = ctx.translation.gettext
        ctx.ngettext = ctx.translation.ngettext
        ctx._ = ctx.gettext

    def getLanguagePreferences(self):
        """Return a list of preferred languages, most preferred first.

        The list may be empty.  By default, just use parseAcceptLanguage_ on
        the ``ACCEPT_LANGUAGE`` header.  The list will be passed to
        ``filterLanguagePreferences``.

        .. _parseAcceptLanguage:
           aquarium.parse.AcceptLanguage-module.html#parseAcceptLanguage

        """
        from aquarium.parse.AcceptLanguage import parseAcceptLanguage
        acceptLanguage = self._ctx.wsa.getCgiEnv().get("HTTP_ACCEPT_LANGUAGE")
        languages = parseAcceptLanguage(acceptLanguage)
        self.filterLanguagePreferences(languages)
        return languages

    def filterLanguagePreferences(self, languages):
        """Update the ``languages`` list by applying additional logic.

        If not None, use ``properties.GETTEXT_ULTIMATE_FALLBACK`` (which
        defaults to "en-us") as an ultimate fallback.  That means it gets
        appended to the list of languages if it isn't already there.  It also
        means that if it's in the list of languages, everything after it is
        deleted.

        This deleting behavior is strange but useful.  Normally, everything in
        the code is in "en-us".  However, the "en-us" translation catalog is
        usually empty.  If the user requests ``["en-us", "zh-cn"]`` and a
        translation isn't found for a string in "en-us", you don't want
        gettext to fallback to "zh-cn".  You want it to just use the string
        itself.  Hence, if a string isn't found in the
        ``properties.GETTEXT_ULTIMATE_FALLBACK`` catalog, the string in the
        source code will be used.

        """
        ultimate = getattr(properties, "GETTEXT_ULTIMATE_FALLBACK", "en-us")
        if not ultimate:
            return
        if ultimate not in languages:
            languages.append(ultimate)
        index = languages.index(ultimate)
        languages[index+1:] = []

and, most importantly:

"""Parse the ``Accept-Language`` header."""

__docformat__ = "restructuredtext"

# Created: Thu Apr 28 02:52:11 PDT 2005
# Author: Shannon -jj Behrens
# Email: jjinux@users.sourceforge.net
#
# Copyright (c) Shannon -jj Behrens.  All rights reserved.

import re


languageRegEx = re.compile(r"^[a-z]{2}(-[a-z]{2})?$", re.I)


def parseAcceptLanguage(header=None):
    """Parse the ``Accept-Language`` header.

    Return a list of language tags sorted by their "q" values.  For example,
    "en-us,en;q=0.5" should return ``["en-us", "en"]``.  If there is no
    ``Accept-Language`` header present, default to ``[]``.

    """
    if header is None:
        return []
    langs = header.split(",")
    qs = []
    for lang in langs:
        pieces = lang.split(";")
        lang, params = pieces[0].strip().lower(), pieces[1:]
        if not languageRegEx.match(lang):
            continue
        q = 1
        for param in params:
            lvalue, rvalue = param.split("=")
            lvalue = lvalue.strip().lower()
            rvalue = rvalue.strip()
            if lvalue == "q":
                q = float(rvalue)
        qs.append((lang, q))
    qs.sort(lambda a, b: -cmp(a[1], b[1]))
    return [lang for (lang, q) in qs]

If you have any questions, just ask!

Change History

Changed 4 years ago by bbangert

  • status changed from new to closed
  • resolution set to fixed

(In [1588]) Removing name mapping from deprecated functions for Python 2.3 compat. Fixes #150.

Changed 4 years ago by bbangert

  • status changed from closed to reopened
  • resolution fixed deleted

Specified wrong fix.

Changed 4 years ago by max

Looks quite similar to TurboGears?' implementation:

 http://trac.turbogears.org/turbogears/browser/trunk/turbogears/i18n/utils.py#L35
 http://trac.turbogears.org/turbogears/browser/trunk/turbogears/util.py#L319

May be this could be extracted into tiny WSGI middleware or added to Paste?

Changed 4 years ago by dds

  • status changed from reopened to closed
  • resolution set to fixed

Changed 4 years ago by pjenvey

  • status changed from closed to reopened
  • resolution fixed deleted
  • milestone set to 0.9.5

Changed 4 years ago by bbangert

  • status changed from reopened to new
  • cc ianb@… added
  • owner changed from thejimmyg to bbangert

I'm under the impression this should be added directly to the Request object as its directly derived from HTTP request data. Should it be in paste.wsgiwrappers.WSGIRequest or is this a good time to start the Response subclass in Pylons?

I personally think it should be in the Paste one, as this is likely to be something anyone using a nice Request object could want to get.

ian: coments?

Changed 4 years ago by bbangert

  • description modified (diff)

Changed 4 years ago by bbangert

  • status changed from new to assigned
  • description modified (diff)

Changed 4 years ago by pjenvey

  • cc dds added

dds - the link to your darcs is dead

Changed 4 years ago by dds

Since Ian apparently didn't want it in paste, I moved it into a separate library (though I'd prefer it to be in paste).

Please refer to:

 http://bosabosa.org/~dds/darcs/index.cgi?r=wsgi18n;a=tree;f=/wsgi18n

Changed 4 years ago by jjinux

Since Ian apparently didn't want it in paste

I'm beginning to think Ian doesn't like my code ;)

*sigh* ;)

Changed 4 years ago by bbangert

The accept language parsing has now been implemented based off the parse code in paste.httpheaders. The WSGIRequest object uses this code to provide a WSGIRequest.languages property that returns a list of filtered language arguments.

I'm leaving this ticket open until I add a few more unit tests of the language parsing.

Changed 4 years ago by bbangert

  • status changed from assigned to closed
  • resolution set to fixed

Ok, the Pylons translation has been updated to support multiple languages so they can now be passed in as a list properly from Request.languages. This was updated and fixed in r1792, r1791, r1785.

Changed 4 years ago by dds

  • keywords i18n added
  • component changed from controllers to I18N & Unicode
Note: See TracTickets for help on using tickets.


Powered by Pylons - Contact Administrators