Шаблоны Django: Получить текущий URL на другом языке

Я использую i18n_patterns создавать языковые префиксы в приложении Django.

Мои URL выглядят так:

/de/contact/
/fr/contact/
/it/contact/

В моем базовом шаблоне я перебираю все доступные языки, чтобы показать ссылки переключения языков.

{% get_available_languages as languages %}
<nav id="language_chooser">
    <ul>
        {% for lang_code, lang_name in languages %}
            {% language lang_code %}
            <li><a href="{% url 'home' %}" alt="{{ lang_name }}" title="{{ lang_name }}">{{ lang_code }}</a></li
            {% endlanguage %}
        {% endfor %}
    </ul>
</nav>

В этом случае я переворачиваю "домашний" URL. Есть ли способ получить переведенный URL текущей страницы?

Если я нахожусь на немецкой версии моей "контактной" страницы, я хочу, чтобы ссылка "fr" указывала на французскую версию "контактной" страницы, а не на "домашнюю" страницу.

14 ответов

Решение

Я не использую языковые префиксы, но вместо этого перевел URL. Однако этот тег шаблона также должен помочь вам:

# This Python file uses the following encoding: utf-8

from django import template
from django.core.urlresolvers import reverse
from django.core.urlresolvers import resolve
from django.utils import translation

register = template.Library()

class TranslatedURL(template.Node):
    def __init__(self, language):
        self.language = language
    def render(self, context):
        view = resolve(context['request'].path)
        request_language = translation.get_language()
        translation.activate(self.language)
        url = reverse(view.url_name, args=view.args, kwargs=view.kwargs)
        translation.activate(request_language)
        return url

@register.tag(name='translate_url')
def do_translate_url(parser, token):
    language = token.split_contents()[1]
    return TranslatedURL(language)

Возвращает текущий URL на желаемом языке. Используйте это так: {% translate_url de %}

Комментарии и предложения по улучшению приветствуются.

Я думаю, что стоит упомянуть, что есть встроенная функция под названием translate_url,

from django.urls import translate_url
translate_url(url, lang_code)

Этот фрагмент должен сделать это:

https://djangosnippets.org/snippets/2875/

После того , как вы добавили это в качестве пользовательского тега шаблона, вы можете сделать что-то вроде:

<a href='{% change_lang 'fr' %}'>View this page in French</a>

Простое решение было бы использовать Джанго translate_url функция с тегом шаблона:

# utils/templatetags/utils.py
from django.template import Library
from django.urls import translate_url as django_translate_url

register = Library()

@register.simple_tag(takes_context=True)
def translate_url(context, lang_code):
    path = context.get('request').get_full_path()
    return django_translate_url(path, lang_code)

Затем используйте его в своем html для выбора языка:

{% load i18n utils %}
{% get_available_languages as languages %}

<ul>
{% for lang_code, lang_name in languages %}
     <li><a href="{% translate_url lang_code %}">{{ lang_code }}</a></li>
{% endfor %}
</ul>

И для хрефлангов:

{% get_available_languages as languages %}
{% for lang_code, lang_name in languages %}
    <link rel="alternate" hreflang="{{lang_code}}" href="{% translate_url lang_code %}" />
{% endfor %}

Надеюсь это поможет.

Используйте django_hreflang:

{% load hreflang %}

<ul>
    <li><a href="{% translate_url 'en' %}" hreflang="en">English</a></li>
    <li><a href="{% translate_url 'ru' %}" hreflang="ru">Russian</a></li>
</ul>

Я использую стандартную форму языка из документов

<form action="{% url 'set_language' %}" method="post" id="lang_changer">
{% csrf_token %}
<input name="next" type="hidden" value="{{ redirect_to }}" />
<select name="language">
{% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %}
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected="selected"{% endif %}>
    {{ language.name_local }} ({{ language.code }})
</option>
{% endfor %}
</select>
<input type="submit" value="Go" />
</form>

и исправление jquery для работы с префиксами url lang:

$('#lang_changer input[name="next"]').attr('value', '/'+window.location.pathname.substring(4));

запустить, когда страница готова.

Я думаю, что вы добавляете ненужные осложнения к проблеме. То, что вы ищете, это простой выбор языка. Django предоставляет эту функциональность из коробки и всегда перенаправляет на текущую страницу (на другом языке).

Это задокументировано здесь:

https://docs.djangoproject.com/en/dev/topics/i18n/translation/

Единственное, что set_language view ожидает параметр POST, поэтому вам нужно использовать <form> элемент; вы не можете использовать простой <a href="..."> ссылка на сайт. Однако иногда вы хотите, чтобы селектор языка выглядел как ссылка, а не как форма с виджетом выбора. Мое предложение состоит в том, чтобы использовать форму, но стилизовать ее, чтобы она выглядела как ссылка.

Ваш шаблон может выглядеть так:

<nav id="language_chooser">
    <ul>
        {% get_language_info_list for LANGUAGES as languages %}
        {% for language in languages %}
            <form action="{% url 'set_language' %}" method="post">
                {% csrf_token %}
                <input name="next" type="hidden" value="{{ redirect_to }}" />
                <input name="language" type="hidden" value="{{ language.code }}" />
                <button type="submit">{{ language.local_name }}"</button>
            </form>
        {% endfor %}
    </ul>
</nav>

А затем вы используете CSS для стилизации форм и отправки кнопок, чтобы они выглядели как обычные ссылки:

ul#language_chooser form {
    display: inline; 
    margin: 0;
    padding: 0;
}

ul#language_chooser button {
    margin: 0;
    padding: 0;
    border: none;
    background: none;
    color: blue; /* whatever you like */
    text-decoration: underline; /* if you like */
}

Решение Django 3, основанное на ответе Джонхатана:

Файл: App/templatetags.py или App/templatetags/change_lang.py:

from django.template.defaultfilters import register
from django.urls import translate_url


@register.simple_tag(takes_context=True)
def change_lang(context, lang=None, *args, **kwargs):
    path = context['request'].path
    return translate_url(path, lang)

Шаблон:

{% load trans change_lang %}
{% trans 'View page in other languages:' %}
<a href="{% change_lang 'en' %}">English</a>
| <a href="{% change_lang 'de' %}">Deutsch</a>

Для Django 2.0 (основываясь на ответе Филиппа Зедлера)

Пользовательский шаблон:

from django import template
from django.urls import reverse
from django.urls import resolve
from django.utils import translation
register = template.Library()

@register.simple_tag(takes_context=True)
def translate_url(context, language):
  view = resolve(context['request'].path)
  request_language = translation.get_language()
  translation.activate(language)
  url = reverse(view.app_name+":"+view.url_name, args=view.args, kwargs=view.kwargs, )
  translation.activate(request_language)
  return url

В шаблоне:

{% get_available_languages as LANGUAGES %}
<ul>
  {% for lang_code, lang_name in LANGUAGES %}
    <li><a href="{% translate_url lang_code %}">{{ lang_name }}</a></li>
  {% endfor %}
</ul>

Я предпочел бы прокомментировать принятый ответ, но не могу, поэтому я публикую свой собственный. Я использую довольно похожее решение на основе: https://djangosnippets.org/snippets/2875/

Есть проблема в том, что оба resolve а также reverse методы могут привести к сбою:

  • resolve может вызвать исключение Resolver404, особенно когда вы уже отображаете страницу 404 (вместо этого вызывается ошибка 500, очень раздражающая и трудно обнаруживаемая, особенно с DEBUG=True, не отображающей реальные 404)
  • reverse может произойти сбой при попытке получить страницу с другим языком, который фактически не имеет перевода.

Возможно, обратное зависит в большей степени от того, какой метод перевода вы используете или что-то в этом роде, но разрешение сбоя на странице 404 довольно очевидно.

В случае исключения вы можете захотеть либо вернуть тот же URL-адрес, либо, возможно, URL-адрес страницы индекса, а не вызывать исключение в шаблоне. Код может выглядеть так:

from django.core.urlresolvers import resolve, reverse
from django.utils.translation import activate, get_language


@register.simple_tag(takes_context=True, name="change_lang")
def change_lang(context, lang=None, *args, **kwargs):
    url = context['request'].path
    cur_language = get_language()
    try:
        url_parts = resolve(url)
        activate(lang)
        url = reverse(url_parts.view_name, kwargs=url_parts.kwargs)
    except:
        url = reverse("index") #or whatever page you want to link to
        # or just pass if you want to return same url
    finally:
        activate(cur_language)
    return "%s" % url

Я постарался сделать это максимально простым - использовать динамический reverse() с любым количеством kwargs, так что переключение языка (или любой другой подобный материал) будет перенаправлено на текущее представление.

Добавлен простой тег шаблона в файл директории templatetags (например, templatetags/helpers.py):

from django.core.urlresolvers import reverse

register = template.Library()


@register.simple_tag
def get_url_with_kwargs(request):
    url_name = ''.join([
        request.resolver_match.app_name,
        ':',
        request.resolver_match.url_name,
    ])

    url_kwargs = request.resolver_match.kwargs

    return reverse(url_name, None, None, url_kwargs)

Что можно использовать в шаблоне переключения языка следующим образом:

{% load helpers %}

{% get_available_languages as available_languages %}
{% get_language_info_list for available_languages as language_info_list %}

{% for language in language_info_list %}

    {% language language.code %}

        {% get_url_with_kwargs request as url_with_kwargs %}
        <a href="{{ url_with_kwargs }}">{{ language.code }}</a>

    {% endlanguage %}

{% endfor %}

У меня работает довольно хорошо.

Проблема, с которой я столкнулся с пользовательским тегом шаблона, состоит в том, что функция вычисляет эквивалент другого языка на основе текущего URL- адреса, поскольку я использую пакет modeltranslation, тогда слаг всегда был одинаковым между URL-адресами. например:

example.com/en/article/english-slug
example.com/es/articulo/english-slug

Чтобы исправить это, я выбрал немного другой подход, вычисляя альтернативные URL-адреса на уровне представления и предоставляя их в контексте шаблона.

Чтобы это работало:

1. Создайте файл utils.py со следующей вспомогательной функцией.

from django.utils.translation import activate, get_language
from django.conf import settings

def getAlternateUrls(object):
    #iterate through all translated languages and get its url for alt lang meta tag                      
    cur_language = get_language()
    altUrls = {}
    for language in settings.LANGUAGES:
        try:
            code = language[0]
            activate(code)
            url = object.get_absolute_url()
            altUrls[code] = url
        finally:
            activate(cur_language)
    return altUrls;

2. Ваши модели определяют обратный URL: get_absolute_url

3. Добавьте переменную контекста, которая будет содержать словарь URL в вашем views.py

from .utils import getAlternateUrls
...
def MyView(DetailView):
    def get_context_data(self, **kwargs):
        context['alt_urls'] = getAlternateUrls(self.object)

4- Сгенерируйте метатеги альтернативных URL в разделе заголовка шаблона.

<!-- alternate lang -->
{% for key, value in alt_urls.items %}
<link rel="alternate" hreflang="{{ key }}" href="http://{{ request.get_host }}{{ value}}">
{% endfor %}
{% endblock %}

Проверено в Django 1.8

У меня работает в Django 2.2

Создать собственный тег шаблона

from django import template
from django.urls import translate_url

register = template.Library()


@register.simple_tag(takes_context=True)
def change_lang(context, lang=None, *args, **kwargs):
    path = context['request'].path
    return translate_url(path, lang)

В шаблоне

{% load change_lang %}
<a href="{% change_lang 'en' %}">English</a>
<a href="{% change_lang 'es' %}">Español</a>

Использование кажется самым простым подходом. Другие ответы на этот вопрос, за которые проголосовали, создали для меня другие проблемы с использованием Django 4: браузер всегда видел язык по умолчанию, а структура сообщений давала некоторые ошибки. С использованиемtranslate_url()не имеет этих недостатков.

from django.urls.base import translate_url, кажется, недокументировано.

Создайте собственный тег шаблона следующим образом:

      class TranslatedURL(template.Node):
    def __init__(self, language):
        self.language = language

    def render(self, context):
        path = resolve(context["request"].path)
        url = reverse(path.view_name, args=path.args, kwargs=path.kwargs)
        new_url = translate_url(url, self.language)
        return new_url


@register.tag(name="translate_url")
def do_translate_url_django(parser, token):
    language_code = token.split_contents()[1]
    return TranslatedURL(language_code)

После того, как вы загрузили новый тег, используйте его следующим образом:

{% translate_url_django nl %}

Другие вопросы по тегам