from importlib import import_module from unittest.mock import patch from django.conf import settings from django.template.context import Context from django.test.utils import override_settings from django.utils import translation from cms import api from cms.test_utils.testcases import CMSTestCase from cms.utils import get_language_from_request, i18n from cms.views import details @override_settings( LANGUAGE_CODE='en', LANGUAGES=(('fr', 'French'), ('en', 'English'), ('de', 'German'), ('es', 'Spanish')), CMS_LANGUAGES={ 1: [ {'code': 'en', 'name': 'English', 'public': True}, {'code': 'fr', 'name': 'French', 'public': False}, ], 'default': { 'public': True, 'hide_untranslated': False, }, }, SITE_ID=1, ) class TestLanguages(CMSTestCase): def test_language_code(self): self.assertEqual(i18n.get_language_code('en', site_id=1), 'en') self.assertEqual(i18n.get_current_language(), 'en') def test_get_languages_default_site(self): result = i18n.get_languages(1) self.assertEqual(2, len(result)) lang = result[0] self.assertEqual(lang['code'], 'en') self.assertEqual(i18n.get_language_code(lang['code'], site_id=1), 'en') self.assertEqual(lang['public'], True) lang = result[1] self.assertEqual(lang['code'], 'fr') self.assertEqual(i18n.get_language_code(lang['code'], site_id=1), 'fr') self.assertEqual(lang['public'], False) def test_get_languages_defined_site(self): result = i18n.get_languages(1) self.assertEqual(2, len(result)) lang = result[0] self.assertEqual(lang['code'], 'en') self.assertEqual(i18n.get_language_code(lang['code'], 1), 'en') self.assertEqual(lang['public'], True) lang = result[1] self.assertEqual(lang['code'], 'fr') self.assertEqual(i18n.get_language_code(lang['code'], 1), 'fr') self.assertEqual(lang['public'], False) def test_get_languages_undefined_site(self): result = i18n.get_languages(66) self.assertEqual(4, len(result)) self.assertEqual(result[0]['code'], 'fr') self.assertEqual(i18n.get_language_code(result[0]['code'], site_id=66), 'fr') self.assertEqual(result[1]['code'], 'en') self.assertEqual(i18n.get_language_code(result[1]['code'], site_id=66), 'en') self.assertEqual(result[2]['code'], 'de') self.assertEqual(i18n.get_language_code(result[2]['code'], site_id=66), 'de') self.assertEqual(result[3]['code'], 'es') self.assertEqual(i18n.get_language_code(result[3]['code'], site_id=66), 'es') for lang in result: self.assertEqual(lang['public'], True) self.assertEqual(lang['hide_untranslated'], False) @override_settings( LANGUAGE_CODE='en', LANGUAGES=(('fr', 'French'), ('en', 'English'), ('de', 'German'), ('es', 'Spanish')), CMS_LANGUAGES={ 1: [ {'code': 'en', 'name': 'English', 'public': True}, {'code': 'fr', 'name': 'French', 'public': False}, ], }, SITE_ID=1, ) class TestLanguagesNoDefault(CMSTestCase): def x_test_get_languages_default_site(self): result = i18n.get_languages() self.assertEqual(2, len(result)) lang = result[0] self.assertEqual(lang['code'], 'en') self.assertEqual(i18n.get_language_code(lang['code']), 'en') self.assertEqual(lang['public'], True) lang = result[1] self.assertEqual(lang['code'], 'fr') self.assertEqual(i18n.get_language_code(lang['code']), 'fr') self.assertEqual(lang['public'], False) def test_get_languages_defined_site(self): result = i18n.get_languages(1) self.assertEqual(2, len(result)) lang = result[0] self.assertEqual(lang['code'], 'en') self.assertEqual(i18n.get_language_code(lang['code'], site_id=1), 'en') self.assertEqual(lang['public'], True) lang = result[1] self.assertEqual(lang['code'], 'fr') self.assertEqual(i18n.get_language_code(lang['code'], site_id=1), 'fr') self.assertEqual(lang['public'], False) def test_get_languages_undefined_site(self): result = i18n.get_languages(66) self.assertEqual(4, len(result)) self.assertEqual(result[0]['code'], 'fr') self.assertEqual(i18n.get_language_code(result[0]['code'], site_id=66), 'fr') self.assertEqual(result[1]['code'], 'en') self.assertEqual(i18n.get_language_code(result[1]['code'], site_id=66), 'en') self.assertEqual(result[2]['code'], 'de') self.assertEqual(i18n.get_language_code(result[2]['code'], site_id=66), 'de') self.assertEqual(result[3]['code'], 'es') self.assertEqual(i18n.get_language_code(result[3]['code'], site_id=66), 'es') for lang in result: self.assertEqual(lang['public'], True) self.assertEqual(lang['hide_untranslated'], True) @override_settings( LANGUAGE_CODE='en-us', LANGUAGES=(('fr-ca', 'French (Canada)'), ('en-us', 'English (US)'), ('en-gb', 'English (UK)'), ('de', 'German'), ('es', 'Spanish')), CMS_LANGUAGES={ 1: [ {'code': 'en-us', 'name': 'English (US)', 'public': True}, {'code': 'fr-ca', 'name': 'French (Canada)', 'public': False}, ], 'default': { 'public': True, 'hide_untranslated': False, }, }, SITE_ID=1, ) class TestLanguageCodesEnUS(CMSTestCase): def test_language_code(self): self.assertEqual(i18n.get_language_code('en-us', 1), 'en-us') self.assertEqual(i18n.get_current_language(), 'en-us') def test_get_languages_default_site(self): result = i18n.get_languages(1) self.assertEqual(2, len(result)) lang = result[0] self.assertEqual(lang['code'], 'en-us') self.assertEqual(i18n.get_language_code(lang['code'], site_id=1), 'en-us') self.assertEqual(lang['public'], True) lang = result[1] self.assertEqual(lang['code'], 'fr-ca') self.assertEqual(i18n.get_language_code(lang['code'], site_id=1), 'fr-ca') self.assertEqual(lang['public'], False) def test_get_languages_defined_site(self): result = i18n.get_languages(1) self.assertEqual(2, len(result)) lang = result[0] self.assertEqual(lang['code'], 'en-us') self.assertEqual(i18n.get_language_code(lang['code'], site_id=1), 'en-us') self.assertEqual(lang['public'], True) lang = result[1] self.assertEqual(lang['code'], 'fr-ca') self.assertEqual(i18n.get_language_code(lang['code'], site_id=1), 'fr-ca') self.assertEqual(lang['public'], False) def test_get_languages_undefined_site(self): result = i18n.get_languages(66) self.assertEqual(5, len(result)) self.assertEqual(result[0]['code'], 'fr-ca') self.assertEqual(i18n.get_language_code(result[0]['code'], site_id=66), 'fr-ca') self.assertEqual(result[1]['code'], 'en-us') self.assertEqual(i18n.get_language_code(result[1]['code'], site_id=66), 'en-us') self.assertEqual(result[2]['code'], 'en-gb') self.assertEqual(i18n.get_language_code(result[2]['code'], site_id=66), 'en-gb') self.assertEqual(result[3]['code'], 'de') self.assertEqual(i18n.get_language_code(result[3]['code'], site_id=66), 'de') self.assertEqual(result[4]['code'], 'es') self.assertEqual(i18n.get_language_code(result[4]['code'], site_id=66), 'es') for lang in result: self.assertEqual(lang['public'], True) self.assertEqual(lang['hide_untranslated'], False) @override_settings( LANGUAGE_CODE='en-gb', LANGUAGES=(('fr-ca', 'French (Canada)'), ('en-us', 'English (US)'), ('en-gb', 'English (UK)'), ('de', 'German'), ('es', 'Spanish')), CMS_LANGUAGES={ 1: [ {'code': 'en-gb', 'name': 'English (UK)', 'public': True}, {'code': 'fr-ca', 'name': 'French (Canada)', 'public': False}, ], 'default': { 'public': True, 'hide_untranslated': False, }, }, SITE_ID=1, ) class TestLanguageCodesEnGB(CMSTestCase): def test_language_code(self): self.assertEqual(i18n.get_language_code('en-gb', site_id=1), 'en-gb') self.assertEqual(i18n.get_current_language(), 'en-gb') def test_get_languages_default_site(self): result = i18n.get_languages(site_id=1) self.assertEqual(2, len(result)) lang = result[0] self.assertEqual(lang['code'], 'en-gb') self.assertEqual(i18n.get_language_code(lang['code'], site_id=1), 'en-gb') self.assertEqual(lang['public'], True) lang = result[1] self.assertEqual(lang['code'], 'fr-ca') self.assertEqual(i18n.get_language_code(lang['code'], site_id=1), 'fr-ca') self.assertEqual(lang['public'], False) def test_get_languages_defined_site(self): result = i18n.get_languages(1) self.assertEqual(2, len(result)) lang = result[0] self.assertEqual(lang['code'], 'en-gb') self.assertEqual(i18n.get_language_code(lang['code'], site_id=1), 'en-gb') self.assertEqual(lang['public'], True) lang = result[1] self.assertEqual(lang['code'], 'fr-ca') self.assertEqual(i18n.get_language_code(lang['code'], site_id=1), 'fr-ca') self.assertEqual(lang['public'], False) def test_get_languages_undefined_site(self): result = i18n.get_languages(66) self.assertEqual(5, len(result)) self.assertEqual(result[0]['code'], 'fr-ca') self.assertEqual(i18n.get_language_code(result[0]['code'], site_id=66), 'fr-ca') self.assertEqual(result[1]['code'], 'en-us') self.assertEqual(i18n.get_language_code(result[1]['code'], site_id=66), 'en-us') self.assertEqual(result[2]['code'], 'en-gb') self.assertEqual(i18n.get_language_code(result[2]['code'], site_id=66), 'en-gb') self.assertEqual(result[3]['code'], 'de') self.assertEqual(i18n.get_language_code(result[3]['code'], site_id=66), 'de') self.assertEqual(result[4]['code'], 'es') self.assertEqual(i18n.get_language_code(result[4]['code'], site_id=66), 'es') for lang in result: self.assertEqual(lang['public'], True) self.assertEqual(lang['hide_untranslated'], False) @override_settings( LANGUAGE_CODE='en', LANGUAGES=(('fr', 'French'), ('en', 'English'), ('de', 'German'), ('es', 'Spanish')), CMS_LANGUAGES={ 1: [ {'code': 'en', 'name': 'English', 'public': True}, {'code': 'fr', 'name': 'French', 'public': False}, ], 2: [ {'code': 'de', 'name': 'German', 'public': True}, {'code': 'es', 'name': 'Spanish', 'public': True}, ], 'default': { 'public': True, 'hide_untranslated': False, }, }, ) class TestGetCurrentLanguageWithoutSiteID(CMSTestCase): """ Test get_current_language when SITE_ID is not set in settings. """ @override_settings(SITE_ID=None) def test_get_current_language_returns_valid_language_code(self): """Test that get_current_language returns a valid language from settings.LANGUAGES.""" with translation.override('en'): result = i18n.get_current_language() self.assertEqual(result, 'en') @override_settings(SITE_ID=None) def test_get_current_language_with_base_language_match(self): """Test that get_current_language matches base language when exact match not found.""" with translation.override('en-us'): result = i18n.get_current_language() # Should match 'en' from settings.LANGUAGES self.assertEqual(result, 'en') @override_settings(SITE_ID=None) def test_get_current_language_with_no_match_returns_active_language(self): """Test that get_current_language returns active language when no match found.""" with translation.override('it'): # Italian not in settings.LANGUAGES result = i18n.get_current_language() self.assertEqual(result, 'it') @override_settings(SITE_ID=None) def test_get_current_language_with_region_variant(self): """Test that get_current_language handles region variants correctly.""" with translation.override('de-at'): # Austrian German result = i18n.get_current_language() # Should match 'de' from settings.LANGUAGES self.assertEqual(result, 'de') @override_settings( LANGUAGE_CODE='en', LANGUAGES=[ ('en', 'English'), ('de', 'German'), ('fr', 'French') ], CMS_LANGUAGES={ 1: [{'code': 'de', 'name': 'German', 'public': True}, {'code': 'fr', 'name': 'French', 'public': True}], 'default': { 'fallbacks': ['de', 'fr'], }, }, SITE_ID=1, ) class TestLanguagesNotInCMSLanguages(CMSTestCase): def test_get_fallback_languages(self): languages = i18n.get_fallback_languages('en', 1) self.assertEqual(languages, ['de', 'fr']) @override_settings( LANGUAGE_CODE='en', LANGUAGES=(('fr', 'French'), ('en', 'English'), ('de', 'German'), ('es', 'Spanish')), CMS_LANGUAGES={ 1: [ {'code': 'en', 'name': 'English', 'public': False}, {'code': 'fr', 'name': 'French', 'public': True}, ], 'default': { 'fallbacks': ['en', 'fr'], 'redirect_on_fallback': False, 'public': True, 'hide_untranslated': False, } }, SITE_ID=1, ) class TestLanguageFallbacks(CMSTestCase): def test_language_code(self): ''' No redirect_on_fallback will return 200 with the fallback content ''' self.create_homepage("home", "nav_playground.html", "fr") response = self.client.get('/') # no language code will cause a redirect. self.assertEqual(response.status_code, 302) response = self.client.get('/en/') self.assertEqual(response.status_code, 200) response = self.client.get('/fr/') self.assertEqual(response.status_code, 200) @override_settings( CMS_LANGUAGES={ 1: [ {'code': 'en', 'name': 'English', 'public': True}, {'code': 'fr', 'name': 'French', 'public': True}, ] }, ) def test_session_language(self): page = self.create_homepage("home", "nav_playground.html", "en") api.create_page_content('fr', "home", page) response = self.client.get('/') self.assertEqual(response.status_code, 302) self.assertRedirects(response, '/en/') engine = import_module(settings.SESSION_ENGINE) store = engine.SessionStore() store.save() # we need to make load() work, or the cookie is worthless self.client.cookies[settings.SESSION_COOKIE_NAME] = store.session_key # ugly and long set of session self.client.cookies[settings.LANGUAGE_COOKIE_NAME] = 'fr' response = self.client.get('/') self.assertEqual(response.status_code, 302) self.assertRedirects(response, '/fr/') self.client.get('/en/') self.assertEqual(self.client.cookies[settings.LANGUAGE_COOKIE_NAME].value, 'en') response = self.client.get('/') self.assertEqual(response.status_code, 302) self.assertRedirects(response, '/en/') @override_settings( CMS_LANGUAGES={ 'default': { 'fallbacks': ['en', 'fr'], 'public': True, # no 404s 'redirect_on_fallback': False } }, ) def test_no_redirect_on_fallback(self): homepage = self.create_homepage( "home", "nav_playground.html", "fr" ) page_data = self.get_new_page_data_dbfields( language="fr" ) page = api.create_page(**page_data) response = self.client.get(page.get_absolute_url(language="en")) self.assertEqual(response.status_code, 200) # homepage should be the same response = self.client.get(homepage.get_absolute_url(language="en")) self.assertEqual(response.status_code, 200) @override_settings( CMS_LANGUAGES={ 'default': { 'fallbacks': ['en', 'fr'], 'public': True, # no 404s 'redirect_on_fallback': False } }, ) def test_no_redirect_on_fallback_content(self): """ Test that the fallback content will be displayed """ homepage = self.create_homepage( "home", "nav_playground.html", "fr" ) homepage_ph_fr = homepage.get_placeholders( "fr").get(slot="body") api.add_plugin( homepage_ph_fr, plugin_type="TextPlugin", language="fr", body="Hello, world!", ) page_data = self.get_new_page_data_dbfields( language="fr" ) page = api.create_page(**page_data) page_ph_fr = page.get_placeholders( "fr").get(slot="body") api.add_plugin( page_ph_fr, plugin_type="TextPlugin", language="fr", body="Hello, world!", ) with patch("cms.views.render_pagecontent") as mock_render: # normal page path = page.get_absolute_url(language="en") request = self.get_request(path, "en") details(request, slug=page.get_path("en")) mock_render.assert_called_once_with( request, page.get_content_obj() ) context = Context({'request': self.get_request(path, "fr")}) rendered_placeholder = self._render_placeholder(page_ph_fr, context) self.assertEqual(rendered_placeholder, "Hello, world!") with patch("cms.views.render_pagecontent") as mock_render: # homepage should be the same path = homepage.get_absolute_url(language="en") request = self.get_request(path) details(request, slug=homepage.get_path("en")) mock_render.assert_called_once_with( request, homepage.get_content_obj() ) # check that the french plugins will render context = Context({'request': request}) rendered_placeholder = self._render_placeholder(homepage_ph_fr, context) self.assertEqual(rendered_placeholder, "Hello, world!") @override_settings( LANGUAGES=( ('en', 'English'), ('de', 'Deutsch'), ('it', 'Italian'), ('fr', 'French'), ), CMS_LANGUAGES={ 'default': { 'fallbacks': ['en', 'fr'], 'public': True, 'redirect_on_fallback': False, 'hide_untranslated': False, }, }, ) def test_no_redirect_on_fallback_placeholder_rendering(self): """ When redirect_on_fallback is False and hide_untranslated is False, requesting a page in a language that has no content should render the placeholder content from the fallback language, not empty placeholders. Regression test for issue #8556. """ homepage = self.create_homepage( "home", "nav_playground.html", "en", ) homepage_ph = homepage.get_placeholders("en").get(slot="body") api.add_plugin( homepage_ph, plugin_type="TextPlugin", language="en", body="English content", ) # Request the page in Italian which has no translation; # the fallback chain is ['en', 'fr'] so English content should appear. response = self.client.get(homepage.get_absolute_url(language="it")) self.assertEqual(response.status_code, 200) self.assertContains(response, "English content") @override_settings( CMS_LANGUAGES={ 'default': { 'fallbacks': ['en', 'fr'], 'redirect_on_fallback': True, } } ) def test_redirect_on_fallback(self): page_data = self.get_new_page_data_dbfields( language="fr" ) page = api.create_page(**page_data) response_fr = self.client.get(page.get_absolute_url(language="fr")) self.assertEqual(response_fr.status_code, 200) response_en = self.client.get(page.get_absolute_url(language="en")) self.assertRedirects(response_en, page.get_absolute_url(language="fr")) # homepage should be no different. home_page_data = self.get_new_page_data_dbfields( language="fr", ) self.create_homepage(**home_page_data) response_fr = self.client.get(page.get_absolute_url(language="fr")) self.assertEqual(response_fr.status_code, 200) response_en = self.client.get(page.get_absolute_url(language="en")) self.assertRedirects(response_en, page.get_absolute_url(language="fr")) @override_settings( LANGUAGE_CODE='en', LANGUAGES=(('fr', 'French'), ('en', 'English'), ('de', 'German'), ('es', 'Spanish')), CMS_LANGUAGES={ 1: [ {'code': 'en', 'name': 'English', 'public': False}, {'code': 'fr', 'name': 'French', 'public': True}, ], 'default': { 'fallbacks': ['en', 'fr'], 'redirect_on_fallback': False, 'public': True, 'hide_untranslated': False, } }, SITE_ID=1, ) class TestGetLanguageFromRequest(CMSTestCase): def test_get_language_from_request_does_not_return_empty_string_from_post(self): request = self.get_request(language='en', post_data={ 'language': '', }) self.assertEqual(get_language_from_request(request), 'en') def test_get_language_from_request_does_not_return_empty_string_from_get(self): request = self.get_request('/en/?language=', language='en') self.assertEqual(get_language_from_request(request), 'en') @override_settings( LANGUAGE_CODE='es', LANGUAGES=(('fr', 'French'), ('en', 'English'), ('de', 'German'), ('es', 'Spanish')), SITE_ID=None, ) class TestLanguagesWithoutSiteID(CMSTestCase): """ Test language handling when SITE_ID is not set in settings. """ def test_get_languages_without_site_id_uses_all_languages(self): """Test that get_languages uses all LANGUAGES when SITE_ID is not set.""" result = i18n.get_languages(site_id=1) self.assertEqual(result, [{'code': 'fr', 'name': 'French'}, {'code': 'en', 'name': 'English'}, {'code': 'de', 'name': 'German'}, {'code': 'es', 'name': 'Spanish'}]) @override_settings(USE_I18N=False) def test_get_language_code_without_site_id_and_use_i18n_false_returns_site_language(self): """Verify that when SITE_ID is not set and USE_I18N is False, i18n.get_languages() returns only the current site's default language (a single-item list) rather than using the full global LANGUAGES setting.""" """Test that get_languages uses all LANGUAGES when SITE_ID is not set.""" result = i18n.get_languages(site_id=1) self.assertEqual(result, [{'code': 'es', 'name': 'es'}])