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
+1
View File
@@ -574,6 +574,7 @@ class EventSettingsSerializer(serializers.Serializer):
'presale_start_show_date', 'presale_start_show_date',
'locales', 'locales',
'locale', 'locale',
'region',
'last_order_modification_date', 'last_order_modification_date',
'show_quota_left', 'show_quota_left',
'waiting_list_enabled', 'waiting_list_enabled',
+1 -1
View File
@@ -180,7 +180,7 @@ class PdfDataSerializer(serializers.Field):
res = {} res = {}
ev = instance.subevent or instance.order.event 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 # This needs to have some extra performance improvements to avoid creating hundreds of queries when
# we serialize a list. # we serialize a list.
+4 -2
View File
@@ -1,7 +1,7 @@
from decimal import Decimal from decimal import Decimal
from django.db.models import Q 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 hierarkey.proxy import HierarkeyProxy
from rest_framework import serializers from rest_framework import serializers
from rest_framework.exceptions import ValidationError 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.i18n import I18nAwareModelSerializer
from pretix.api.serializers.order import CompatibleJSONField from pretix.api.serializers.order import CompatibleJSONField
from pretix.base.auth import get_auth_backends from pretix.base.auth import get_auth_backends
from pretix.base.i18n import get_language_without_region
from pretix.base.models import ( from pretix.base.models import (
Device, GiftCard, GiftCardTransaction, Organizer, SeatingPlan, Team, Device, GiftCard, GiftCardTransaction, Organizer, SeatingPlan, Team,
TeamAPIToken, TeamInvite, User, TeamAPIToken, TeamInvite, User,
@@ -145,7 +146,7 @@ class TeamInviteSerializer(serializers.ModelSerializer):
}) })
}, },
event=None, event=None,
locale=get_language() # TODO: expose? locale=get_language_without_region() # TODO: expose?
) )
except SendMailException: except SendMailException:
pass # Already logged pass # Already logged
@@ -217,6 +218,7 @@ class OrganizerSettingsSerializer(serializers.Serializer):
'giftcard_length', 'giftcard_length',
'giftcard_expiry_years', 'giftcard_expiry_years',
'locales', 'locales',
'region',
'event_team_provisioning', 'event_team_provisioning',
'primary_color', 'primary_color',
'theme_color_success', 'theme_color_success',
+2 -2
View File
@@ -582,7 +582,7 @@ class OrderViewSet(viewsets.ModelViewSet):
auth=request.auth, auth=request.auth,
) )
with language(order.locale): with language(order.locale, self.request.event.settings.region):
order_placed.send(self.request.event, order=order) order_placed.send(self.request.event, order=order)
if order.status == Order.STATUS_PAID: if order.status == Order.STATUS_PAID:
order_paid.send(self.request.event, order=order) order_paid.send(self.request.event, order=order)
@@ -886,7 +886,7 @@ class OrderPositionViewSet(mixins.DestroyModelMixin, viewsets.ReadOnlyModelViewS
price = get_price(**kwargs) price = get_price(**kwargs)
tr = kwargs.get('tax_rule', kwargs.get('item').tax_rule) 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({ return Response({
'gross': price.gross, 'gross': price.gross,
'gross_formatted': money_filter(price.gross, self.request.event.currency, hide_currency=True), 'gross_formatted': money_filter(price.gross, self.request.event.currency, hide_currency=True),
+1 -1
View File
@@ -115,7 +115,7 @@ class TemplateBasedMailRenderer(BaseHTMLMailRenderer):
'body': body_md, 'body': body_md,
'subject': str(subject), 'subject': str(subject),
'color': settings.PRETIX_PRIMARY_COLOR, '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: if self.event:
htmlctx['event'] = self.event htmlctx['event'] = self.event
+3 -3
View File
@@ -35,7 +35,7 @@ from pretix.base.forms.widgets import (
BusinessBooleanRadio, DatePickerWidget, SplitDateTimePickerWidget, BusinessBooleanRadio, DatePickerWidget, SplitDateTimePickerWidget,
TimePickerWidget, UploadedFileWidget, 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 import InvoiceAddress, Question, QuestionOption
from pretix.base.models.tax import ( from pretix.base.models.tax import (
EU_COUNTRIES, cc_to_vat_prefix, is_eu_country, EU_COUNTRIES, cc_to_vat_prefix, is_eu_country,
@@ -216,8 +216,8 @@ class WrappedPhoneNumberPrefixWidget(PhoneNumberPrefixWidget):
def guess_country(event): def guess_country(event):
# Try to guess the initial country from either the country of the merchant # 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 :) # or the locale. This will hopefully save at least some users some scrolling :)
locale = get_language() locale = get_language_without_region()
country = event.settings.invoice_address_from_country country = event.settings.region or event.settings.invoice_address_from_country
if not country: if not country:
valid_countries = countries.countries valid_countries = countries.countries
if '-' in locale: if '-' in locale:
+34 -2
View File
@@ -66,10 +66,42 @@ class LazyNumber:
return number_format(self.value, decimal_pos=self.decimal_pos) 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 @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() _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: try:
yield yield
finally: finally:
+1 -1
View File
@@ -144,7 +144,7 @@ class BaseReportlabInvoiceRenderer(BaseInvoiceRenderer):
def _upper(self, val): def _upper(self, val):
# We uppercase labels, but not in every language # We uppercase labels, but not in every language
if get_language() == 'el': if get_language().startswith('el'):
return val return val
return val.upper() return val.upper()
+26 -14
View File
@@ -15,7 +15,8 @@ from django.utils.translation.trans_real import (
parse_accept_lang_header, 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 ( from pretix.multidomain.urlreverse import (
get_event_domain, get_organizer_domain, 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 # 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 # 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. # set and can be taken into account for the decision.
if hasattr(request, 'event') and not request.path.startswith(get_script_prefix() + 'control'): if not request.path.startswith(get_script_prefix() + 'control'):
if language not in request.event.settings.locales: if hasattr(request, 'event'):
firstpart = language.split('-')[0] if language not in request.event.settings.locales:
if firstpart in request.event.settings.locales: firstpart = language.split('-')[0]
language = firstpart if firstpart in request.event.settings.locales:
else: language = firstpart
language = request.event.settings.locale else:
for lang in request.event.settings.locales: language = request.event.settings.locale
if lang.startswith(firstpart + '-'): for lang in request.event.settings.locales:
language = lang if lang.startswith(firstpart + '-'):
break 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) translation.activate(language)
request.LANGUAGE_CODE = translation.get_language() request.LANGUAGE_CODE = get_language_without_region()
tzname = None tzname = None
if hasattr(request, 'event'): 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\"' resp['P3P'] = 'CP=\"ALL DSP COR CUR ADM TAI OUR IND COM NAV INT\"'
img_src = [] img_src = []
gs = GlobalSettingsObject() gs = global_settings_object(request)
if gs.settings.leaflet_tiles: if gs.settings.leaflet_tiles:
img_src.append(gs.settings.leaflet_tiles[:gs.settings.leaflet_tiles.index("/", 10)].replace("{s}", "*")) img_src.append(gs.settings.leaflet_tiles[:gs.settings.leaflet_tiles.index("/", 10)].replace("{s}", "*"))
+6 -6
View File
@@ -857,7 +857,7 @@ class Order(LockModel, LoggedModel):
for k, v in self.event.meta_data.items(): for k, v in self.event.meta_data.items():
context['meta_' + k] = v context['meta_' + k] = v
with language(self.locale): with language(self.locale, self.event.settings.region):
recipient = self.email recipient = self.email
if position and position.attendee_email: if position and position.attendee_email:
recipient = position.attendee_email recipient = position.attendee_email
@@ -890,7 +890,7 @@ class Order(LockModel, LoggedModel):
) )
def resend_link(self, user=None, auth=None): 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_template = self.event.settings.mail_text_resend_link
email_context = get_email_context(event=self.event, order=self) email_context = get_email_context(event=self.event, order=self)
email_subject = _('Your order: %(code)s') % {'code': self.code} 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): def _send_paid_mail_attendee(self, position, user):
from pretix.base.services.mail import SendMailException 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_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_context = get_email_context(event=self.order.event, order=self.order, position=position)
email_subject = _('Event registration confirmed: %(code)s') % {'code': self.order.code} 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): def _send_paid_mail(self, invoice, user, mail_text):
from pretix.base.services.mail import SendMailException 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_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_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} 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(): for k, v in self.event.meta_data.items():
context['meta_' + k] = v context['meta_' + k] = v
with language(self.order.locale): with language(self.order.locale, self.order.event.settings.region):
recipient = self.attendee_email recipient = self.attendee_email
try: try:
email_content = render_mail(template, context) email_content = render_mail(template, context)
@@ -2132,7 +2132,7 @@ class OrderPosition(AbstractPosition):
def resend_link(self, user=None, auth=None): 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_template = self.event.settings.mail_text_resend_link
email_context = get_email_context(event=self.order.event, order=self.order, position=self) 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} email_subject = _('Your event registration: %(code)s') % {'code': self.order.code}
+1 -1
View File
@@ -125,7 +125,7 @@ class WaitingListEntry(LoggedModel):
self.voucher = v self.voucher = v
self.save() self.save()
with language(self.locale): with language(self.locale, self.event.settings.region):
mail( mail(
self.email, self.email,
_('You have been selected from the waitinglist for {event}').format(event=str(self.event)), _('You have been selected from the waitinglist for {event}').format(event=str(self.event)),
+2 -1
View File
@@ -421,6 +421,7 @@ class Renderer:
self.layout = layout self.layout = layout
self.background_file = background_file self.background_file = background_file
self.variables = get_variables(event) self.variables = get_variables(event)
self.event = event
if self.background_file: if self.background_file:
self.bg_bytes = self.background_file.read() self.bg_bytes = self.background_file.read()
self.bg_pdf = PdfFileReader(BytesIO(self.bg_bytes), strict=False) 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): def _get_text_content(self, op: OrderPosition, order: Order, o: dict, inner=False):
if o.get('locale', None) and not inner: 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) return self._get_text_content(op, order, o, True)
ev = self._get_ev(op, order) ev = self._get_ev(op, order)
+2 -2
View File
@@ -24,7 +24,7 @@ logger = logging.getLogger(__name__)
def _send_wle_mail(wle: WaitingListEntry, subject: LazyI18nString, message: LazyI18nString, subevent: SubEvent): 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) email_context = get_email_context(event_or_subevent=subevent or wle.event, event=wle.event)
try: try:
mail( 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, def _send_mail(order: Order, subject: LazyI18nString, message: LazyI18nString, subevent: SubEvent,
refund_amount: Decimal, user: User, positions: list): refund_amount: Decimal, user: User, positions: list):
with language(order.locale): with language(order.locale, order.event.settings.region):
try: try:
ia = order.invoice_address ia = order.invoice_address
except InvoiceAddress.DoesNotExist: except InvoiceAddress.DoesNotExist:
+5 -2
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) 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) responses = register_data_exporters.send(event)
for receiver, response in responses: for receiver, response in responses:
ex = response(event, set_progress) ex = response(event, set_progress)
@@ -67,15 +67,18 @@ def multiexport(self, organizer: Organizer, user: User, device: int, token: int,
if user: if user:
locale = user.locale locale = user.locale
timezone = user.timezone timezone = user.timezone
region = None # todo: add to user?
else: else:
e = allowed_events.first() e = allowed_events.first()
if e: if e:
locale = e.settings.locale locale = e.settings.locale
timezone = e.settings.timezone timezone = e.settings.timezone
region = e.settings.region
else: else:
locale = settings.LANGUAGE_CODE locale = settings.LANGUAGE_CODE
timezone = settings.TIME_ZONE timezone = settings.TIME_ZONE
with language(locale), override(timezone): region = None
with language(locale, region), override(timezone):
if isinstance(form_data['events'][0], str): if isinstance(form_data['events'][0], str):
events = allowed_events.filter(slug__in=form_data.get('events'), organizer=organizer) events = allowed_events.filter(slug__in=form_data.get('events'), organizer=organizer)
else: else:
+4 -4
View File
@@ -43,7 +43,7 @@ def build_invoice(invoice: Invoice) -> Invoice:
lp = invoice.order.payments.last() 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 = invoice.event.settings.get('invoice_address_from')
invoice.invoice_from_name = invoice.event.settings.get('invoice_address_from_name') invoice.invoice_from_name = invoice.event.settings.get('invoice_address_from_name')
invoice.invoice_from_zipcode = invoice.event.settings.get('invoice_address_from_zipcode') 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.date = timezone.now().date()
cancellation.payment_provider_text = '' cancellation.payment_provider_text = ''
cancellation.file = None 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 = invoice.event.settings.get('invoice_address_from')
cancellation.invoice_from_name = invoice.event.settings.get('invoice_address_from_name') cancellation.invoice_from_name = invoice.event.settings.get('invoice_address_from_name')
cancellation.invoice_from_zipcode = invoice.event.settings.get('invoice_address_from_zipcode') cancellation.invoice_from_zipcode = invoice.event.settings.get('invoice_address_from_zipcode')
@@ -297,7 +297,7 @@ def invoice_pdf_task(invoice: int):
return None return None
if i.file: if i.file:
i.file.delete() i.file.delete()
with language(i.locale): with language(i.locale, i.event.settings.region):
fname, ftype, fcontent = i.event.invoice_renderer.generate(i) fname, ftype, fcontent = i.event.invoice_renderer.generate(i)
i.file.save(fname, ContentFile(fcontent)) i.file.save(fname, ContentFile(fcontent))
i.save() i.save()
@@ -328,7 +328,7 @@ def build_preview_invoice_pdf(event):
if not locale or locale == '__user__': if not locale or locale == '__user__':
locale = event.settings.locale 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(), order = event.orders.create(status=Order.STATUS_PENDING, datetime=timezone.now(),
expires=timezone.now(), code="PREVIEW", total=100 * event.tax_rules.count()) expires=timezone.now(), code="PREVIEW", total=100 * event.tax_rules.count())
invoice = Invoice( invoice = Invoice(
+1 -1
View File
@@ -290,7 +290,7 @@ def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: st
except Order.DoesNotExist: except Order.DoesNotExist:
order = None order = None
else: else:
with language(order.locale): with language(order.locale, event.settings.region):
if position: if position:
try: try:
position = order.positions.get(pk=position) position = order.positions.get(pk=position)
+2 -2
View File
@@ -65,7 +65,7 @@ def import_orders(event: Event, fileid: str, settings: dict, locale: str, user)
# TODO: quotacheck? # TODO: quotacheck?
cf = CachedFile.objects.get(id=fileid) cf = CachedFile.objects.get(id=fileid)
user = User.objects.get(pk=user) user = User.objects.get(pk=user)
with language(locale): with language(locale, event.settings.region):
cols = get_all_columns(event) cols = get_all_columns(event)
parsed = parse_csv(cf.file) parsed = parse_csv(cf.file)
orders = [] orders = []
@@ -163,7 +163,7 @@ def import_orders(event: Event, fileid: str, settings: dict, locale: str, user)
) )
for o in orders: for o in orders:
with language(o.locale): with language(o.locale, event.settings.region):
order_placed.send(event, order=o) order_placed.send(event, order=o)
if o.status == Order.STATUS_PAID: if o.status == Order.STATUS_PAID:
order_paid.send(event, order=o) order_paid.send(event, order=o)
+10 -8
View File
@@ -23,7 +23,9 @@ from django_scopes import scopes_disabled
from pretix.api.models import OAuthApplication from pretix.api.models import OAuthApplication
from pretix.base.channels import get_all_sales_channels from pretix.base.channels import get_all_sales_channels
from pretix.base.email import get_email_context 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 ( from pretix.base.models import (
CartPosition, Device, Event, GiftCard, Item, ItemVariation, Order, CartPosition, Device, Event, GiftCard, Item, ItemVariation, Order,
OrderPayment, OrderPosition, Quota, Seat, SeatCategoryMapping, User, 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 # send_mail will trigger PDF generation later
if send_mail: if send_mail:
with language(order.locale): with language(order.locale, order.event.settings.region):
if order.total == Decimal('0.00'): if order.total == Decimal('0.00'):
email_template = order.event.settings.mail_text_order_approved_free email_template = order.event.settings.mail_text_order_approved_free
email_subject = _('Order approved and confirmed: %(code)s') % {'code': order.code} 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: if send_mail:
email_template = order.event.settings.mail_text_order_denied email_template = order.event.settings.mail_text_order_denied
email_context = get_email_context(event=order.event, order=order, comment=comment) 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} email_subject = _('Order denied: %(code)s') % {'code': order.code}
try: try:
order.send_mail( order.send_mail(
@@ -422,7 +424,7 @@ def _cancel_order(order, user=None, send_mail: bool=True, api_token=None, device
if send_mail: if send_mail:
email_template = order.event.settings.mail_text_order_canceled 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_context = get_email_context(event=order.event, order=order)
email_subject = _('Order canceled: %(code)s') % {'code': order.code} email_subject = _('Order canceled: %(code)s') % {'code': order.code}
try: try:
@@ -777,7 +779,7 @@ def _create_order(event: Event, email: str, positions: List[CartPosition], now_d
event=event, event=event,
email=email, email=email,
datetime=now_dt, datetime=now_dt,
locale=locale, locale=get_language_without_region(locale),
total=total, total=total,
testmode=True if sales_channel.testmode_supported and event.testmode else False, testmode=True if sales_channel.testmode_supported and event.testmode else False,
meta_info=json.dumps(meta_info or {}), meta_info=json.dumps(meta_info or {}),
@@ -1033,7 +1035,7 @@ def send_expiry_warnings(sender, **kwargs):
# Race condition # Race condition
continue continue
with language(o.locale): with language(o.locale, settings.region):
o.expiry_reminder_sent = True o.expiry_reminder_sent = True
o.save(update_fields=['expiry_reminder_sent']) o.save(update_fields=['expiry_reminder_sent'])
email_template = settings.mail_text_order_expire_warning email_template = settings.mail_text_order_expire_warning
@@ -1110,7 +1112,7 @@ def send_download_reminders(sender, **kwargs):
if not send: if not send:
continue continue
with language(o.locale): with language(o.locale, o.event.settings.region):
o.download_reminder_sent = True o.download_reminder_sent = True
o.save(update_fields=['download_reminder_sent']) o.save(update_fields=['download_reminder_sent'])
email_template = event.settings.mail_text_download_reminder 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=[]): 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_template = order.event.settings.mail_text_order_changed
email_context = get_email_context(event=order.event, order=order) email_context = get_email_context(event=order.event, order=order)
email_subject = _('Your order has been changed: %(code)s') % {'code': order.code} email_subject = _('Your order has been changed: %(code)s') % {'code': order.code}
+3 -3
View File
@@ -23,7 +23,7 @@ logger = logging.getLogger(__name__)
def generate_orderposition(order_position: int, provider: str): def generate_orderposition(order_position: int, provider: str):
order_position = OrderPosition.objects.select_related('order', 'order__event').get(id=order_position) 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) responses = register_ticket_outputs.send(order_position.order.event)
for receiver, response in responses: for receiver, response in responses:
prov = response(order_position.order.event) 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): def generate_order(order: int, provider: str):
order = Order.objects.select_related('event').get(id=order) 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) responses = register_ticket_outputs.send(order.event)
for receiver, response in responses: for receiver, response in responses:
prov = response(order.event) prov = response(order.event)
@@ -75,7 +75,7 @@ class DummyRollbackException(Exception):
def preview(event: int, provider: str): def preview(event: int, provider: str):
event = Event.objects.get(id=event) 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, item = event.items.create(name=_("Sample product"), default_price=42.23,
description=_("Sample product description")) description=_("Sample product description"))
item2 = event.items.create(name=_("Sample workshop"), default_price=23.40) item2 = event.items.create(name=_("Sample workshop"), default_price=23.40)
+20
View File
@@ -832,6 +832,20 @@ DEFAULTS = {
label=_("Default language"), 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': { 'show_dates_on_frontpage': {
'default': 'True', 'default': 'True',
'type': bool, '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. # N.B.: When actually fleshing out this stub, adding it to the OrganizerUpdateForm should be considered.
pass pass
def global_settings_object(holder):
if not hasattr(holder, '_global_settings_object'):
holder._global_settings_object = GlobalSettingsObject()
return holder._global_settings_object
+2 -3
View File
@@ -19,9 +19,7 @@ from pytz import common_timezones, timezone
from pretix.base.channels import get_all_sales_channels from pretix.base.channels import get_all_sales_channels
from pretix.base.email import get_available_placeholders from pretix.base.email import get_available_placeholders
from pretix.base.forms import ( from pretix.base.forms import I18nModelForm, PlaceholderValidator, SettingsForm
I18nModelForm, PlaceholderValidator, SettingsForm,
)
from pretix.base.models import Event, Organizer, TaxRule, Team from pretix.base.models import Event, Organizer, TaxRule, Team
from pretix.base.models.event import EventMetaValue, SubEvent from pretix.base.models.event import EventMetaValue, SubEvent
from pretix.base.reldate import RelativeDateField, RelativeDateTimeField from pretix.base.reldate import RelativeDateField, RelativeDateTimeField
@@ -459,6 +457,7 @@ class EventSettingsForm(SettingsForm):
'presale_start_show_date', 'presale_start_show_date',
'locales', 'locales',
'locale', 'locale',
'region',
'show_quota_left', 'show_quota_left',
'waiting_list_enabled', 'waiting_list_enabled',
'waiting_list_hours', 'waiting_list_hours',
+5 -1
View File
@@ -10,11 +10,15 @@ from pretix.base.signals import register_global_settings
class GlobalSettingsForm(SettingsForm): class GlobalSettingsForm(SettingsForm):
auto_fields = [
'region'
]
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.obj = GlobalSettingsObject() self.obj = GlobalSettingsObject()
super().__init__(*args, obj=self.obj, **kwargs) super().__init__(*args, obj=self.obj, **kwargs)
self.fields = OrderedDict([ self.fields = OrderedDict(list(self.fields.items()) + [
('footer_text', I18nFormField( ('footer_text', I18nFormField(
widget=I18nTextInput, widget=I18nTextInput,
required=False, required=False,
+1
View File
@@ -223,6 +223,7 @@ class OrganizerSettingsForm(SettingsForm):
'giftcard_length', 'giftcard_length',
'giftcard_expiry_years', 'giftcard_expiry_years',
'locales', 'locales',
'region',
'event_team_provisioning', 'event_team_provisioning',
'primary_color', 'primary_color',
'theme_color_success', 'theme_color_success',
@@ -83,6 +83,7 @@
{% bootstrap_field sform.locales layout="control" %} {% bootstrap_field sform.locales layout="control" %}
{% bootstrap_field sform.locale layout="control" %} {% bootstrap_field sform.locale layout="control" %}
{% bootstrap_field sform.timezone layout="control" %} {% bootstrap_field sform.timezone layout="control" %}
{% bootstrap_field sform.region layout="control" %}
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>{% trans "Attendee data" %}</legend> <legend>{% trans "Attendee data" %}</legend>
@@ -45,6 +45,7 @@
<fieldset> <fieldset>
<legend>{% trans "Localization" %}</legend> <legend>{% trans "Localization" %}</legend>
{% bootstrap_field sform.locales layout="control" %} {% bootstrap_field sform.locales layout="control" %}
{% bootstrap_field sform.region layout="control" %}
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>{% trans "Shop design" %}</legend> <legend>{% trans "Shop design" %}</legend>
+2 -2
View File
@@ -19,7 +19,6 @@ from django.http import (
) )
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse from django.urls import reverse
from django.utils import translation
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.timezone import now from django.utils.timezone import now
from django.utils.translation import gettext, gettext_lazy as _ 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.plugins.stripe.payment import StripeSettingsHolder
from pretix.presale.style import regenerate_css from pretix.presale.style import regenerate_css
from ...base.i18n import language
from ...base.models.items import ItemMetaProperty from ...base.models.items import ItemMetaProperty
from ...base.settings import SETTINGS_AFFECTING_CSS, LazyI18nStringList from ...base.settings import SETTINGS_AFFECTING_CSS, LazyI18nStringList
from ..logdisplay import OVERVIEW_BANLIST from ..logdisplay import OVERVIEW_BANLIST
@@ -659,7 +659,7 @@ class MailSettingsPreview(EventPermissionRequiredMixin, View):
if matched is not None: if matched is not None:
idx = matched.group('idx') idx = matched.group('idx')
if idx in self.supported_locale: 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( msgs[self.supported_locale[idx]] = markdown_compile_email(
v.format_map(self.placeholders(preview_item)) v.format_map(self.placeholders(preview_item))
) )
+4 -4
View File
@@ -666,7 +666,7 @@ class OrderCancellationRequestDelete(OrderView):
}, user=self.request.user) }, user=self.request.user)
messages.success(self.request, _('The request has been removed. If you want, you can now inform the 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={ return redirect(reverse('control:event.order.sendmail', kwargs={
'event': self.request.event.slug, 'event': self.request.event.slug,
'organizer': self.request.event.organizer.slug, 'organizer': self.request.event.organizer.slug,
@@ -937,7 +937,7 @@ class OrderRefundView(OrderView):
if giftcard_value and self.order.email: if giftcard_value and self.order.email:
messages.success(self.request, _('A new gift card was created. You can now send the user their ' messages.success(self.request, _('A new gift card was created. You can now send the user their '
'gift card code.')) '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={ return redirect(reverse('control:event.order.sendmail', kwargs={
'event': self.request.event.slug, 'event': self.request.event.slug,
'organizer': self.request.event.organizer.slug, 'organizer': self.request.event.organizer.slug,
@@ -1779,7 +1779,7 @@ class OrderSendMail(EventPermissionRequiredMixin, OrderViewMixin, FormView):
code=self.kwargs['code'].upper() code=self.kwargs['code'].upper()
) )
self.preview_output = {} 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_context = get_email_context(event=order.event, order=order)
email_template = LazyI18nString(form.cleaned_data['message']) email_template = LazyI18nString(form.cleaned_data['message'])
email_subject = str(form.cleaned_data['subject']).format_map(TolerantDict(email_context)) email_subject = str(form.cleaned_data['subject']).format_map(TolerantDict(email_context))
@@ -1842,7 +1842,7 @@ class OrderPositionSendMail(OrderSendMail):
attendee_email__isnull=False attendee_email__isnull=False
) )
self.preview_output = {} 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_context = get_email_context(event=position.order.event, order=position.order, position=position)
email_template = LazyI18nString(form.cleaned_data['message']) email_template = LazyI18nString(form.cleaned_data['message'])
email_subject = str(form.cleaned_data['subject']).format_map(TolerantDict(email_context)) email_subject = str(form.cleaned_data['subject']).format_map(TolerantDict(email_context))
+1 -1
View File
@@ -188,7 +188,7 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView):
pass pass
if "preview" in request.POST: 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() p = self._get_preview_position()
fname, mimet, data = self.generate( fname, mimet, data = self.generate(
p, p,
+3 -2
View File
@@ -1,8 +1,9 @@
from django.core.cache import cache from django.core.cache import cache
from django.utils.translation import get_language
from django_countries import Countries from django_countries import Countries
from django_countries.fields import CountryField from django_countries.fields import CountryField
from pretix.base.i18n import get_language_without_region
class CachedCountries(Countries): class CachedCountries(Countries):
_cached_lists = {} _cached_lists = {}
@@ -14,7 +15,7 @@ class CachedCountries(Countries):
django-countries performs a unicode-aware sorting based on pyuca which is incredibly django-countries performs a unicode-aware sorting based on pyuca which is incredibly
slow. slow.
""" """
cache_key = "countries:all:{}".format(get_language()) cache_key = "countries:all:{}".format(get_language_without_region())
if self.cache_subkey: if self.cache_subkey:
cache_key += ":" + self.cache_subkey cache_key += ":" + self.cache_subkey
if cache_key in self._cached_lists: if cache_key in self._cached_lists:
@@ -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'
]
+1
View File
@@ -126,6 +126,7 @@ def get_javascript_format_without_seconds(format_name):
def get_moment_locale(locale=None): def get_moment_locale(locale=None):
cur_lang = locale or translation.get_language() cur_lang = locale or translation.get_language()
cur_lang = cur_lang.lower()
if cur_lang in moment_locales: if cur_lang in moment_locales:
return cur_lang return cur_lang
if '-' in cur_lang or '_' in cur_lang: if '-' in cur_lang or '_' in cur_lang:
+1 -1
View File
@@ -144,7 +144,7 @@ def render_pdf(event, positions, opt):
offsetx = opt['margins'][3] + (i % opt['cols']) * opt['offsets'][0] offsetx = opt['margins'][3] + (i % opt['cols']) * opt['offsets'][0]
offsety = opt['margins'][2] + (opt['rows'] - 1 - i // opt['cols']) * opt['offsets'][1] offsety = opt['margins'][2] + (opt['rows'] - 1 - i // opt['cols']) * opt['offsets'][1]
p.translate(offsetx, offsety) 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) r.draw_page(p, op.order, op, show_page=False)
p.translate(-offsetx, -offsety) p.translate(-offsetx, -offsety)
+1 -1
View File
@@ -26,7 +26,7 @@ logger = logging.getLogger(__name__)
def notify_incomplete_payment(o: Order): 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_template = o.event.settings.mail_text_order_expire_warning
email_context = get_email_context(event=o.event, order=o) email_context = get_email_context(event=o.event, order=o)
email_subject = gettext('Your order received an incomplete payment: %(code)s') % {'code': o.code} email_subject = gettext('Your order received an incomplete payment: %(code)s') % {'code': o.code}
+2 -2
View File
@@ -52,7 +52,7 @@ def send_mails(event: Event, user: int, subject: dict, message: dict, orders: li
continue continue
try: 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) email_context = get_email_context(event=event, order=o, position_or_address=p, position=p)
mail( mail(
p.attendee_email, 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: if send_to_order and o.email:
try: 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) email_context = get_email_context(event=event, order=o, position_or_address=ia)
mail( mail(
o.email, o.email,
+1 -1
View File
@@ -127,7 +127,7 @@ class SenderView(EventPermissionRequiredMixin, FormView):
if self.request.POST.get("action") == "preview": if self.request.POST.get("action") == "preview":
for l in self.request.event.settings.locales: for l in self.request.event.settings.locales:
with language(l): with language(l, self.request.event.settings.region):
context_dict = TolerantDict() context_dict = TolerantDict()
for k, v in get_available_placeholders(self.request.event, ['event', 'order', for k, v in get_available_placeholders(self.request.event, ['event', 'order',
'position_or_address']).items(): 'position_or_address']).items():
@@ -82,7 +82,7 @@ class AllTicketsPDF(BaseExporter):
if op.order.event != o.event: if op.order.event != o.event:
o = PdfTicketOutput(op.event) o = PdfTicketOutput(op.event)
with language(op.order.locale): with language(op.order.locale, o.event.settings.region):
layout = o.layout_map.get( layout = o.layout_map.get(
(op.item_id, op.order.sales_channel), (op.item_id, op.order.sales_channel),
o.layout_map.get( o.layout_map.get(
@@ -79,7 +79,7 @@ class PdfTicketOutput(BaseTicketOutput):
def generate_order(self, order: Order): def generate_order(self, order: Order):
merger = PdfFileMerger() merger = PdfFileMerger()
with language(order.locale): with language(order.locale, self.event.settings.region):
for op in order.positions_with_tickets: for op in order.positions_with_tickets:
layout = override_layout.send_chained( layout = override_layout.send_chained(
order.event, 'layout', orderposition=op, layout=self.layout_map.get( 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) outbuffer = self._draw_page(layout, op, order)
return 'order%s%s.pdf' % (self.event.slug, order.code), 'application/pdf', outbuffer.read() return 'order%s%s.pdf' % (self.event.slug, order.code), 'application/pdf', outbuffer.read()
+2 -1
View File
@@ -12,6 +12,7 @@ from pretix.helpers.i18n import (
get_javascript_format_without_seconds, get_moment_locale, get_javascript_format_without_seconds, get_moment_locale,
) )
from ..base.i18n import get_language_without_region
from .signals import ( from .signals import (
footer_link, global_html_footer, global_html_head, global_html_page_header, footer_link, global_html_footer, global_html_head, global_html_page_header,
html_footer, html_head, 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_date_format'] = get_javascript_format_without_seconds('DATE_INPUT_FORMATS')
ctx['js_time_format'] = get_javascript_format_without_seconds('TIME_INPUT_FORMATS') ctx['js_time_format'] = get_javascript_format_without_seconds('TIME_INPUT_FORMATS')
ctx['js_locale'] = get_moment_locale() 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['settings'] = pretix_settings
ctx['django_settings'] = settings ctx['django_settings'] = settings
+4 -1
View File
@@ -329,7 +329,10 @@ def iframe_entry_view_wrapper(view_func):
locale = request.GET.get('locale') locale = request.GET.get('locale')
if locale and locale in [lc for lc, ll in settings.LANGUAGES]: 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) resp = view_func(request, *args, **kwargs)
max_age = 10 * 365 * 24 * 60 * 60 max_age = 10 * 365 * 24 * 60 * 60
set_cookie_without_samesite( set_cookie_without_samesite(
+1 -1
View File
@@ -599,7 +599,7 @@ class OrganizerIcalDownload(OrganizerViewMixin, View):
) )
if 'locale' in request.GET and request.GET.get('locale') in dict(settings.LANGUAGES): 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) cal = get_ical(events)
else: else:
cal = get_ical(events) cal = get_ical(events)
+2 -2
View File
@@ -1,7 +1,6 @@
from django.conf import settings from django.conf import settings
from django.contrib import messages from django.contrib import messages
from django.shortcuts import get_object_or_404, redirect, render 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.decorators import method_decorator
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _, pgettext_lazy 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.multidomain.urlreverse import eventreverse
from pretix.presale.views import EventViewMixin from pretix.presale.views import EventViewMixin
from ...base.i18n import get_language_without_region
from ...base.models import Item, ItemVariation, WaitingListEntry from ...base.models import Item, ItemVariation, WaitingListEntry
from ..forms.waitinglist import WaitingListForm from ..forms.waitinglist import WaitingListForm
from . import allow_frame_if_namespaced from . import allow_frame_if_namespaced
@@ -27,7 +27,7 @@ class WaitingView(EventViewMixin, FormView):
kwargs['event'] = self.request.event kwargs['event'] = self.request.event
kwargs['instance'] = WaitingListEntry( kwargs['instance'] = WaitingListEntry(
item=self.item_and_variation[0], variation=self.item_and_variation[1], 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 subevent=self.subevent
) )
return kwargs return kwargs
+2 -1
View File
@@ -302,8 +302,9 @@ class WidgetAPIProductList(EventListMixin, View):
return self._get_event_view(request, **kwargs) return self._get_event_view(request, **kwargs)
def dispatch(self, request, *args, **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]: 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) return self.get(request, **kwargs)
else: else:
return self.get(request, **kwargs) return self.get(request, **kwargs)
+33 -6
View File
@@ -1,15 +1,42 @@
from pretix.base.i18n import language from django.utils.translation import get_language
from pretix.base.i18n import get_language_without_region, language
from pretix.helpers.i18n import get_javascript_format, get_moment_locale from pretix.helpers.i18n import get_javascript_format, get_moment_locale
def test_js_formats(): def test_js_formats():
with language('de'): with language('de'):
assert get_javascript_format('DATETIME_INPUT_FORMATS') == 'DD.MM.YYYY HH:mm:ss' assert get_javascript_format('DATE_INPUT_FORMATS') == 'DD.MM.YYYY'
with language('en'): with language('en'):
assert get_javascript_format('DATETIME_INPUT_FORMATS') == 'YYYY-MM-DD HH:mm:ss' assert get_javascript_format('DATE_INPUT_FORMATS') == 'YYYY-MM-DD'
with language('en-US'):
assert get_javascript_format('DATE_INPUT_FORMATS') == 'MM/DD/YYYY'
def test_get_locale(): def test_get_locale():
get_moment_locale('af') == 'af' assert get_moment_locale('af') == 'af'
get_moment_locale('de_Informal') == 'de' assert get_moment_locale('de_Informal') == 'de'
get_moment_locale('de-AT') == 'de' assert get_moment_locale('de-US') == 'de'
assert get_moment_locale('en-US') == 'en'
assert get_moment_locale('en-CA') == 'en-ca'
def test_set_region():
with language('de'):
assert get_language() == 'de'
assert get_language_without_region() == 'de'
with language('de', 'US'):
assert get_language() == 'de-us'
assert get_language_without_region() == 'de'
with language('de', 'DE'):
assert get_language() == 'de-de'
assert get_language_without_region() == 'de'
with language('de-informal', 'DE'):
assert get_language() == 'de-informal'
assert get_language_without_region() == 'de-informal'
with language('pt', 'PT'):
assert get_language() == 'pt-pt'
assert get_language_without_region() == 'pt-pt'
with language('pt-pt', 'BR'):
assert get_language() == 'pt-pt'
assert get_language_without_region() == 'pt-pt'
+14
View File
@@ -2683,6 +2683,20 @@ class CheckoutTestCase(BaseCheckoutTestCase, TestCase):
o.payments.first().confirm() o.payments.first().confirm()
assert len(djmail.outbox) == 0 assert len(djmail.outbox) == 0
def test_locale_region_not_saved(self):
self.event.settings.origin = 'US'
self.event.settings.locales = ['de']
self.event.settings.locale = 'de'
with scopes_disabled():
CartPosition.objects.create(
event=self.event, cart_id=self.session_key, item=self.ticket,
price=0, expires=now() + timedelta(minutes=10)
)
self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
with scopes_disabled():
self.assertEqual(Order.objects.first().locale, 'de')
class QuestionsTestCase(BaseCheckoutTestCase, TestCase): class QuestionsTestCase(BaseCheckoutTestCase, TestCase):
+47 -1
View File
@@ -27,7 +27,7 @@ class EventTestMixin:
self.orga = Organizer.objects.create(name='CCC', slug='ccc') self.orga = Organizer.objects.create(name='CCC', slug='ccc')
self.event = Event.objects.create( self.event = Event.objects.create(
organizer=self.orga, name='30C3', slug='30c3', organizer=self.orga, name='30C3', slug='30c3',
date_from=datetime.datetime(now().year + 1, 12, 26, tzinfo=datetime.timezone.utc), date_from=datetime.datetime(now().year + 1, 12, 26, 14, 0, tzinfo=datetime.timezone.utc),
live=True, sales_channels=['web', 'bar'] live=True, sales_channels=['web', 'bar']
) )
self.user = User.objects.create_user('dummy@dummy.dummy', 'dummy') self.user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
@@ -1306,3 +1306,49 @@ class EventSlugBanlistValidatorTest(EventTestMixin, SoupTest):
event.save() event.save()
self.assertEqual(Event.objects.filter(name='download').count(), 0) self.assertEqual(Event.objects.filter(name='download').count(), 0)
class EventLocaleTest(EventTestMixin, SoupTest):
@scopes_disabled()
def setUp(self):
super().setUp()
self.event.settings.locales = ['de', 'en']
self.event.settings.locale = 'de'
self.event.settings.timezone = 'UTC'
def test_german_by_default(self):
response = self.client.get(
'/%s/%s/' % (self.orga.slug, self.event.slug)
)
self.assertEqual(response.status_code, 200)
self.assertIn('26. Dezember', response.rendered_content)
self.assertIn('14:00', response.rendered_content)
def test_english_no_region_set(self):
self.client.get('/locale/set?locale=en')
response = self.client.get(
'/%s/%s/' % (self.orga.slug, self.event.slug)
)
self.assertEqual(response.status_code, 200)
self.assertIn('Dec. 26,', response.rendered_content)
self.assertIn('14:00', response.rendered_content)
def test_english_region_US(self):
self.event.settings.region = 'US'
self.client.get('/locale/set?locale=en')
response = self.client.get(
'/%s/%s/' % (self.orga.slug, self.event.slug)
)
self.assertEqual(response.status_code, 200)
self.assertIn('Dec. 26,', response.rendered_content)
self.assertIn('2 p.m.', response.rendered_content)
def test_german_region_US(self):
self.event.settings.region = 'US'
self.client.get('/locale/set?locale=de')
response = self.client.get(
'/%s/%s/' % (self.orga.slug, self.event.slug)
)
self.assertEqual(response.status_code, 200)
self.assertIn('26. Dezember', response.rendered_content)
self.assertIn('14:00', response.rendered_content)