mirror of
				https://github.com/django/django.git
				synced 2025-10-26 07:06:08 +00:00 
			
		
		
		
	get_language_from_request() uses Accept-Language and/or Cookie to determine the correct redirect. Upstream caches need the matching Vary header to cache the result.
		
			
				
	
	
		
			396 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			396 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import os
 | |
| 
 | |
| from django.conf import settings
 | |
| from django.core.exceptions import ImproperlyConfigured
 | |
| from django.http import HttpResponse, HttpResponsePermanentRedirect
 | |
| from django.middleware.locale import LocaleMiddleware
 | |
| from django.template import Context, Template
 | |
| from django.test import SimpleTestCase, override_settings
 | |
| from django.test.client import RequestFactory
 | |
| from django.test.utils import override_script_prefix
 | |
| from django.urls import clear_url_caches, reverse, translate_url
 | |
| from django.utils import translation
 | |
| 
 | |
| 
 | |
| class PermanentRedirectLocaleMiddleWare(LocaleMiddleware):
 | |
|     response_redirect_class = HttpResponsePermanentRedirect
 | |
| 
 | |
| 
 | |
| @override_settings(
 | |
|     USE_I18N=True,
 | |
|     LOCALE_PATHS=[
 | |
|         os.path.join(os.path.dirname(__file__), 'locale'),
 | |
|     ],
 | |
|     LANGUAGE_CODE='en-us',
 | |
|     LANGUAGES=[
 | |
|         ('nl', 'Dutch'),
 | |
|         ('en', 'English'),
 | |
|         ('pt-br', 'Brazilian Portuguese'),
 | |
|     ],
 | |
|     MIDDLEWARE=[
 | |
|         'django.middleware.locale.LocaleMiddleware',
 | |
|         'django.middleware.common.CommonMiddleware',
 | |
|     ],
 | |
|     ROOT_URLCONF='i18n.patterns.urls.default',
 | |
|     TEMPLATES=[{
 | |
|         'BACKEND': 'django.template.backends.django.DjangoTemplates',
 | |
|         'DIRS': [os.path.join(os.path.dirname(__file__), 'templates')],
 | |
|         'OPTIONS': {
 | |
|             'context_processors': [
 | |
|                 'django.template.context_processors.i18n',
 | |
|             ],
 | |
|         },
 | |
|     }],
 | |
| )
 | |
| class URLTestCaseBase(SimpleTestCase):
 | |
|     """
 | |
|     TestCase base-class for the URL tests.
 | |
|     """
 | |
| 
 | |
|     def setUp(self):
 | |
|         # Make sure the cache is empty before we are doing our tests.
 | |
|         clear_url_caches()
 | |
| 
 | |
|     def tearDown(self):
 | |
|         # Make sure we will leave an empty cache for other testcases.
 | |
|         clear_url_caches()
 | |
| 
 | |
| 
 | |
| class URLPrefixTests(URLTestCaseBase):
 | |
|     """
 | |
|     Tests if the `i18n_patterns` is adding the prefix correctly.
 | |
|     """
 | |
|     def test_not_prefixed(self):
 | |
|         with translation.override('en'):
 | |
|             self.assertEqual(reverse('not-prefixed'), '/not-prefixed/')
 | |
|             self.assertEqual(reverse('not-prefixed-included-url'), '/not-prefixed-include/foo/')
 | |
|         with translation.override('nl'):
 | |
|             self.assertEqual(reverse('not-prefixed'), '/not-prefixed/')
 | |
|             self.assertEqual(reverse('not-prefixed-included-url'), '/not-prefixed-include/foo/')
 | |
| 
 | |
|     def test_prefixed(self):
 | |
|         with translation.override('en'):
 | |
|             self.assertEqual(reverse('prefixed'), '/en/prefixed/')
 | |
|         with translation.override('nl'):
 | |
|             self.assertEqual(reverse('prefixed'), '/nl/prefixed/')
 | |
|         with translation.override(None):
 | |
|             self.assertEqual(reverse('prefixed'), '/%s/prefixed/' % settings.LANGUAGE_CODE)
 | |
| 
 | |
|     @override_settings(ROOT_URLCONF='i18n.patterns.urls.wrong')
 | |
|     def test_invalid_prefix_use(self):
 | |
|         msg = 'Using i18n_patterns in an included URLconf is not allowed.'
 | |
|         with self.assertRaisesMessage(ImproperlyConfigured, msg):
 | |
|             reverse('account:register')
 | |
| 
 | |
| 
 | |
| @override_settings(ROOT_URLCONF='i18n.patterns.urls.disabled')
 | |
| class URLDisabledTests(URLTestCaseBase):
 | |
| 
 | |
|     @override_settings(USE_I18N=False)
 | |
|     def test_prefixed_i18n_disabled(self):
 | |
|         with translation.override('en'):
 | |
|             self.assertEqual(reverse('prefixed'), '/prefixed/')
 | |
|         with translation.override('nl'):
 | |
|             self.assertEqual(reverse('prefixed'), '/prefixed/')
 | |
| 
 | |
| 
 | |
| class RequestURLConfTests(SimpleTestCase):
 | |
| 
 | |
|     @override_settings(ROOT_URLCONF='i18n.patterns.urls.path_unused')
 | |
|     def test_request_urlconf_considered(self):
 | |
|         request = RequestFactory().get('/nl/')
 | |
|         request.urlconf = 'i18n.patterns.urls.default'
 | |
|         middleware = LocaleMiddleware(lambda req: HttpResponse())
 | |
|         with translation.override('nl'):
 | |
|             middleware.process_request(request)
 | |
|         self.assertEqual(request.LANGUAGE_CODE, 'nl')
 | |
| 
 | |
| 
 | |
| @override_settings(ROOT_URLCONF='i18n.patterns.urls.path_unused')
 | |
| class PathUnusedTests(URLTestCaseBase):
 | |
|     """
 | |
|     If no i18n_patterns is used in root URLconfs, then no language activation
 | |
|     activation happens based on url prefix.
 | |
|     """
 | |
| 
 | |
|     def test_no_lang_activate(self):
 | |
|         response = self.client.get('/nl/foo/')
 | |
|         self.assertEqual(response.status_code, 200)
 | |
|         self.assertEqual(response.headers['content-language'], 'en')
 | |
|         self.assertEqual(response.context['LANGUAGE_CODE'], 'en')
 | |
| 
 | |
| 
 | |
| class URLTranslationTests(URLTestCaseBase):
 | |
|     """
 | |
|     Tests if the pattern-strings are translated correctly (within the
 | |
|     `i18n_patterns` and the normal `patterns` function).
 | |
|     """
 | |
|     def test_no_prefix_translated(self):
 | |
|         with translation.override('en'):
 | |
|             self.assertEqual(reverse('no-prefix-translated'), '/translated/')
 | |
|             self.assertEqual(reverse('no-prefix-translated-slug', kwargs={'slug': 'yeah'}), '/translated/yeah/')
 | |
| 
 | |
|         with translation.override('nl'):
 | |
|             self.assertEqual(reverse('no-prefix-translated'), '/vertaald/')
 | |
|             self.assertEqual(reverse('no-prefix-translated-slug', kwargs={'slug': 'yeah'}), '/vertaald/yeah/')
 | |
| 
 | |
|         with translation.override('pt-br'):
 | |
|             self.assertEqual(reverse('no-prefix-translated'), '/traduzidos/')
 | |
|             self.assertEqual(reverse('no-prefix-translated-slug', kwargs={'slug': 'yeah'}), '/traduzidos/yeah/')
 | |
| 
 | |
|     def test_users_url(self):
 | |
|         with translation.override('en'):
 | |
|             self.assertEqual(reverse('users'), '/en/users/')
 | |
| 
 | |
|         with translation.override('nl'):
 | |
|             self.assertEqual(reverse('users'), '/nl/gebruikers/')
 | |
|             self.assertEqual(reverse('prefixed_xml'), '/nl/prefixed.xml')
 | |
| 
 | |
|         with translation.override('pt-br'):
 | |
|             self.assertEqual(reverse('users'), '/pt-br/usuarios/')
 | |
| 
 | |
|     def test_translate_url_utility(self):
 | |
|         with translation.override('en'):
 | |
|             self.assertEqual(translate_url('/en/nonexistent/', 'nl'), '/en/nonexistent/')
 | |
|             self.assertEqual(translate_url('/en/users/', 'nl'), '/nl/gebruikers/')
 | |
|             # Namespaced URL
 | |
|             self.assertEqual(translate_url('/en/account/register/', 'nl'), '/nl/profiel/registreren/')
 | |
|             # path() URL pattern
 | |
|             self.assertEqual(translate_url('/en/account/register-as-path/', 'nl'), '/nl/profiel/registreren-als-pad/')
 | |
|             self.assertEqual(translation.get_language(), 'en')
 | |
|             # URL with parameters.
 | |
|             self.assertEqual(
 | |
|                 translate_url('/en/with-arguments/regular-argument/', 'nl'),
 | |
|                 '/nl/with-arguments/regular-argument/',
 | |
|             )
 | |
|             self.assertEqual(
 | |
|                 translate_url('/en/with-arguments/regular-argument/optional.html', 'nl'),
 | |
|                 '/nl/with-arguments/regular-argument/optional.html',
 | |
|             )
 | |
| 
 | |
|         with translation.override('nl'):
 | |
|             self.assertEqual(translate_url('/nl/gebruikers/', 'en'), '/en/users/')
 | |
|             self.assertEqual(translation.get_language(), 'nl')
 | |
| 
 | |
| 
 | |
| class URLNamespaceTests(URLTestCaseBase):
 | |
|     """
 | |
|     Tests if the translations are still working within namespaces.
 | |
|     """
 | |
|     def test_account_register(self):
 | |
|         with translation.override('en'):
 | |
|             self.assertEqual(reverse('account:register'), '/en/account/register/')
 | |
|             self.assertEqual(reverse('account:register-as-path'), '/en/account/register-as-path/')
 | |
| 
 | |
|         with translation.override('nl'):
 | |
|             self.assertEqual(reverse('account:register'), '/nl/profiel/registreren/')
 | |
|             self.assertEqual(reverse('account:register-as-path'), '/nl/profiel/registreren-als-pad/')
 | |
| 
 | |
| 
 | |
| class URLRedirectTests(URLTestCaseBase):
 | |
|     """
 | |
|     Tests if the user gets redirected to the right URL when there is no
 | |
|     language-prefix in the request URL.
 | |
|     """
 | |
|     def test_no_prefix_response(self):
 | |
|         response = self.client.get('/not-prefixed/')
 | |
|         self.assertEqual(response.status_code, 200)
 | |
| 
 | |
|     def test_en_redirect(self):
 | |
|         response = self.client.get('/account/register/', HTTP_ACCEPT_LANGUAGE='en')
 | |
|         self.assertRedirects(response, '/en/account/register/')
 | |
| 
 | |
|         response = self.client.get(response.headers['location'])
 | |
|         self.assertEqual(response.status_code, 200)
 | |
| 
 | |
|     def test_en_redirect_wrong_url(self):
 | |
|         response = self.client.get('/profiel/registreren/', HTTP_ACCEPT_LANGUAGE='en')
 | |
|         self.assertEqual(response.status_code, 404)
 | |
| 
 | |
|     def test_nl_redirect(self):
 | |
|         response = self.client.get('/profiel/registreren/', HTTP_ACCEPT_LANGUAGE='nl')
 | |
|         self.assertRedirects(response, '/nl/profiel/registreren/')
 | |
| 
 | |
|         response = self.client.get(response.headers['location'])
 | |
|         self.assertEqual(response.status_code, 200)
 | |
| 
 | |
|     def test_nl_redirect_wrong_url(self):
 | |
|         response = self.client.get('/account/register/', HTTP_ACCEPT_LANGUAGE='nl')
 | |
|         self.assertEqual(response.status_code, 404)
 | |
| 
 | |
|     def test_pt_br_redirect(self):
 | |
|         response = self.client.get('/conta/registre-se/', HTTP_ACCEPT_LANGUAGE='pt-br')
 | |
|         self.assertRedirects(response, '/pt-br/conta/registre-se/')
 | |
| 
 | |
|         response = self.client.get(response.headers['location'])
 | |
|         self.assertEqual(response.status_code, 200)
 | |
| 
 | |
|     def test_pl_pl_redirect(self):
 | |
|         # language from outside of the supported LANGUAGES list
 | |
|         response = self.client.get('/account/register/', HTTP_ACCEPT_LANGUAGE='pl-pl')
 | |
|         self.assertRedirects(response, '/en/account/register/')
 | |
| 
 | |
|         response = self.client.get(response.headers['location'])
 | |
|         self.assertEqual(response.status_code, 200)
 | |
| 
 | |
|     @override_settings(
 | |
|         MIDDLEWARE=[
 | |
|             'i18n.patterns.tests.PermanentRedirectLocaleMiddleWare',
 | |
|             'django.middleware.common.CommonMiddleware',
 | |
|         ],
 | |
|     )
 | |
|     def test_custom_redirect_class(self):
 | |
|         response = self.client.get('/account/register/', HTTP_ACCEPT_LANGUAGE='en')
 | |
|         self.assertRedirects(response, '/en/account/register/', 301)
 | |
| 
 | |
| 
 | |
| class URLVaryAcceptLanguageTests(URLTestCaseBase):
 | |
|     """
 | |
|     'Accept-Language' is not added to the Vary header when using prefixed URLs.
 | |
|     """
 | |
|     def test_no_prefix_response(self):
 | |
|         response = self.client.get('/not-prefixed/')
 | |
|         self.assertEqual(response.status_code, 200)
 | |
|         self.assertEqual(response.get('Vary'), 'Accept-Language')
 | |
| 
 | |
|     def test_en_redirect(self):
 | |
|         """
 | |
|         The redirect to a prefixed URL depends on 'Accept-Language' and
 | |
|         'Cookie', but once prefixed no header is set.
 | |
|         """
 | |
|         response = self.client.get('/account/register/', HTTP_ACCEPT_LANGUAGE='en')
 | |
|         self.assertRedirects(response, '/en/account/register/')
 | |
|         self.assertEqual(response.get('Vary'), 'Accept-Language, Cookie')
 | |
| 
 | |
|         response = self.client.get(response.headers['location'])
 | |
|         self.assertEqual(response.status_code, 200)
 | |
|         self.assertFalse(response.get('Vary'))
 | |
| 
 | |
| 
 | |
| class URLRedirectWithoutTrailingSlashTests(URLTestCaseBase):
 | |
|     """
 | |
|     Tests the redirect when the requested URL doesn't end with a slash
 | |
|     (`settings.APPEND_SLASH=True`).
 | |
|     """
 | |
|     def test_not_prefixed_redirect(self):
 | |
|         response = self.client.get('/not-prefixed', HTTP_ACCEPT_LANGUAGE='en')
 | |
|         self.assertRedirects(response, '/not-prefixed/', 301)
 | |
| 
 | |
|     def test_en_redirect(self):
 | |
|         response = self.client.get('/account/register', HTTP_ACCEPT_LANGUAGE='en', follow=True)
 | |
|         # We only want one redirect, bypassing CommonMiddleware
 | |
|         self.assertEqual(response.redirect_chain, [('/en/account/register/', 302)])
 | |
|         self.assertRedirects(response, '/en/account/register/', 302)
 | |
| 
 | |
|         response = self.client.get('/prefixed.xml', HTTP_ACCEPT_LANGUAGE='en', follow=True)
 | |
|         self.assertRedirects(response, '/en/prefixed.xml', 302)
 | |
| 
 | |
| 
 | |
| class URLRedirectWithoutTrailingSlashSettingTests(URLTestCaseBase):
 | |
|     """
 | |
|     Tests the redirect when the requested URL doesn't end with a slash
 | |
|     (`settings.APPEND_SLASH=False`).
 | |
|     """
 | |
|     @override_settings(APPEND_SLASH=False)
 | |
|     def test_not_prefixed_redirect(self):
 | |
|         response = self.client.get('/not-prefixed', HTTP_ACCEPT_LANGUAGE='en')
 | |
|         self.assertEqual(response.status_code, 404)
 | |
| 
 | |
|     @override_settings(APPEND_SLASH=False)
 | |
|     def test_en_redirect(self):
 | |
|         response = self.client.get('/account/register-without-slash', HTTP_ACCEPT_LANGUAGE='en')
 | |
|         self.assertRedirects(response, '/en/account/register-without-slash', 302)
 | |
| 
 | |
|         response = self.client.get(response.headers['location'])
 | |
|         self.assertEqual(response.status_code, 200)
 | |
| 
 | |
| 
 | |
| class URLResponseTests(URLTestCaseBase):
 | |
|     """Tests if the response has the correct language code."""
 | |
|     def test_not_prefixed_with_prefix(self):
 | |
|         response = self.client.get('/en/not-prefixed/')
 | |
|         self.assertEqual(response.status_code, 404)
 | |
| 
 | |
|     def test_en_url(self):
 | |
|         response = self.client.get('/en/account/register/')
 | |
|         self.assertEqual(response.status_code, 200)
 | |
|         self.assertEqual(response.headers['content-language'], 'en')
 | |
|         self.assertEqual(response.context['LANGUAGE_CODE'], 'en')
 | |
| 
 | |
|     def test_nl_url(self):
 | |
|         response = self.client.get('/nl/profiel/registreren/')
 | |
|         self.assertEqual(response.status_code, 200)
 | |
|         self.assertEqual(response.headers['content-language'], 'nl')
 | |
|         self.assertEqual(response.context['LANGUAGE_CODE'], 'nl')
 | |
| 
 | |
|     def test_wrong_en_prefix(self):
 | |
|         response = self.client.get('/en/profiel/registreren/')
 | |
|         self.assertEqual(response.status_code, 404)
 | |
| 
 | |
|     def test_wrong_nl_prefix(self):
 | |
|         response = self.client.get('/nl/account/register/')
 | |
|         self.assertEqual(response.status_code, 404)
 | |
| 
 | |
|     def test_pt_br_url(self):
 | |
|         response = self.client.get('/pt-br/conta/registre-se/')
 | |
|         self.assertEqual(response.status_code, 200)
 | |
|         self.assertEqual(response.headers['content-language'], 'pt-br')
 | |
|         self.assertEqual(response.context['LANGUAGE_CODE'], 'pt-br')
 | |
| 
 | |
|     def test_en_path(self):
 | |
|         response = self.client.get('/en/account/register-as-path/')
 | |
|         self.assertEqual(response.status_code, 200)
 | |
|         self.assertEqual(response.headers['content-language'], 'en')
 | |
|         self.assertEqual(response.context['LANGUAGE_CODE'], 'en')
 | |
| 
 | |
|     def test_nl_path(self):
 | |
|         response = self.client.get('/nl/profiel/registreren-als-pad/')
 | |
|         self.assertEqual(response.status_code, 200)
 | |
|         self.assertEqual(response.headers['content-language'], 'nl')
 | |
|         self.assertEqual(response.context['LANGUAGE_CODE'], 'nl')
 | |
| 
 | |
| 
 | |
| class URLRedirectWithScriptAliasTests(URLTestCaseBase):
 | |
|     """
 | |
|     #21579 - LocaleMiddleware should respect the script prefix.
 | |
|     """
 | |
|     def test_language_prefix_with_script_prefix(self):
 | |
|         prefix = '/script_prefix'
 | |
|         with override_script_prefix(prefix):
 | |
|             response = self.client.get('/prefixed/', HTTP_ACCEPT_LANGUAGE='en', SCRIPT_NAME=prefix)
 | |
|             self.assertRedirects(response, '%s/en/prefixed/' % prefix, target_status_code=404)
 | |
| 
 | |
| 
 | |
| class URLTagTests(URLTestCaseBase):
 | |
|     """
 | |
|     Test if the language tag works.
 | |
|     """
 | |
|     def test_strings_only(self):
 | |
|         t = Template("""{% load i18n %}
 | |
|             {% language 'nl' %}{% url 'no-prefix-translated' %}{% endlanguage %}
 | |
|             {% language 'pt-br' %}{% url 'no-prefix-translated' %}{% endlanguage %}""")
 | |
|         self.assertEqual(t.render(Context({})).strip().split(),
 | |
|                          ['/vertaald/', '/traduzidos/'])
 | |
| 
 | |
|     def test_context(self):
 | |
|         ctx = Context({'lang1': 'nl', 'lang2': 'pt-br'})
 | |
|         tpl = Template("""{% load i18n %}
 | |
|             {% language lang1 %}{% url 'no-prefix-translated' %}{% endlanguage %}
 | |
|             {% language lang2 %}{% url 'no-prefix-translated' %}{% endlanguage %}""")
 | |
|         self.assertEqual(tpl.render(ctx).strip().split(),
 | |
|                          ['/vertaald/', '/traduzidos/'])
 | |
| 
 | |
|     def test_args(self):
 | |
|         tpl = Template("""{% load i18n %}
 | |
|             {% language 'nl' %}{% url 'no-prefix-translated-slug' 'apo' %}{% endlanguage %}
 | |
|             {% language 'pt-br' %}{% url 'no-prefix-translated-slug' 'apo' %}{% endlanguage %}""")
 | |
|         self.assertEqual(tpl.render(Context({})).strip().split(),
 | |
|                          ['/vertaald/apo/', '/traduzidos/apo/'])
 | |
| 
 | |
|     def test_kwargs(self):
 | |
|         tpl = Template("""{% load i18n %}
 | |
|             {% language 'nl'  %}{% url 'no-prefix-translated-slug' slug='apo' %}{% endlanguage %}
 | |
|             {% language 'pt-br' %}{% url 'no-prefix-translated-slug' slug='apo' %}{% endlanguage %}""")
 | |
|         self.assertEqual(tpl.render(Context({})).strip().split(),
 | |
|                          ['/vertaald/apo/', '/traduzidos/apo/'])
 |