Django templates: récupère L'URL actuelle dans une autre langue
J'utilise i18n_patterns
pour créer des préfixes de langue dans une application Django.
Mes URL ressemblent à ceci:
/de/contact/
/fr/contact/
/it/contact/
Dans mon modèle de base, je boucle toutes les langues disponibles pour afficher les liens de commutation de langue.
{% 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>
Dans ce cas, j'inverse l'URL "home". Existe-t-il un moyen d'obtenir une URL traduite de la page actuelle à la place?
Si je suis sur la version allemande de ma page "contact", je veux que le lien "fr" pointe vers la version française de la page "contact", pas vers la page "Accueil".
11 réponses
Je n'utilise pas de préfixes de langue, mais des URL traduites à la place. Cependant, cette balise de modèle devrait également vous aider:
# 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)
Renvoie l'url actuelle dans la langue souhaitée. Utilisez-le comme ceci: {% translate_url de %}
Les commentaires et suggestions d'améliorations sont les bienvenus.
Cet extrait devrait le faire:
Https://djangosnippets.org/snippets/2875/
Une fois que vous avez ajouté cela en tant que tag de modèle personnalisé , vous pouvez faire quelque chose comme:
<a href='{% change_lang 'fr' %}'>View this page in French</a>
Utiliser 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>
Je pense qu'il vaut la peine de mentionner qu'il existe une fonction intégrée appelée translate_url
.
from django.urls import translate_url
translate_url(url, lang_code)
Je pense que vous ajoutez une complication inutile au problème. Ce que vous cherchez est un simple sélecteur de langue. Django fournit cette fonctionnalité hors de la boîte, et il redirige toujours vers la page en cours (dans une autre langue).
Ceci est documenté ici:
Https://docs.djangoproject.com/en/dev/topics/i18n/translation/#django.conf.urls.i18n.set_language
La seule chose est que la vue set_language
attend un paramètre POST, vous devez donc utiliser un élément <form>
; vous ne pouvez pas utiliser un simple lien <a href="...">
. Cependant, vous voulez parfois que le sélecteur de langue ressemble à un lien, pas à un formulaire avec un widget select. Ma proposition est d'utiliser un formulaire, mais le style qu'il ressemble à un lien.
Votre modèle peut ressembler à ceci:
<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>
Et puis vous utilisez CSS pour styliser les formulaires et soumettre des boutons pour ressembler à des liens normaux:
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 */
}
J'utilise le formulaire de langue standard de docs
<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>
Et jQuery fix pour travailler avec les préfixes URL lang:
$('#lang_changer input[name="next"]').attr('value', '/'+window.location.pathname.substring(4));
Exécuter lorsque la page est prête.
Le problème que j'ai eu avec la balise template personnalisée est que la fonction calcule l'autre équivalent de langue en fonction de l'url actuelle, comme je l'utilise paquet modeltranslation ensuite, la limace était toujours la même entre les URL. par exemple:
example.com/en/article/english-slug
example.com/es/articulo/english-slug
Pour résoudre ce problème, j'ai adopté une approche légèrement différente, en calculant les URL alternatives au niveau de la vue et en les rendant disponibles dans le contexte du modèle.
Pour que cela fonctionne:
1-Créer un utils.py fichier avec la fonction d'assistance suivante
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-demandez à vos modèles de définir l'url inverse: get_absolute_url
3-Ajouter une variable de contexte qui contiendra le dictionnaire urls dans votre views.py
from .utils import getAlternateUrls
...
def MyView(DetailView):
def get_context_data(self, **kwargs):
context['alt_urls'] = getAlternateUrls(self.object)
4-générer les balises meta URL alternatives à la section head du modèle
<!-- alternate lang -->
{% for key, value in alt_urls.items %}
<link rel="alternate" hreflang="{{ key }}" href="http://{{ request.get_host }}{{ value}}">
{% endfor %}
{% endblock %}
Testé dans Django 1.8
J'ai essayé de le rendre aussi simple que possible - d'utiliser dynamic reverse()
avec n'importe quel nombre de kwargs
, de sorte que le commutateur de langue (ou toute autre substance similaire) redirigera vers la vue actuelle.
Ajout d'une balise de modèle simple dans le fichier dir templatetags (par exemple, 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)
Qui pourrait être utilisé dans le modèle de commutateur de langue comme ceci:
{% 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 %}
Fonctionne assez bien pour moi.
Je préfère commenter la réponse acceptée, mais je ne peux pas donc je poste la mienne. J'utilise une solution assez similaire basée sur: https://djangosnippets.org/snippets/2875/
Il y a un problème que les méthodes resolve
et reverse
peuvent planter:
- {[1] } peut déclencher une exception Resolver404, surtout lorsque vous affichez déjà une page 404 (provoquant une erreur 500 à la place, très ennuyeuse et difficile à détecter surtout avec DEBUG = True n'affichant pas de vrai 404)
-
reverse
peut crash lorsque vous essayez d'obtenir une page avec une langue différente qui n'a pas de traduction.
Peut-être que l'inverse dépend plus du type de méthode de traduction que vous utilisez ou autre, mais résoudre le crash dans la page 404 est assez évident.
En cas d'exception, vous pouvez renvoyer la même url ou peut-être l'url vers la page d'index plutôt que d'augmenter l'exception dans le modèle. Le Code peut ressembler à ceci:
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
Pour Django 2.0 (basé sur la réponse de Philipp Zedler )
Modèle Personnalisé:
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
Dans Le Modèle:
{% 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>
Une solution simple serait d'utiliser Django translate_url
function, avec une balise de modèle:
# 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)
Ensuite, utilisez-le de cette façon dans votre html pour la sélection de la langue:
{% 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>
Et pour hreflangs:
{% get_available_languages as languages %}
{% for lang_code, lang_name in languages %}
<link rel="alternate" hreflang="{{lang_code}}" href="{% translate_url lang_code %}" />
{% endfor %}
J'espère que cela aide.