test the django views i18n setlanguage view def getinactivelanguagecodeself the setlanguage view can be used to change the session language the user is redirected to the next argument if provided the language is set in a cookie the setlanguage view only redirects to the next argument if it is safe the setlanguage view only redirects to the next argument if it is safe and its scheme is https if the request was sent over https insecure url in post data insecure url in http referer the setlanguage view redirects to the url in the referer header when there isn t a next parameter the setlanguage view redirects to when there isn t a referer or next parameter the setlanguage view redirects to the next parameter for requests not accepting html response content the setlanguage view doesn t redirect to the http referer header if the request doesn t accept html response content the setlanguage view returns 204 by default for requests not accepting html response content the fallback to root url for the setlanguage view works for requests not accepting html response content we force saving language to a cookie rather than a session by excluding session middleware and those which do require it the setlanguage view decodes the httpreferer url and preserves an encoded query string the url view must exist for this to work as a regression test c3a4 decodes to 26 to and reverse test django views i18n views other than setlanguage overridesettingslanguagecodede def testgetformatsself formats getformats test 3 possible types in getformats integer string and list self assertequalformatsfirstdayofweek 1 self assertequalformatsdecimalseparator self assertequal formatstimeinputformats h m s h m s f h m def testjsi18nself response content must include a line like this is to be translated value of transtxt python variable json dumps is used to be able to check unicode strings message with context msgctxt default plural function the jsoncatalog returns the language catalog and settings as json the javascriptcatalog shouldn t load the fallback language in the case that the current selected language is actually the one translated from and hence missing translation files completely this happens easily when you re translating from english to other languages and you ve set settings languagecode to some other language than english same as above for the jsoncatalog view here we also check for the expected json format let s make sure that the fallback language is still working properly in cases where the selected language cannot be found the fallback language works when there are several levels of fallback translation catalogs the fallback to a language with less plural forms maintains the real language s number of plural forms and correct translations check if the javascript i18n view returns an empty language catalog if the default language is nonenglish the selected language is english and there is not en translation available see 13388 3594 and 13726 for more details same as above with the difference that there is an en translation available the javascript i18n view must return a non empty language catalog with the proper english translations see 13726 for more details makes sure that the fallback language is still working properly in cases where the selected language cannot be found force a language via get otherwise the gettext functions are a noop nonbmp characters should not break the javascriptcatalog 21725 check if the javascript i18n view returns a complete language catalog if the default language is enus the selected language has a translation available and a catalog composed by djangojs domain translations of multiple python packages is requested see 13388 3594 and 13514 for more details similar to above but with neither default or requested language being english the template is loaded directly not via a template loader and should be opened as utf8 charset as is the default specified on template engines the test cases use fixtures translations from these apps test the django views i18n set_language view return language code for a language which is not activated the set_language view can be used to change the session language the user is redirected to the next argument if provided the language is set in a cookie the set_language view only redirects to the next argument if it is safe the set_language view only redirects to the next argument if it is safe and its scheme is https if the request was sent over https insecure url in post data insecure url in http referer the set_language view redirects to the url in the referer header when there isn t a next parameter the set_language view redirects to when there isn t a referer or next parameter the set_language view redirects to the next parameter for requests not accepting html response content the set_language view doesn t redirect to the http referer header if the request doesn t accept html response content the set_language view returns 204 by default for requests not accepting html response content the fallback to root url for the set_language view works for requests not accepting html response content we force saving language to a cookie rather than a session by excluding session middleware and those which do require it the set_language view decodes the http_referer url and preserves an encoded query string the url view must exist for this to work as a regression test c3 a4 decodes to ä 26 to and reverse test django views i18n views other than set_language test 3 possible types in get_formats integer string and list the javascript_catalog can be deployed with language settings response content must include a line like this is to be translated value of trans_txt python variable json dumps is used to be able to check unicode strings message with context msgctxt default plural function the json_catalog returns the language catalog and settings as json the javascript_catalog shouldn t load the fallback language in the case that the current selected language is actually the one translated from and hence missing translation files completely this happens easily when you re translating from english to other languages and you ve set settings language_code to some other language than english same as above for the json_catalog view here we also check for the expected json format let s make sure that the fallback language is still working properly in cases where the selected language cannot be found the fallback language works when there are several levels of fallback translation catalogs the fallback to a language with less plural forms maintains the real language s number of plural forms and correct translations check if the javascript i18n view returns an empty language catalog if the default language is non english the selected language is english and there is not en translation available see 13388 3594 and 13726 for more details same as above with the difference that there is an en translation available the javascript i18n view must return a non empty language catalog with the proper english translations see 13726 for more details makes sure that the fallback language is still working properly in cases where the selected language cannot be found force a language via get otherwise the gettext functions are a noop non bmp characters should not break the javascript_catalog 21725 check if the javascript i18n view returns a complete language catalog if the default language is en us the selected language has a translation available and a catalog composed by djangojs domain translations of multiple python packages is requested see 13388 3594 and 13514 for more details similar to above but with neither default or requested language being english the template is loaded directly not via a template loader and should be opened as utf 8 charset as is the default specified on template engines the test cases use fixtures translations from these apps
import gettext
import json
from os import path
from unittest import mock

from django.conf import settings
from django.test import (
    RequestFactory,
    SimpleTestCase,
    TestCase,
    modify_settings,
    override_settings,
)
from django.test.selenium import SeleniumTestCase
from django.urls import reverse
from django.utils.translation import get_language, override
from django.views.i18n import JavaScriptCatalog, get_formats

from ..urls import locale_dir


@override_settings(ROOT_URLCONF="view_tests.urls")
class SetLanguageTests(TestCase):
    

    def _get_inactive_language_code(self):
        
        current_language = get_language()
        return [code for code, name in settings.LANGUAGES if code != current_language][
            0
        ]

    def test_setlang(self):
        
        lang_code = self._get_inactive_language_code()
        post_data = {"language": lang_code, "next": "/"}
        response = self.client.post(
            "/i18n/setlang/", post_data, headers={"referer": "/i_should_not_be_used/"}
        )
        self.assertRedirects(response, "/")
        
        language_cookie = self.client.cookies[settings.LANGUAGE_COOKIE_NAME]
        self.assertEqual(language_cookie.value, lang_code)
        self.assertEqual(language_cookie["domain"], "")
        self.assertEqual(language_cookie["path"], "/")
        self.assertEqual(language_cookie["max-age"], "")
        self.assertEqual(language_cookie["httponly"], "")
        self.assertEqual(language_cookie["samesite"], "")
        self.assertEqual(language_cookie["secure"], "")

    def test_setlang_unsafe_next(self):
        
        lang_code = self._get_inactive_language_code()
        post_data = {"language": lang_code, "next": "//unsafe/redirection/"}
        response = self.client.post("/i18n/setlang/", data=post_data)
        self.assertEqual(response.url, "/")
        self.assertEqual(
            self.client.cookies[settings.LANGUAGE_COOKIE_NAME].value, lang_code
        )

    def test_setlang_http_next(self):
        
        lang_code = self._get_inactive_language_code()
        non_https_next_url = "http://testserver/redirection/"
        post_data = {"language": lang_code, "next": non_https_next_url}
        
        response = self.client.post("/i18n/setlang/", data=post_data, secure=True)
        self.assertEqual(response.url, "/")
        self.assertEqual(
            self.client.cookies[settings.LANGUAGE_COOKIE_NAME].value, lang_code
        )
        
        response = self.client.post(
            "/i18n/setlang/", secure=True, headers={"referer": non_https_next_url}
        )
        self.assertEqual(response.url, "/")
        self.assertEqual(
            self.client.cookies[settings.LANGUAGE_COOKIE_NAME].value, lang_code
        )

    def test_setlang_redirect_to_referer(self):
        
        lang_code = self._get_inactive_language_code()
        post_data = {"language": lang_code}
        response = self.client.post(
            "/i18n/setlang/", post_data, headers={"referer": "/i18n/"}
        )
        self.assertRedirects(response, "/i18n/", fetch_redirect_response=False)
        self.assertEqual(
            self.client.cookies[settings.LANGUAGE_COOKIE_NAME].value, lang_code
        )

    def test_setlang_default_redirect(self):
        
        lang_code = self._get_inactive_language_code()
        post_data = {"language": lang_code}
        response = self.client.post("/i18n/setlang/", post_data)
        self.assertRedirects(response, "/")
        self.assertEqual(
            self.client.cookies[settings.LANGUAGE_COOKIE_NAME].value, lang_code
        )

    def test_setlang_performs_redirect_for_ajax_if_explicitly_requested(self):
        
        lang_code = self._get_inactive_language_code()
        post_data = {"language": lang_code, "next": "/"}
        response = self.client.post(
            "/i18n/setlang/", post_data, headers={"accept": "application/json"}
        )
        self.assertRedirects(response, "/")
        self.assertEqual(
            self.client.cookies[settings.LANGUAGE_COOKIE_NAME].value, lang_code
        )

    def test_setlang_doesnt_perform_a_redirect_to_referer_for_ajax(self):
        
        lang_code = self._get_inactive_language_code()
        post_data = {"language": lang_code}
        headers = {"HTTP_REFERER": "/", "HTTP_ACCEPT": "application/json"}
        response = self.client.post("/i18n/setlang/", post_data, **headers)
        self.assertEqual(response.status_code, 204)
        self.assertEqual(
            self.client.cookies[settings.LANGUAGE_COOKIE_NAME].value, lang_code
        )

    def test_setlang_doesnt_perform_a_default_redirect_for_ajax(self):
        
        lang_code = self._get_inactive_language_code()
        post_data = {"language": lang_code}
        response = self.client.post(
            "/i18n/setlang/", post_data, headers={"accept": "application/json"}
        )
        self.assertEqual(response.status_code, 204)
        self.assertEqual(
            self.client.cookies[settings.LANGUAGE_COOKIE_NAME].value, lang_code
        )

    def test_setlang_unsafe_next_for_ajax(self):
        
        lang_code = self._get_inactive_language_code()
        post_data = {"language": lang_code, "next": "//unsafe/redirection/"}
        response = self.client.post(
            "/i18n/setlang/", post_data, headers={"accept": "application/json"}
        )
        self.assertEqual(response.url, "/")
        self.assertEqual(
            self.client.cookies[settings.LANGUAGE_COOKIE_NAME].value, lang_code
        )

    def test_setlang_reversal(self):
        self.assertEqual(reverse("set_language"), "/i18n/setlang/")

    def test_setlang_cookie(self):
        
        
        test_settings = {
            "MIDDLEWARE": ["django.middleware.common.CommonMiddleware"],
            "LANGUAGE_COOKIE_NAME": "mylanguage",
            "LANGUAGE_COOKIE_AGE": 3600 * 7 * 2,
            "LANGUAGE_COOKIE_DOMAIN": ".example.com",
            "LANGUAGE_COOKIE_PATH": "/test/",
            "LANGUAGE_COOKIE_HTTPONLY": True,
            "LANGUAGE_COOKIE_SAMESITE": "Strict",
            "LANGUAGE_COOKIE_SECURE": True,
        }
        with self.settings(**test_settings):
            post_data = {"language": "pl", "next": "/views/"}
            response = self.client.post("/i18n/setlang/", data=post_data)
            language_cookie = response.cookies.get("mylanguage")
            self.assertEqual(language_cookie.value, "pl")
            self.assertEqual(language_cookie["domain"], ".example.com")
            self.assertEqual(language_cookie["path"], "/test/")
            self.assertEqual(language_cookie["max-age"], 3600 * 7 * 2)
            self.assertIs(language_cookie["httponly"], True)
            self.assertEqual(language_cookie["samesite"], "Strict")
            self.assertIs(language_cookie["secure"], True)

    def test_setlang_decodes_http_referer_url(self):
        
        
        self.assertEqual(
            reverse("with_parameter", kwargs={"parameter": "x"}), "/test-setlang/x/"
        )
        lang_code = self._get_inactive_language_code()
        
        encoded_url = "/test-setlang/%C3%A4/?foo=bar&baz=alpha%26omega"
        response = self.client.post(
            "/i18n/setlang/", {"language": lang_code}, headers={"referer": encoded_url}
        )
        self.assertRedirects(response, encoded_url, fetch_redirect_response=False)
        self.assertEqual(
            self.client.cookies[settings.LANGUAGE_COOKIE_NAME].value, lang_code
        )

    @modify_settings(
        MIDDLEWARE={
            "append": "django.middleware.locale.LocaleMiddleware",
        }
    )
    def test_lang_from_translated_i18n_pattern(self):
        response = self.client.post(
            "/i18n/setlang/",
            data={"language": "nl"},
            follow=True,
            headers={"referer": "/en/translated/"},
        )
        self.assertEqual(self.client.cookies[settings.LANGUAGE_COOKIE_NAME].value, "nl")
        self.assertRedirects(response, "/nl/vertaald/")
        
        response = self.client.post(
            "/i18n/setlang/",
            data={"language": "en"},
            follow=True,
            headers={"referer": "/nl/vertaald/"},
        )
        self.assertRedirects(response, "/en/translated/")


@override_settings(ROOT_URLCONF="view_tests.urls")
class I18NViewTests(SimpleTestCase):
    

    @override_settings(LANGUAGE_CODE="de")
    def test_get_formats(self):
        formats = get_formats()
        
        self.assertEqual(formats["FIRST_DAY_OF_WEEK"], 1)
        self.assertEqual(formats["DECIMAL_SEPARATOR"], ",")
        self.assertEqual(
            formats["TIME_INPUT_FORMATS"], ["%H:%M:%S", "%H:%M:%S.%f", "%H:%M"]
        )

    def test_jsi18n(self):
        
        for lang_code in ["es", "fr", "ru"]:
            with override(lang_code):
                catalog = gettext.translation("djangojs", locale_dir, [lang_code])
                trans_txt = catalog.gettext("this is to be translated")
                response = self.client.get("/jsi18n/")
                self.assertEqual(
                    response.headers["Content-Type"], 'text/javascript; charset="utf-8"'
                )
                
                
                
                self.assertContains(response, json.dumps(trans_txt), 1)
                if lang_code == "fr":
                    
                    self.assertContains(response, '"month name\\u0004May": "mai"', 1)

    @override_settings(USE_I18N=False)
    def test_jsi18n_USE_I18N_False(self):
        response = self.client.get("/jsi18n/")
        
        self.assertContains(
            response,
            "django.pluralidx = function(count) { return (count == 1) ? 0 : 1; };",
        )
        self.assertNotContains(response, "var newcatalog =")

    def test_jsoni18n(self):
        
        with override("de"):
            response = self.client.get("/jsoni18n/")
            data = json.loads(response.content.decode())
            self.assertIn("catalog", data)
            self.assertIn("formats", data)
            self.assertEqual(
                data["formats"]["TIME_INPUT_FORMATS"],
                ["%H:%M:%S", "%H:%M:%S.%f", "%H:%M"],
            )
            self.assertEqual(data["formats"]["FIRST_DAY_OF_WEEK"], 1)
            self.assertIn("plural", data)
            self.assertEqual(data["catalog"]["month name\x04May"], "Mai")
            self.assertIn("DATETIME_FORMAT", data["formats"])
            self.assertEqual(data["plural"], "(n != 1)")

    def test_jsi18n_with_missing_en_files(self):
        
        with self.settings(LANGUAGE_CODE="es"), override("en-us"):
            response = self.client.get("/jsi18n/")
            self.assertNotContains(response, "esto tiene que ser traducido")

    def test_jsoni18n_with_missing_en_files(self):
        
        with self.settings(LANGUAGE_CODE="es"), override("en-us"):
            response = self.client.get("/jsoni18n/")
            data = json.loads(response.content.decode())
            self.assertIn("catalog", data)
            self.assertIn("formats", data)
            self.assertIn("plural", data)
            self.assertEqual(data["catalog"], {})
            self.assertIn("DATETIME_FORMAT", data["formats"])
            self.assertIsNone(data["plural"])

    def test_jsi18n_fallback_language(self):
        
        with self.settings(LANGUAGE_CODE="fr"), override("fi"):
            response = self.client.get("/jsi18n/")
            self.assertContains(response, "il faut le traduire")
            self.assertNotContains(response, "Untranslated string")

    def test_jsi18n_fallback_language_with_custom_locale_dir(self):
        
        locale_paths = [
            path.join(
                path.dirname(path.dirname(path.abspath(__file__))),
                "custom_locale_path",
            ),
        ]
        with self.settings(LOCALE_PATHS=locale_paths), override("es_MX"):
            response = self.client.get("/jsi18n/")
            self.assertContains(
                response, "custom_locale_path: esto tiene que ser traducido"
            )
            response = self.client.get("/jsi18n_no_packages/")
            self.assertContains(
                response, "custom_locale_path: esto tiene que ser traducido"
            )

    def test_i18n_fallback_language_plural(self):
        
        with self.settings(LANGUAGE_CODE="pt"), override("ru"):
            response = self.client.get("/jsi18n/")
            self.assertEqual(
                response.context["catalog"]["{count} plural3"],
                ["{count} plural3 p3", "{count} plural3 p3s", "{count} plural3 p3t"],
            )
            self.assertEqual(
                response.context["catalog"]["{count} plural2"],
                ["{count} plural2", "{count} plural2s", ""],
            )
        with self.settings(LANGUAGE_CODE="ru"), override("pt"):
            response = self.client.get("/jsi18n/")
            self.assertEqual(
                response.context["catalog"]["{count} plural3"],
                ["{count} plural3", "{count} plural3s"],
            )
            self.assertEqual(
                response.context["catalog"]["{count} plural2"],
                ["{count} plural2", "{count} plural2s"],
            )

    def test_i18n_english_variant(self):
        with override("en-gb"):
            response = self.client.get("/jsi18n/")
            self.assertIn(
                '"this color is to be translated": "this colour is to be translated"',
                response.context["catalog_str"],
            )

    def test_i18n_language_non_english_default(self):
        
        with self.settings(LANGUAGE_CODE="fr"), override("en-us"):
            response = self.client.get("/jsi18n/")
            self.assertNotContains(response, "Choisir une heure")

    @modify_settings(INSTALLED_APPS={"append": "view_tests.app0"})
    def test_non_english_default_english_userpref(self):
        
        with self.settings(LANGUAGE_CODE="fr"), override("en-us"):
            response = self.client.get("/jsi18n_english_translation/")
            self.assertContains(response, "this app0 string is to be translated")

    def test_i18n_language_non_english_fallback(self):
        
        with self.settings(LANGUAGE_CODE="fr"), override("none"):
            response = self.client.get("/jsi18n/")
            self.assertContains(response, "Choisir une heure")

    def test_escaping(self):
        
        response = self.client.get("/jsi18n_admin/?language=de")
        self.assertContains(response, "\\x04")

    @modify_settings(INSTALLED_APPS={"append": ["view_tests.app5"]})
    def test_non_BMP_char(self):
        
        with self.settings(LANGUAGE_CODE="en-us"), override("fr"):
            response = self.client.get("/jsi18n/app5/")
            self.assertContains(response, "emoji")
            self.assertContains(response, "\\ud83d\\udca9")

    @modify_settings(INSTALLED_APPS={"append": ["view_tests.app1", "view_tests.app2"]})
    def test_i18n_language_english_default(self):
        
        base_trans_string = (
            "il faut traduire cette cha\\u00eene de caract\\u00e8res de "
        )
        app1_trans_string = base_trans_string + "app1"
        app2_trans_string = base_trans_string + "app2"
        with self.settings(LANGUAGE_CODE="en-us"), override("fr"):
            response = self.client.get("/jsi18n_multi_packages1/")
            self.assertContains(response, app1_trans_string)
            self.assertContains(response, app2_trans_string)

            response = self.client.get("/jsi18n/app1/")
            self.assertContains(response, app1_trans_string)
            self.assertNotContains(response, app2_trans_string)

            response = self.client.get("/jsi18n/app2/")
            self.assertNotContains(response, app1_trans_string)
            self.assertContains(response, app2_trans_string)

    @modify_settings(INSTALLED_APPS={"append": ["view_tests.app3", "view_tests.app4"]})
    def test_i18n_different_non_english_languages(self):
        
        with self.settings(LANGUAGE_CODE="fr"), override("es-ar"):
            response = self.client.get("/jsi18n_multi_packages2/")
            self.assertContains(response, "este texto de app3 debe ser traducido")

    def test_i18n_with_locale_paths(self):
        extended_locale_paths = settings.LOCALE_PATHS + [
            path.join(
                path.dirname(path.dirname(path.abspath(__file__))),
                "app3",
                "locale",
            ),
        ]
        with self.settings(LANGUAGE_CODE="es-ar", LOCALE_PATHS=extended_locale_paths):
            with override("es-ar"):
                response = self.client.get("/jsi18n/")
                self.assertContains(response, "este texto de app3 debe ser traducido")

    def test_i18n_unknown_package_error(self):
        view = JavaScriptCatalog.as_view()
        request = RequestFactory().get("/")
        msg = "Invalid package(s) provided to JavaScriptCatalog: unknown_package"
        with self.assertRaisesMessage(ValueError, msg):
            view(request, packages="unknown_package")
        msg += ",unknown_package2"
        with self.assertRaisesMessage(ValueError, msg):
            view(request, packages="unknown_package+unknown_package2")

    def test_template_encoding(self):
        
        from django.views.i18n import Path

        view = JavaScriptCatalog.as_view()
        request = RequestFactory().get("/")
        with mock.patch.object(Path, "open") as m:
            view(request)
            m.assert_called_once_with(encoding="utf-8")


@override_settings(ROOT_URLCONF="view_tests.urls")
class I18nSeleniumTests(SeleniumTestCase):
    
    available_apps = [
        "django.contrib.admin",
        "django.contrib.auth",
        "django.contrib.contenttypes",
        "view_tests",
    ]

    @override_settings(LANGUAGE_CODE="de")
    def test_javascript_gettext(self):
        from selenium.webdriver.common.by import By

        self.selenium.get(self.live_server_url + "/jsi18n_template/")
        elem = self.selenium.find_element(By.ID, "gettext")
        self.assertEqual(elem.text, "Entfernen")
        elem = self.selenium.find_element(By.ID, "ngettext_sing")
        self.assertEqual(elem.text, "1 Element")
        elem = self.selenium.find_element(By.ID, "ngettext_plur")
        self.assertEqual(elem.text, "455 Elemente")
        elem = self.selenium.find_element(By.ID, "ngettext_onnonplural")
        self.assertEqual(elem.text, "Bild")
        elem = self.selenium.find_element(By.ID, "pgettext")
        self.assertEqual(elem.text, "Kann")
        elem = self.selenium.find_element(By.ID, "npgettext_sing")
        self.assertEqual(elem.text, "1 Resultat")
        elem = self.selenium.find_element(By.ID, "npgettext_plur")
        self.assertEqual(elem.text, "455 Resultate")
        elem = self.selenium.find_element(By.ID, "formats")
        self.assertEqual(
            elem.text,
            "DATE_INPUT_FORMATS is an object; DECIMAL_SEPARATOR is a string; "
            "FIRST_DAY_OF_WEEK is a number;",
        )

    @modify_settings(INSTALLED_APPS={"append": ["view_tests.app1", "view_tests.app2"]})
    @override_settings(LANGUAGE_CODE="fr")
    def test_multiple_catalogs(self):
        from selenium.webdriver.common.by import By

        self.selenium.get(self.live_server_url + "/jsi18n_multi_catalogs/")
        elem = self.selenium.find_element(By.ID, "app1string")
        self.assertEqual(
            elem.text, "il faut traduire cette chaîne de caractères de app1"
        )
        elem = self.selenium.find_element(By.ID, "app2string")
        self.assertEqual(
            elem.text, "il faut traduire cette chaîne de caractères de app2"
        )