Add region setting to supplement localization (#1875)

This commit is contained in:
Raphael Michel
2020-12-14 13:15:38 +01:00
committed by GitHub
parent e311341d01
commit 04bfa63a5e
46 changed files with 281 additions and 90 deletions

View File

@@ -574,6 +574,7 @@ class EventSettingsSerializer(serializers.Serializer):
'presale_start_show_date',
'locales',
'locale',
'region',
'last_order_modification_date',
'show_quota_left',
'waiting_list_enabled',

View File

@@ -180,7 +180,7 @@ class PdfDataSerializer(serializers.Field):
res = {}
ev = instance.subevent or instance.order.event
with language(instance.order.locale):
with language(instance.order.locale, instance.order.event.settings.region):
# This needs to have some extra performance improvements to avoid creating hundreds of queries when
# we serialize a list.

View File

@@ -1,7 +1,7 @@
from decimal import Decimal
from django.db.models import Q
from django.utils.translation import get_language, gettext_lazy as _
from django.utils.translation import gettext_lazy as _
from hierarkey.proxy import HierarkeyProxy
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
@@ -9,6 +9,7 @@ from rest_framework.exceptions import ValidationError
from pretix.api.serializers.i18n import I18nAwareModelSerializer
from pretix.api.serializers.order import CompatibleJSONField
from pretix.base.auth import get_auth_backends
from pretix.base.i18n import get_language_without_region
from pretix.base.models import (
Device, GiftCard, GiftCardTransaction, Organizer, SeatingPlan, Team,
TeamAPIToken, TeamInvite, User,
@@ -145,7 +146,7 @@ class TeamInviteSerializer(serializers.ModelSerializer):
})
},
event=None,
locale=get_language() # TODO: expose?
locale=get_language_without_region() # TODO: expose?
)
except SendMailException:
pass # Already logged
@@ -217,6 +218,7 @@ class OrganizerSettingsSerializer(serializers.Serializer):
'giftcard_length',
'giftcard_expiry_years',
'locales',
'region',
'event_team_provisioning',
'primary_color',
'theme_color_success',

View File

@@ -582,7 +582,7 @@ class OrderViewSet(viewsets.ModelViewSet):
auth=request.auth,
)
with language(order.locale):
with language(order.locale, self.request.event.settings.region):
order_placed.send(self.request.event, order=order)
if order.status == Order.STATUS_PAID:
order_paid.send(self.request.event, order=order)
@@ -886,7 +886,7 @@ class OrderPositionViewSet(mixins.DestroyModelMixin, viewsets.ReadOnlyModelViewS
price = get_price(**kwargs)
tr = kwargs.get('tax_rule', kwargs.get('item').tax_rule)
with language(data.get('locale') or self.request.event.settings.locale):
with language(data.get('locale') or self.request.event.settings.locale, self.request.event.settings.region):
return Response({
'gross': price.gross,
'gross_formatted': money_filter(price.gross, self.request.event.currency, hide_currency=True),

View File

@@ -115,7 +115,7 @@ class TemplateBasedMailRenderer(BaseHTMLMailRenderer):
'body': body_md,
'subject': str(subject),
'color': settings.PRETIX_PRIMARY_COLOR,
'rtl': get_language() in settings.LANGUAGES_RTL
'rtl': get_language() in settings.LANGUAGES_RTL or get_language().split('-')[0] in settings.LANGUAGES_RTL,
}
if self.event:
htmlctx['event'] = self.event

View File

@@ -35,7 +35,7 @@ from pretix.base.forms.widgets import (
BusinessBooleanRadio, DatePickerWidget, SplitDateTimePickerWidget,
TimePickerWidget, UploadedFileWidget,
)
from pretix.base.i18n import language
from pretix.base.i18n import get_language_without_region, language
from pretix.base.models import InvoiceAddress, Question, QuestionOption
from pretix.base.models.tax import (
EU_COUNTRIES, cc_to_vat_prefix, is_eu_country,
@@ -216,8 +216,8 @@ class WrappedPhoneNumberPrefixWidget(PhoneNumberPrefixWidget):
def guess_country(event):
# Try to guess the initial country from either the country of the merchant
# or the locale. This will hopefully save at least some users some scrolling :)
locale = get_language()
country = event.settings.invoice_address_from_country
locale = get_language_without_region()
country = event.settings.region or event.settings.invoice_address_from_country
if not country:
valid_countries = countries.countries
if '-' in locale:

View File

@@ -66,10 +66,42 @@ class LazyNumber:
return number_format(self.value, decimal_pos=self.decimal_pos)
ALLOWED_LANGUAGES = dict(settings.LANGUAGES)
def get_language_without_region(lng=None):
"""
Returns the currently active language, but strips what pretix calls a ``region``. For example,
if the currently active language is ``en-us``, you will be returned ``en`` since pretix does not
ship with separate language files for ``en-us``. If the currently active language is ``pt-br``,
you will be returned ``pt-br`` since there are separate language files for ``pt-br``.
tl;dr: You will be always passed a language that is defined in settings.LANGUAGES.
"""
lng = lng or translation.get_language() or settings.LANGUAGE_CODE
if lng not in ALLOWED_LANGUAGES:
lng = lng.split('-')[0]
if lng not in ALLOWED_LANGUAGES:
lng = settings.LANGUAGE_CODE
return lng
@contextmanager
def language(lng):
def language(lng, region=None):
"""
Temporarily change the active language to ``lng``. Will automatically be rolled back when the
context manager returns.
You can optionally pass a "region". For example, if you pass ``en`` as ``lng`` and ``US`` as
``region``, the active language will be ``en-us``, which will mostly affect date/time
formatting. If you pass a ``lng`` that already contains a region, e.g. ``pt-br``, the ``region``
attribute will be ignored.
"""
_lng = translation.get_language()
translation.activate(lng or settings.LANGUAGE_CODE)
lng = lng or settings.LANGUAGE_CODE
if '-' not in lng and region:
lng += '-' + region.lower()
translation.activate(lng)
try:
yield
finally:

View File

@@ -144,7 +144,7 @@ class BaseReportlabInvoiceRenderer(BaseInvoiceRenderer):
def _upper(self, val):
# We uppercase labels, but not in every language
if get_language() == 'el':
if get_language().startswith('el'):
return val
return val.upper()

View File

@@ -15,7 +15,8 @@ from django.utils.translation.trans_real import (
parse_accept_lang_header,
)
from pretix.base.settings import GlobalSettingsObject
from pretix.base.i18n import get_language_without_region
from pretix.base.settings import global_settings_object
from pretix.multidomain.urlreverse import (
get_event_domain, get_organizer_domain,
)
@@ -35,19 +36,30 @@ class LocaleMiddleware(MiddlewareMixin):
# Normally, this middleware runs *before* the event is set. However, on event frontend pages it
# might be run a second time by pretix.presale.EventMiddleware and in this case the event is already
# set and can be taken into account for the decision.
if hasattr(request, 'event') and not request.path.startswith(get_script_prefix() + 'control'):
if language not in request.event.settings.locales:
firstpart = language.split('-')[0]
if firstpart in request.event.settings.locales:
language = firstpart
else:
language = request.event.settings.locale
for lang in request.event.settings.locales:
if lang.startswith(firstpart + '-'):
language = lang
break
if not request.path.startswith(get_script_prefix() + 'control'):
if hasattr(request, 'event'):
if language not in request.event.settings.locales:
firstpart = language.split('-')[0]
if firstpart in request.event.settings.locales:
language = firstpart
else:
language = request.event.settings.locale
for lang in request.event.settings.locales:
if lang.startswith(firstpart + '-'):
language = lang
break
if '-' not in language and request.event.settings.region:
language += '-' + request.event.settings.region
elif hasattr(request, 'organizer'):
if '-' not in language and request.organizer.settings.region:
language += '-' + request.organizer.settings.region
else:
gs = global_settings_object(request)
if '-' not in language and gs.settings.region:
language += '-' + gs.settings.region
translation.activate(language)
request.LANGUAGE_CODE = translation.get_language()
request.LANGUAGE_CODE = get_language_without_region()
tzname = None
if hasattr(request, 'event'):
@@ -192,7 +204,7 @@ class SecurityMiddleware(MiddlewareMixin):
resp['P3P'] = 'CP=\"ALL DSP COR CUR ADM TAI OUR IND COM NAV INT\"'
img_src = []
gs = GlobalSettingsObject()
gs = global_settings_object(request)
if gs.settings.leaflet_tiles:
img_src.append(gs.settings.leaflet_tiles[:gs.settings.leaflet_tiles.index("/", 10)].replace("{s}", "*"))

View File

@@ -857,7 +857,7 @@ class Order(LockModel, LoggedModel):
for k, v in self.event.meta_data.items():
context['meta_' + k] = v
with language(self.locale):
with language(self.locale, self.event.settings.region):
recipient = self.email
if position and position.attendee_email:
recipient = position.attendee_email
@@ -890,7 +890,7 @@ class Order(LockModel, LoggedModel):
)
def resend_link(self, user=None, auth=None):
with language(self.locale):
with language(self.locale, self.event.settings.region):
email_template = self.event.settings.mail_text_resend_link
email_context = get_email_context(event=self.event, order=self)
email_subject = _('Your order: %(code)s') % {'code': self.code}
@@ -1514,7 +1514,7 @@ class OrderPayment(models.Model):
def _send_paid_mail_attendee(self, position, user):
from pretix.base.services.mail import SendMailException
with language(self.order.locale):
with language(self.order.locale, self.order.event.settings.region):
email_template = self.order.event.settings.mail_text_order_paid_attendee
email_context = get_email_context(event=self.order.event, order=self.order, position=position)
email_subject = _('Event registration confirmed: %(code)s') % {'code': self.order.code}
@@ -1532,7 +1532,7 @@ class OrderPayment(models.Model):
def _send_paid_mail(self, invoice, user, mail_text):
from pretix.base.services.mail import SendMailException
with language(self.order.locale):
with language(self.order.locale, self.order.event.settings.region):
email_template = self.order.event.settings.mail_text_order_paid
email_context = get_email_context(event=self.order.event, order=self.order, payment_info=mail_text)
email_subject = _('Payment received for your order: %(code)s') % {'code': self.order.code}
@@ -2104,7 +2104,7 @@ class OrderPosition(AbstractPosition):
for k, v in self.event.meta_data.items():
context['meta_' + k] = v
with language(self.order.locale):
with language(self.order.locale, self.order.event.settings.region):
recipient = self.attendee_email
try:
email_content = render_mail(template, context)
@@ -2132,7 +2132,7 @@ class OrderPosition(AbstractPosition):
def resend_link(self, user=None, auth=None):
with language(self.order.locale):
with language(self.order.locale, self.order.event.settings.region):
email_template = self.event.settings.mail_text_resend_link
email_context = get_email_context(event=self.order.event, order=self.order, position=self)
email_subject = _('Your event registration: %(code)s') % {'code': self.order.code}

View File

@@ -125,7 +125,7 @@ class WaitingListEntry(LoggedModel):
self.voucher = v
self.save()
with language(self.locale):
with language(self.locale, self.event.settings.region):
mail(
self.email,
_('You have been selected from the waitinglist for {event}').format(event=str(self.event)),

View File

@@ -421,6 +421,7 @@ class Renderer:
self.layout = layout
self.background_file = background_file
self.variables = get_variables(event)
self.event = event
if self.background_file:
self.bg_bytes = self.background_file.read()
self.bg_pdf = PdfFileReader(BytesIO(self.bg_bytes), strict=False)
@@ -487,7 +488,7 @@ class Renderer:
def _get_text_content(self, op: OrderPosition, order: Order, o: dict, inner=False):
if o.get('locale', None) and not inner:
with language(o['locale']):
with language(o['locale'], self.event.settings.region):
return self._get_text_content(op, order, o, True)
ev = self._get_ev(op, order)

View File

@@ -24,7 +24,7 @@ logger = logging.getLogger(__name__)
def _send_wle_mail(wle: WaitingListEntry, subject: LazyI18nString, message: LazyI18nString, subevent: SubEvent):
with language(wle.locale):
with language(wle.locale, wle.event.settings.region):
email_context = get_email_context(event_or_subevent=subevent or wle.event, event=wle.event)
try:
mail(
@@ -41,7 +41,7 @@ def _send_wle_mail(wle: WaitingListEntry, subject: LazyI18nString, message: Lazy
def _send_mail(order: Order, subject: LazyI18nString, message: LazyI18nString, subevent: SubEvent,
refund_amount: Decimal, user: User, positions: list):
with language(order.locale):
with language(order.locale, order.event.settings.region):
try:
ia = order.invoice_address
except InvoiceAddress.DoesNotExist:

View File

@@ -32,7 +32,7 @@ def export(self, event: Event, fileid: str, provider: str, form_data: Dict[str,
)
file = CachedFile.objects.get(id=fileid)
with language(event.settings.locale), override(event.settings.timezone):
with language(event.settings.locale, event.settings.region), override(event.settings.timezone):
responses = register_data_exporters.send(event)
for receiver, response in responses:
ex = response(event, set_progress)
@@ -67,15 +67,18 @@ def multiexport(self, organizer: Organizer, user: User, device: int, token: int,
if user:
locale = user.locale
timezone = user.timezone
region = None # todo: add to user?
else:
e = allowed_events.first()
if e:
locale = e.settings.locale
timezone = e.settings.timezone
region = e.settings.region
else:
locale = settings.LANGUAGE_CODE
timezone = settings.TIME_ZONE
with language(locale), override(timezone):
region = None
with language(locale, region), override(timezone):
if isinstance(form_data['events'][0], str):
events = allowed_events.filter(slug__in=form_data.get('events'), organizer=organizer)
else:

View File

@@ -43,7 +43,7 @@ def build_invoice(invoice: Invoice) -> Invoice:
lp = invoice.order.payments.last()
with language(invoice.locale):
with language(invoice.locale, invoice.event.settings.region):
invoice.invoice_from = invoice.event.settings.get('invoice_address_from')
invoice.invoice_from_name = invoice.event.settings.get('invoice_address_from_name')
invoice.invoice_from_zipcode = invoice.event.settings.get('invoice_address_from_zipcode')
@@ -244,7 +244,7 @@ def generate_cancellation(invoice: Invoice, trigger_pdf=True):
cancellation.date = timezone.now().date()
cancellation.payment_provider_text = ''
cancellation.file = None
with language(invoice.locale):
with language(invoice.locale, invoice.event.settings.region):
cancellation.invoice_from = invoice.event.settings.get('invoice_address_from')
cancellation.invoice_from_name = invoice.event.settings.get('invoice_address_from_name')
cancellation.invoice_from_zipcode = invoice.event.settings.get('invoice_address_from_zipcode')
@@ -297,7 +297,7 @@ def invoice_pdf_task(invoice: int):
return None
if i.file:
i.file.delete()
with language(i.locale):
with language(i.locale, i.event.settings.region):
fname, ftype, fcontent = i.event.invoice_renderer.generate(i)
i.file.save(fname, ContentFile(fcontent))
i.save()
@@ -328,7 +328,7 @@ def build_preview_invoice_pdf(event):
if not locale or locale == '__user__':
locale = event.settings.locale
with rolledback_transaction(), language(locale):
with rolledback_transaction(), language(locale, event.settings.region):
order = event.orders.create(status=Order.STATUS_PENDING, datetime=timezone.now(),
expires=timezone.now(), code="PREVIEW", total=100 * event.tax_rules.count())
invoice = Invoice(

View File

@@ -290,7 +290,7 @@ def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: st
except Order.DoesNotExist:
order = None
else:
with language(order.locale):
with language(order.locale, event.settings.region):
if position:
try:
position = order.positions.get(pk=position)

View File

@@ -65,7 +65,7 @@ def import_orders(event: Event, fileid: str, settings: dict, locale: str, user)
# TODO: quotacheck?
cf = CachedFile.objects.get(id=fileid)
user = User.objects.get(pk=user)
with language(locale):
with language(locale, event.settings.region):
cols = get_all_columns(event)
parsed = parse_csv(cf.file)
orders = []
@@ -163,7 +163,7 @@ def import_orders(event: Event, fileid: str, settings: dict, locale: str, user)
)
for o in orders:
with language(o.locale):
with language(o.locale, event.settings.region):
order_placed.send(event, order=o)
if o.status == Order.STATUS_PAID:
order_paid.send(event, order=o)

View File

@@ -23,7 +23,9 @@ from django_scopes import scopes_disabled
from pretix.api.models import OAuthApplication
from pretix.base.channels import get_all_sales_channels
from pretix.base.email import get_email_context
from pretix.base.i18n import LazyLocaleException, language
from pretix.base.i18n import (
LazyLocaleException, get_language_without_region, language,
)
from pretix.base.models import (
CartPosition, Device, Event, GiftCard, Item, ItemVariation, Order,
OrderPayment, OrderPosition, Quota, Seat, SeatCategoryMapping, User,
@@ -260,7 +262,7 @@ def approve_order(order, user=None, send_mail: bool=True, auth=None, force=False
# send_mail will trigger PDF generation later
if send_mail:
with language(order.locale):
with language(order.locale, order.event.settings.region):
if order.total == Decimal('0.00'):
email_template = order.event.settings.mail_text_order_approved_free
email_subject = _('Order approved and confirmed: %(code)s') % {'code': order.code}
@@ -311,7 +313,7 @@ def deny_order(order, comment='', user=None, send_mail: bool=True, auth=None):
if send_mail:
email_template = order.event.settings.mail_text_order_denied
email_context = get_email_context(event=order.event, order=order, comment=comment)
with language(order.locale):
with language(order.locale, order.event.settings.region):
email_subject = _('Order denied: %(code)s') % {'code': order.code}
try:
order.send_mail(
@@ -422,7 +424,7 @@ def _cancel_order(order, user=None, send_mail: bool=True, api_token=None, device
if send_mail:
email_template = order.event.settings.mail_text_order_canceled
with language(order.locale):
with language(order.locale, order.event.settings.region):
email_context = get_email_context(event=order.event, order=order)
email_subject = _('Order canceled: %(code)s') % {'code': order.code}
try:
@@ -777,7 +779,7 @@ def _create_order(event: Event, email: str, positions: List[CartPosition], now_d
event=event,
email=email,
datetime=now_dt,
locale=locale,
locale=get_language_without_region(locale),
total=total,
testmode=True if sales_channel.testmode_supported and event.testmode else False,
meta_info=json.dumps(meta_info or {}),
@@ -1033,7 +1035,7 @@ def send_expiry_warnings(sender, **kwargs):
# Race condition
continue
with language(o.locale):
with language(o.locale, settings.region):
o.expiry_reminder_sent = True
o.save(update_fields=['expiry_reminder_sent'])
email_template = settings.mail_text_order_expire_warning
@@ -1110,7 +1112,7 @@ def send_download_reminders(sender, **kwargs):
if not send:
continue
with language(o.locale):
with language(o.locale, o.event.settings.region):
o.download_reminder_sent = True
o.save(update_fields=['download_reminder_sent'])
email_template = event.settings.mail_text_download_reminder
@@ -1150,7 +1152,7 @@ def send_download_reminders(sender, **kwargs):
def notify_user_changed_order(order, user=None, auth=None, invoices=[]):
with language(order.locale):
with language(order.locale, order.event.settings.region):
email_template = order.event.settings.mail_text_order_changed
email_context = get_email_context(event=order.event, order=order)
email_subject = _('Your order has been changed: %(code)s') % {'code': order.code}

View File

@@ -23,7 +23,7 @@ logger = logging.getLogger(__name__)
def generate_orderposition(order_position: int, provider: str):
order_position = OrderPosition.objects.select_related('order', 'order__event').get(id=order_position)
with language(order_position.order.locale):
with language(order_position.order.locale, order_position.order.event.settings.region):
responses = register_ticket_outputs.send(order_position.order.event)
for receiver, response in responses:
prov = response(order_position.order.event)
@@ -41,7 +41,7 @@ def generate_orderposition(order_position: int, provider: str):
def generate_order(order: int, provider: str):
order = Order.objects.select_related('event').get(id=order)
with language(order.locale):
with language(order.locale, order.event.settings.region):
responses = register_ticket_outputs.send(order.event)
for receiver, response in responses:
prov = response(order.event)
@@ -75,7 +75,7 @@ class DummyRollbackException(Exception):
def preview(event: int, provider: str):
event = Event.objects.get(id=event)
with rolledback_transaction(), language(event.settings.locale):
with rolledback_transaction(), language(event.settings.locale, event.settings.region):
item = event.items.create(name=_("Sample product"), default_price=42.23,
description=_("Sample product description"))
item2 = event.items.create(name=_("Sample workshop"), default_price=23.40)

View File

@@ -832,6 +832,20 @@ DEFAULTS = {
label=_("Default language"),
)
},
'region': {
'default': None,
'type': str,
'form_class': forms.ChoiceField,
'serializer_class': serializers.ChoiceField,
'serializer_kwargs': lambda: dict(**country_choice_kwargs()),
'form_kwargs': lambda: dict(
label=_('Region'),
help_text=_('Will be used to determine date and time formatting as well as default country for customer '
'addresses and phone numbers. For formatting, this takes less priority than the language and '
'is therefore mostly relevant for languages used in different regions globally (like English).'),
**country_choice_kwargs()
),
},
'show_dates_on_frontpage': {
'default': 'True',
'type': bool,
@@ -2403,3 +2417,9 @@ def validate_organizer_settings(organizer, settings_dict):
#
# N.B.: When actually fleshing out this stub, adding it to the OrganizerUpdateForm should be considered.
pass
def global_settings_object(holder):
if not hasattr(holder, '_global_settings_object'):
holder._global_settings_object = GlobalSettingsObject()
return holder._global_settings_object

View File

@@ -19,9 +19,7 @@ from pytz import common_timezones, timezone
from pretix.base.channels import get_all_sales_channels
from pretix.base.email import get_available_placeholders
from pretix.base.forms import (
I18nModelForm, PlaceholderValidator, SettingsForm,
)
from pretix.base.forms import I18nModelForm, PlaceholderValidator, SettingsForm
from pretix.base.models import Event, Organizer, TaxRule, Team
from pretix.base.models.event import EventMetaValue, SubEvent
from pretix.base.reldate import RelativeDateField, RelativeDateTimeField
@@ -459,6 +457,7 @@ class EventSettingsForm(SettingsForm):
'presale_start_show_date',
'locales',
'locale',
'region',
'show_quota_left',
'waiting_list_enabled',
'waiting_list_hours',

View File

@@ -10,11 +10,15 @@ from pretix.base.signals import register_global_settings
class GlobalSettingsForm(SettingsForm):
auto_fields = [
'region'
]
def __init__(self, *args, **kwargs):
self.obj = GlobalSettingsObject()
super().__init__(*args, obj=self.obj, **kwargs)
self.fields = OrderedDict([
self.fields = OrderedDict(list(self.fields.items()) + [
('footer_text', I18nFormField(
widget=I18nTextInput,
required=False,

View File

@@ -223,6 +223,7 @@ class OrganizerSettingsForm(SettingsForm):
'giftcard_length',
'giftcard_expiry_years',
'locales',
'region',
'event_team_provisioning',
'primary_color',
'theme_color_success',

View File

@@ -83,6 +83,7 @@
{% bootstrap_field sform.locales layout="control" %}
{% bootstrap_field sform.locale layout="control" %}
{% bootstrap_field sform.timezone layout="control" %}
{% bootstrap_field sform.region layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Attendee data" %}</legend>

View File

@@ -45,6 +45,7 @@
<fieldset>
<legend>{% trans "Localization" %}</legend>
{% bootstrap_field sform.locales layout="control" %}
{% bootstrap_field sform.region layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Shop design" %}</legend>

View File

@@ -19,7 +19,6 @@ from django.http import (
)
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse
from django.utils import translation
from django.utils.functional import cached_property
from django.utils.timezone import now
from django.utils.translation import gettext, gettext_lazy as _
@@ -55,6 +54,7 @@ from pretix.multidomain.urlreverse import get_event_domain
from pretix.plugins.stripe.payment import StripeSettingsHolder
from pretix.presale.style import regenerate_css
from ...base.i18n import language
from ...base.models.items import ItemMetaProperty
from ...base.settings import SETTINGS_AFFECTING_CSS, LazyI18nStringList
from ..logdisplay import OVERVIEW_BANLIST
@@ -659,7 +659,7 @@ class MailSettingsPreview(EventPermissionRequiredMixin, View):
if matched is not None:
idx = matched.group('idx')
if idx in self.supported_locale:
with translation.override(self.supported_locale[idx]):
with language(self.supported_locale[idx], self.request.event.settings.region):
msgs[self.supported_locale[idx]] = markdown_compile_email(
v.format_map(self.placeholders(preview_item))
)

View File

@@ -666,7 +666,7 @@ class OrderCancellationRequestDelete(OrderView):
}, user=self.request.user)
messages.success(self.request, _('The request has been removed. If you want, you can now inform the user.'))
with language(self.order.locale):
with language(self.order.locale, self.request.event.settings.region):
return redirect(reverse('control:event.order.sendmail', kwargs={
'event': self.request.event.slug,
'organizer': self.request.event.organizer.slug,
@@ -937,7 +937,7 @@ class OrderRefundView(OrderView):
if giftcard_value and self.order.email:
messages.success(self.request, _('A new gift card was created. You can now send the user their '
'gift card code.'))
with language(self.order.locale):
with language(self.order.locale, self.request.event.settings.region):
return redirect(reverse('control:event.order.sendmail', kwargs={
'event': self.request.event.slug,
'organizer': self.request.event.organizer.slug,
@@ -1779,7 +1779,7 @@ class OrderSendMail(EventPermissionRequiredMixin, OrderViewMixin, FormView):
code=self.kwargs['code'].upper()
)
self.preview_output = {}
with language(order.locale):
with language(order.locale, self.request.event.settings.region):
email_context = get_email_context(event=order.event, order=order)
email_template = LazyI18nString(form.cleaned_data['message'])
email_subject = str(form.cleaned_data['subject']).format_map(TolerantDict(email_context))
@@ -1842,7 +1842,7 @@ class OrderPositionSendMail(OrderSendMail):
attendee_email__isnull=False
)
self.preview_output = {}
with language(position.order.locale):
with language(position.order.locale, self.request.event.settings.region):
email_context = get_email_context(event=position.order.event, order=position.order, position=position)
email_template = LazyI18nString(form.cleaned_data['message'])
email_subject = str(form.cleaned_data['subject']).format_map(TolerantDict(email_context))

View File

@@ -188,7 +188,7 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView):
pass
if "preview" in request.POST:
with rolledback_transaction(), language(request.event.settings.locale):
with rolledback_transaction(), language(request.event.settings.locale, request.event.settings.region):
p = self._get_preview_position()
fname, mimet, data = self.generate(
p,

View File

@@ -1,8 +1,9 @@
from django.core.cache import cache
from django.utils.translation import get_language
from django_countries import Countries
from django_countries.fields import CountryField
from pretix.base.i18n import get_language_without_region
class CachedCountries(Countries):
_cached_lists = {}
@@ -14,7 +15,7 @@ class CachedCountries(Countries):
django-countries performs a unicode-aware sorting based on pyuca which is incredibly
slow.
"""
cache_key = "countries:all:{}".format(get_language())
cache_key = "countries:all:{}".format(get_language_without_region())
if self.cache_subkey:
cache_key += ":" + self.cache_subkey
if cache_key in self._cached_lists:

View File

@@ -0,0 +1,18 @@
# Date according to https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
SHORT_DATE_FORMAT = 'm/d/Y'
SHORT_DATETIME_FORMAT = 'm/d/Y P'
TIME_FORMAT = 'P'
WEEK_FORMAT = '\\W W, o'
WEEK_DAY_FORMAT = 'D, M jS'
DATE_INPUT_FORMATS = [
'%m/%d/%Y',
'%Y-%m-%d',
'%m/%d/%y',
]
TIME_INPUT_FORMATS = [
'%I:%M %p',
'%H:%M:%S', # '14:30:59'
'%H:%M:%S.%f', # '14:30:59.000200'
'%H:%M', # '14:30'
]

View File

@@ -126,6 +126,7 @@ def get_javascript_format_without_seconds(format_name):
def get_moment_locale(locale=None):
cur_lang = locale or translation.get_language()
cur_lang = cur_lang.lower()
if cur_lang in moment_locales:
return cur_lang
if '-' in cur_lang or '_' in cur_lang:

View File

@@ -144,7 +144,7 @@ def render_pdf(event, positions, opt):
offsetx = opt['margins'][3] + (i % opt['cols']) * opt['offsets'][0]
offsety = opt['margins'][2] + (opt['rows'] - 1 - i // opt['cols']) * opt['offsets'][1]
p.translate(offsetx, offsety)
with language(op.order.locale):
with language(op.order.locale, op.order.event.settings.region):
r.draw_page(p, op.order, op, show_page=False)
p.translate(-offsetx, -offsety)

View File

@@ -26,7 +26,7 @@ logger = logging.getLogger(__name__)
def notify_incomplete_payment(o: Order):
with language(o.locale):
with language(o.locale, o.event.settings.region):
email_template = o.event.settings.mail_text_order_expire_warning
email_context = get_email_context(event=o.event, order=o)
email_subject = gettext('Your order received an incomplete payment: %(code)s') % {'code': o.code}

View File

@@ -52,7 +52,7 @@ def send_mails(event: Event, user: int, subject: dict, message: dict, orders: li
continue
try:
with language(o.locale):
with language(o.locale, event.settings.region):
email_context = get_email_context(event=event, order=o, position_or_address=p, position=p)
mail(
p.attendee_email,
@@ -80,7 +80,7 @@ def send_mails(event: Event, user: int, subject: dict, message: dict, orders: li
if send_to_order and o.email:
try:
with language(o.locale):
with language(o.locale, event.settings.region):
email_context = get_email_context(event=event, order=o, position_or_address=ia)
mail(
o.email,

View File

@@ -127,7 +127,7 @@ class SenderView(EventPermissionRequiredMixin, FormView):
if self.request.POST.get("action") == "preview":
for l in self.request.event.settings.locales:
with language(l):
with language(l, self.request.event.settings.region):
context_dict = TolerantDict()
for k, v in get_available_placeholders(self.request.event, ['event', 'order',
'position_or_address']).items():

View File

@@ -82,7 +82,7 @@ class AllTicketsPDF(BaseExporter):
if op.order.event != o.event:
o = PdfTicketOutput(op.event)
with language(op.order.locale):
with language(op.order.locale, o.event.settings.region):
layout = o.layout_map.get(
(op.item_id, op.order.sales_channel),
o.layout_map.get(

View File

@@ -79,7 +79,7 @@ class PdfTicketOutput(BaseTicketOutput):
def generate_order(self, order: Order):
merger = PdfFileMerger()
with language(order.locale):
with language(order.locale, self.event.settings.region):
for op in order.positions_with_tickets:
layout = override_layout.send_chained(
order.event, 'layout', orderposition=op, layout=self.layout_map.get(
@@ -111,7 +111,7 @@ class PdfTicketOutput(BaseTicketOutput):
)
)
)
with language(order.locale):
with language(order.locale, self.event.settings.region):
outbuffer = self._draw_page(layout, op, order)
return 'order%s%s.pdf' % (self.event.slug, order.code), 'application/pdf', outbuffer.read()

View File

@@ -12,6 +12,7 @@ from pretix.helpers.i18n import (
get_javascript_format_without_seconds, get_moment_locale,
)
from ..base.i18n import get_language_without_region
from .signals import (
footer_link, global_html_footer, global_html_head, global_html_page_header,
html_footer, html_head, html_page_header,
@@ -116,7 +117,7 @@ def _default_context(request):
ctx['js_date_format'] = get_javascript_format_without_seconds('DATE_INPUT_FORMATS')
ctx['js_time_format'] = get_javascript_format_without_seconds('TIME_INPUT_FORMATS')
ctx['js_locale'] = get_moment_locale()
ctx['html_locale'] = translation.get_language_info(translation.get_language()).get('public_code', translation.get_language())
ctx['html_locale'] = translation.get_language_info(get_language_without_region()).get('public_code', translation.get_language())
ctx['settings'] = pretix_settings
ctx['django_settings'] = settings

View File

@@ -329,7 +329,10 @@ def iframe_entry_view_wrapper(view_func):
locale = request.GET.get('locale')
if locale and locale in [lc for lc, ll in settings.LANGUAGES]:
with language(locale):
region = None
if hasattr(request, 'event'):
region = request.event.settings.region
with language(locale, region):
resp = view_func(request, *args, **kwargs)
max_age = 10 * 365 * 24 * 60 * 60
set_cookie_without_samesite(

View File

@@ -599,7 +599,7 @@ class OrganizerIcalDownload(OrganizerViewMixin, View):
)
if 'locale' in request.GET and request.GET.get('locale') in dict(settings.LANGUAGES):
with language(request.GET.get('locale')):
with language(request.GET.get('locale'), self.request.organizer.settings.region):
cal = get_ical(events)
else:
cal = get_ical(events)

View File

@@ -1,7 +1,6 @@
from django.conf import settings
from django.contrib import messages
from django.shortcuts import get_object_or_404, redirect, render
from django.utils import translation
from django.utils.decorators import method_decorator
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _, pgettext_lazy
@@ -12,6 +11,7 @@ from pretix.base.templatetags.urlreplace import url_replace
from pretix.multidomain.urlreverse import eventreverse
from pretix.presale.views import EventViewMixin
from ...base.i18n import get_language_without_region
from ...base.models import Item, ItemVariation, WaitingListEntry
from ..forms.waitinglist import WaitingListForm
from . import allow_frame_if_namespaced
@@ -27,7 +27,7 @@ class WaitingView(EventViewMixin, FormView):
kwargs['event'] = self.request.event
kwargs['instance'] = WaitingListEntry(
item=self.item_and_variation[0], variation=self.item_and_variation[1],
event=self.request.event, locale=translation.get_language(),
event=self.request.event, locale=get_language_without_region(),
subevent=self.subevent
)
return kwargs

View File

@@ -302,8 +302,9 @@ class WidgetAPIProductList(EventListMixin, View):
return self._get_event_view(request, **kwargs)
def dispatch(self, request, *args, **kwargs):
o = getattr(request, 'event', request.organizer)
if 'lang' in request.GET and request.GET.get('lang') in [lc for lc, ll in settings.LANGUAGES]:
with language(request.GET.get('lang')):
with language(request.GET.get('lang'), o.settings.region):
return self.get(request, **kwargs)
else:
return self.get(request, **kwargs)