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
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()
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.
If there is no parameter, it checks for the locale
cookie.
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).