test - url pk django
Plantillas Django: Obtener URL actual en otro idioma (11)
Estoy usando i18n_patterns
para crear prefijos de idioma en una aplicación Django.
Mis URL se ven así:
/de/contact/
/fr/contact/
/it/contact/
En mi plantilla base, repaso todos los idiomas disponibles para mostrar los enlaces de cambio de idioma.
{% 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>
En este caso, estoy invirtiendo la URL "de inicio". ¿Hay alguna manera de obtener una URL traducida de la página actual?
Si estoy en la versión en alemán de mi página de "contacto", quiero que el enlace "fr" apunte a la versión en francés de la página de "contacto", no a la página de "inicio".
Creo que estás agregando complicaciones innecesarias al problema. Lo que estás buscando es un selector de idioma simple. Django proporciona esa funcionalidad fuera de la caja y siempre redirige a la página actual (en otro idioma).
Esto se documenta aquí:
https://docs.djangoproject.com/en/dev/topics/i18n/translation/#django.conf.urls.i18n.set_language
Lo único es que la vista set_language
espera un parámetro POST, por lo que necesita usar un elemento <form>
; no puede utilizar un enlace <a href="...">
simple. Sin embargo, a veces desea que el selector de idioma se vea como un enlace, no como un formulario con un widget de selección. Mi propuesta es usar un formulario, pero diseñarlo para que parezca un enlace.
Su plantilla podría verse así:
<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>
Y luego usas CSS para dar estilo a los formularios y enviar botones para que parezcan enlaces normales:
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 */
}
Creo que vale la pena mencionar que hay una función incorporada llamada translate_url
.
from django.urls import translate_url
translate_url(url, lang_code)
El problema que tuve con la etiqueta de plantilla personalizada es que la función calcula el equivalente en otro idioma según la url actual, ya que estoy usando el paquete modeltranslation, entonces la bala siempre fue la misma entre las urls. p.ej:
example.com/en/article/english-slug
example.com/es/articulo/english-slug
Para solucionarlo adopté un enfoque ligeramente diferente, calculando las URL alternativas en el nivel de visualización y las tengo disponibles en el contexto de la plantilla.
Para que esto funcione:
1- Crea un archivo utils.py con la siguiente función auxiliar.
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- Haga que sus modelos definan la url inversa: get_absolute_url
3- Agrega una variable de contexto que contendrá el diccionario de urls en tus views.py
from .utils import getAlternateUrls
...
def MyView(DetailView):
def get_context_data(self, **kwargs):
context[''alt_urls''] = getAlternateUrls(self.object)
4- Genere las metaetiquetas de URL alternativas en la sección de encabezado de la plantilla
<!-- alternate lang -->
{% for key, value in alt_urls.items %}
<link rel="alternate" hreflang="{{ key }}" href="http://{{ request.get_host }}{{ value}}">
{% endfor %}
{% endblock %}
Probado en Django 1.8
Este fragmento debe hacerlo:
https://djangosnippets.org/snippets/2875/
Una vez que hayas agregado eso como una etiqueta de plantilla personalizada , entonces puedes hacer algo como:
<a href=''{% change_lang ''fr'' %}''>View this page in French</a>
No estoy usando prefijos de idioma, sino URL traducidas. Sin embargo, esta etiqueta de plantilla también debería ayudarte a:
# 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)
Devuelve la url actual en el idioma deseado. Úsalo así: {% translate_url de %}
Los comentarios y sugerencias de mejoras son bienvenidos.
Para Django 2.0 (basado en la answer Philipp Zedler)
Plantilla personalizada:
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
En plantilla:
{% 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>
Prefiero hacer un comentario sobre la respuesta aceptada, pero no puedo, así que estoy publicando la mía. Estoy usando una solución bastante similar basada en: https://djangosnippets.org/snippets/2875/
Hay un problema que los métodos de resolve
e reverse
pueden fallar:
-
resolve
puede generar la excepción Resolver404, especialmente cuando ya se muestra la página 404 (en su lugar, se producen 500 errores, muy molestos y difíciles de detectar, especialmente con DEBUG = Verdadero que no se muestra con 404 real) -
reverse
puede bloquearse cuando intenta obtener una página con un idioma diferente que en realidad no tiene traducción.
Tal vez el reverso depende más del tipo de método de traducción que use o lo que sea, pero resolver el fallo dentro de la página 404 es bastante obvio
En caso de excepción, es posible que desee devolver la misma URL o tal vez la URL a la página de índice en lugar de generar una excepción en la plantilla. El código puede verse así:
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
Traté de hacerlo lo más simple posible: usar reverse()
dinámico reverse()
con cualquier número de kwargs
, para que el cambio de idioma (o cualquier otro elemento similar) redirija a la vista actual.
Se agregó una etiqueta de plantilla simple en el archivo dir de templatetags (por ejemplo, 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)
Que se podría utilizar en una plantilla de cambio de idioma como esta:
{% 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 %}
Funciona para mí bastante bien.
Una solución sencilla sería utilizar la función translate_url
de Django con una etiqueta de plantilla:
# 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)
Luego úselo de esta manera en su html para la selección de idioma:
{% 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>
Y para 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 %}
Espero que esto ayude.
Utilice 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>
Yo uso la forma de lenguaje estándar de los 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>
y jquery fix para trabajar con los prefijos de lang url:
$(''#lang_changer input[name="next"]'').attr(''value'', ''/''+window.location.pathname.substring(4));
ejecutar cuando la página está lista.