From ed1af309908d1c8c94a189209b0a40479ee95320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Romain=20S=C3=A9bille?= <20045330+rsebille@users.noreply.github.com> Date: Tue, 23 May 2023 16:30:30 +0200 Subject: [PATCH] [Django 4.1+] Make i18n works for LANGUAGE_CODE with a country code (#206) When using a `LANGUAGE_CODE` with a country code and a supported language (_ie_: `fr-FR`) the current implementation of `Select2Mixin.i18n_name` will return `None` which means default _select2_ language, so English. Co-authored-by: Johannes Maron --- CONTRIBUTING.rst | 2 +- django_select2/forms.py | 15 ++++++--- tests/test_forms.py | 71 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index edec7794..f4367f9c 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -13,7 +13,7 @@ To install the package and its dependencies for development including tests dependencies, please do: python -m pip install -e .[test] - + You may ran the tests via:: python -m pytest diff --git a/django_select2/forms.py b/django_select2/forms.py index 045693e1..1c0c7059 100644 --- a/django_select2/forms.py +++ b/django_select2/forms.py @@ -54,12 +54,11 @@ import django from django import forms -from django.contrib.admin.widgets import SELECT2_TRANSLATIONS, AutocompleteMixin +from django.contrib.admin.widgets import AutocompleteMixin from django.core import signing from django.db.models import Q from django.forms.models import ModelChoiceIterator from django.urls import reverse -from django.utils.translation import get_language from .cache import cache from .conf import settings @@ -89,7 +88,15 @@ class Select2Mixin: @property def i18n_name(self): """Name of the i18n file for the current language.""" - return SELECT2_TRANSLATIONS.get(get_language()) + if django.VERSION < (4, 1): + from django.contrib.admin.widgets import SELECT2_TRANSLATIONS + from django.utils.translation import get_language + + return SELECT2_TRANSLATIONS.get(get_language()) + else: + from django.contrib.admin.widgets import get_select2_language + + return get_select2_language() def build_attrs(self, base_attrs, extra_attrs=None): """Add select2 data attributes.""" @@ -258,7 +265,7 @@ def __init__(self, attrs=None, choices=(), **kwargs): if dependent_fields is not None: self.dependent_fields = dict(dependent_fields) if not (self.data_view or self.data_url): - raise ValueError('You must ether specify "data_view" or "data_url".') + raise ValueError('You must either specify "data_view" or "data_url".') self.userGetValTextFuncName = kwargs.pop("userGetValTextFuncName", "null") def get_url(self): diff --git a/tests/test_forms.py b/tests/test_forms.py index c7ba75a1..c7281949 100644 --- a/tests/test_forms.py +++ b/tests/test_forms.py @@ -2,6 +2,7 @@ import os from collections.abc import Iterable +import django import pytest from django.db.models import QuerySet from django.urls import reverse @@ -822,3 +823,73 @@ def test_dependent_fields_clear_after_change_parent( ) ) assert city2_container.text == "" + + +@pytest.fixture( + name="widget", + params=[ + (Select2Widget, {}), + (HeavySelect2Widget, {"data_view": "heavy_data_1"}), + (HeavySelect2MultipleWidget, {"data_view": "heavy_data_1"}), + (ModelSelect2Widget, {}), + (ModelSelect2TagWidget, {}), + ], + ids=lambda p: p[0], +) +def widget_fixture(request): + widget_class, widget_kwargs = request.param + return widget_class(**widget_kwargs) + + +@pytest.mark.skipif(django.VERSION < (4, 1), reason="Only for Django 4.1+") +@pytest.mark.parametrize( + "locale,expected", + [ + ("fr-FR", "fr"), + # Some locales with a country code are natively supported by select2's i18n + ("pt-BR", "pt-BR"), + ("sr-Cyrl", "sr-Cyrl"), + ], + ids=repr, +) +def test_i18n_name_property_with_country_code_in_locale(widget, locale, expected): + """Test we fall back to the language code if the locale contain an unsupported country code.""" + with translation.override(locale): + assert widget.i18n_name == expected + + +@pytest.mark.skipif(django.VERSION < (4, 1), reason="Only for Django 4.1+") +def test_i18n_media_js_with_country_code_in_locale(widget): + translation.activate("fr-FR") + assert tuple(widget.media._js) == ( + "admin/js/vendor/select2/select2.full.min.js", + "admin/js/vendor/select2/i18n/fr.js", + "django_select2/django_select2.js", + ) + + +@pytest.mark.skipif(django.VERSION >= (4, 1), reason="Only for Django 4.0 and previous") +@pytest.mark.parametrize( + "locale,expected", + [ + ("fr-FR", None), + # Some locales with a country code are natively supported by select2's i18n + ("pt-BR", "pt-BR"), + ("sr-Cyrl", "sr-Cyrl"), + ], +) +def test_i18n_name_property_with_country_code_in_locale_for_older_django( + widget, locale, expected +): + """No fallback for locale with an unsupported country code.""" + with translation.override(locale): + assert widget.i18n_name == expected + + +@pytest.mark.skipif(django.VERSION >= (4, 1), reason="Only for Django 4.0 and previous") +def test_i18n_media_js_with_country_code_in_locale_for_older_django(widget): + translation.activate("fr-FR") + assert tuple(widget.media._js) == ( + "admin/js/vendor/select2/select2.full.min.js", + "django_select2/django_select2.js", + )