Шаблоны 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 %}