Reputation: 349
In yet another variant of the "set_language isn't working" category of questions, I have this head scratcher.
In my page I have a language selector made of three parts:
Hidden form
<form action="{% url 'set_language' %}" method="post" id="language-form" name="language-form">
{% csrf_token %}
<input type="hidden" name="language" id="language" value="" />
</form>
Javascript form submission function
function set_language(code) {
$('#language').val(code);
$('#language-form').submit();
}
And the html list (shown here as rendered in the browser, rather than the template language):
<li class="active"><a href="javascript:set_language('en-gb')"><img src="/static/img/locale/en-gb.png"> Great Britain (English) (en-gb)</a></li>
<li><a href="javascript:set_language('nl-be')"><img src="/static/img/locale/nl-be.png"> Belgium (Dutch) (nl-be)</a></li>
<li><a href="javascript:set_language('fr-be')"><img src="/static/img/locale/fr-be.png"> Belgium (French) (fr-be)</a></li>
<li><a href="javascript:set_language('nl-nl')"><img src="/static/img/locale/nl-nl.png"> Netherlands (Dutch) (nl-nl)</a></li>
<li><a href="javascript:set_language('fy-nl')"><img src="/static/img/locale/fy-nl.png"> Netherlands (Frisian) (fy-nl)</a></li>
I can confirm that the above functions as expected, and submits a POST request with content such as:
csrfmiddlewaretoken=5Vcs9XdvHs7uJwPg4BHtnVPhPW3mMT0w&language=nl-nl
and that the development server sees the post, as shown by:
[08/Jul/2015 23:58:33]"POST /i18n/setlang/ HTTP/1.1" 302 0
[08/Jul/2015 23:58:33]"GET /en-gb/ HTTP/1.1" 200 6738
I can also confirm that the various site urls do work when manually entered, so I can goto localhost/en-gb/foo
and localhost/fr-be/foo
just fine.
I have specifically excluded the next field in the form so that the redirect returns to the current page (and that works as expected too).
The problem is that whilst all these things are triggering, the language isn't being changed after the redirect.
My cookie appears to be devoid of any language data. I've attempted to display the session._language value in the output - that failed as you would expect, so I tried:
{% with settings.LANGUAGE_SESSION_KEY as langkey %}
<p>Lang: {{ request.session.langkey }} {{ langkey }}</p>
{% endwith %}
which returns "Lang:" and that's it (i.e. it looks like it isn't set or is empty).
I'm now at a loss. I believe that set_language should redirect from /en-gb/foo
to /fr-be/foo
, but this isn't happening. Having looked through the code calls, I can also confirm that from set_language code that check_for_language(lang_code)
when lang_code == 'fr-be'
returns true (as it does for all the other languages specified in my settings - see below), and returns false for values like 'ff'. It also returns true for values of 'en', 'fr, 'nl', and 'fy' too, even though they don't work as urls (they result in 404 errors).
Can anyone:
Thank you
Settings.py
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
)
ROOT_URLCONF = 'wp4.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.core.context_processors.i18n',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
# Internationalization
# https://docs.djangoproject.com/en/1.8/topics/i18n/
LANGUAGE_CODE = 'en-gb'
LANGUAGES = (
('en-gb', _('Great Britain (English)')),
('nl-be', _('Belgium (Dutch)')),
('fr-be', _('Belgium (French)')),
('nl-nl', _('Netherlands (Dutch)')),
('fy-nl', _('Netherlands (Frisian)')),
)
LOCALE_PATHS = (
os.path.join(BASE_DIR, 'locale'),
)
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
Upvotes: 2
Views: 2473
Reputation: 349
Ok, few bits of further observation have resulted in the following answers and opinions.
To answer my specific questions first:
This is where I venture into opinion based on reviewing the django code and paths.
I think there is a logic error in the set_language()
code and documentation. This is perhaps more an expectation against what the code says it does, as the docs list the resolution of next as:
I left next
empty thinking to trigger step 2. My expectation is that if the new language was 'fr-be', that it would translate the url from /nl-be/foo/bar/
into /fr-be/foo/bar/
. It doesn't. Instead it sets next to remain as /nl-be/foo/bar/
and then sets the session._language value to 'fr-be'. This session value is then ignored as the page is loaded, because the locale middleware (get_language_from_request()
) processes the request by looking for a language code in the url first, and only if it doesn't find one, does it look at the other options (such as session, cookie, request data, settings).
This means that the session value of 'fr-be' is ignored in favour of the 'nl-be' in the url, and then something else clears the session value before the template is rendered (which is why I find the session value to be None when I query it in the template).
I would argue that this is a logic misstep, because if you wanted to change the language and have i18n running, then the url is always going to have the language code of your present language in it, so step 2 would always result in the language change being ignored.
Whilst I'm going to use my workaround (i.e. ensuring that every page knows its own urls minus the language code prefix, and setting that as the next
value explicitly), I think this is something that should be changed (or at least the docs made explicit that you can't change language using the second method of determining next).
Upvotes: 7