feat: shop buy, forum replies, tickets, messages, sessions, and UX hardening
Deploy / release (push) Skipped
Deploy / deploy (push) Failing after 4m23s

Complete remaining product gaps: credit-based shop purchases, guild thread replies, help ticket detail/reply/close, offline message compose, sign-out-everywhere via JWT version, ads delete confirm, soft-fail feedback, rate limits, loading states, and single auth() in site layout.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
SimoandCursor committed 2026-07-21 21:21:02 +02:00
1 parent ed7db6e048
commit 803e8f36c1
47 files changed
+2793 -358

No files matched your search

+156 -9
View File
@@ -112,7 +112,7 @@
},
"messages": {
"title": "Berichten",
"subtitle": "Vriendschapsverzoeken en offline berichten die op je wachten",
"subtitle": "Vriendschapsverzoeken, offline inbox en berichten naar vrienden",
"requestsTitle": "Vriendschapsverzoeken",
"requestsEmptySubtitle": "Niemand heeft nog gevraagd om je vriend te zijn",
"requestsSubtitle": "{count, plural, one {# openstaand verzoek} other {# openstaande verzoeken}}",
@@ -124,18 +124,46 @@
"offlineEmptySubtitle": "Je inbox is leeg",
"offlineSubtitle": "{count, plural, one {# bericht ontvangen} other {# berichten ontvangen}}",
"offlineEmptyState": "Je hebt geen offline berichten.",
"composeTitle": "Offline bericht sturen",
"composeSubtitle": "Afgeleverd in de hotel messenger wanneer je vriend offline is",
"composeEmptyFriends": "Voeg vrienden toe in het hotel om offline berichten te sturen.",
"composeFriendLabel": "Vriend",
"composeFriendPlaceholder": "Kies een vriend",
"composeMessageLabel": "Bericht",
"composeMessagePlaceholder": "Schrijf je bericht (max. 500 tekens)…",
"composeSend": "Bericht versturen",
"unknownUser": "Gebruiker #{id}",
"success": {
"accepted": "Vriendschapsverzoek geaccepteerd.",
"declined": "Vriendschapsverzoek geweigerd."
"declined": "Vriendschapsverzoek geweigerd.",
"sent": "Offline bericht verstuurd."
},
"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."
"error": "Er ging iets mis. Probeer het opnieuw.",
"notFriend": "Je kunt alleen vrienden berichten.",
"emptyMessage": "Schrijf een bericht.",
"rateLimited": "Te veel berichten. Wacht even en probeer opnieuw."
}
},
"sessions": {
"title": "Loginactiviteit",
"subtitle": "Recente aanmeldingen en mislukte pogingen op je account",
"recentTitle": "Recente aanmeldingen",
"recentEmpty": "Nog geen aanmeldingen geregistreerd.",
"colWhen": "Wanneer",
"colIp": "IP",
"colDevice": "Apparaat",
"failedTitle": "Mislukte pogingen",
"failedSubtitle": "Verkeerd wachtwoord voor je gebruikersnaam",
"unknownDevice": "Onbekend apparaat",
"signOutEverywhereTitle": "Overal uitloggen",
"signOutEverywhereDescription": "Beëindigt alle websitesessies op elk apparaat. Je moet hier ook opnieuw inloggen.",
"signOutEverywhereButton": "Alle sessies beëindigen",
"successSignedOutAll": "Alle sessies zijn beëindigd."
},
"profile": {
"noMotto": "Geen motto",
"statRank": "Rang",
@@ -237,7 +265,8 @@
"postComment": "Reactie plaatsen",
"toComment": "om een reactie achter te laten.",
"success": {
"posted": "Reactie geplaatst."
"posted": "Reactie geplaatst.",
"reaction": "Reactie bijgewerkt."
},
"errors": {
"empty": "Schrijf een reactie voordat je plaatst.",
@@ -353,6 +382,35 @@
"error": "Er ging iets mis. Probeer het opnieuw."
}
},
"guildForumThread": {
"backToForum": "← Terug naar forum",
"unnamedGuild": "Naamloos gilde",
"noSubject": "(geen onderwerp)",
"inGuildForum": "{guild} forum",
"pinned": "Vastgezet",
"locked": "Vergrendeld",
"postsTitle": "Berichten",
"postsSubtitle": "{count, plural, one {# bericht} other {# berichten}}",
"postsEmpty": "Geen berichten in deze discussie.",
"userNumber": "Gebruiker #{id}",
"replyTitle": "Reageren",
"replySubtitle": "Voeg je bericht toe aan deze discussie",
"replyPlaceholder": "Schrijf je reactie…",
"replySubmit": "Reactie plaatsen",
"lockedHint": "Deze discussie is vergrendeld — nieuwe reacties zijn uitgeschakeld.",
"loginToReply": "Log in om te reageren.",
"loginLink": "Inloggen",
"success": {
"replied": "Reactie geplaatst."
},
"errors": {
"invalid": "Voer een bericht in.",
"notFound": "Die discussie is niet gevonden.",
"locked": "Deze discussie is vergrendeld.",
"ratelimit": "Je plaatst te snel. Wacht even en probeer opnieuw.",
"error": "Er ging iets mis. Probeer het opnieuw."
}
},
"shop": {
"title": "Winkel",
"subtitle": "Vul je account op en pak exclusieve pakketten, valuta en badges.",
@@ -361,7 +419,22 @@
"allPackages": "Alle pakketten",
"packagesAvailable": "{count, plural, one {# pakket beschikbaar} other {# pakketten beschikbaar}}",
"empty": "Momenteel geen pakketten beschikbaar.",
"buy": "Kopen"
"buy": "Kopen",
"loginToBuy": "Log in om te kopen",
"creditsHint": "Pakketten betaal je met in-game credits. Waardeer eerst op als je meer credits nodig hebt.",
"topUpLink": "Credits opwaarderen",
"priceCredits": "{credits} credits (≈ {usd})",
"priceCreditsShort": "{credits} cr",
"success": {
"bought": "Pakket gekocht — beloningen zijn aan je account toegevoegd.",
"boughtNamed": "Je hebt \"{name}\" gekocht — beloningen zijn aan je account toegevoegd."
},
"errors": {
"invalid": "Dit pakket is niet meer beschikbaar.",
"credits": "Je hebt niet genoeg credits voor dit pakket.",
"ratelimit": "Je koopt te snel achter elkaar. Wacht even en probeer het opnieuw.",
"error": "Er ging iets mis. Probeer het opnieuw."
}
},
"topup": {
"title": "Opwaarderen met PayPal",
@@ -476,6 +549,36 @@
"error": "Er ging iets mis. Probeer het opnieuw."
}
},
"helpTicketDetail": {
"backToList": "← Jouw tickets",
"ticketId": "Ticket #{id}",
"statusOpen": "Open",
"statusClosed": "Gesloten",
"messagesTitle": "Gesprek",
"messagesSubtitle": "{count, plural, one {# bericht} other {# berichten}}",
"you": "Jij",
"userNumber": "Gebruiker #{id}",
"replyTitle": "Reageren",
"replySubtitle": "Stuur een vervolg naar het team",
"replyPlaceholder": "Schrijf je bericht…",
"replySubmit": "Antwoord versturen",
"closeTitle": "Ticket sluiten",
"closeSubtitle": "Markeer dit verzoek als opgelost als je geen hulp meer nodig hebt",
"closeSubmit": "Ticket sluiten",
"closedHint": "Dit ticket is gesloten. Open een nieuw ticket als je meer hulp nodig hebt.",
"success": {
"replied": "Antwoord verstuurd.",
"closed": "Ticket gesloten."
},
"errors": {
"invalid": "Voer een bericht in.",
"notFound": "Dat ticket is niet gevonden.",
"closedTicket": "Dit ticket is gesloten.",
"moderated": "Dat bericht 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",
"subtitle": "We openen af en toe sollicitaties. Als je een positie ziet die perfect bij je past, aarzel dan niet om te solliciteren.",
@@ -486,7 +589,17 @@
"alreadyApplied": "Je hebt al gesolliciteerd voor {rank}",
"aboutYou": "Over jou",
"aboutPlaceholder": "Vertel ons waarom jij een geweldige {rank} zou zijn…",
"applyFor": "Solliciteer voor {rank}"
"applyFor": "Solliciteer voor {rank}",
"success": {
"submitted": "Sollicitatie ingediend — staff bekijkt deze binnenkort."
},
"errors": {
"empty": "Schrijf minstens 10 tekens over jezelf.",
"invalid": "Ongeldige sollicitatie.",
"duplicate": "Je hebt al gesolliciteerd voor deze positie.",
"ratelimit": "Je dient te snel in. Wacht even en probeer opnieuw.",
"error": "Er ging iets mis. Probeer het opnieuw."
}
},
"applyTeam": {
"title": "Solliciteer voor het {hotel} team",
@@ -496,7 +609,17 @@
"pending": "Je sollicitatie is in behandeling",
"aboutYou": "Over jou",
"aboutPlaceholder": "Vertel ons waarom jij een geweldige aanwinst zou zijn voor {rank}…",
"applyFor": "Solliciteer voor {rank}"
"applyFor": "Solliciteer voor {rank}",
"success": {
"submitted": "Sollicitatie ingediend — staff bekijkt deze binnenkort."
},
"errors": {
"empty": "Schrijf minstens 10 tekens over jezelf.",
"invalid": "Ongeldige sollicitatie.",
"duplicate": "Je hebt al gesolliciteerd voor dit team.",
"ratelimit": "Je dient te snel in. Wacht even en probeer opnieuw.",
"error": "Er ging iets mis. Probeer het opnieuw."
}
},
"radio": {
"title": "Radio",
@@ -656,7 +779,16 @@
"colStatus": "Status",
"statusPending": "In afwachting",
"statusApproved": "Goedgekeurd",
"statusPlayed": "Gedraaid"
"statusPlayed": "Gedraaid",
"success": {
"posted": "Verzoek ingediend."
},
"errors": {
"empty": "Vul een nummer of artiest in.",
"invalid": "Ongeldig verzoek.",
"ratelimit": "Je dient te snel in. Wacht even en probeer opnieuw.",
"error": "Er ging iets mis. Probeer het opnieuw."
}
},
"client": {
"title": "Nitro client",
@@ -694,7 +826,15 @@
"experiencePlaceholder": "Eerdere DJ- of presentatie-ervaring?",
"motivationLabel": "Motivatie",
"motivationPlaceholder": "Waarom wil je lid worden van het radioteam?",
"submitButton": "Sollicitatie indienen"
"submitButton": "Sollicitatie indienen",
"success": {
"submitted": "Sollicitatie ingediend — we nemen contact met je op."
},
"errors": {
"invalid": "Vul alle verplichte velden in.",
"ratelimit": "Je dient te snel in. Wacht even en probeer opnieuw.",
"error": "Er ging iets mis. Probeer het opnieuw."
}
},
"settings": {
"accountSettings": "Accountinstellingen",
@@ -1165,6 +1305,13 @@
"delete": "Verwijderen",
"noAds": "Nog geen advertenties",
"confirmDelete": "Deze advertentie verwijderen?",
"deleteSuccess": "Advertentie verwijderd!",
"deleteError": "Advertentie verwijderen mislukt.",
"adsCount": "Totaal advertenties",
"latest": "Laatste",
"mostRecentAd": "Meest recente advertentie",
"allAds": "Alle advertenties",
"colPreview": "Voorbeeld",
"colId": "ID",
"colName": "Naam",
"colImage": "Afbeelding",