Проблема при попытке изменить язык из шаблона Django
Мне нужно включить две кнопки или ссылки, чтобы позволить пользователям менять язык между английским и испанским языками. Я прочитал документы и попробовал это:
<form action="/i18n/setlang/" method="post">{% csrf_token %}
<input name="language" type="hidden" value="es" />
<input type="submit" value="ES" />
</form>
Однако каждый раз, когда я нажимаю кнопку, страница перезагружается, но язык не меняется вообще. Я что-то пропустил?
Примечание: я не установил next
, так как я просто хочу перезагрузить текущую страницу на желаемом языке.
Если я использую форму по умолчанию, предоставленную документами, результат будет таким же: страница перезагрузится, но язык не изменится:
<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>
ОБНОВЛЕНИЕ:
После дальнейшего тестирования я заметил, что есть проблема с использованием обоих i18n_patterns
а также patterns
в urls.py
, В настоящее время у меня есть файл, который выглядит так:
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')),
)
И это не похоже на работу. Однако, если я удалю i18n_patterns
и изменить его на patterns
тогда похоже на работу
urlpatterns = patterns('',
url(r'^contents/', include('contents.urls')),
url(r'^events/', include('events.urls')),
# ...
)
urlpatterns += patterns('',
url(r'^i18n/', include('django.conf.urls.i18n')),
)
Документы говорят, что вы не должны включать его внутрь i18n_patterns
так что я думаю, что это должно работать, но это не так! Неважно, если вы включите django.conf.urls.i18n
до или после i18n_patterns
это всегда делает то же самое.
11 ответов
После дополнительного тестирования и благодаря связанному с @AronYsidoro вопросу, я наконец-то нашел проблему и очень простое решение, которое фактически решает эту проблему.
Во-первых, позвольте мне объяснить проблему: при работе с i18_patterns
в вашем urls.py
добавить код языка, если вы позвоните по URL set_language
изменить язык без указания next
, по умолчанию это текущий, но с добавленным кодом старого языка! Итак, язык возвращается к оригиналу! И, если вы явно укажете next
Вы должны быть уверены, что не указали код языка в начале.
Если вы используете {{ request.path }}
или же {{ request.get_full_path }}
указать next
в качестве текущей страницы это не будет работать, так как оно также возвращает код языка.
Итак, как нам удалить этот нежелательный языковой код, чтобы перезагрузить текущую страницу с языком, измененным при использовании i18n_patterns
? Легко, нам просто нужно нарезать 3 первых символа (косая черта и код языка двух символов)!
Здесь у вас есть два примера. Первый в форме выбора (с языками выбора), а другой в виде кнопки (для каждого языка).
Я действительно надеюсь, что это поможет кому-то еще. Вы можете просто скопировать и вставить код, и он должен работать. Однако, если вы используете "форму кнопки", вам просто нужно выбрать нужный язык!
Изменить язык из списка:
<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>
Изменить язык как кнопку:
<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>
Сумма возможных вариантов:
Измените язык сеанса пользователя с помощью select
На Django docs есть отличное подробное описание с примером.
Изменить язык сеанса пользователя с помощью кнопок
Нет необходимости повторять форму для каждой кнопки, как предлагает @Caumons, вместо этого вы можете просто включить в форму столько же кнопок, сколько языков.
<form action="{% url 'set_language' %}" method="post">
{% csrf_token %}
<input name="next" type="hidden" value="{{ request.get_full_path|slice:'3:' }}" />
<ul class="nav navbar-nav navbar-right language menu">
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %}
<li>
<button type="submit"
name="language"
value="{{ language.code }}"
class="{% if language.code == LANGUAGE_CODE %}selected{% endif %}">
{{ language.name_local }}
</button>
</li>
{% endfor %}
</ul>
</form>
Вы, конечно, можете настроить кнопки так, чтобы они выглядели как ссылки или как угодно.
Изменить отображаемый язык со ссылками
Если не требуется, чтобы язык сеанса пользователя по умолчанию был изменен, то для изменения содержимого можно использовать простые ссылки:
<ul class="nav navbar-nav navbar-right language menu">
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %}
<li>
<a href="/{{ language.code }}{{ request.get_full_path|slice:'3:' }}"
class="{% if language.code == LANGUAGE_CODE %}selected{% endif %}"
lang="{{ language.code }}">
{{ language.name_local }}
</a>
</li>
{% endfor %}
</ul>
SEO
Я не совсем уверен, что контент удобен для SEO, если форма используется для изменения языка сеанса, как рекомендует Django. Поэтому возможно, что ссылка <a>
Разметка добавлена как скрытая под <button>
элемент.
Если вам нужно только два языка, напр. Английский и французский, и вы определили это в своем settings.py, вы установили язык по умолчанию и правильно настроили urls.py в своем основном приложении. Затем просто используйте это в своем шаблоне (или частично, на верхней панели и т. Д.)btn-kinito "btn-header
- это просто классы стилей, которыми вы можете управлять с помощью CSS или JS.
Цикл или итерация внутри - это просто цикл через LANGUAGES[]
список, который вы определили в settings.py
, затем создается кнопка. с символом "|" и пространство
чтобы он выглядел мило, так как у нас всего два языка.
В {% url 'set_language' %}
- это представление перенаправления Django под названием set_language, которое перенаправляет на URL. Вот почему в urls.py ваших основных приложений вам нужно указатьpath('i18n/', include('django.conf.urls.i18n')),
В этом случае. Таким образом, после создания кнопки для каждого языка в списке вы сможете быть перенаправлены на этот URL-адрес.
<div class="btn-header">
<form action="{% url 'set_language' %}" method="post">
{% csrf_token %}
<input name="next" type="hidden" value="{{ redirect_to }}" />
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %}
<button type="submit" name="language" value="{{ language.code }}"
class="btn-kinito">
{{ language.code }}
</button>|
{% endfor %}
</form>
</div>
Думаю, что для urls.py это могло бы выглядеть так:
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
from django.conf.urls.i18n import i18n_patterns
# I don't want my admin translated
urlpatterns = [
path('admin/', admin.site.urls),
]
urlpatterns += i18n_patterns (
path('i18n/', include('django.conf.urls.i18n')),
path('', include('pages.urls')),
path('cats', include('cats.urls')),
path('dogs', include('dogs.urls')),
prefix_default_language=False,
) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
В prefix_default_language=False,
это необязательно, и он удаляет языковой префикс по умолчанию из URL-адреса, что имеет смысл, если у вас только два или три языка. Хотя в прошлом у меня были проблемы с этим, когдаprefix_default_language=False,
не сработало.
Как исправить проблему с prefix_default_language=False,
НЕ работает или не удаляет языковой префикс по умолчанию из URL-адресов
В моем settings.py я изменил:
LANGUAGE_CODE = 'en-us'
к LANGUAGE_CODE = 'en'
(кажется, решил)
Если в вашей текущей системе у вас есть только 2 языка, просто используйте как показано ниже:
{% ifequal LANGUAGE_CODE "en" %}
<a href="/es{{ request.get_full_path }}">Spanish</a>
{% else %}
<a href="/en{{ request.get_full_path }}">English</a>
{% endifequal %}
Нет необходимости в форме, URL и отправить и т. Д. Это сработало для меня.
Я знаю, что это не надежное решение, но мне нужна была кнопка переключения (не раскрывающийся список, поскольку я хочу переключаться между двумя языками).
{% get_language_info_list for LANGUAGES as languages %}
{% if LANGUAGE_CODE == languages.0.code %}
<form action="{% url 'set_language' %}" method="post">
{% csrf_token %}
<div class="lang-btn">
<input name="next" type="hidden" value="{{ redirect_to }}" />
<input name="language" type="hidden" value="{{ languages.1.code }}" />
<button type="submit"><img width="30" src="{% static 'united-kingdom.png' %}" alt=""></button>
</div>
</form>
{% else %}
<form action="{% url 'set_language' %}" method="post">
{% csrf_token %}
<div class="lang-btn">
<input name="next" type="hidden" value="{{ redirect_to }}" />
<input name="language" type="hidden" value="{{ languages.0.code }}" />
<button type="submit"><img width="30" src="{% static 'turkey.png' %}" alt=""></button>
</div>
</form>
{% endif %}
Помимо добавления формы, предложенной здесь:
<form action="{% url 'set_language' %}" method="post">
{% csrf_token %}
{{ request.get_full_path_info|slice:'3:'}}
<input name="next" type="hidden" value="{{ languageless_url }}" />
<ul class="nav navbar-nav navbar-right language menu">
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %}
<li>
<button type="submit" name="language" value="{{ language.code }}"
class="{% if language.code == LANGUAGE_CODE %}selected{% endif %}">
{{ language.code }}
</button>
</li>
{% endfor %}
</ul>
</form>
Я бы предложил добавить обработчик контекста (app.context_processors.py):
def language_processor(request):
"""
This is needed for language picker to work
"""
return {
'languageless_url':
'/' + '/'.join(request.get_full_path().split('/')[2:])
}
Это позволяет исключить логику из шаблона. Также не забудьте указать свой процессор в настройках шаблона:
'context_processors': [
'app.context_processors.language_processor',
{% load i18n %}
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}
<div class="btn-header">
<form action="{% url 'set_language' %}" method="post">
{% csrf_token %}
<input name="next" type="hidden" value="{{ redirect_to }}" />
{% for language in languages %}
{% if language.code != LANGUAGE_CODE %}
<button type="submit" name="language" value="{{ language.code }}">{{ language.name }}</button>
{% endif %}
{% endfor %}
</form>
</div>
если у вас два языка. Он покажет вам только тот, который выключен.
Кажется, многие люди (как и я) потратили много времени на поиск правильного решения, которое работает для всех случаев. «Трюк» заключается в том, чтобы установить
После установки выбора языка Django ищет следующий параметр в данных POST или GET. Если он найден и Django считает его безопасным URL-адресом (т. е. он не указывает на другой хост и использует безопасную схему), будет выполнено перенаправление на этот URL-адрес. В противном случае Django может вернуться к перенаправлению пользователя на URL-адрес из заголовка Referer.
Наконец, я хотел иметь функцию , которую я мог бы вызывать для любых изменений языка , вызванных ссылками, кнопками, выпадающими меню или чем-то еще . Я закончил с этой маленькой функцией js:
django_language_set(language_code){
url = "{% url 'set_language' %}";
data = {
language: language_code,
next: '',
csrfmiddlewaretoken: '{{ csrf_token }}'
};
this.form_post(url, data)
}
form_post(path, params, method='post') {
/* simulates a post submit, call like:
form_post('/home', {language: 'de', next: ''})"
*/
const form = document.createElement('form');
form.method = method;
form.action = path;
for (const key in params) {
if (params.hasOwnProperty(key)) {
const hiddenField = document.createElement('input');
hiddenField.type = 'hidden';
hiddenField.name = key;
hiddenField.value = params[key];
form.appendChild(hiddenField);
}
}
document.body.appendChild(form);
form.submit();
}
Вызовите функцию, например
<a @click="django_language_set('fr')">French</a>
Добавляю решение с кнопками и значком флагов:
<form action="{% url 'set_language' %}" method="post">
{% csrf_token %}
{% get_current_language as LANGUAGE_CODE %}
<input name="next" type="hidden" value="{{ languageless_url }}" />
<a class="btn dropdown-sm-toggle" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<img src="/static/img/flags/{{LANGUAGE_CODE}}.svg" width="20" height="30"></a>
</a>
<ul class="dropdown-menu dropdown-menu-end" id="language-list">
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %}
<li>
<button class="dropdown-item" type="submit" name="language" value="{{ language.code }}"
class="{% if language.code == LANGUAGE_CODE %}selected{% endif %}">
<img src="/static/img/flags/{{ language.code }}.svg" width="20" height="30">
{#{{ language.code }}#}{{ language.name_local }}
</button>
</li>
{% endfor %}
</ul>
</form>
Django 3.02
<div class="uk-flex">
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}
<div class="languages">
<p>{% trans "Language" %}:</p>
<ul class="languages">
{% for language in languages %}
<li>
<a href="/{{ language.code }}/{{ request.get_full_path |slice:'4:'}}" {% if language.code == LANGUAGE_CODE %} class="selected"{% endif %}>
{{ language.name_local }}
</a>
</li>
{% endfor %}
</ul>
</div>
</div>