3

我的情况如下:

我正在开发一个多语言站点,目前我使用 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

4 回答 4

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,那么这个视图就是你所需要的。但是不要像那样复制粘贴它。在该视图中保持激活。我想你可能需要那个:)

于 2014-01-03T07:03:11.423 回答
1

解决了!!!感谢 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)

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

满意++(操作...在 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)
于 2014-01-03T14:00:05.357 回答
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 中更改:

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

因此,不再将语言置于会话中(我浪费了我的时间)。

希望这可以帮助!

于 2020-03-02T23:11:29.250 回答
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'])

于 2021-01-09T23:30:52.890 回答