dnscms: translate events app

This commit is contained in:
2026-05-19 21:25:28 +02:00
parent 696e6b8f11
commit 9ca9f5db11
5 changed files with 356 additions and 78 deletions
+11 -8
View File
@@ -2,6 +2,8 @@ from urllib.parse import urlencode
from django.urls import reverse
from django.utils import timezone
from django.utils.translation import gettext, gettext_lazy as _
from django.utils.translation import ngettext
from wagtail.admin.ui.tables import Column, DateColumn
from wagtail.admin.ui.tables.pages import PageStatusColumn, PageTitleColumn
from wagtail.admin.views.pages.choose_parent import ChooseParentView
@@ -18,8 +20,9 @@ class EventDateColumn(Column):
return ""
if len(occurrences) == 1:
local = timezone.localtime(occurrences[0].start)
return local.strftime("%Y-%m-%d kl %H:%M")
return f"{len(occurrences)} forekomster"
return local.strftime(gettext("%Y-%m-%d at %H:%M"))
count = len(occurrences)
return ngettext("%(count)d occurrence", "%(count)d occurrences", count) % {"count": count}
class OrganizersColumn(Column):
@@ -64,22 +67,22 @@ class EventPageListingViewSet(PageListingViewSet):
index_view_class = EventPageIndexView
choose_parent_view_class = EventChooseParentView
icon = "date"
menu_label = "Arrangementer"
menu_label = _("Events")
menu_order = 1
add_to_admin_menu = True
ordering = "-latest_revision_created_at"
columns = [
PageTitleColumn("title", label="Tittel", sort_key="title", classname="title"),
EventDateColumn("event_date", label="Dato", width="13%"),
OrganizersColumn("organizers", label="Arrangører", width="12%"),
PageTitleColumn("title", label=_("Title"), sort_key="title", classname="title"),
EventDateColumn("event_date", label=_("Date"), width="13%"),
OrganizersColumn("organizers", label=_("Organizers"), width="12%"),
DateColumn(
"latest_revision_created_at",
label="Oppdatert",
label=_("Updated"),
sort_key="latest_revision_created_at",
width="10%",
),
PageStatusColumn("status", label="Status", sort_key="live", width="10%"),
PageStatusColumn("status", label=_("Status"), sort_key="live", width="10%"),
]
+70 -61
View File
@@ -75,11 +75,12 @@ class EventCategory(models.Model):
help_text=_("The name of the category as it will appear in URLs."),
)
show_in_filters = models.BooleanField(
default=False, help_text="Skal denne kategorien være mulig å filtrere på i programmet?"
default=False,
help_text=_("Should this category be available as a filter in the event programme?"),
)
PIG_CHOICES = [
("", "Ingen"),
("", _("None")),
] + ALL_PIGS
pig = models.CharField(
@@ -87,14 +88,14 @@ class EventCategory(models.Model):
choices=PIG_CHOICES,
default="",
blank=True,
help_text="Standardgris for arrangementer av denne typen.",
help_text=_("Default pig for events of this kind."),
)
panels = [
TitleFieldPanel("name"),
FieldPanel("slug"),
FieldPanel("show_in_filters"),
FieldPanel("pig", heading="Gris"),
FieldPanel("pig", heading=_("Pig")),
]
graphql_fields = [
@@ -105,8 +106,8 @@ class EventCategory(models.Model):
]
class Meta:
verbose_name = "Event category"
verbose_name_plural = "Event categories"
verbose_name = _("event category")
verbose_name_plural = _("event categories")
ordering = ["name"]
def __str__(self):
@@ -134,8 +135,8 @@ class EventOrganizerLink(Orderable):
return f"{self.organizer.name}"
class Meta:
verbose_name = "Arrangør"
verbose_name_plural = "Arrangører"
verbose_name = _("organizer")
verbose_name_plural = _("organizers")
constraints = [
UniqueConstraint(
"event", "organizer", name="event_organizer_link_event_organizer_unique"
@@ -163,13 +164,13 @@ class EventOrganizer(ClusterableModel):
blank=True,
on_delete=models.PROTECT,
related_name="organizers",
help_text="Om en samfundsforening eller et utvalg står bak, velg det her.",
help_text=_("If a DNS association or committee is behind it, choose it here."),
)
external_url = models.URLField(
blank=True,
max_length=512,
help_text="Lenke til nettstedet til ekstern arrangør",
help_text=_("Link to the external organizer's website"),
)
panels = [
@@ -178,15 +179,15 @@ class EventOrganizer(ClusterableModel):
FieldPanel(
"association",
widget=AssociationChooserWidget(linked_fields={"association": "#id_association"}),
heading="Intern arrangør",
heading=_("Internal organizer"),
),
MultiFieldPanel(
heading="Ekstern arrangør",
heading=_("External organizer"),
children=[
FieldPanel(
"external_url",
heading="Nettsted",
help_text="La denne stå tom om arrangøren finnes i lista over.",
heading=_("Website"),
help_text=_("Leave this empty if the organizer exists in the list above."),
),
],
),
@@ -200,8 +201,8 @@ class EventOrganizer(ClusterableModel):
]
class Meta:
verbose_name = "Event organizer"
verbose_name_plural = "Event organizers"
verbose_name = _("event organizer")
verbose_name_plural = _("event organizers")
ordering = ["name"]
def __str__(self):
@@ -234,19 +235,19 @@ class EventPage(HeadlessMixin, WPImportedPageMixin, Page):
blank=True,
on_delete=models.SET_NULL,
related_name="+",
help_text=(
"Velg et bilde til bruk i programmet og andre visningsflater. "
"Bør være et bilde eller en illustrasjon uten tekst "
" ikke gjenbruk et Facebook-cover ukritisk!"
help_text=_(
"Choose an image for use in the programme and other surfaces. "
"Should be a photo or an illustration without too much text "
" don't reuse a Facebook cover uncritically!"
),
)
subtitle = models.CharField(
blank=True,
max_length=128,
help_text=(
"En kort tekst som kommer rett under under tittelen. "
"La denne gjerne stå tom om du fikk plass til det meste i tittelen."
help_text=_(
"A short text that appears right below the title. "
"Feel free to leave it empty if you fit most of it in the main title."
),
)
lead = RichTextField(features=["italic", "link"], blank=True)
@@ -262,8 +263,8 @@ class EventPage(HeadlessMixin, WPImportedPageMixin, Page):
)
PIG_CHOICES = [
("", "Ingen"),
("automatic", "Automatisk"),
("", _("None")),
("automatic", _("Automatic")),
] + ALL_PIGS
pig = models.CharField(
@@ -271,21 +272,21 @@ class EventPage(HeadlessMixin, WPImportedPageMixin, Page):
choices=PIG_CHOICES,
default="automatic",
blank=True,
help_text=(
"Grisen som henger på arrangementssiden. "
"Automatisk fører til at en velges basert på arrangementets kategori."
help_text=_(
"The pig that hangs out on the event page. "
"Automatic causes one to be chosen based on the event's category."
),
)
ticket_url = models.URLField(
blank=True,
max_length=1024,
help_text="Lenke direkte til billettkjøp, f.eks. TicketCo eller Ticketmaster",
help_text=_("Direct link to ticket purchase, e.g. TicketCo, Billetto or Ticketmaster"),
)
facebook_url = models.URLField(
blank=True,
max_length=1024,
help_text="Lenke direkte til arrangementet på Facebook",
help_text=_("Direct link to the event on Facebook"),
)
free = models.BooleanField(null=False, default=False)
@@ -294,63 +295,65 @@ class EventPage(HeadlessMixin, WPImportedPageMixin, Page):
price_member = models.CharField(max_length=32, blank=True)
ticket_panels = [
FieldPanel("free", heading="Gratis", help_text="Er dette arrangementet gratis for alle?"),
FieldPanel("free", heading=_("Free"), help_text=_("Is this event free for everyone?")),
MultiFieldPanel(
children=[
FieldRowPanel(
children=[
FieldPanel("price_regular", heading="Ordinær pris"),
FieldPanel("price_student", heading="Pris for studenter"),
FieldPanel("price_member", heading="Pris for medlemmer av DNS"),
FieldPanel("price_regular", heading=_("Regular price")),
FieldPanel("price_student", heading=_("Price for students")),
FieldPanel("price_member", heading=_("Price for DNS members")),
],
help_text="",
),
HelpPanel(
content=mark_safe(
"Skriv <strong>0</strong> om gratis. "
"Tomt felt skjuler priskategorien. Om mulig, skriv kun tall."
_(
"Write <strong>0</strong> for free. "
"An empty field hides the price category. "
"If possible, write digits only."
)
)
),
],
attrs={"id": "specific_pricing_panel"},
),
FieldPanel("ticket_url", heading="Billettkjøpslenke"),
FieldPanel("ticket_url", heading=_("Ticket purchase link")),
]
content_panels = Page.content_panels + [
FieldPanel("subtitle", heading="Undertittel"),
FieldPanel("subtitle", heading=_("Subtitle")),
FieldPanel("featured_image"),
FieldPanel("lead", heading="Ingress"),
FieldPanel("lead", heading=_("Lead")),
FieldPanel("body"),
FieldPanel("categories", widget=forms.CheckboxSelectMultiple),
MultiFieldPanel(
heading="Arrangører",
heading=_("Organizers"),
children=[
HelpPanel(
content=("Hvem står bak arrangementet?"),
content=_("Who is behind the event?"),
),
MultipleChooserPanel(
"organizer_links", chooser_field_name="organizer", label="Arrangør"
"organizer_links", chooser_field_name="organizer", label=_("Organizer")
),
],
),
FieldPanel("pig", heading="Gris"),
FieldPanel("pig", heading=_("Pig")),
FieldPanel(
"facebook_url",
heading="Facebook-lenke",
help_text="Lenke direkte til arrangementet på Facebook.",
heading=_("Facebook link"),
help_text=_("Direct link to the event on Facebook."),
),
MultiFieldPanel(heading="Priser og billettkjøp", children=ticket_panels),
MultiFieldPanel(heading=_("Pricing and tickets"), children=ticket_panels),
MultiFieldPanel(
heading="Dato, tid og lokale",
heading=_("Date, time and venue"),
children=[
HelpPanel(
content=(
"Om arrangementet går over flere dager, "
"legg inn hver dag som en egen forekomst."
content=_(
"If the event spans several days, add each day as a separate occurrence."
),
),
InlinePanel("occurrences", min_num=1, label="Forekomst"),
InlinePanel("occurrences", min_num=1, label=_("Occurrence")),
],
),
]
@@ -553,22 +556,24 @@ class EventOccurrence(Orderable):
blank=True,
max_length=128,
help_text=mark_safe(
"Bruk denne <em>om ingen av lokalene som kan velges til venstre</em> passer. "
"F.eks. <em>Frederikkeplassen</em> eller <em>Sirkusteltet</em>."
_(
"Use this <em>if none of the venues that can be selected on the left</em> fit. "
"E.g. <em>Frederikkeplassen</em> or <em>Sirkusteltet</em>."
)
),
)
panels = [
FieldRowPanel(
children=[
FieldPanel("start", heading="Start"),
FieldPanel("end", heading="Slutt"),
FieldPanel("start", heading=_("Start")),
FieldPanel("end", heading=_("End")),
],
),
FieldRowPanel(
children=[
FieldPanel("venue", heading="Lokale"),
FieldPanel("venue_custom", heading="Lokale som fritekst"),
FieldPanel("venue", heading=_("Venue")),
FieldPanel("venue_custom", heading=_("Venue as free text")),
],
),
]
@@ -583,14 +588,18 @@ class EventOccurrence(Orderable):
def clean(self):
if self.venue and self.venue_custom:
raise ValidationError(
{"venue_custom": "Du kan ikke både velge et lokale og skrive noe i dette feltet."}
{
"venue_custom": _(
"You can't both pick a venue and write something in this field."
)
}
)
if not self.venue and not self.venue_custom:
raise ValidationError({"venue": "Lokale er påkrevd."})
raise ValidationError({"venue": _("Venue is required.")})
def __str__(self):
return f"{self.start}--{self.end}"
class Meta:
verbose_name = "Forekomst"
verbose_name_plural = "Forekomster"
verbose_name = _("occurrence")
verbose_name_plural = _("occurrences")
+5 -4
View File
@@ -1,3 +1,4 @@
from django.utils.translation import gettext_lazy as _
from wagtail.admin.viewsets.chooser import ChooserViewSet
@@ -5,10 +6,10 @@ class EventOrganizerChooserViewSet(ChooserViewSet):
model = "events.EventOrganizer"
icon = "group"
per_page = 30
page_title = "Choose organizers"
choose_one_text = "Choose an organizer"
choose_another_text = "Choose another organizer"
edit_item_text = "Edit this organizer"
page_title = _("Choose organizers")
choose_one_text = _("Choose an organizer")
choose_another_text = _("Choose another organizer")
edit_item_text = _("Edit this organizer")
form_fields = ["name", "association", "external_url"]