feat: public events/polls, friends graph, captcha, SSE hardening, and admin UX
Deploy / release (push) Skipped
Deploy / deploy (push) Successful in 2m55s

Ship product gaps: register/vote pages, friend add/accept/decline/remove, email verify TTL, captcha on login/forgot, soft-fail user actions, SSE abort/shared client, Commando Centrum error toasts, admin delete for events/polls, and IT/NL i18n fills.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
SimoandCursor committed 2026-07-21 21:08:33 +02:00
1 parent 2ff08e5127
commit ed7db6e048
76 files changed
+4834 -1376

No files matched your search

+210 -22
View File
@@ -7,6 +7,8 @@
"staff": "Team",
"rankings": "Rankings",
"guilds": "Gildes",
"events": "Evenementen",
"polls": "Peilingen",
"leaderboards": "Klassementen",
"rareValues": "Zeldzame waarden",
"shop": "Winkel",
@@ -39,6 +41,15 @@
"lightMode": "Lichte modus",
"darkMode": "Donkere modus"
},
"emails": {
"verify": {
"subject": "Verifieer je e-mail · {hotel}",
"heading": "Verifieer je e-mail",
"body": "Welkom bij {hotel}! Bevestig dit e-mailadres om je account af te ronden.",
"button": "E-mail verifiëren",
"fallback": "Werkt de knop niet? Plak dan deze link in je browser:"
}
},
"pages": {
"error": {
"code": "Fout",
@@ -88,7 +99,16 @@
"emptySubtitle": "Je hebt nog geen vrienden toegevoegd",
"subtitle": "{count, plural, one {Je hebt # vriend} other {Je hebt # vrienden}}",
"emptyState": "Voeg vrienden toe in het hotel en ze verschijnen hier.",
"noMotto": "Geen motto"
"noMotto": "Geen motto",
"remove": "Verwijderen",
"success": {
"removed": "Vriend verwijderd."
},
"errors": {
"notFound": "Die vriendschap is niet gevonden.",
"invalid": "Ongeldige vriend.",
"error": "Er ging iets mis. Probeer het opnieuw."
}
},
"messages": {
"title": "Berichten",
@@ -99,11 +119,22 @@
"requestsEmptyState": "Geen openstaande vriendschapsverzoeken.",
"wantsToBeFriend": "wil je vriend zijn.",
"accept": "Accepteren",
"decline": "Weigeren",
"offlineTitle": "Offline berichten",
"offlineEmptySubtitle": "Je inbox is leeg",
"offlineSubtitle": "{count, plural, one {# bericht ontvangen} other {# berichten ontvangen}}",
"offlineEmptyState": "Je hebt geen offline berichten.",
"unknownUser": "Gebruiker #{id}"
"unknownUser": "Gebruiker #{id}",
"success": {
"accepted": "Vriendschapsverzoek geaccepteerd.",
"declined": "Vriendschapsverzoek geweigerd."
},
"errors": {
"notFound": "Dat vriendschapsverzoek is niet gevonden.",
"unauthorized": "Je kunt dit vriendschapsverzoek niet behandelen.",
"invalid": "Ongeldig vriendschapsverzoek.",
"error": "Er ging iets mis. Probeer het opnieuw."
}
},
"profile": {
"noMotto": "Geen motto",
@@ -131,7 +162,26 @@
"roomsTitle": "Kamers",
"roomsSubtitle": "Kamers die ze bezitten",
"roomsEmpty": "Geen kamers om te tonen.",
"roomUntitled": "Naamloze kamer"
"roomUntitled": "Naamloze kamer",
"addFriend": "Vriend toevoegen",
"alreadyFriends": "Jullie zijn vrienden",
"requestPending": "Verzoek in behandeling",
"respondInMessages": "Reageer in Berichten",
"success": {
"sent": "Vriendschapsverzoek verzonden.",
"guestbook": "Gastenboekbericht geplaatst."
},
"errors": {
"self": "Je kunt jezelf geen vriendschapsverzoek sturen.",
"alreadyFriends": "Jullie zijn al vrienden.",
"alreadyPending": "Je hebt deze gebruiker al een verzoek gestuurd.",
"incomingPending": "Deze gebruiker heeft jou al een verzoek gestuurd — accepteer of weiger het in Berichten.",
"empty": "Schrijf een bericht voordat je plaatst.",
"moderated": "Dat bericht is geblokkeerd door moderatie.",
"ratelimit": "Je plaatst te snel. Wacht even en probeer opnieuw.",
"invalid": "Ongeldig verzoek.",
"error": "Er ging iets mis. Probeer het opnieuw."
}
},
"rankings": {
"title": "Rankings",
@@ -185,7 +235,18 @@
"addComment": "Reactie toevoegen",
"commentPlaceholder": "Schrijf je reactie…",
"postComment": "Reactie plaatsen",
"toComment": "om een reactie achter te laten."
"toComment": "om een reactie achter te laten.",
"success": {
"posted": "Reactie geplaatst."
},
"errors": {
"empty": "Schrijf een reactie voordat je plaatst.",
"moderated": "Die reactie is geblokkeerd door moderatie.",
"ratelimit": "Je reageert te snel. Wacht even en probeer opnieuw.",
"invalid": "Ongeldige reactie.",
"notFound": "Dat artikel is niet gevonden.",
"error": "Er ging iets mis. Probeer het opnieuw."
}
},
"photos": {
"title": "Foto's",
@@ -269,7 +330,10 @@
"pinned": "Vastgepind",
"locked": "Gesloten",
"userNumber": "Gebruiker #{id}",
"unknown": "Onbekend"
"unknown": "Onbekend",
"success": {
"posted": "Discussie geplaatst."
}
},
"guildForumNew": {
"backToForum": "← Terug naar forum",
@@ -281,7 +345,13 @@
"messageLabel": "Bericht",
"messagePlaceholder": "Schrijf je openingsbericht…",
"postThread": "Discussie plaatsen",
"cancel": "Annuleren"
"cancel": "Annuleren",
"errors": {
"invalid": "Onderwerp en bericht zijn verplicht.",
"notFound": "Dat gilde is niet gevonden.",
"ratelimit": "Je plaatst te snel. Wacht even en probeer opnieuw.",
"error": "Er ging iets mis. Probeer het opnieuw."
}
},
"shop": {
"title": "Winkel",
@@ -395,7 +465,16 @@
"colStatus": "Status",
"colCreated": "Aangemaakt",
"statusOpen": "Open",
"statusClosed": "Gesloten"
"statusClosed": "Gesloten",
"success": {
"created": "Ticket ingediend."
},
"errors": {
"invalid": "Vul een onderwerp en beschrijving in.",
"moderated": "Dat ticket is geblokkeerd door moderatie.",
"ratelimit": "Je dient te snel in. Wacht even en probeer opnieuw.",
"error": "Er ging iets mis. Probeer het opnieuw."
}
},
"applyStaff": {
"title": "Solliciteer voor {hotel} team",
@@ -472,7 +551,68 @@
"loginToPost": "Log in om een shout te plaatsen.",
"latestTitle": "Laatste shouts",
"latestSubtitle": "De meest recente berichten van luisteraars",
"empty": "Nog geen shouts. Wees de eerste!"
"empty": "Nog geen shouts. Wees de eerste!",
"success": {
"posted": "Shout geplaatst."
},
"errors": {
"invalid": "Schrijf een bericht voordat je shouwt.",
"moderated": "Die shout is geblokkeerd door moderatie.",
"ratelimit": "Je shout te snel. Wacht even en probeer opnieuw.",
"error": "Er ging iets mis. Probeer het opnieuw."
}
},
"events": {
"title": "Evenementen",
"subtitle": "Aankomende en recente hotelevenementen waaraan je kunt meedoen",
"empty": "Nog geen evenementen.",
"back": "← Evenementen",
"when": "Wanneer",
"status": "Status",
"statusPublished": "Open",
"statusCompleted": "Afgerond",
"registrations": "Inschrijvingen",
"registered": "{count} ingeschreven",
"spots": "{count} / {max} plaatsen",
"registerTitle": "Inschrijven",
"registerSubtitle": "Reserveer je plek voor dit evenement",
"register": "Inschrijven voor evenement",
"registering": "Bezig met inschrijven…",
"registerSuccess": "Je bent ingeschreven!",
"registerError": "Inschrijven voor dit evenement is mislukt.",
"loginToRegister": "Log in om je in te schrijven voor dit evenement.",
"alreadyRegistered": "Je bent al ingeschreven.",
"eventEnded": "Dit evenement is afgelopen.",
"eventFull": "Dit evenement is vol.",
"cannotRegister": "Inschrijven is niet beschikbaar.",
"prizesTitle": "Prijzen",
"winnersTitle": "Winnaars",
"unknownUser": "Gebruiker #{id}"
},
"polls": {
"title": "Peilingen",
"subtitle": "Deel je mening in community-peilingen",
"empty": "Nog geen peilingen.",
"back": "← Peilingen",
"statusActive": "Open",
"statusClosed": "Gesloten",
"questionsCount": "{count} vragen",
"ends": "Eindigt {date}",
"voteTitle": "Breng je stem uit",
"voteSubtitle": "Beantwoord elke vraag hieronder",
"submitVote": "Stem indienen",
"voting": "Bezig met indienen…",
"voteSuccess": "Bedankt voor je stem!",
"voteError": "Je stem kon niet worden ingediend.",
"loginToVote": "Log in om te stemmen in deze peiling.",
"alreadyVoted": "Je hebt al gestemd.",
"pollClosed": "Deze peiling is gesloten.",
"cannotVote": "Stemmen is niet beschikbaar.",
"textPlaceholder": "Jouw antwoord…",
"resultsTitle": "Resultaten",
"voteCount": "{count} ({pct}%)",
"totalVotes": "{count} stemmen in totaal",
"textResponses": "{count} vrije antwoorden"
},
"radioContests": {
"title": "Radiowedstrijden",
@@ -627,7 +767,9 @@
"createOne": "Account aanmaken",
"forgotPassword": "Wachtwoord vergeten?",
"errorInvalidCredentials": "Ongeldige gebruikersnaam of wachtwoord",
"errorInvalid2fa": "Ongeldige 2FA-code"
"errorInvalid2fa": "Ongeldige 2FA-code",
"errorUnverified": "Verifieer je e-mail voordat je inlogt.",
"errorCaptcha": "Captcha-verificatie mislukt. Probeer het opnieuw."
},
"register": {
"title": "Account aanmaken",
@@ -663,7 +805,8 @@
"sentNotice": "Als dat e-mailadres geregistreerd is, is er een resetlink verzonden. Controleer je inbox.",
"emailPlaceholder": "Je e-mailadres",
"sendResetLink": "Resetlink verzenden",
"backToLogin": "Terug naar inloggen"
"backToLogin": "Terug naar inloggen",
"errorCaptcha": "Captcha-verificatie mislukt. Probeer het opnieuw."
},
"reset": {
"title": "Nieuw wachtwoord instellen",
@@ -1972,16 +2115,35 @@
"title": "Teamactiviteiten",
"subtitle": "Audittrail van teamacties in het beheerderspaneel",
"noLogs": "Nog geen teamactiviteit geregistreerd",
"noResults": "Geen overeenkomende activiteiten",
"colId": "ID",
"colUser": "Gebruiker",
"colStaff": "Team",
"colAction": "Actie",
"colTarget": "Doelwit",
"colDescription": "Beschrijving",
"colIp": "IP",
"colWhen": "Wanneer",
"colTimestamp": "Tijdstempel",
"filterByUser": "Filteren op gebruiker…",
"filterByAction": "Filteren op actie…",
"clearFilters": "Filters wissen",
"searchPlaceholder": "Zoek op actie, beschrijving of IP…",
"totalActivities": "Totaal activiteiten",
"onThisPage": "Op deze pagina",
"recent": "Laatste minuut",
"authorizationErrors": "Autorisatiefouten",
"authorizationOnly": "Alleen auth-fouten",
"allActivities": "Alle activiteiten",
"staffId": "Team-ID",
"actionType": "Actietype",
"filter": "Filter",
"activities": "{count} activiteiten",
"staffWithId": "#{id} {name}",
"userPrefix": "gebruiker",
"pagination": "Pagina {page} van {pages} · {total} totaal",
"next": "Volgende",
"prev": "Vorige",
"unknownUser": "onbekend",
"emptyValue": "-",
"colTime": "Tijd",
@@ -2014,10 +2176,18 @@
"wordfilter": {
"title": "Woordfilter",
"subtitle": "Beheer gefilterde woorden en zinnen in het hotel",
"filteredWords": "Gefilterde woorden",
"addWord": "Woord toevoegen",
"addWords": "Woorden toevoegen",
"words": "Woorden",
"noWords": "Nog geen gefilterde woorden",
"delete": "Verwijderen",
"confirmDeleteTitle": "Woord verwijderen",
"confirmDelete": "\"{word}\" verwijderen uit het filter?",
"added": "Woord toegevoegd aan filter",
"deleted": "Woord verwijderd uit filter",
"addError": "Kon woord niet toevoegen (bestaat mogelijk al)",
"deleteError": "Kon woord niet verwijderen",
"colId": "ID",
"colWord": "Woord",
"colAddedBy": "Toegevoegd door",
@@ -2290,6 +2460,9 @@
"uploading": "Bezig met uploaden…",
"delete": "Verwijderen",
"deleting": "Bezig met verwijderen…",
"cancel": "Annuleren",
"saved": "Favicon succesvol opgeslagen!",
"deleted": "Favicon succesvol verwijderd!",
"error": "Er is iets misgegaan",
"confirmDelete": "Favicon verwijderen? De site zal geen aangepaste favicon meer tonen.",
"generator": {
@@ -2309,6 +2482,13 @@
"delete": "Verwijderen",
"noSettings": "Geen instellingen gevonden",
"confirmDelete": "Instelling \"{key}\" verwijderen?",
"keysInDatabase": "{count} sleutels in de database",
"managedInForm": "{count} beheerd in het formulier",
"documented": "{count} gedocumenteerd",
"saved": "Instellingen opgeslagen.",
"saveFailed": "Instellingen opslaan mislukt.",
"saveSettings": "Instellingen opslaan",
"viewOnly": "Je kunt instellingen bekijken, maar hebt bewerkingsrechten nodig.",
"colKey": "Sleutel",
"colValue": "Waarde",
"colUpdated": "Bijgewerkt",
@@ -2325,19 +2505,27 @@
},
"import": {
"title": "Import",
"subtitle": "Import assets from external sources into your hotel.",
"subtitle": "Importeer assets van externe bronnen naar je hotel.",
"badges": "Badges",
"badgesDescription": "Import and manage badge assets from external sources.",
"furni": "Furni",
"furniDescription": "Import furniture definitions, SWF files, and icons.",
"clothing": "Clothing",
"clothingDescription": "Import clothing and wearables for avatars.",
"effects": "Effects",
"effectsDescription": "Import avatar effects and animations.",
"pets": "Pets",
"petsDescription": "Import pet data and icons.",
"clone": "Clone",
"cloneDescription": "Clone existing items from one source to another."
"badgesDescription": "Importeer en beheer badge-assets van externe bronnen.",
"furni": "Meubels",
"furniDescription": "Importeer meubeldefinities, SWF-bestanden en iconen.",
"clothing": "Kleding",
"clothingDescription": "Importeer kleding en wearables voor avatars.",
"effects": "Effecten",
"effectsDescription": "Importeer avatar-effecten en animaties.",
"pets": "Huisdieren",
"petsDescription": "Importeer huisdiergegevens en iconen.",
"clone": "Klonen",
"cloneDescription": "Kloon bestaande items van de ene bron naar de andere.",
"upload": "Upload .nitro",
"uploadDescription": "Upload .nitro-bundles en iconen direct zonder SWF-conversie.",
"sync": "Auto-sync",
"syncDescription": "Importeer automatisch ontbrekende meubels van alle geconfigureerde bronnen.",
"repairIcons": "Iconen repareren",
"repairIconsDescription": "Download ontbrekende icoonbestanden voor meubels die al in de database staan.",
"audit": "Cataloguscontrole",
"auditDescription": "Scan je catalogus op ontbrekende items, kapotte verwijzingen en items die ontbreken in kloonbronnen."
},
"translations": {
"title": "Translations",