# tests/unit/utils/test_i18n.py """ Unit tests for i18n utilities. Tests cover: - Language configuration - Translation loading - Translation lookup - Accept-Language parsing - Language resolution - Jinja2 integration """ import pytest from app.utils.i18n import ( DEFAULT_LANGUAGE, LANGUAGE_FLAGS, LANGUAGE_NAMES, LANGUAGE_NAMES_EN, SUPPORTED_LANGUAGES, TranslationContext, clear_translation_cache, create_translation_context, get_jinja2_globals, get_language_choices, get_language_info, get_locales_path, get_nested_value, is_rtl_language, load_translations, parse_accept_language, resolve_store_dashboard_language, resolve_storefront_language, t, translate, ) @pytest.mark.unit @pytest.mark.utils class TestLanguageConfiguration: """Test language configuration constants""" def test_supported_languages(self): """Test SUPPORTED_LANGUAGES contains expected languages""" assert "en" in SUPPORTED_LANGUAGES assert "fr" in SUPPORTED_LANGUAGES assert "de" in SUPPORTED_LANGUAGES assert "lb" in SUPPORTED_LANGUAGES def test_default_language_is_french(self): """Test default language is French (Luxembourg context)""" assert DEFAULT_LANGUAGE == "fr" def test_language_names_defined(self): """Test all languages have names defined""" for lang in SUPPORTED_LANGUAGES: assert lang in LANGUAGE_NAMES assert lang in LANGUAGE_NAMES_EN assert lang in LANGUAGE_FLAGS def test_get_locales_path_exists(self): """Test locales path is accessible""" path = get_locales_path() assert path is not None @pytest.mark.unit @pytest.mark.utils class TestTranslationLoading: """Test translation loading functionality""" def test_load_translations_english(self): """Test loading English translations""" clear_translation_cache() translations = load_translations("en") assert isinstance(translations, dict) def test_load_translations_french(self): """Test loading French translations""" clear_translation_cache() translations = load_translations("fr") assert isinstance(translations, dict) def test_load_translations_unsupported(self): """Test loading unsupported language falls back to default""" clear_translation_cache() translations = load_translations("xx") # Non-existent # Should fall back to French (default) assert isinstance(translations, dict) def test_clear_translation_cache(self): """Test clearing translation cache""" load_translations("en") clear_translation_cache() # Should not raise load_translations("en") @pytest.mark.unit @pytest.mark.utils class TestNestedValue: """Test get_nested_value function""" def test_get_nested_value_simple(self): """Test getting simple key""" data = {"key": "value"} result = get_nested_value(data, "key") assert result == "value" def test_get_nested_value_nested(self): """Test getting nested key""" data = {"level1": {"level2": {"level3": "value"}}} result = get_nested_value(data, "level1.level2.level3") assert result == "value" def test_get_nested_value_missing(self): """Test getting missing key returns key path""" data = {"key": "value"} result = get_nested_value(data, "missing.key") assert result == "missing.key" def test_get_nested_value_with_default(self): """Test getting missing key with default""" data = {"key": "value"} result = get_nested_value(data, "missing.key", "default") assert result == "default" def test_get_nested_value_non_string(self): """Test getting non-string value returns default""" data = {"key": {"nested": "obj"}} result = get_nested_value(data, "key", "default") assert result == "default" @pytest.mark.unit @pytest.mark.utils class TestTranslate: """Test translate function""" def test_translate_with_language(self): """Test translate with specified language""" result = translate("common.save", language="en") # Should return something (either translation or key) assert result is not None def test_translate_default_language(self): """Test translate uses default language when not specified""" result = translate("common.save") assert result is not None def test_translate_missing_key(self): """Test translate returns key when not found""" result = translate("nonexistent.key.path") assert result == "nonexistent.key.path" def test_translate_with_interpolation(self): """Test translate with variable interpolation""" # Create a translation that uses variables result = translate("test.key", language="en", name="John") # Should return something (translation with vars or key) assert result is not None def test_t_alias(self): """Test t() is alias for translate()""" result1 = translate("common.save", language="en") result2 = t("common.save", language="en") assert result1 == result2 @pytest.mark.unit @pytest.mark.utils class TestTranslationContext: """Test TranslationContext class""" def test_translation_context_init(self): """Test TranslationContext initialization""" ctx = TranslationContext("en") assert ctx.language == "en" def test_translation_context_default_language(self): """Test TranslationContext uses default when not specified""" ctx = TranslationContext() assert ctx.language == DEFAULT_LANGUAGE def test_translation_context_callable(self): """Test TranslationContext is callable""" ctx = TranslationContext("en") result = ctx("common.save") assert result is not None def test_translation_context_set_language(self): """Test set_language changes language""" ctx = TranslationContext("en") ctx.set_language("fr") assert ctx.language == "fr" def test_translation_context_set_unsupported(self): """Test set_language rejects unsupported language""" ctx = TranslationContext("en") ctx.set_language("xx") assert ctx.language == "en" # Should not change @pytest.mark.unit @pytest.mark.utils class TestJinja2Integration: """Test Jinja2 integration functions""" def test_create_translation_context(self): """Test create_translation_context factory""" ctx = create_translation_context("de") assert isinstance(ctx, TranslationContext) assert ctx.language == "de" def test_get_jinja2_globals(self): """Test get_jinja2_globals returns required globals""" globals = get_jinja2_globals("en") assert "_" in globals assert "t" in globals assert "SUPPORTED_LANGUAGES" in globals assert "DEFAULT_LANGUAGE" in globals assert "LANGUAGE_NAMES" in globals assert "LANGUAGE_FLAGS" in globals assert "current_language" in globals assert globals["current_language"] == "en" def test_get_jinja2_globals_default_language(self): """Test get_jinja2_globals uses default when not specified""" globals = get_jinja2_globals() assert globals["current_language"] == DEFAULT_LANGUAGE @pytest.mark.unit @pytest.mark.utils class TestLanguageResolution: """Test language resolution functions""" def test_resolve_store_dashboard_user_preferred(self): """Test store dashboard prefers user's language""" result = resolve_store_dashboard_language("en", "fr") assert result == "en" def test_resolve_store_dashboard_store_fallback(self): """Test store dashboard falls back to store setting""" result = resolve_store_dashboard_language(None, "de") assert result == "de" def test_resolve_store_dashboard_default(self): """Test store dashboard uses default when nothing set""" result = resolve_store_dashboard_language(None, None) assert result == DEFAULT_LANGUAGE def test_resolve_storefront_customer_preferred(self): """Test storefront prefers customer's language""" result = resolve_storefront_language("de", "fr", "en", None) assert result == "de" def test_resolve_storefront_session(self): """Test storefront uses session language""" result = resolve_storefront_language(None, "de", "fr", None) assert result == "de" def test_resolve_storefront_store(self): """Test storefront uses store default""" result = resolve_storefront_language(None, None, "en", None) assert result == "en" def test_resolve_storefront_browser(self): """Test storefront uses browser language""" result = resolve_storefront_language(None, None, None, "de") assert result == "de" def test_resolve_storefront_enabled_filter(self): """Test storefront enabled_languages filters automatic fallback but not explicit choices""" # Explicit user choice (customer_preferred) should be respected # even if not in store's enabled list result = resolve_storefront_language("de", None, None, None, ["en", "fr"]) assert result == "de" # But automatic fallback (browser, store default) should be filtered result = resolve_storefront_language(None, None, None, "de", ["en", "fr"]) assert result in ["en", "fr", DEFAULT_LANGUAGE] @pytest.mark.unit @pytest.mark.utils class TestAcceptLanguageParsing: """Test Accept-Language header parsing""" def test_parse_accept_language_simple(self): """Test parsing simple Accept-Language""" result = parse_accept_language("fr") assert result == "fr" def test_parse_accept_language_with_region(self): """Test parsing Accept-Language with region""" result = parse_accept_language("fr-FR") assert result == "fr" def test_parse_accept_language_multiple(self): """Test parsing multiple languages with quality""" result = parse_accept_language("de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7") assert result == "de" # Highest quality supported def test_parse_accept_language_none(self): """Test parsing None returns None""" result = parse_accept_language(None) assert result is None def test_parse_accept_language_unsupported(self): """Test parsing unsupported language returns None""" result = parse_accept_language("zh-CN") assert result is None @pytest.mark.unit @pytest.mark.utils class TestUtilityFunctions: """Test utility functions""" def test_get_language_choices(self): """Test get_language_choices returns tuples""" choices = get_language_choices() assert len(choices) == len(SUPPORTED_LANGUAGES) for code, name in choices: assert code in SUPPORTED_LANGUAGES assert name == LANGUAGE_NAMES[code] def test_get_language_info_supported(self): """Test get_language_info for supported language""" info = get_language_info("en") assert info["code"] == "en" assert info["name"] == "English" assert info["name_en"] == "English" assert "flag" in info def test_get_language_info_unsupported(self): """Test get_language_info for unsupported language""" info = get_language_info("xx") # Should fallback to default assert info["code"] == DEFAULT_LANGUAGE def test_is_rtl_language_false(self): """Test is_rtl_language returns False for LTR languages""" for lang in SUPPORTED_LANGUAGES: assert is_rtl_language(lang) is False def test_is_rtl_language_arabic(self): """Test is_rtl_language returns True for RTL languages""" assert is_rtl_language("ar") is True assert is_rtl_language("he") is True