Files
crewli/dev-docs/COPY_CATALOGUE.md
bert.hausmans a97922d6a4 docs(form-builder): document S3a PR 2 complex field types and update FORM-05 stub note
- Add VitePress pages for AVAILABILITY_PICKER and SECTION_PRIORITY
  and a TAG_PICKER configuration note. Wire them into the organisator
  sidebar under a new Formulieren section alongside the existing
  "Wat is een formulier" page.
- BACKLOG.md: nuance FORM-05 — the stub-shaped behaviour for public
  event_registration submissions is already shipping via the existing
  TriggerPersonIdentityMatchOnFormSubmit listener (writes 'pending').
  The real work (PersonIdentityService::detectMatchesByValues + an
  extra branch in resolveStatus) is what remains. Added a done entry
  for S3a PR 2 to the Opgeloste items list.
- API.md: add VALIDATION_FAILED to the public-form error code table
  and document the SECTION_PRIORITY shape error messages (Dutch copy
  served under errors."values.{slug}").
- COPY_CATALOGUE.md: new S3a PR 2 section capturing the seeder
  help_text, the IdentityMatchBanner copy (clearly marking the
  backend message as authoritative), all empty/error state copy for
  the three new components, and the SECTION_PRIORITY shape error
  strings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 20:34:34 +02:00

7.8 KiB

Crewli — In-app copy catalogue

Single source of truth for user-facing Dutch copy. Seeded from /dev-docs/ARCH-FORM-BUILDER.md §30.

Purpose

Centralises terminology and warnings so every screen, tooltip, and error message stays consistent. Without this, "Dienst" / "Shift" / "Taak" drift across pages and confuse organisers; "Privacy-gevoelig" and "Bevat persoonsgegevens" appear side-by-side for the same flag.

This catalogue is the canonical reference for both backend validation messages (Form Requests) and frontend rendering (Vue components). Whenever you write Dutch copy that a user will read, look here first.

Growth strategy

Living document. Each session that adds user-facing UI:

  1. Check first. Search for the concept (in Dutch and English) before coining a new term. If it's here, use the existing wording verbatim.
  2. Propose new terms in PR. Add to the relevant section. Brief rationale in the commit message.
  3. Reviewer (Bert) signs off on terminology before merge. Once a term is in the catalogue, it's binding for future sessions.

The catalogue stays in /dev-docs/ (developer-facing) rather than /docs/ (end-user-facing). End-user docs are written using this catalogue, not about it.

Update workflow

  • Adding a tooltip → update §30.2 here.
  • Adding a warning → update §30.3 here.
  • Renaming a concept → update §30.1 here AND grep for all current Dutch occurrences (frontend, validation messages, end-user docs) and update them in the same PR.
  • Wholesale restructure → discuss in design-document.md first; then rewrite this catalogue in one commit.

Naming conventions

Concept Canonical Dutch term Never use
form_schema Formulier Schema, template
form_field Veld Vraag, item
form_template Formulier-sjabloon Template (alleen in dev-docs)
form_field_library Veldenbibliotheek Library, bibliotheek alleen
form_submission Inzending Submission, antwoord
is_filterable Filterbaar Queryable, zoekbaar
is_pii Bevat persoonsgegevens Privacy-gevoelig
freeze_on_submit Bevriezen na inzending Vergrendelen
consent_version Toestemmingsversie Consent-versie

Tooltip catalogue (selection)

is_filterable:
  "Filterbaar — dit veld wordt extra geïndexeerd voor snelle filtering
   in overzichten. Alleen aanvinken voor velden die je daadwerkelijk als
   filter gebruikt (bijvoorbeeld: shirtmaat wel, motivatie niet)."

is_pii:
  "Bevat persoonsgegevens — bij retentie-verwerking worden deze waardes
   geanonimiseerd volgens je privacy-instellingen. Vink aan voor velden
   zoals telefoon, e-mail, noodcontact, medische info."

is_unique:
  "Uniek per formulier — waardes van dit veld moeten uniek zijn over alle
   inzendingen heen. Geschikt voor bijvoorbeeld BSN of werknemersnummer.
   Dubbele waardes worden afgewezen."

freeze_on_submit:
  "Bevriezen na eerste inzending — zodra iemand het formulier indient
   kunnen de velden niet meer gewijzigd worden. Gebruik dit voor
   contracten, signatures, of formulieren waar de structuur vast moet
   staan voor audit-doeleinden."

snapshot_mode:
  never: "Geen snapshot — wijzigingen worden alleen in het activity log
         bijgehouden."
  on_submit: "Snapshot bij inzending — bij elke indiening wordt het
             complete formulier gesnapshot voor audit-doeleinden."
  always: "Altijd snapshot — elke wijziging (ook drafts) wordt
          gesnapshot. Gebruikt meer opslag maar biedt het volledige
          audit-spoor."

retention_days:
  "Bewaartermijn — na deze periode (vanaf inzendingsdatum) worden
   PII-velden automatisch geanonimiseerd. Typische waardes: 1095 dagen
   (3 jaar) voor vrijwilligers, 2555 dagen (7 jaar) voor contracten,
   null voor onbeperkt bewaren."

Warning catalogue (selection)

binding_change_with_submissions:
  "Je staat op het punt de koppeling van dit veld te wijzigen terwijl er
   al {count} ingediende inzendingen zijn. De historische waardes blijven
   bestaan, maar zijn niet meer de bron-van-waarheid. Dit kan niet
   ongedaan worden gemaakt."

delete_schema_with_submissions:
  "Dit formulier heeft {count} inzendingen. Als je het verwijdert, blijven
   de inzendingen bewaard als archief maar zijn niet meer nieuw in te
   dienen. Type de naam van het formulier om te bevestigen:"

field_type_change:
  "Je wijzigt het veldtype van {old} naar {new}. Bestaande waardes worden
   mogelijk niet correct omgezet — sommige kunnen onleesbaar worden.
   Aanbevolen: maak een nieuw veld aan in plaats van dit veld te
   wijzigen."

public_token_rotation:
  "Je roteert de publieke link voor dit formulier. Bestaande gebruikers
   kunnen nog 7 dagen inzenden met de oude link; daarna krijgen ze een
   410 Gone foutmelding."

S3a PR 2 — Public form field types & identity-match

Seeder help_text (showcase demo fields)

beschikbaarheid.help_text:
  "Vink alle dagdelen aan waarop je kunt werken."

sectie_voorkeur.help_text:
  "Sleep je voorkeuren in volgorde. Nummer 1 is je eerste keuze."

IdentityMatchBanner — single source of truth is the backend

Copy is served by PublicFormSubmissionResource::formatIdentityMatch() in identity_match.message. The frontend fallbacks in IdentityMatchBanner.vue duplicate these exactly and must stay in sync whenever the backend copy changes.

identity_match.pending.title:  "We controleren je gegevens"
identity_match.pending.body:
  "We kijken of je al bekend bent bij de organisator. Je gegevens
   worden automatisch gekoppeld zodra zij dit bevestigen."
(backend message:
  "We controleren of je al bekend bent bij de organisator. Je gegevens
   worden gekoppeld zodra zij dit bevestigen.")

identity_match.matched.title: "Gegevens gekoppeld"
identity_match.matched.body:
  "Je bent automatisch gekoppeld aan je bestaande account bij de
   organisator."
(backend message:
  "Je account is gekoppeld aan een bekende deelnemer.")

identity_match.none.title:    "Aanmelding ontvangen"
identity_match.none.body:
  "De organisator neemt contact met je op zodra je aanmelding is
   verwerkt."
(backend message:
  "Geen bestaand account gevonden — je wordt als nieuwe deelnemer
   geregistreerd.")

Empty-state copy (public form field components)

FieldTagPicker.empty:
  "Er zijn nog geen tags beschikbaar voor dit formulier."

FieldAvailabilityPicker.empty:
  "Er zijn nog geen tijdsloten beschikbaar."

FieldAvailabilityPicker.error:
  "Kon beschikbaarheidsopties niet laden."    [button: "Opnieuw proberen"]

FieldSectionPriority.empty:
  "Er zijn nog geen secties gepubliceerd voor registratie."

FieldSectionPriority.error:
  "Kon secties niet laden."                   [button: "Opnieuw proberen"]

FieldSectionPriority.cap_hint:
  "Maximaal {max_priorities} voorkeuren"

FieldSectionPriority.first_rank_hint:
  "Tik of sleep een sectie hieronder om je eerste voorkeur te kiezen."

SECTION_PRIORITY — FormValueService shape errors

Returned under errors."values.{slug}" in the standard VALIDATION_FAILED envelope.

"Ongeldig formaat voor sectievoorkeuren."
"Je kunt maximaal 5 voorkeuren opgeven."
"Ongeldig voorkeur-element op positie {n}."
"section_id ontbreekt op positie {n}."
"priority ontbreekt of is ongeldig op positie {n}."
"priority moet tussen 1 en 5 liggen (positie {n})."
"Dezelfde sectie mag slechts één keer worden opgegeven."
"Elke prioriteit mag slechts één keer worden toegekend."
"Eén of meer secties horen niet bij dit evenement."