Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Switch language in Django with the translated url redirect

I have internationalization correctly installed.

It's works with urls like:

/en/bookings/ #English
/es/reservas/ #Spanish

In the home page the language switching works fine too.

- What's the issue?

When I change the language in a translated page, like /en/bookings/, if I turn the language to Spanish (es) I am redirected to /en/bookings/ again and I see the page in English.

If I change the prefix (like this answer) the redirection goes to /es/bookings/ that doesn't exists.

I don't want to be redirected to the home page.

- What I like?

If I am in the /en/bookings/ and switch to Spanish I want to be redirected to /es/reservas/, for all the translated urls.

What is the best way?

Thanks.

like image 797
Ignacio Pérez Avatar asked Mar 17 '23 22:03

Ignacio Pérez


2 Answers

I had similar problem so I sending my resolution to save Your time.

main(urls.py)

from django.conf.urls import include, url
from django.conf.urls.i18n import i18n_patterns

urlpatterns = [
    url(r'^i18n/', include('django.conf.urls.i18n')), 
]

urlpatterns += i18n_patterns(
    url(r'^', include('index.urls', namespace='index')),
)

(index.urls.py)

from django.conf.urls import url
from django.views.generic import TemplateView
from django.utils.translation import ugettext_lazy as _


urlpatterns = [
    url(r'^$', TemplateView.as_view(template_name='index/index.html'), name='index'),
    url(_(r'^python-programming/$'), TemplateView.as_view(template_name='index/new_page.html'),
        name='new_page'),
]

Creating template tag to return urls for current location in all languages that we support (index.templatetags.helper_tags.py)

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

register = Library()


@register.simple_tag(takes_context=True)
def change_lang(context, lang=None, *args, **kwargs):
    """
    Get active page's url by a specified language
    Usage: {% change_lang 'en' %}
    """

    path = context['request'].path
    url_parts = resolve(path)

    url = path
    cur_language = get_language()
    try:
        activate(lang)
        url = reverse(url_parts.view_name, kwargs=url_parts.kwargs)
    finally:
        activate(cur_language)

    return "%s" % url

Creating middleware to change site language when user will click at alternative link to this sub site but in different language (middleware.py)

from django.utils import translation
from django.conf import settings
from django.utils.deprecation import MiddlewareMixin


class LangBasedOnUrlMiddleware(MiddlewareMixin):

    @staticmethod
    def process_request(request):

        if hasattr(request, 'session'):
            active_session_lang = request.session.get(translation.LANGUAGE_SESSION_KEY)

            if active_session_lang == request.LANGUAGE_CODE:
                return

            if any(request.LANGUAGE_CODE in language for language in settings.LANGUAGES):
                translation.activate(request.LANGUAGE_CODE)
                request.session[translation.LANGUAGE_SESSION_KEY] = request.LANGUAGE_CODE

Adding it to (settings.py) just after LocaleMiddleware

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.locale.LocaleMiddleware',
    'our_app.middleware.LangBasedOnUrlMiddleware',
]

Sample usage in template:

{% load i18n %}
{% load helper_tags %}
    {% get_available_languages as languages %}
    {% for lang_code, lang_name in languages %}
        <a href="{{ request.scheme }}://{{ request.META.HTTP_HOST }}{% change_lang lang_code %}">
    {% endfor %}
like image 95
Marcin Dorociak Avatar answered Mar 19 '23 11:03

Marcin Dorociak


When I had the same problem, I implemented a custom template tag (current_url) that, given the request in context, re-renders the url for the active language:

{% load custom_tags %}
<ul>
{% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %}
    {# IMPORTANT! enclose the 'current_url' tag in a 'language' block #}
    {% language language.code %}
        <li {% if language.code == LANGUAGE_CODE %}class="active"{% endif %}>
            <a href="{% current_url %}">{{ language.name_local }}</a>
        </li>
    {% endlanguage %}
{% endfor %}
</ul>

Here is the code for the custom tag (custom_tags.py):

import six
import sys
from django.template import Node, TemplateSyntaxError, Library
from django.conf import settings

register = Library()

class CurrentURLNode(Node):

    def __init__(self, asvar=None):
        self.asvar = asvar

    def render(self, context):
        request = context['request']
        from django.core.urlresolvers import reverse, NoReverseMatch
        url = ''
        try:
            url = reverse(request.resolver_match.view_name, args=request.resolver_match.args, kwargs=request.resolver_match.kwargs, current_app=context.current_app)
        except NoReverseMatch:
            exc_info = sys.exc_info()
            if settings.SETTINGS_MODULE:
                project_name = settings.SETTINGS_MODULE.split('.')[0]
                try:
                    url = reverse(project_name + '.' + request.resolver_match.view_name,
                          args=request.resolver_match.args, kwargs=request.resolver_match.kwargs,
                          current_app=context.current_app)
                except NoReverseMatch:
                    if self.asvar is None:                      
                        six.reraise(*exc_info)
            else:
                if self.asvar is None:
                    raise

        if self.asvar:
            context[self.asvar] = url
            return ''
        else:
            return url

@register.tag
def current_url(parser, token):
    bits = token.split_contents()
    bits = bits[1:]
    asvar = None
    if len(bits) >= 2 and bits[-2] == 'as':
        asvar = bits[-1]
        bits = bits[:-2]
    if len(bits):
        raise TemplateSyntaxError("Unexpected arguments to current_url tag")
    return CurrentURLNode(asvar)

There is no need to use the 'set_language' django view. There is no need to make a POST request to change the active language. With only html archors linking all your internationalized content together, it's better for SEO.

like image 23
suselrd Avatar answered Mar 19 '23 11:03

suselrd