diff --git a/src/pretix/base/forms/questions.py b/src/pretix/base/forms/questions.py index 75ad9e3575..3932b7c165 100644 --- a/src/pretix/base/forms/questions.py +++ b/src/pretix/base/forms/questions.py @@ -41,7 +41,6 @@ from io import BytesIO import dateutil.parser import pycountry import pytz -from babel import Locale from django import forms from django.conf import settings from django.contrib import messages @@ -52,7 +51,6 @@ from django.core.validators import ( ) from django.db.models import QuerySet from django.forms import Select, widgets -from django.utils import translation from django.utils.formats import date_format from django.utils.html import escape 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 ( 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.i18n import get_format_without_seconds from pretix.presale.signals import question_form_fields @@ -264,17 +264,14 @@ class WrappedPhonePrefixSelect(Select): def __init__(self, initial=None): choices = [("", "---------")] - language = get_babel_locale() # changed from default implementation that used the django locale - locale = Locale(translation.to_locale(language)) - for prefix, values in _COUNTRY_CODE_TO_REGION_CODE.items(): - prefix = "+%d" % prefix - if initial and initial in values: - self.initial = prefix - for country_code in values: - country_name = locale.territories.get(country_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')}) + + if initial: + for prefix, values in _COUNTRY_CODE_TO_REGION_CODE.items(): + if initial in values: + self.initial = "+%d" % prefix + break + choices += get_phone_prefixes_sorted_and_localized() + super().__init__(choices=choices, attrs={'aria-label': pgettext_lazy('phonenumber', 'International area code')}) def render(self, name, value, *args, **kwargs): return super().render(name, value or self.initial, *args, **kwargs) diff --git a/src/pretix/helpers/countries.py b/src/pretix/helpers/countries.py index 9150315fb2..56bd8c4dcc 100644 --- a/src/pretix/helpers/countries.py +++ b/src/pretix/helpers/countries.py @@ -19,11 +19,17 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # +import pyuca +from babel.core import Locale from django.core.cache import cache +from django.utils import translation from django_countries import Countries 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): @@ -83,3 +89,35 @@ class FastCountryField(CountryField): *self._check_multiple(), *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