问题
I'm using i18n_patterns
to create language prefixes in a Django app.
My URLs look like this:
/de/contact/
/fr/contact/
/it/contact/
In my base template, I'm looping over all available languages to show the language switch links.
{% 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>
In this case, I'm reversing the "home" URL. Is there a way to get a translated URL of the current page instead?
If I'm on the German version of my "contact" page, I want the "fr" link to point to the French version of the "contact" page, not to the "home" page.
回答1:
I'm not using language prefixes, but translated urls instead. However, this template tag should also help you:
# This Python file uses the following encoding: utf-8
from django import template
from django.core.urlresolvers import reverse # from django.urls for Django >= 2.0
from django.core.urlresolvers import resolve # from django.urls for Django >= 2.0
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)
It returns the current url in the desired language. Use it like this: {% translate_url de %}
Comments and suggestions for improvements are welcome.
回答2:
This snippet should do it:
https://djangosnippets.org/snippets/2875/
Once you've added that as a custom template tag, then you can do something like:
<a href='{% change_lang 'fr' %}'>View this page in French</a>
回答3:
I think it is worth mentioning that there is a built-in function called translate_url
.
from django.urls import translate_url
translate_url(url, lang_code)
回答4:
Use 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>
回答5:
I think you are adding unneccessary complication to the problem. What you are looking for is a simple language selector. Django provides that functionality out of the box, and it always redirects to the current page (in another language).
This is documented here:
https://docs.djangoproject.com/en/dev/topics/i18n/translation/#django.conf.urls.i18n.set_language
The only thing is that the set_language
view expects a POST parameter, so you need to use a <form>
element; you cannot use a simple <a href="...">
link. However, sometimes you want the language selector to look like a link, not like a form with a select widget. My proposal is to use a form, but style it to look like a link.
Your template might look like this:
<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>
And then you use CSS to style the forms and submit buttons to look like normal links:
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 */
}
回答6:
I use standart language form from 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>
and jquery fix to work with url lang prefixes:
$('#lang_changer input[name="next"]').attr('value', '/'+window.location.pathname.substring(4));
run when page ready.
回答7:
A straightforward solution would be to use Django's translate_url function with a template tag:
# 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)
Then use it this way in your html for language selection :
{% 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>
And for 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 %}
Hope this helps.
回答8:
The problem I had with the custom template tag is that the function calculates the other language equivalent based on the current url, as I am using modeltranslation package then the slug was always the same between urls. e.g.:
example.com/en/article/english-slug
example.com/es/articulo/english-slug
To fix that I took a slightly different approach, calculating the alternate urls at view level and have them available in the template context.
For this to work:
1- Create a utils.py file with the following helper function
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- Have your models define the reverse url: get_absolute_url
3- Add a context variable that will hold the urls dictionary in your views.py
from .utils import getAlternateUrls
...
def MyView(DetailView):
def get_context_data(self, **kwargs):
context['alt_urls'] = getAlternateUrls(self.object)
4- Generate the alternate urls meta tags at the head section of the template
<!-- alternate lang -->
{% for key, value in alt_urls.items %}
<link rel="alternate" hreflang="{{ key }}" href="http://{{ request.get_host }}{{ value}}">
{% endfor %}
{% endblock %}
Tested in Django 1.8
回答9:
I tried to make it as simple as possible - to use dynamic reverse()
with any number of kwargs
, so that language switch (or any other similar stuff) will redirect to the current view.
Added simple template tag in templatetags dir file (for example, 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)
Which could be used in language switch template like this:
{% 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 %}
Works for me quite well.
回答10:
I'd rather comment on accepted answer, but can't so I am posting my own. I am using fairly similar solution based on: https://djangosnippets.org/snippets/2875/
There is problem that both resolve
and reverse
methods can crash:
resolve
can raise Resolver404 exception, especially when you are already displaying 404 page (causing 500 error instead, very annoying and hard to detect especially with DEBUG=True not displaying real 404)reverse
can crash when you are trying to get page with different language that actually doesn't have translation.
Maybe reverse depends more on what kind of translations method you use or whatever, but resolve crash within 404 page is pretty obvious.
In case of exception you may want to either return same url or maybe url to index page rather than raising exception in template. Code may look like this:
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
回答11:
For Django 2.0 (based on Philipp Zedler's answer)
Custom template:
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
In Template:
{% 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>
回答12:
Works for me in Django 2.2
Create a custom template tag
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)
In the template
{% load change_lang %}
<a href="{% change_lang 'en' %}">English</a>
<a href="{% change_lang 'es' %}">Español</a>
来源:https://stackoverflow.com/questions/11437454/django-templates-get-current-url-in-another-language