Django: change app language programmatically (without /i18n/setlang/ view)

▼魔方 西西 提交于 2019-12-04 18:42:06

activate alone does not do the trick.

Check out django.views.i18n.set_language:

def set_language(request):
    """
    Redirect to a given url while setting the chosen language in the
    session or cookie. The url and the language code need to be
    specified in the request parameters.

    Since this view changes how the user will see the rest of the site, it must
    only be accessed as a POST request. If called as a GET request, it will
    redirect to the page in the request (the 'next' parameter) without changing
    any state.
    """
    next = request.REQUEST.get('next')
    if not is_safe_url(url=next, host=request.get_host()):
        next = request.META.get('HTTP_REFERER')
        if not is_safe_url(url=next, host=request.get_host()):
            next = '/'
    response = http.HttpResponseRedirect(next)
    if request.method == 'POST':
        lang_code = request.POST.get('language', None)
        if lang_code and check_for_language(lang_code):
            if hasattr(request, 'session'):
                request.session['django_language'] = lang_code
            else:
                response.set_cookie(settings.LANGUAGE_COOKIE_NAME, lang_code)
    return response

It has everything you need to do to set language in view programmatically. since you already have localemiddleware, then that view is all you need. But do not copy paste it just like that. Keep that activate in that view. I think you might need that one though :)

Solved!!! Thanks to Dmytriy Voloshyn which informed me about the magic i18n_patterns (I did not know them :P).

In order to obtain what I want, these are the steps I've done:

[1] Set up i18n_patterns in my base urls.py:

from django.conf.urls import include, url
from django.conf.urls.i18n import i18n_patterns

urlpatterns = i18n_patterns(
    '',
    url(r'^', include('mysite.core.urls')),
    url(r'^foo/', include('mysite.foo.urls')),
    # ...
)

[2] Write an utility class to change the path prefix:

import re

from django.utils.encoding import force_text
from django.utils.translation import check_for_language


class PathUtils(object):
LANGUAGE_PREFIX_REGEX = re.compile('^/[a-z]{2}/')

@classmethod
def __pathHasValidPrefix(cls, path):
    matches = cls.LANGUAGE_PREFIX_REGEX.findall(path)
    if not matches:
        return False
    return check_for_language(matches[0].strip('/'))


@classmethod
def replaceLanguagePrefix(cls, path, newPrefix):
    """
    Returns the original path string with the language prefix replaced by the given one.
    Returns the unmodified path if language prefix is not found or is invalid (the language is not
    available for the application).



    :param path: (str) url path
    :param newPrefix: (str) 2 chars language code (ie: "IT", "DE", "ES"...)
    :return: (str) Path with new prefix
    """
    path, newPrefix = force_text(path), force_text(newPrefix)
    if not check_for_language(newPrefix) or not cls.__pathHasValidPrefix(path):
        return path
    return cls.LANGUAGE_PREFIX_REGEX.sub('/{0}/'.format(newPrefix), path)

[3] Use that class in my view after user preferences form submission:

def form_valid(self, form):
     form.save()
     self.success_url = PathUtils.replaceLanguagePrefix(self.success_url, form.cleaned_data['locale'])
     return super(UserSettingsUpdateView, self).form_valid(form)

[4] Override the default LocaleMiddleware in order to read user preferences:

from django.middleware.locale import LocaleMiddleware as BaseLocaleMiddleware
from django.utils.translation import activate


class LocaleMiddleware(BaseLocaleMiddleware):
"""
Override Django LocaleMiddleware in order to read user preferences.
"""

def __userHasLanguagePreference(self, request):
    return request.user.is_authenticated() and request.user.locale


def __activateUserFavoriteLanguage(self, request):
    activate(request.user.locale)
    request.LANGUAGE_CODE = request.user.locale


def process_request(self, request):
    if self.__userHasLanguagePreference(request):
        self.__activateUserFavoriteLanguage(request)
    else:
        super(LocaleMiddleware, self).process_request(request)

It is important to import middleware in the right order for this implementation, AuthenticationMiddleware MUST be imported before LocaleMiddleware, otherwise user would be missing in request (and accessing request.user will rise an exception!).

satisfaction++ (ops… in Python: satisfaction += 1)

UPDATE:

I simplified my approach in order to rely only on the custom LocaleMiddleware, this is the updated class:

from django.middleware.locale import LocaleMiddleware as BaseLocaleMiddleware
from django.utils.translation import get_language_from_path

from myapp.PathUtils import PathUtils


class LocaleMiddleware(BaseLocaleMiddleware):
    """
    Override Django LocaleMiddleware in order to read user preferences.
    """

    def __userHasLanguagePreference(self, request):
        return request.user.is_authenticated() and request.user.locale


    def __honorUserLanguagePreference(self, request):
        preferred = request.user.locale
        language = get_language_from_path(request.path_info, supported=self._supported_languages)
        if language != preferred:
            request.path_info = PathUtils.replaceLanguagePrefix(request.path_info, preferred)


    def process_request(self, request):
        if self.__userHasLanguagePreference(request):
            self.__honorUserLanguagePreference(request)
        super(LocaleMiddleware, self).process_request(request)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!