Issue trying to change language from Django template

Caumons picture Caumons · Aug 23, 2013 · Viewed 18.9k times · Source

I need to include two buttons or links to allow users change language between English and Spanish. I've read the docs and tried this:

<form action="/i18n/setlang/" method="post">{% csrf_token %}
    <input name="language" type="hidden" value="es" />
    <input type="submit" value="ES" />
</form>

However, every time I click the button, the page is reloaded but the language doesn't change at all. Am I missing something?

Note: I haven't set next, as I just want to reload the current page in the desired language.

If I use the default form provided by the docs the result is the same: the page reloads but language isn't changed:

<form action="{% url 'set_language' %}" method="post">
    {% 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>

UPDATE:

After further testing, I've noticed that there is a problem using both i18n_patterns and patterns in the urls.py. Currently I have a file that looks like:

urlpatterns = i18n_patterns('',
    url(r'^contents/', include('contents.urls')),
    url(r'^events/', include('events.urls')),
    # ...
)
urlpatterns += patterns('',
    url(r'^i18n/', include('django.conf.urls.i18n')),
)

And this doesn't seem to work. However, if I remove the i18n_patterns and change it to patterns then it seems to work:

urlpatterns = patterns('',
    url(r'^contents/', include('contents.urls')),
    url(r'^events/', include('events.urls')),
    # ...
)
urlpatterns += patterns('',
    url(r'^i18n/', include('django.conf.urls.i18n')),
)

The docs say that you don't have to include it inside i18n_patterns, so I think this should work, but it doesn't! It doesn't matter if you include django.conf.urls.i18n before or after i18n_patterns it always does the same.

Answer

Caumons picture Caumons · Aug 23, 2013

After more testing and thanks to the related question linked by @AronYsidoro I've finally found the issue and a very simple solution that actually solves this.

First, let me explain the problem: When working with i18_patterns in your urls.py to prepend the language code, if you call the URL set_language to change the language without specifying next, it defaults to the current one, but with the prepended old language code! So, the language gets back to the original! And, if you explicitly specify next, you must be sure to do not include the language code at the begining.

If you use {{ request.path }} or {{ request.get_full_path }} to specify the next as the current page this won't work as it returns the language code too.

So, how do we remove this undesired language code to reload the current page with the language changed when using i18n_patterns? Easy, we just have to slice the 3 first chars (the slash and the two chars language code)!

Here you have two examples. The first one in form of a select (with the languages as choices) and the other one in form of a button (per language).

I really hope this helps someone else. You can just copy and paste the code and it should work. However, if using the "button form", you just have to set the language to your desired!

Change language from list:

<form action="{% url 'set_language' %}" method="post">
  {% csrf_token %}
  <input name="next" type="hidden" value="{{ request.get_full_path|slice:'3:' }}" />
  <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="Change" />
</form>

Change language as button:

<form action="{% url 'set_language' %}" method="post">
  {% csrf_token %}
  <input name="next" type="hidden" value="{{ request.get_full_path|slice:'3:' }}" />
  <input name="language" type="hidden" value="es" />
  <input type="submit" value="ES" />
</form>