Django1.4:在模板中设置语言链接以与i18n_patterns一起使用的通用方法?

时间:2012-04-19 08:31:12

标签: django localization internationalization url-pattern django-1.4

我开始在Django 1.4中使用新的i18n_patterns。基本上,我希望在我的所有模板标题上为每种支持的语言提供语言链接。我已将我的标题实现为一个单独的模板,该模板包含在其他模板中。

有没有办法保持我的标头通用并解决这个问题而不在模板上下文中传递当前视图名称或当前网址?我想这是一个问题,我如何以通用方式从模板内部检索当前视图或网址。

顺便说一句,我发现我之前用set_lang视图改变活动语言的方法将使用url_patterns打破,因为在更改语言后,它会在重定向到引用视图时将其更改回来。

任何帮助都可以理解在模板中设置语言链接以便以通用方式与url_patterns一起使用的常用方法,我们将不胜感激!

4 个答案:

答案 0 :(得分:5)

基本上,设置语言有两种不同的方法。您可以使用i18n_patterns自动为您的网址添加语言代码前缀,或者您可以使用django.views.i18n.set_language视图更改用户会话中的语言代码值(如果您的话,则使用Cookie项目没有会话支持。)

值得注意的是LocaleMiddleware用于确定语言的算法:

  • 首先,它在请求的URL中查找语言前缀。只有在root URLconf中使用i18n_patterns函数时才会执行此操作。有关语言前缀以及如何国际化URL模式的详细信息,请参阅国际化:URL模式。

  • 如果失败,它会在当前用户的会话中查找django_language键。

  • 如果失败,则会查找Cookie。使用的Cookie名称由LANGUAGE_COOKIE_NAME设置设置。 (默认名称为django_language。)

  • 如果失败,它会查看Accept-Language HTTP标头。此标头由您的浏览器发送,并按优先级顺序告诉服务器您喜欢哪种语言。 Django尝试标题中的每种语言,直到找到可用翻译的语言。

  • 如果失败,则使用全局LANGUAGE_CODE设置。

您可能遇到的问题是,您无法使用set_language从已使用语言前缀提供服务的网址重定向,除非您专门在其中传递了next参数发布数据。这是因为set_language将默认重定向到引荐来源,其中包含先前的语言前缀,然后LocaleMiddleware将以旧语言查看并提供内容(因为它会查找语言前缀)检查django_language会话变量之前的URL。

一个例子,为清楚起见:

  1. 您的用户位于/ en / news / article / 1000,并点击了将“language = es”发布到set_language的链接。

  2. set_language看到'language = es',检查'es'是否可用,然后将'django_language'会话变量(或cookie)设置为'es'

    < / LI>
  3. 由于您尚未设置'next',因此会重定向到reqeuest.META ['HTTP_REFERRER']的值,即/ en / news / article / 1000

  4. LocaleMiddlewaresource)在网址中看到“en”前缀,并激活“en”语言并将request.LANGUAGE_CODE设置为“en”

  5. 我看到两种可能的解决方案:

    1. 编写您自己的set_language视图(请参阅原始来源here),该视图将检查引荐来源中的语言前缀(使用django.utils.translation.get_language_from_path),并将其更改为重新定向之前新选择语言的前缀。

    2. 使用javascript在客户端执行相同的操作,并设置next POST参数。真的,这有点傻;使用javascript动态地在用户的首选语言代码中添加所有网址可能会更简单,并完全忘记set_language

    3. 似乎这个新的set_language视图应该是Django的默认行为。 There was a ticket raised,其中包括拟议的实施,但没有真正描述问题,随后被关闭。我建议您打开一张新的故障单,其中包含对您的用例的更好描述,现有set_language实施所导致的问题以及您提出的解决方案。

答案 1 :(得分:2)

今天和Django 1.7有同样的问题,我设计了这个解决方案 - 不是很干,但似乎工作正常(我所有的测试都在通过,所以...)。 我没有使用内置的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.POST.get('next', request.GET.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 = '/'
lang_code = request.POST.get('language', None)
# Start changed part
next = urlparse(next).path  # Failsafe when next is take from HTTP_REFERER
# We need to be able to filter out the language prefix from the next URL
current_language = translation.get_language_from_path(next)
translation.activate(current_language)
next_data = resolve(next)
translation.activate(lang_code)  # this should ensure we get the right URL for the next page
next = reverse(next_data.view_name, args=next_data.args, kwargs=next_data.kwargs)
# End changed part
response = http.HttpResponseRedirect(next)
if request.method == 'POST':
    if lang_code and check_for_language(lang_code):
        if hasattr(request, 'session'):
            request.session[LANGUAGE_SESSION_KEY] = lang_code
        else:
            response.set_cookie(settings.LANGUAGE_COOKIE_NAME, lang_code,
                                max_age=settings.LANGUAGE_COOKIE_AGE,
                                path=settings.LANGUAGE_COOKIE_PATH,
                                domain=settings.LANGUAGE_COOKIE_DOMAIN)
return response

总结一下,我解析()下一个视图参数,然后在激活新语言后将数据传递给reverse()。希望这会有所帮助。

答案 2 :(得分:2)

实际上,没有必要摆弄你的观点。 Django有一个方便的切片标记,因此您只需使用{{ request.path|slice:'3:' }}作为链接URL。这会关闭语言代码前缀,因此语言由set_lang函数设置。

答案 3 :(得分:1)

我为长时间的延迟道歉。谢谢大家的答案。

首先评论Chris的两个解决方案选项: 自定义set_language和javascript都不适合这个目的,因为url模式的整体美感是SEO友好。

此外,简单地替换URL中的前缀语言不能被视为基于urlpattern的URL的完整解决方案,因为整个URL也可以是可翻译的。例如:英语为/en/profile/,法语为/fr/profil/。 要解决这样的问题,需要捕获viewfunc和参数,以便针对不同的语言对其进行反转。

幸运的是,我的项目暂时不使用可翻译的URL,我采取了以下方法。

  1. django.core.context_processors.request添加到TEMPLATE_CONTEXT_PROCESSORS,以便请求在模板呈现环境中可用。

  2. 在呈现视图时在视图中使用RequestContext。对我来说,情况总是如此,与主题无关。

  3. 编写一个非常简单的templatetag,它需要上下文并使用一个参数语言代码来返回给定语言的当前URL。它基本上确保当前URL具有有效的语言代码前缀,并简单地返回另一个与替换语言代码相同的当前URL的字符串。

  4. 例如:

    from django import template
    register = template.Library()
    
    @register.simple_tag(takes_context=True)
    def get_current_url_for_lang(context, lang_code):
        request=context.get('request',False)
        if not request:
            return None
    
        request=context['request']
        curr_url=request.path
        if len(curr_url) < 4 or curr_url[0] != '/' or curr_url[3] != '/':
            return curr_url
    
        if context.get('LANGUAGES',False):
            codes = []
            for code,name in context['LANGUAGES']:
                codes.append(code)
    
            curr_langcode = curr_url[1:3]
            if lang_code not in codes or curr_langcode not in codes:
                return curr_url
    
        changed_url = '/'+lang_code+curr_url[3:]
        return changed_url
    

    此外,如果一个人不喜欢将完整的请求注入上下文,那么编写自己的context_processor就会非常简单,只需将request.path推送到current_url_path,并在templatetag中使用{ {1}}。

    您的意见一如既往地受到欢迎!