【问题标题】:Django1.4: Generic way to set language links in template to work with i18n_patterns?Django1.4:在模板中设置语言链接以使用 i18n_patterns 的通用方法?
【发布时间】:2012-04-30 17:57:09
【问题描述】:

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

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

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

任何帮助找出在模板中设置语言链接以以通用方式与 url_patterns 一起使用的通用方法将不胜感激!

【问题讨论】:

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


    【解决方案1】:

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

    【讨论】:

    • 如果您告诉用户更改语言的方式只是某处的链接,那么这是一个很棒的解决方案。
    • 这应该是没有模板标签没有额外代码的完美答案<a href="/{{ language.0 }}{{ request.path|slice:'3:' }}"><span class="flag flag-{{ language.0 }}"></span> {{ language.0|language_name_local|capfirst }}</a>
    【解决方案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
    

    总而言之,我 resolve() 了 next 的视图参数,然后在激活新语言后将数据传递给 reverse()。希望这会有所帮助。

    【讨论】:

    • 很好的解决方案,谢谢,但它缺少一件事 - HTTP_REFERER 包含主机名,因此translation.get_language_from_path 在这种情况下返回None。我已经导入了 urlparse 并在“开始更改部分”的开头添加了这一行 next = urlparse(next).path
    • 好点-我没有注意到,因为我只使用过 ?next= (并且仅将引荐来源网址保留为默认值,以防万一);我会相应地更新答案。
    • FWIW,next 是 Python 中的内置函数(至少从 2.6 开始),不应用作变量名。 docs.python.org/3/library/functions.html#next
    • @AndréTerra,你说得对,next() 是内置的,但是 a)它没有在函数范围内使用,所以阴影应该没有任何影响; b) 下一个来自 Django 的代码,所以也许你应该和他们一起开一张票。
    【解决方案3】:

    对于长时间的延迟,我深表歉意。谢谢大家的回答。

    首先评论一下克里斯的两个解决方案: 自定义 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 具有有效的语言代码前缀,并简单地返回另一个字符串,该字符串与替换的语言代码相同。

    例如:

    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 中使用它。

    一如既往地欢迎您的 cmets!

    【讨论】:

      【解决方案4】:

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

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

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

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

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

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

      • 否则,它将使用全局 LANGUAGE_CODE 设置。

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

      为了清楚起见,举个例子:

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

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

      3. 由于你没有设置'next',它会重定向到reqeuest.META['HTTP_REFERRER']的值,即/en/news/article/1000

      4. LocaleMiddleware (source) 看到 url 中的 'en' 前缀,并激活 'en' 语言并将 request.LANGUAGE_CODE 设置为 'en'

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

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

      2. 在客户端使用javascript做同样的操作,设置nextPOST参数。真的,这有点傻;仅使用 javascript 将所有 url 动态添加到用户的首选语言代码之前可能会更简单,而完全忘记 set_language

      看来这个新的set_language 视图应该是Django 的默认行为。 There was a ticket raised,其中包括一个提议的实现,但没有真正描述问题,随后被关闭。我建议开一张新票,更好地描述您的用例、现有set_language 实现引起的问题以及您提出的解决方案。

      【讨论】:

      • 关于将 set_language 与 i18n_patterns 一起使用,我认为您可以添加自定义中间件类,在请求上下文中添加不带语言前缀的 URL。我试着写了一个简单的中间件类here.
      猜你喜欢
      • 2013-01-29
      • 2011-05-09
      • 2012-06-14
      • 1970-01-01
      • 1970-01-01
      • 2012-07-05
      • 1970-01-01
      • 1970-01-01
      • 2015-06-19
      相关资源
      最近更新 更多