diff --git a/src/pretix/base/email.py b/src/pretix/base/email.py index a473910b2b..6e80661d79 100644 --- a/src/pretix/base/email.py +++ b/src/pretix/base/email.py @@ -32,7 +32,9 @@ from django.db.models import Count from django.dispatch import receiver from django.template.loader import get_template from django.utils.timezone import now -from django.utils.translation import get_language, gettext_lazy as _ +from django.utils.translation import ( + get_language, gettext_lazy as _, pgettext_lazy, +) from inlinestyler.utils import inline_css from pretix.base.i18n import ( @@ -550,16 +552,27 @@ def base_placeholders(sender, **kwargs): ] name_scheme = PERSON_NAME_SCHEMES[sender.settings.name_scheme] + if "concatenation_for_salutation" in name_scheme: + concatenation_for_salutation = name_scheme["concatenation_for_salutation"] + else: + concatenation_for_salutation = name_scheme["concatenation"] + + ph.append(SimpleFunctionalMailTextPlaceholder( + "name_for_salutation", ["position_or_address"], + lambda position_or_address: concatenation_for_salutation(get_best_name(position_or_address, parts=True)), + _("Mr Doe"), + )) + for f, l, w in name_scheme['fields']: if f == 'full_name': continue ph.append(SimpleFunctionalMailTextPlaceholder( - 'attendee_name_%s' % f, ['position'], lambda position, f=f: position.attendee_name_parts.get(f, ''), + 'attendee_name_%s' % f, ['position'], lambda position, f=f: get_name_parts_localized(position.attendee_name_parts, f), name_scheme['sample'][f] )) ph.append(SimpleFunctionalMailTextPlaceholder( 'name_%s' % f, ['position_or_address'], - lambda position_or_address, f=f: get_best_name(position_or_address, parts=True).get(f, ''), + lambda position_or_address, f=f: get_name_parts_localized(get_best_name(position_or_address, parts=True), f), name_scheme['sample'][f] )) @@ -570,3 +583,10 @@ def base_placeholders(sender, **kwargs): )) return ph + + +def get_name_parts_localized(name_parts, key): + value = name_parts.get(key, "") + if key == "salutation": + return pgettext_lazy("person_name_salutation", value) + return value diff --git a/src/pretix/base/forms/questions.py b/src/pretix/base/forms/questions.py index f0bfaa0d71..19f8e139c4 100644 --- a/src/pretix/base/forms/questions.py +++ b/src/pretix/base/forms/questions.py @@ -119,7 +119,7 @@ class NamePartsWidget(forms.MultiWidget): if fname == 'title' and self.titles: widgets.append(Select(attrs=a, choices=[('', '')] + [(d, d) for d in self.titles[1]])) elif fname == 'salutation': - widgets.append(Select(attrs=a, choices=[('', '---')] + [(s, s) for s in PERSON_NAME_SALUTATIONS])) + widgets.append(Select(attrs=a, choices=[('', '---')] + PERSON_NAME_SALUTATIONS)) else: widgets.append(self.widget(attrs=a)) super().__init__(widgets, attrs) @@ -217,7 +217,7 @@ class NamePartsFormField(forms.MultiValueField): d.pop('max_length', None) field = forms.ChoiceField( **d, - choices=[('', '---')] + [(s, s) for s in PERSON_NAME_SALUTATIONS] + choices=[('', '---')] + PERSON_NAME_SALUTATIONS ) else: field = forms.CharField(**defaults) diff --git a/src/pretix/base/migrations/0187_normalize_salutation.py b/src/pretix/base/migrations/0187_normalize_salutation.py new file mode 100644 index 0000000000..f0c523fa82 --- /dev/null +++ b/src/pretix/base/migrations/0187_normalize_salutation.py @@ -0,0 +1,62 @@ +# Generated by Django 3.2.2 on 2021-05-11 06:55 + +from django.conf import settings +from django.db import migrations +from django.utils.translation import pgettext + +from pretix.base.i18n import language +from pretix.base.settings import PERSON_NAME_SALUTATIONS + + +def normalize_salutations(apps, schema_editor): + models = ( + ("pretixbase", "Customer", "name_parts"), + ("pretixbase", "InvoiceAddress", "name_parts"), + ("pretixbase", "Membership", "attendee_name_parts"), + ("pretixbase", "CartPosition", "attendee_name_parts"), + ("pretixbase", "OrderPosition", "attendee_name_parts"), + ("pretix_exhibitors", "Exhibitor", "contact_name_parts"), + ) + + for salutation, value in PERSON_NAME_SALUTATIONS: + salutations = [] + for lang in settings.LANGUAGES: + with language(lang[0]): + salutation_localized = pgettext("person_name_salutation", salutation) + if ( + salutation_localized != salutation + and salutation_localized not in salutations + ): + salutations.append(salutation_localized) + + for app, model, key in models: + sort_params = {} + sort_params["{}__salutation__in".format(key)] = salutations + try: + m = apps.get_model(app, model) + except LookupError: + continue + try: + qs = m.objects + except AttributeError: + qs = m.all + + for o in qs.filter(**sort_params): + val = getattr(o, key) + val["salutation"] = salutation + setattr(o, key, val) + o.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ("pretixbase", "0186_invoice_sent_to_organizer"), + ] + + operations = [ + migrations.RunPython( + normalize_salutations, + migrations.RunPython.noop, + ), + ] diff --git a/src/pretix/base/settings.py b/src/pretix/base/settings.py index a546fb8e21..10325243dc 100644 --- a/src/pretix/base/settings.py +++ b/src/pretix/base/settings.py @@ -2442,10 +2442,29 @@ PERSON_NAME_TITLE_GROUPS = OrderedDict([ ]) PERSON_NAME_SALUTATIONS = [ - pgettext_lazy("person_name_salutation", "Ms"), - pgettext_lazy("person_name_salutation", "Mr"), + ("Ms", pgettext_lazy("person_name_salutation", "Ms")), + ("Mr", pgettext_lazy("person_name_salutation", "Mr")), + ("Mx", pgettext_lazy("person_name_salutation", "Mx")), ] + +def concatenation_for_salutation(d): + salutation = d.get("salutation") + title = d.get("title") + given_name = d.get("given_name") + family_name = d.get("family_name") + # degree (after name) is not used in salutation + # see https://www.schreibwerkstatt.co.at/2012/12/25/der-umgang-mit-akademischen-graden/ + + if salutation == "Mx": + salutation = None + elif salutation: + salutation = pgettext("person_name_salutation", salutation) + given_name = None + + return " ".join(filter(None, (salutation, title, given_name, family_name))) + + PERSON_NAME_SCHEMES = OrderedDict([ ('given_family', { 'fields': ( @@ -2613,6 +2632,7 @@ PERSON_NAME_SCHEMES = OrderedDict([ 'concatenation': lambda d: ' '.join( str(p) for p in (d.get(key, '') for key in ["given_name", "family_name"]) if p ), + 'concatenation_for_salutation': concatenation_for_salutation, 'sample': { 'salutation': pgettext_lazy('person_name_sample', 'Mr'), 'given_name': pgettext_lazy('person_name_sample', 'John'), @@ -2630,6 +2650,7 @@ PERSON_NAME_SCHEMES = OrderedDict([ 'concatenation': lambda d: ' '.join( str(p) for p in (d.get(key, '') for key in ["title", "given_name", "family_name"]) if p ), + 'concatenation_for_salutation': concatenation_for_salutation, 'sample': { 'salutation': pgettext_lazy('person_name_sample', 'Mr'), 'title': pgettext_lazy('person_name_sample', 'Dr'), @@ -2653,6 +2674,7 @@ PERSON_NAME_SCHEMES = OrderedDict([ str((', ' if d.get('degree') else '')) + str(d.get('degree', '')) ), + 'concatenation_for_salutation': concatenation_for_salutation, 'sample': { 'salutation': pgettext_lazy('person_name_sample', 'Mr'), 'title': pgettext_lazy('person_name_sample', 'Dr'),