如何从webapp2的cookie /标题/会话中确定语言?


问题内容

我想利用webapp2的新功能进行本地化,该功能还具有针对时间和货币的特定于语言环境的格式。

Django有一个很好的函数,称为get_language_from_request,在我完全迁移到webapp2之前,我已经使用了它,现在我使用的是来自webapp2的i18n,我可以在用gettext编写的本地化之间切换,并编译到名为messages.mo的文件中,我的应用程序可以阅读和显示。然后,我确定并优先考虑了以下几种获取用户语言的方法:1.
HTTP GET例如。hl = pt-br代表巴西葡萄牙语。2. HTTP
SESSION变量,我叫i18n_language。3.应该设置并获取Cookie,但我不知道该怎么做。4.我可以得到的HTTP标头,在这里我也不知道。我正在寻找djnango如何get_language_from_request使用我以前使用的便捷方式完成操作,现在我不再导入django,我仍然希望此功能适用于现在基于webapp2的代码。

def get_language_from_request(self, request):
    """
    Analyzes the request to find what language the user wants the system to
    show. If the user requests a sublanguage where we have a main language, we send
    out the main language.
    """
    if self.request.get('hl'):
      self.session['i18n_language'] = self.request.get('hl')
      return self.request.get('hl')

    if self.session:
      lang_code = self.session.get('i18n_language', None)
      if lang_code:
        logging.info('language found in session')
        return lang_code

    lang_code = Cookies(self).get(LANGUAGE_COOKIE_NAME)
    if lang_code:
        logging.info('language found in cookies')
        return lang_code

    accept = os.environ.get('HTTP_ACCEPT_LANGUAGE', '')
    for accept_lang, unused in self.parse_accept_lang_header(accept):
      logging.info('accept_lang:'+accept_lang)
      lang_code = accept_lang

    return lang_code

我看到django代码可用,但是例如,我不知道webapp2的i18n有多少功能,如果没有.mo本地化版本,我是否需要回退,因为pt-br等语言应回退到pt
pt-br和其他方言类似。

实际设置我可以使用的语言

i18n.get_i18n().set_locale(language)

我要求您提供帮助,以优先考虑获取用户语言的各种方式,并且我也想知道您的想法如何继续实施。还是您认为我可以只使用会话变量来完成操作,而不能对“完整”的解决方案那么周全,因为我无论如何都主要针对地理使用来修复语言,因为现在我唯一实际使用的翻译是巴西葡萄牙语和英语,但我想要它也准备好切换到西班牙语和俄语以及其他语言,因此,我希望能够切换到用户语言并将其至少保存到webapp2会话中,并且知道您对使用cookie和标头获取用户的想法语言。

我以前从django那里得到si的原始代码看起来像这样,我再也不能使用了,因为它已锁定到django.mo文件且特定于django

def get_language_from_request(request):
    """
    Analyzes the request to find what language the user wants the system to
    show. Only languages listed in settings.LANGUAGES are taken into account.
    If the user requests a sublanguage where we have a main language, we send
    out the main language.
    """
    global _accepted
    from django.conf import settings
    globalpath = os.path.join(os.path.dirname(sys.modules[settings.__module__].__file__), 'locale')
    supported = dict(settings.LANGUAGES)

    if hasattr(request, 'session'):
        lang_code = request.session.get('django_language', None)
        if lang_code in supported and lang_code is not None and check_for_language(lang_code):
            return lang_code

    lang_code = request.COOKIES.get(settings.LANGUAGE_COOKIE_NAME)

    if lang_code and lang_code not in supported:
        lang_code = lang_code.split('-')[0] # e.g. if fr-ca is not supported fallback to fr

    if lang_code and lang_code in supported and check_for_language(lang_code):
        return lang_code

    accept = request.META.get('HTTP_ACCEPT_LANGUAGE', '')
    for accept_lang, unused in parse_accept_lang_header(accept):
        if accept_lang == '*':
            break

        # We have a very restricted form for our language files (no encoding
        # specifier, since they all must be UTF-8 and only one possible
        # language each time. So we avoid the overhead of gettext.find() and
        # work out the MO file manually.

        # 'normalized' is the root name of the locale in POSIX format (which is
        # the format used for the directories holding the MO files).
        normalized = locale.locale_alias.get(to_locale(accept_lang, True))
        if not normalized:
            continue
        # Remove the default encoding from locale_alias.
        normalized = normalized.split('.')[0]

        if normalized in _accepted:
            # We've seen this locale before and have an MO file for it, so no
            # need to check again.
            return _accepted[normalized]

        for lang, dirname in ((accept_lang, normalized),
                (accept_lang.split('-')[0], normalized.split('_')[0])):
            if lang.lower() not in supported:
                continue
            langfile = os.path.join(globalpath, dirname, 'LC_MESSAGES',
                    'django.mo')
            if os.path.exists(langfile):
                _accepted[normalized] = lang
                return lang

    return settings.LANGUAGE_CODE

是否可以针对每个请求执行此操作?我想我也应该将标题设置为语言self.response.headers['Content-Language'] = language

根据我的期望,如果我选择使用http标头,则可以直接从django接受一些功能,但是我不知道它的作用,因此也许您可以从django为我解释以下代码:

def parse_accept_lang_header(lang_string):
    """
    Parses the lang_string, which is the body of an HTTP Accept-Language
    header, and returns a list of (lang, q-value), ordered by 'q' values.

    Any format errors in lang_string results in an empty list being returned.
    """
    result = []
    pieces = accept_language_re.split(lang_string)
    if pieces[-1]:
        return []
    for i in range(0, len(pieces) - 1, 3):
        first, lang, priority = pieces[i : i + 3]
        if first:
            return []
        priority = priority and float(priority) or 1.0
        result.append((lang, priority))
    result.sort(lambda x, y: -cmp(x[1], y[1]))
    return result

谢谢

更新资料

我发现我无法在请求处理程序的initialize函数中使用会话,可能是因为尚未创建会话对象。因此,我在BaseHandler渲染函数中放置了用于从会话中获取语言的代码,它似乎可以正常工作。考虑标题或cookie值也很不错。


问题答案:

这是我的工作-
我有一个基本请求处理程序,所有请求处理程序都继承自该请求处理程序,然后在这里有一个包含可用语言的常量,并且我重写了init方法来为每个请求设置语言:

import webapp2
from webapp2_extras import i18n

AVAILABLE_LOCALES = ['en_GB', 'es_ES']

class BaseHandler(webapp2.RequestHandler):
    def __init__(self, request, response):
        """ Override the initialiser in order to set the language.
        """
        self.initialize(request, response)

        # first, try and set locale from cookie
        locale = request.cookies.get('locale')
        if locale in AVAILABLE_LOCALES:
            i18n.get_i18n().set_locale(locale)
        else:
            # if that failed, try and set locale from accept language header
            header = request.headers.get('Accept-Language', '')  # e.g. en-gb,en;q=0.8,es-es;q=0.5,eu;q=0.3
            locales = [locale.split(';')[0] for locale in header.split(',')]
            for locale in locales:
                if locale in AVAILABLE_LOCALES:
                    i18n.get_i18n().set_locale(locale)
                    break
            else:
                # if still no locale set, use the first available one
                i18n.get_i18n().set_locale(AVAILABLE_LOCALES[0])

首先,我检查Cookie,然后检查标题,如果没有找到有效的语言,则默认使用第一种可用的语言。

要设置cookie,我有一个单独的控制器,看起来像这样:

import base

class Index(base.BaseHandler):
    """ Set the language cookie (if locale is valid), then redirect back to referrer
    """
    def get(self, locale):
        if locale in self.available_locales:
            self.response.set_cookie('locale', locale, max_age = 15724800)  # 26 weeks' worth of seconds

        # redirect to referrer or root
        url = self.request.headers.get('Referer', '/')
        self.redirect(url)

因此,像www.example.com/locale/en_GB这样的URL会将语言环境更改为en_GB,设置cookie并返回引荐来源网址(这具有能够在任何页面上切换语言并将其保留在同一页面上的优势。页)。

此方法未考虑标头中语言环境的部分匹配,例如“ en”而不是“
en_GB”,但由于我在应用程序中启用的语言列表是固定的(语言环境更改网址很难在页脚中编码),我不太担心。

高温超导