Django:以编程方式更改app语言(不带/ i18n / setlang / view)

时间:2014-01-02 17:36:17

标签: django internationalization django-i18n

我的情况如下:

我正在开发一个多语言网站,目前我使用Django视图 / i18n / setlang / 让用户从下拉菜单中切换语言,一切正常......但是现在,我希望以编程方式设置语言,具体来说我有一个带有一系列设置的表单,其中有一个“最喜欢的语言”语音,一旦用户提交表单,我的视图保存用户模型,理论上它应该使用保存的首选项设置应用程序语言,但它不起作用。我试过的是:

from django.utils.translation import activate

activate(lang)

但结果很奇怪:重定向后的UI仍然是旧语言,但成功更新的消息(django消息框架)以预期的语言显示!

我还检查了Django视图的源代码:https://github.com/django/django/blob/master/django/views/i18n.py

我看到他们在会话中保存所选语言(如果可用的话)(我已激活会话),所以我尝试了:

self.request.session['_language'] = form.cleaned_data['favouriteLanguage']

......但是没有用,我该怎么办?

我正在使用Django 1.6,我安装的django中间件如下:

    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.locale.LocaleMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',

ps:在我的两次尝试中,模板中的{{request.LANGUAGE_CODE}}打印旧的语言代码:(

4 个答案:

答案 0 :(得分:2)

单独激活不起作用。

查看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

它具有您以编程方式在视图中设置语言所需的一切。既然你已经有了localemiddleware,那么这个视图就是你所需要的。但是不要像那样复制粘贴它。在该视图中保持激活状态。我想你可能需要那个:)

答案 1 :(得分:2)

解决!!!感谢Dmytriy Voloshyn向我通报了魔法 i18n_patterns (我不知道他们:P)。

为了获得我想要的东西,这些是我已经完成的步骤:

[1]在我的基础urls.py中设置i18n_patterns:

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]编写一个实用程序类来更改路径前缀:

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]在用户首选项表单提交后在我的视图中使用该类:

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]覆盖默认的LocaleMiddleware以读取用户首选项:

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)

为此实现以正确的顺序导入中间件非常重要,必须在LocaleMiddleware之前导入AuthenticationMiddleware,否则用户将在请求中丢失(并且访问request.user将引发异常!)。

满意度++(ops ...在Python中:满意度+ = 1)

更新:

我简化了我的方法,只依赖于自定义的LocaleMiddleware,这是更新的类:

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)

答案 2 :(得分:1)

Django的文档摘录如下:https://docs.djangoproject.com/en/3.0/topics/i18n/translation/#explicitly-setting-the-active-language

您可能希望显式设置当前会话的活动语言。例如,也许从另一个系统中检索了用户的语言偏好。已经为您介绍了django.utils.translation.activate()。这仅适用于当前线程。要将整个会话的语言保留在cookie中,请在响应上设置LANGUAGE_COOKIE_NAME cookie:

from django.conf import settings
from django.http import HttpResponse
from django.utils import translation
user_language = 'fr'
translation.activate(user_language)
response = HttpResponse(...)
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, user_language)

请注意,对于Django 3.0来说很重要:

在Django 3.0中已更改:

在旧版本中,您可以在当前会话中设置语言。

因此,不再需要在会话中使用该语言了(我已经浪费了时间)。

希望这会有所帮助!

答案 3 :(得分:0)

这是一篇出色的帖子,它为我提供了有关如何在我的解决方案中正确实施它的深入了解。但是,随着新版本的Django 3.0+,我需要做一些修改。

我用过 request.META.get("HTTP_ACCEPT_LANGUAGE", "en") 代替 request.locale。我还修改了私有方法,使其更加 Pythonic。

class LocaleMiddleware(BaseLocaleMiddleware):
    """
    Override Django LocaleMiddleware in order to read user preferences.
    """
    @staticmethod
    def __user_has_language_preference(request):
        return request.user.is_authenticated and request.META.get('HTTP_ACCEPT_LANGUAGE', "en")

    @staticmethod
    def __recognize_user_favorite_language(request):
        preferred = request.user.profile.user_language
        language = get_language_path(request.path_info)

        if language != preferred:
            request.path_info = PathUtils.replace_language_prefix(request.path_info, preferred)

    def process_request(self, request):
        if self.__user_has_language_preference(request):
            self.__recognize_user_favorite_language(request)

        super(LocaleMiddleware, self).process_request(request)

然后在 form.is_valid 方法中,我将其更改为基于从保存在用户模型中的用户语言首选项中获取的上下文数据的语言。

self.success_url = PathUtils.replace_language_prefix(self.success_url, form.cleaned_data['user_language'])