Unicode-aware sorting of phone prefixes (#2523)

This commit is contained in:
Raphael Michel
2022-03-14 15:24:59 +01:00
committed by GitHub
parent 06b0ef906a
commit 959b076b52
2 changed files with 50 additions and 15 deletions

View File

@@ -41,7 +41,6 @@ from io import BytesIO
import dateutil.parser import dateutil.parser
import pycountry import pycountry
import pytz import pytz
from babel import Locale
from django import forms from django import forms
from django.conf import settings from django.conf import settings
from django.contrib import messages from django.contrib import messages
@@ -52,7 +51,6 @@ from django.core.validators import (
) )
from django.db.models import QuerySet from django.db.models import QuerySet
from django.forms import Select, widgets from django.forms import Select, widgets
from django.utils import translation
from django.utils.formats import date_format from django.utils.formats import date_format
from django.utils.html import escape from django.utils.html import escape
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
@@ -87,7 +85,9 @@ from pretix.base.templatetags.rich_text import rich_text
from pretix.control.forms import ( from pretix.control.forms import (
ExtFileField, ExtValidationMixin, SizeValidationMixin, SplitDateTimeField, ExtFileField, ExtValidationMixin, SizeValidationMixin, SplitDateTimeField,
) )
from pretix.helpers.countries import CachedCountries from pretix.helpers.countries import (
CachedCountries, get_phone_prefixes_sorted_and_localized,
)
from pretix.helpers.escapejson import escapejson_attr from pretix.helpers.escapejson import escapejson_attr
from pretix.helpers.i18n import get_format_without_seconds from pretix.helpers.i18n import get_format_without_seconds
from pretix.presale.signals import question_form_fields from pretix.presale.signals import question_form_fields
@@ -264,17 +264,14 @@ class WrappedPhonePrefixSelect(Select):
def __init__(self, initial=None): def __init__(self, initial=None):
choices = [("", "---------")] choices = [("", "---------")]
language = get_babel_locale() # changed from default implementation that used the django locale
locale = Locale(translation.to_locale(language)) if initial:
for prefix, values in _COUNTRY_CODE_TO_REGION_CODE.items(): for prefix, values in _COUNTRY_CODE_TO_REGION_CODE.items():
prefix = "+%d" % prefix if initial in values:
if initial and initial in values: self.initial = "+%d" % prefix
self.initial = prefix break
for country_code in values: choices += get_phone_prefixes_sorted_and_localized()
country_name = locale.territories.get(country_code) super().__init__(choices=choices, attrs={'aria-label': pgettext_lazy('phonenumber', 'International area code')})
if country_name:
choices.append((prefix, "{} {}".format(country_name, prefix)))
super().__init__(choices=sorted(choices, key=lambda item: item[1]), attrs={'aria-label': pgettext_lazy('phonenumber', 'International area code')})
def render(self, name, value, *args, **kwargs): def render(self, name, value, *args, **kwargs):
return super().render(name, value or self.initial, *args, **kwargs) return super().render(name, value or self.initial, *args, **kwargs)

View File

@@ -19,11 +19,17 @@
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see # You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>. # <https://www.gnu.org/licenses/>.
# #
import pyuca
from babel.core import Locale
from django.core.cache import cache from django.core.cache import cache
from django.utils import translation
from django_countries import Countries from django_countries import Countries
from django_countries.fields import CountryField from django_countries.fields import CountryField
from phonenumbers.data import _COUNTRY_CODE_TO_REGION_CODE
from pretix.base.i18n import get_language_without_region from pretix.base.i18n import get_babel_locale, get_language_without_region
_collator = pyuca.Collator()
class CachedCountries(Countries): class CachedCountries(Countries):
@@ -83,3 +89,35 @@ class FastCountryField(CountryField):
*self._check_multiple(), *self._check_multiple(),
*self._check_max_length_attribute(**kwargs), *self._check_max_length_attribute(**kwargs),
] ]
_cached_phone_prefixes = {}
def get_phone_prefixes_sorted_and_localized():
language = get_babel_locale() # changed from default implementation that used the django locale
cache_key = "phoneprefixes:all:{}".format(language)
if cache_key in _cached_phone_prefixes:
return _cached_phone_prefixes[cache_key]
val = cache.get(cache_key)
if val:
_cached_phone_prefixes[cache_key] = val
return val
val = []
locale = Locale(translation.to_locale(language))
for prefix, values in _COUNTRY_CODE_TO_REGION_CODE.items():
prefix = "+%d" % prefix
for country_code in values:
country_name = locale.territories.get(country_code)
if country_name:
val.append((prefix, "{} {}".format(country_name, prefix)))
val = sorted(val, key=lambda item: _collator.sort_key(item[1]))
_cached_phone_prefixes[cache_key] = val
cache.set(cache_key, val, 3600 * 24 * 30)
return val