How to decide the language from cookies/headers/session in webapp2?

后端 未结 2 418
既然无缘
既然无缘 2021-01-05 06:06

I\'d like to take advantage of webapp2\'s new features for localization that also has locale-specific formatting for time and currency.

Django has a good function c

2条回答
  •  再見小時候
    2021-01-05 06:37

    Totally based on fishwebby's answer and with some improvements and some design changes, here's what I do:

    """
    Use this handler instead of webapp2.RequestHandler to support localization.
    Fill the AVAILABLE_LOCALES tuple with the acceptable locales.
    """
    
    
    __author__ = 'Cristian Perez '
    
    
    import webapp2
    from webapp2_extras import i18n
    
    
    AVAILABLE_LOCALES = ('en_US', 'es_ES', 'en', 'es')
    
    
    class LocalizedHandler(webapp2.RequestHandler):
    
        def set_locale_from_param(self):
            locale = self.request.get('locale')
            if locale in AVAILABLE_LOCALES:
                i18n.get_i18n().set_locale(locale)
                # Save locale to cookie for future use
                self.save_locale_to_cookie(locale)
                return True
            return False
    
        def set_locale_from_cookie(self):
            locale = self.request.cookies.get('locale')
            if locale in AVAILABLE_LOCALES:
                i18n.get_i18n().set_locale(locale)
                return True
            return False
    
        def set_locale_from_header(self):
            locale_header = self.request.headers.get('Accept-Language')  # e.g. 'es,en-US;q=0.8,en;q=0.6'
            if locale_header:
                locale_header = locale_header.replace(' ', '')
                # Extract all locales and their preference (q)
                locales = []  # e.g. [('es', 1.0), ('en-US', 0.8), ('en', 0.6)]
                for locale_str in locale_header.split(','):
                    locale_parts = locale_str.split(';q=')
                    locale = locale_parts[0]
                    if len(locale_parts) > 1:
                        locale_q = float(locale_parts[1])
                    else:
                        locale_q = 1.0
                    locales.append((locale, locale_q))
    
                # Sort locales according to preference
                locales.sort(key=lambda locale_tuple: locale_tuple[1], reverse=True)
                # Find first exact match
                for locale in locales:
                    for available_locale in AVAILABLE_LOCALES:
                        if locale[0].replace('-', '_').lower() == available_locale.lower():
                            i18n.get_i18n().set_locale(available_locale)
                            return True
    
                # Find first language match (prefix e.g. 'en' for 'en-GB')
                for locale in locales:
                    for available_locale in AVAILABLE_LOCALES:
                        if locale[0].split('-')[0].lower() == available_locale.lower():
                            i18n.get_i18n().set_locale(available_locale)
                            return True
    
            # There was no match
            return False
    
        def set_locale_default(self):
            i18n.get_i18n().set_locale(AVAILABLE_LOCALES[0])
    
        def save_locale_to_cookie(self, locale):
            self.response.set_cookie('locale', locale)
    
        def __init__(self, request, response):
            """
            Override __init__ in order to set the locale
            Based on: http://stackoverflow.com/a/8522855/423171
            """
    
            # Must call self.initialze when overriding __init__
            # http://webapp-improved.appspot.com/guide/handlers.html#overriding-init
            self.initialize(request, response)
    
            # First, try to set locale from GET parameter (will save it to cookie)
            if not self.set_locale_from_param():
                # Second, try to set locale from cookie
                if not self.set_locale_from_cookie():
                    # Third, try to set locale from Accept-Language header
                    if not self.set_locale_from_header():
                        # Fourth, set locale to first available option
                        self.set_locale_default()
    
    1. It checks for the locale parameter in the URL, and if it exits, it sets a cookie with that locale for future use. In that way you can change the locale anywhere just using that locale parameter, but still avoid the parameter in upcoming requests.

    2. If there is no parameter, it checks for the locale cookie.

    3. If there is no cookie, it checks for the Accept-Language header. Very importantly, it takes into account the q preference factor of the header and also performs some little magic: language prefixes are accepted. For example, if the browser specifies en-GB but it doesn't exist in the AVAILABLE_LOCALES tuple, en will be selected if it exists, which will work by default with en_US if the locales for en do not exist. It also takes care of casing and format (- or _ as separator).

提交回复
热议问题