Detect the language & django locale-url

2019-03-09 14:35发布

问题:

I want to deploy a website in english & spanish and detect the user browser language & redirect to the correct locale site.

My site is www.elmalabarista.com

I install django-localeurl, but I discover that the language is not correctly detected.

This are my middlewares:

MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware', 
    'django.middleware.locale.LocaleMiddleware',    
    'multilingual.middleware.DefaultLanguageMiddleware',
    'middleware.feedburner.FeedburnerMiddleware',
    'lib.threadlocals.ThreadLocalsMiddleware',
    'middleware.url.UrlMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'maintenancemode.middleware.MaintenanceModeMiddleware',
    'middleware.redirect.RedirectMiddleware',
    'openidconsumer.middleware.OpenIDMiddleware',
    'django.middleware.doc.XViewMiddleware',
    'middleware.ajax_errors.AjaxMiddleware',
    'pingback.middleware.PingbackMiddleware',
    'localeurl.middleware.LocaleURLMiddleware', 
    'multilingual.flatpages.middleware.FlatpageFallbackMiddleware',
    'django.middleware.common.CommonMiddleware',
)

But ALWAYS the site get to US despite the fact my OS & Browser setup is spanish.

LANGUAGES = (
    ('en', ugettext('English')),  
    ('es', ugettext('Spanish')),
)
DEFAULT_LANGUAGE = 1

Then, I hack the middleware of locale-url and do this:

def process_request(self, request):
    locale, path = self.split_locale_from_request(request)
    if request.META.has_key('HTTP_ACCEPT_LANGUAGE'):
        locale = utils.supported_language(request.META['HTTP_ACCEPT_LANGUAGE'].split(',')[0])
    locale_path = utils.locale_path(path, locale)

    if locale_path != request.path_info:
        if request.META.get("QUERY_STRING", ""):
            locale_path = "%s?%s" % (locale_path,
                    request.META['QUERY_STRING'])
        return HttpResponseRedirect(locale_path)
    request.path_info = path
    if not locale:
        locale = settings.LANGUAGE_CODE
    translation.activate(locale)
    request.LANGUAGE_CODE = translation.get_language()

However, this detect fine the language but redirect the "en" urls to "es". So is impossible navigate in english.

UPDATE: This is the final code (after the input from Carl Meyer) with a fix for the case of "/":

def process_request(self, request):
    locale, path = self.split_locale_from_request(request)
    if (not locale) or (locale==''):
        if request.META.has_key('HTTP_ACCEPT_LANGUAGE'):
            locale = utils.supported_language(request.META['HTTP_ACCEPT_LANGUAGE'].split(',')[0])
        else:
            locale = settings.LANGUAGE_CODE
    locale_path = utils.locale_path(path, locale)
    if locale_path != request.path_info:
        if request.META.get("QUERY_STRING", ""):
            locale_path = "%s?%s" % (locale_path, request.META['QUERY_STRING'])
        return HttpResponseRedirect(locale_path)
    request.path_info = path
    translation.activate(locale)
    request.LANGUAGE_CODE = translation.get_language()

回答1:

(Update: django-localeurl's LocaleURLMiddleware now directly supports HTTP Accept-Language as fallback, if LOCALEURL_USE_ACCEPT_LANGUAGE setting is True. So the OP's desired behavior is now available without writing a custom middleware).

It does not make sense to have both Django's built-in LocaleMiddleware and LocaleURLMiddleware enabled. They are intended as mutually exclusive alternatives, and have different logic for choosing a language. Locale-url does what it says on the tin: the locale is defined by a URL component (thus not by the Accept-Language header). Django's LocaleMiddleware will choose the language based on a session value or cookie or Accept-Language header. Enabling both just means that whichever one comes last wins, which is why you're seeing the LocaleURLMiddleware behavior.

It sounds like maybe you want some kind of mix of the two, where the initial language (when visiting the root URL of the site?) is chosen based on Accept-Language, and thereafter defined by the URL? It's not entirely clear what behavior you want, so clarifying that is the first step. Then you'll probably need to write your own LocaleMiddleware that implements that behavior. Your first attempt at hacking LocaleURLMiddleware always uses Accept-Language in place of what's defined in the URL. Instead, you want to check the Accept-Language header further down, in the "if not locale:" section where it defaults to settings.LANGUAGE_CODE. Something more like this (untested code):

def process_request(self, request):
    locale, path = self.split_locale_from_request(request)
    locale_path = utils.locale_path(path, locale)

    if locale_path != request.path_info:
        if request.META.get("QUERY_STRING", ""):
            locale_path = "%s?%s" % (locale_path, request.META['QUERY_STRING'])
        return HttpResponseRedirect(locale_path)
    request.path_info = path
    if not locale:
        if request.META.has_key('HTTP_ACCEPT_LANGUAGE'):
            locale = utils.supported_language(request.META['HTTP_ACCEPT_LANGUAGE'].split(',')[0])
        else:
            locale = settings.LANGUAGE_CODE
    translation.activate(locale)
    request.LANGUAGE_CODE = translation.get_language()


回答2:

i would need this behaviour too. are you using only your custom middleware now to get the language or are you still using the LocaleURLMiddleware and the LocaleMiddleware in combination with the middleware in the code above?



回答3:

really it should be like this:

There can be multiple languages accepted by order of preference

def process_request(self, request):
    locale, path = utils.strip_path(request.path_info)
    if (not locale) or (locale==''):
        if request.META.has_key('HTTP_ACCEPT_LANGUAGE'):
        l = [x.strip()[:2] for x in request.META['HTTP_ACCEPT_LANGUAGE'].split(',')]
        for lang_code in l:
                locale = utils.supported_language(lang_code)
                if locale:
          break
        else:
            locale = settings.LANGUAGE_CODE
    locale_path = utils.locale_path(path, locale)
    if locale_path != request.path_info:
        if request.META.get("QUERY_STRING", ""):
            locale_path = "%s?%s" % (locale_path,
                    request.META['QUERY_STRING'])
        return HttpResponseRedirect(locale_path)
    request.path_info = path
    if not locale:
        try:
            locale = request.LANGUAGE_CODE
        except AttributeError:
            locale = settings.LANGUAGE_CODE
    translation.activate(locale)
    request.LANGUAGE_CODE = translation.get_language()