Files
TREK/shared/src/i18n/nl/admin.ts
T
jubnl ebc98f0a3f v3.4.0 (#1527)
* fix(plugins): unknown column

* fix(mcp): reuse MCP sessions instead of creating one per tool call

The /mcp CORS layer never set exposedHeaders, so Access-Control-Expose-Headers
was absent and browser-context MCP clients (Claude Desktop connectors,
Claude.ai, MCP Inspector) could not read Mcp-Session-Id off the initialize
response. Unable to echo it back, every request looked like a fresh initialize:
one McpServer and one session per tool call, until the per-user cap returned a
429 and the integration died until the container was restarted.

The idle sweep was not at fault — it expires on lastActivity with a 1h default,
so sessions born seconds apart are nowhere near expiry, hence 'cleaned 0'.

- expose Mcp-Session-Id, MCP-Protocol-Version and WWW-Authenticate
- evict a user's least-recently-active session at the cap rather than
  refusing the request, so a client that cannot persist its session id (or a
  proxy that strips the header) can never wedge the server
- close the McpServer/transport orphaned by every session-less non-initialize
  POST, which was leaked: never mapped, never swept, never closed
- return the cap error as JSON-RPC so clients surface the real reason
- warn on session-less POSTs to make a header-stripping proxy diagnosable

* chore(deps): declare @modelcontextprotocol/sdk ^1.29.0

Matches the version already resolved in the lockfile; no dependency-tree change.

* docs(mcp): add the reverse proxy specs for MCP

* fix(plugins): stop the row ⋯ menu being clipped by the sidebar

The menu was an in-flow `absolute` div, and its ancestor (PageSidebar) is
`overflow-hidden` — which clips absolutely-positioned descendants regardless
of z-index. With enough plugins installed a row sits low enough that its menu
runs past the sidebar's bottom edge and gets chopped, taking Delete with it,
so the plugin could no longer be uninstalled from the UI.

Portal the menu to <body> and position it `fixed` against the ⋯ button,
flipping upward when the bottom is tight and re-anchoring on scroll/resize.
A fixed child of <body> has no overflow ancestor, so nothing can clip it.

Closes #1523

* feat(plugins): surface author-signature status and add a scoped re-trust override

TREK has always verified an author's Ed25519 signature and TOFU-pinned the key on
first install, but none of it was ever shown: a successfully-installed UNSIGNED
plugin looked identical to a signed one, and a signature-refused update left the
plugin quietly pinned at its old version with the reason dying in a toast.

Give the four refusal conditions machine-readable codes (SIGNATURE_MISSING /
_INCOMPLETE / _KEY_CHANGED / _INVALID), persist a refusal on the plugin row so the
admin list keeps showing it, and badge Signed/Unsigned in the list and in Discover.

Only SIGNATURE_KEY_CHANGED is overridable — an author can legitimately rotate a key;
a signature that does not verify means the bytes are not what the author signed, and
there is no story where waving that through is right. The override re-pins and
updates in ONE call (POST :id/retrust): a re-pin that waited for a follow-up /update
would leave the plugin pinned to a key no install had ever been verified against if
that second call never came. The artifact must still verify under the new key, so a
re-trust moves the pin from one verified key to another.

assertRetrustable re-derives the condition server-side, so the UI hiding the button
is a convenience, not the control, and it echoes back the full key the admin was
shown so a re-key since the dialog rendered is refused. The rotation is written to
the admin audit log with both fingerprints — after an incident, "which key did we
move from, and to what?" is the question a single key cannot answer.

* fix(plugins): let a plugin's frame reach the hosts an admin added for it

The frame's connect-src was built from the manifest's http:outbound grants alone, but
the child's egress guard is the UNION of those and the hosts an admin added after
install for an operatorEgress plugin (a self-hosted Gotify, an ntfy — hosts the author
cannot know in advance). So such a plugin WITH A UI could call the operator's host from
its server and was CSP-blocked in its own iframe.

Match the frame to the child. The admin consented to these hosts at install and the
child already reaches them, so this widens no trust boundary that isn't already crossed.
Both sources stay validated on the way in, and the interpolation filter is unchanged.

* fix(plugins): report a rejected events.emit on the plugin's own log stream

The host can reject an emit (an undeclared event name, a rate limit). The rejection must
not escape — a detached rejection crashes the child and terminally disables the plugin
over one bad emit — but swallowing it silently left an author with no way to discover
that `emits` was missing from their manifest. Surface it as a warning instead.

* feat(plugin-sdk): expose the raw request body for webhook signature checks

A webhook author must run their HMAC over the exact bytes the sender signed. `body` is
the PARSED value, and re-serializing it will not reproduce those bytes — key order,
whitespace and unicode escaping all differ — so the signature never matches. Document
`rawBodyBase64`, which the host already sets on auth:false routes.

* fix(plugin-sdk): refuse to overwrite a released artifact, and keep the packed zip

A released artifact is IMMUTABLE: the registry pins its sha256, so overwriting the bytes
of a release already in the registry breaks the checksum for everyone who installed that
version — they can no longer install or update it. The old code blanket-caught every
`gh release create` failure (auth, network, a bad repo) and turned it into a --clobber
upload. Probe for the release explicitly and refuse unless --force.

Also stop deleting plugin.zip on the way out. It is the exact bytes the release and the
entry's sha256 pin were computed from; a re-pack on another machine or SDK version can
differ (CRLF, walk order), so anyone re-running `entry`/`sign` afterwards must hash THAT
file, not a rebuild.

* fix(plugin-sdk): don't fail submit when the fork already has an upstream remote

`gh repo clone` of a fork may already have wired `upstream`, in which case a bare
`remote add` exits non-zero and took the whole submit down. Set it either way.

* fix(plugin-sdk): make the dev server behave like the host

Three ways `dev` lied to an author about how their plugin would run in production:

- ctx.db: one try/catch wrapped both the node:sqlite probe AND opening the database, so
  an mkdir/permission failure silently degraded to the in-memory stub — which swallows
  every write while reporting success. A db:own plugin "worked" in dev and persisted
  nothing. Probe separately: fail loudly on a real error, degrade only on old Node, and
  say plainly that the stub discards writes.
- notificationChannel: the host fires it with no acting user and hands the recipient's
  decrypted settings in as a separate `config` argument — send(msg, config, ctx) /
  test(config, ctx). Firing it like an ordinary hook passed `ctx` where `config` belongs,
  so a channel plugin read its settings off ctx and was broken in production.
- /preview pinned tripId 42 while the scaffold seeds trip 1, so the widget's first
  trek:invoke hit assertMember(42) and 500'd. Preview against a trip that exists.

* fix(plugin-sdk): reject unknown flags, wire up create's new flags, and add --help

`parse()` accepts any --x, so a flag a command does not read was silently dropped:
`create --template notification-channel` cheerfully scaffolded a blank plugin. Silently
ignoring an author's explicit instruction is worse than refusing it, so unknown flags are
now an error, and create actually forwards --template/--egress/--required-addons.

A bare --permissions used to split the string "true" into a permission literally named
`true`; listFlag() now treats a valueless flag as absent.

Since an unknown flag is now fatal, `--help` has to exist: it is intercepted before the
flag check and prints usage on stdout with exit 0.

* chore(plugins): drop three unused eslint-disable directives

The no-console rule is not enabled for these files, so the directives were dead and
eslint reports them as unused.

* fix(plugin-sdk): bring preflight back in step with the registry's gates

preflight exists to tell an author what TREK-Plugins' CI will say before they open the
PR. The registry now verifies author signatures, and preflight didn't — so it drifted
into the one failure mode it must never have: a false green. An author trusts a green.

- Verify the signature against the artifact bytes. preflight already downloads them for
  the sha256 check, so this costs one call. Without it, signing with the wrong key (or
  re-packing after signing) sails through and is caught at review.
- Check the signature SHAPE (checkSignatureShape): a key with no signed version, a
  signature with no key, a malformed key or signature. TREK refuses to install a
  half-signed entry, so such an entry is dead on arrival.
- Default apiVersion to 1 before comparing. It is OPTIONAL in the manifest — install/
  manifest.ts and `entry` both default it — so a manifest that legally omits it was
  failing preflight with "manifest apiVersion undefined != entry 1" while the registry
  passed it. A false RED, which teaches authors to ignore the tool.
- Check requiredAddons/pluginDependencies parity, and operatorEgress without an
  http:outbound grant.

The verifier is a port of the host's install/verify-signature.ts (the registry has its
own port); sign.ts's verifyArtifact only understands the bare key/signature pair the SDK
itself emits and cannot judge a minisign key. A test pins all three to the same verdicts.

* feat(plugins): enforce compatibility range

* chore: bump sdk version

* test(e2e): repair the trip-creation specs

create-trip and trip-planner have been failing for a while — long enough for
three separate UI changes to drift past them, which nothing caught because CI
runs vitest only and never invokes Playwright.

Each failure was masking the next:

- The release-notice modal greets a freshly seeded user and its backdrop
  swallowed the click on .add-trip-card. Added a shared dismissSystemNotices()
  helper (the X only shows on the notice's last page, so it has to page through
  first).
- .modal-backdrop no longer exists — the class was namespaced to
  .trek-modal-backdrop so content blockers stop hiding it.
- input[type=text].first() is no longer the Title field: the cover-image search
  inputs now sit above it, so the specs were typing the trip name into the photo
  search box and creating an untitled trip that never matched getByText(title).

* fix(planner): gate drag & drop on pointer type, not viewport width (#1432)

The 3.2.1 fix disabled drag on "mobile", but nothing in the client has ever
detected touch — "mobile" was inferred from viewport width, at four independent
breakpoints. A tablet is a coarse-pointer device at a *desktop* width, so an
iPad (820-1366px) fell on the wrong side of all four, which is why iPhone was
fixed and iPad was not:

- useTripPlanner's isMobile (<768px) is what disarmed `draggable`, so on iPad
  rows stayed draggable and a scroll swipe became an HTML5 drag.
- TripPlannerPage hardcoded isMobile={false} on the desktop PlacesSidebar, so
  its drop handlers and the drop-to-import overlay could never be disabled —
  that overlay is the reported symptom.
- The arrow-button reorder fallback was revealed only below 767px, leaving iPad
  with no drag *and* no fallback.
- touchDragPolyfill loaded drag-drop-touch at >=1024px, synthesising drags from
  touchmove — on a landscape iPad that re-armed the very gesture hijack 3.2.1
  removed.

Adds useIsTouch() ((pointer: coarse)) as a signal separate from isMobile: layout
stays width-driven, so the iPad keeps the desktop two-pane planner, while every
drag affordance is gated on isMobile || isTouch. The reorder arrows now show on
coarse pointers, and the polyfill loads only on hybrid laptops
((pointer: fine) and (any-pointer: coarse)), which also removes the #1440
phantom-dblclick map zoom on tablets.

Guarded by unit tests plus an e2e spec on real WebKit in an iPad Pro 11 context
— the engine matters, since every browser on iPadOS is WebKit underneath, which
is why the reporter hit this in all three they tried.

* test(planner): guard the day-plan reorder arrows against phantom clicks

The hover rule that reveals the reorder arrows had been dead since the
TypeScript migration: it targeted `.place-row:hover .reorder-btns`, and neither
class exists — the component renders `.reorder-buttons` inside an unclassed row.
The buttons sat at opacity:0 on desktop with nothing to reveal them.

That was not merely invisible. opacity:0 still hit-tests, so every itinerary row
and day note carried an invisible, fully clickable target that silently
reordered the trip:

  opacity: "0", visibility: "visible", pointerEvents: "auto"
  elementFromPoint(centre) -> BUTTON, inside .reorder-buttons

The repair — pointer-events: none while hidden, plus a working
.dp-row:hover/:focus-within reveal (focus-within so the buttons are also
reachable by keyboard, which the file's JS-hover pattern cannot do) — lives in
index.css and DayPlanSidebar.tsx. Both files also carry the #1432 drag gating,
so they went with that commit rather than being split mid-file; this commit is
the regression guard for them.

E2E rather than unit, because jsdom does not evaluate :hover. Against the old
CSS it fails on exactly the right assertion: "hidden arrows must not swallow
clicks" — expected false, received true.

* fix(pdf): keep the header gap on day-header overflow pages (#1531)

The repeated <thead> day header (#1471) carried no gap before the first
card on overflow pages: the 12px sat in .day-body's block-start padding,
which a fragmented box only paints on its first fragment. Move it to the
thead cell so it repeats with the header; the day's first page renders
pixel-identically.

* fix(budget): keep "no one paid yet" when editing an expense (#1533)

The ExpenseModal payer initializer fell back to the current user whenever the
edited item had no payer, so reopening an expense saved with "no one paid yet"
silently reselected "You" — and re-saving then recorded the current user as the
payer, corrupting the balances. An absent payer on an existing expense is a
deliberate value, so only a brand-new expense defaults to me.
2026-07-13 12:40:00 +02:00

674 lines
46 KiB
TypeScript

import type { TranslationStrings } from '../types';
const admin: TranslationStrings = {
'admin.notifications.title': 'Meldingen',
'admin.notifications.hint': 'Kies een meldingskanaal. Er kan er slechts één tegelijk actief zijn.',
'admin.notifications.none': 'Uitgeschakeld',
'admin.notifications.email': 'E-mail (SMTP)',
'admin.notifications.webhook': 'Webhook',
'admin.notifications.save': 'Meldingsinstellingen opslaan',
'admin.notifications.saved': 'Meldingsinstellingen opgeslagen',
'admin.notifications.testWebhook': 'Testwebhook verzenden',
'admin.notifications.testWebhookSuccess': 'Testwebhook succesvol verzonden',
'admin.notifications.testWebhookFailed': 'Testwebhook mislukt',
'admin.smtp.title': 'E-mail en meldingen',
'admin.smtp.hint': 'SMTP-configuratie voor het verzenden van e-mailmeldingen.',
'admin.smtp.testButton': 'Test-e-mail verzenden',
'admin.webhook.hint': 'Meldingen verzenden naar een externe webhook (Discord, Slack, enz.).',
'admin.smtp.testSuccess': 'Test-e-mail succesvol verzonden',
'admin.smtp.testFailed': 'Test-e-mail mislukt',
'admin.title': 'Beheer',
'admin.subtitle': 'Gebruikersbeheer en systeeminstellingen',
'admin.tabs.users': 'Gebruikers',
'admin.tabs.categories': 'Categorieën',
'admin.tabs.backup': 'Back-up',
'admin.tabs.audit': 'Audit',
'admin.stats.users': 'Gebruikers',
'admin.stats.trips': 'Reizen',
'admin.stats.places': 'Plaatsen',
'admin.stats.photos': "Foto's",
'admin.stats.files': 'Bestanden',
'admin.table.user': 'Gebruiker',
'admin.table.email': 'E-mail',
'admin.table.role': 'Rol',
'admin.table.created': 'Aangemaakt',
'admin.table.lastLogin': 'Laatste login',
'admin.table.actions': 'Acties',
'admin.you': '(Jij)',
'admin.editUser': 'Gebruiker bewerken',
'admin.newPassword': 'Nieuw wachtwoord',
'admin.newPasswordHint': 'Laat leeg om het huidige wachtwoord te behouden',
'admin.deleteUser': 'Gebruiker "{name}" verwijderen? Alle reizen worden permanent verwijderd.',
'admin.deleteUserTitle': 'Gebruiker verwijderen',
'admin.newPasswordPlaceholder': 'Nieuw wachtwoord invoeren…',
'admin.toast.loadError': 'Beheergegevens laden mislukt',
'admin.toast.userUpdated': 'Gebruiker bijgewerkt',
'admin.toast.updateError': 'Bijwerken mislukt',
'admin.toast.userDeleted': 'Gebruiker verwijderd',
'admin.toast.deleteError': 'Verwijderen mislukt',
'admin.toast.cannotDeleteSelf': 'Je kunt je eigen account niet verwijderen',
'admin.toast.userCreated': 'Gebruiker aangemaakt',
'admin.toast.createError': 'Gebruiker aanmaken mislukt',
'admin.toast.fieldsRequired': 'Gebruikersnaam, e-mail en wachtwoord zijn verplicht',
'admin.createUser': 'Gebruiker aanmaken',
'admin.invite.title': 'Uitnodigingslinks',
'admin.invite.subtitle': 'Eenmalige registratielinks aanmaken',
'admin.invite.create': 'Link aanmaken',
'admin.invite.createAndCopy': 'Aanmaken en kopiëren',
'admin.invite.empty': 'Nog geen uitnodigingslinks aangemaakt',
'admin.invite.maxUses': 'Max. gebruik',
'admin.invite.expiry': 'Verloopt na',
'admin.invite.uses': 'gebruikt',
'admin.invite.expiresAt': 'verloopt op',
'admin.invite.createdBy': 'door',
'admin.invite.active': 'Actief',
'admin.invite.expired': 'Verlopen',
'admin.invite.usedUp': 'Opgebruikt',
'admin.invite.copied': 'Uitnodigingslink gekopieerd',
'admin.invite.copyLink': 'Link kopiëren',
'admin.invite.deleted': 'Uitnodigingslink verwijderd',
'admin.invite.createError': 'Fout bij aanmaken van link',
'admin.invite.deleteError': 'Fout bij verwijderen van link',
'admin.tabs.settings': 'Instellingen',
'admin.allowRegistration': 'Registratie toestaan',
'admin.allowRegistrationHint': 'Nieuwe gebruikers kunnen zichzelf registreren',
'admin.authMethods': 'Authenticatiemethoden',
'admin.passwordLogin': 'Inloggen met wachtwoord',
'admin.passwordLoginHint': 'Gebruikers toestaan om in te loggen met e-mailadres en wachtwoord',
'admin.passwordRegistration': 'Registreren met wachtwoord',
'admin.passwordRegistrationHint': 'Nieuwe gebruikers toestaan om zich te registreren met e-mailadres en wachtwoord',
'admin.oidcLogin': 'Inloggen via SSO',
'admin.oidcLoginHint': 'Gebruikers toestaan om in te loggen via SSO',
'admin.oidcRegistration': 'Automatische SSO-provisioning',
'admin.oidcRegistrationHint': 'Automatisch accounts aanmaken voor nieuwe SSO-gebruikers',
'admin.envOverrideHint':
'De instellingen voor inloggen met wachtwoord worden beheerd via de omgevingsvariabele OIDC_ONLY en kunnen hier niet worden gewijzigd.',
'admin.lockoutWarning': 'Er moet minstens één inlogmethode ingeschakeld blijven',
'admin.requireMfa': 'Tweestapsverificatie (2FA) verplichten',
'admin.requireMfaHint':
'Gebruikers zonder 2FA moeten de installatie in Instellingen voltooien voordat ze de app kunnen gebruiken.',
'admin.apiKeys': 'API-sleutels',
'admin.apiKeysHint': "Optioneel. Schakelt uitgebreide plaatsgegevens in zoals foto's en weer.",
'admin.mapsKey': 'Google Maps API-sleutel',
'admin.mapsKeyHint': 'Vereist voor het zoeken van plaatsen. Verkrijgbaar op console.cloud.google.com',
'admin.mapsKeyHintLong':
"Zonder API-sleutel wordt OpenStreetMap gebruikt voor het zoeken van plaatsen. Met een Google API-sleutel kunnen ook foto's, beoordelingen en openingstijden worden geladen. Verkrijgbaar op console.cloud.google.com.",
'admin.recommended': 'Aanbevolen',
'admin.weatherKey': 'OpenWeatherMap API-sleutel',
'admin.weatherKeyHint': 'Voor weergegevens. Gratis op openweathermap.org',
'admin.unsplashKey': 'Unsplash API-sleutel',
'admin.unsplashKeyHint': 'Voor het zoeken naar afbeeldingen. Gratis op unsplash.com/developers',
'admin.validateKey': 'Testen',
'admin.keyValid': 'Verbonden',
'admin.keyInvalid': 'Ongeldig',
'admin.keySaved': 'API-sleutels opgeslagen',
'admin.oidcTitle': 'Single Sign-On (OIDC)',
'admin.oidcSubtitle': 'Sta inloggen toe via externe providers zoals Google, Apple, Authentik of Keycloak.',
'admin.oidcDisplayName': 'Weergavenaam',
'admin.oidcIssuer': 'Issuer-URL',
'admin.oidcIssuerHint': 'De OpenID Connect Issuer-URL van de provider. Bijv. https://accounts.google.com',
'admin.oidcSaved': 'OIDC-configuratie opgeslagen',
'admin.oidcOnlyMode': 'Wachtwoordauthenticatie uitschakelen',
'admin.oidcOnlyModeHint':
'Indien ingeschakeld, is alleen SSO-login toegestaan. Inloggen en registreren met wachtwoord worden geblokkeerd.',
'admin.fileTypes': 'Toegestane bestandstypen',
'admin.fileTypesHint': 'Configureer welke bestandstypen gebruikers kunnen uploaden.',
'admin.fileTypesFormat': 'Kommagescheiden extensies (bijv. jpg,png,pdf,doc). Gebruik * om alle typen toe te staan.',
'admin.fileTypesSaved': 'Bestandstype-instellingen opgeslagen',
'admin.placesPhotos.title': "Plaatsfoto's",
'admin.placesPhotos.subtitle':
"Haalt foto's op via de Google Places API. Schakel uit om API-quota te besparen. Wikimedia-foto's worden niet beïnvloed.",
'admin.placesAutocomplete.title': 'Plaatsautocomplete',
'admin.placesAutocomplete.subtitle':
'Gebruikt de Google Places API voor zoeksuggesties. Schakel uit om API-quota te besparen.',
'admin.placesDetails.title': 'Plaatsdetails',
'admin.placesDetails.subtitle':
'Haalt gedetailleerde plaatsinformatie (openingstijden, beoordeling, website) op via de Google Places API. Schakel uit om API-quota te besparen.',
'admin.bagTracking.title': 'Bagagetracking',
'admin.bagTracking.subtitle': 'Gewicht en bagagetoewijzing inschakelen voor paklijstitems',
'admin.collab.chat.title': 'Chat',
'admin.collab.chat.subtitle': 'Realtime berichten voor reissamenwerking',
'admin.collab.notes.title': 'Notities',
'admin.collab.notes.subtitle': 'Gedeelde notities en documenten',
'admin.collab.polls.title': 'Polls',
'admin.collab.polls.subtitle': 'Groepspolls en stemmen',
'admin.collab.whatsnext.title': 'Wat nu',
'admin.collab.whatsnext.subtitle': 'Activiteitssuggesties en volgende stappen',
'admin.tabs.config': 'Personalisatie',
'admin.tabs.defaults': 'Standaardinstellingen',
'admin.defaultSettings.title': 'Standaard gebruikersinstellingen',
'admin.defaultSettings.description':
'Stel instantiebrede standaardwaarden in. Gebruikers die een instelling niet hebben gewijzigd, zien deze waarden. Hun eigen wijzigingen hebben altijd voorrang.',
'admin.defaultSettings.saved': 'Standaard opgeslagen',
'admin.defaultSettings.reset': 'Terugzetten naar ingebouwde standaard',
'admin.defaultSettings.resetToBuiltIn': 'terugzetten',
'admin.tabs.templates': 'Paksjablonen',
'admin.packingTemplates.title': 'Paksjablonen',
'admin.packingTemplates.subtitle': 'Herbruikbare paklijsten maken voor je reizen',
'admin.packingTemplates.create': 'Nieuw sjabloon',
'admin.packingTemplates.namePlaceholder': 'Sjabloonnaam (bijv. Strandvakantie)',
'admin.packingTemplates.empty': 'Nog geen sjablonen aangemaakt',
'admin.packingTemplates.items': 'items',
'admin.packingTemplates.categories': 'categorieën',
'admin.packingTemplates.itemName': 'Itemnaam',
'admin.packingTemplates.itemCategory': 'Categorie',
'admin.packingTemplates.categoryName': 'Categorienaam (bijv. Kleding)',
'admin.packingTemplates.addCategory': 'Categorie toevoegen',
'admin.packingTemplates.created': 'Sjabloon aangemaakt',
'admin.packingTemplates.deleted': 'Sjabloon verwijderd',
'admin.packingTemplates.loadError': 'Fout bij laden van sjablonen',
'admin.packingTemplates.createError': 'Fout bij aanmaken van sjabloon',
'admin.packingTemplates.deleteError': 'Fout bij verwijderen van sjabloon',
'admin.packingTemplates.saveError': 'Fout bij opslaan',
'admin.tabs.addons': 'Add-ons',
'admin.tabs.plugins': 'Plugins',
'admin.plugins.rescan': 'Opnieuw scannen',
'admin.plugins.rescanned': 'Pluginmap opnieuw gescand',
'admin.plugins.upload': 'Plugin uploaden',
'admin.plugins.dropToUpload': 'Zet een plugin-.zip neer om te installeren',
'admin.plugins.uploaded': 'Plugin “{name}” geüpload — activeer om uit te voeren',
'admin.plugins.sideloaded': 'Handmatig geladen',
'admin.plugins.devLinkBadge': 'Dev-Link',
'admin.plugins.devLinkHint':
'Geladen vanuit een lokale build-map en hot-reload met echte data — alleen voor ontwikkeling',
'admin.plugins.devLinkTitle': 'Een lokale plugin koppelen',
'admin.plugins.devLinkPathPlaceholder': '/absolute/path/to/your/plugin',
'admin.plugins.devLinkButton': 'Koppelen',
'admin.plugins.devLinkLinked': '{id} gekoppeld — activeer het om te draaien',
'admin.plugins.sideloadedHint': 'Handmatig geüpload — niet uit het register, niet ondertekend en niet gecontroleerd',
'admin.plugins.browse': 'Plugins verkennen',
'admin.plugins.installed': 'Geïnstalleerd',
'admin.plugins.install': 'Installeren',
'admin.plugins.registryEmpty': 'Nog geen plugins beschikbaar in het register.',
'admin.plugins.actionError': 'Actie mislukt',
'admin.plugins.activate': 'Activeren',
'admin.plugins.activated': 'Plugin geactiveerd',
'admin.plugins.deactivate': 'Deactiveren',
'admin.plugins.deactivated': 'Plugin gedeactiveerd',
'admin.plugins.viewErrors': 'Foutenlog bekijken',
'admin.plugins.errorLog': 'Foutenlog',
'admin.plugins.allowedHosts': 'Toegestane hosts',
'admin.plugins.allowedHosts.hint':
'Deze plug-in praat met een dienst die alleen jij kunt benoemen (een zelf-gehoste server). Voeg de hosts toe die hij mag bereiken — andere bereikt hij niet.',
'admin.plugins.allowedHosts.none': 'Nog geen hosts toegevoegd.',
'admin.plugins.allowedHosts.unsupported':
'Deze plug-in gebruikt geen door de beheerder opgegeven hosts. De toegestane hosts staan vast in het manifest.',
'admin.plugins.allowedHosts.restartNote': 'Opslaan herstart de plug-in zodat de nieuwe lijst wordt opgepakt.',
'admin.plugins.allowedHosts.add': 'Toegestane host toevoegen',
'admin.plugins.allowedHosts.count': '{n} toegestane host(s)',
'admin.plugins.operatorEgressPill': '+ hosts die jij toevoegt',
'admin.plugins.operatorEgressHint':
'Deze plug-in praat met een dienst die alleen jij kunt benoemen (een zelf-gehoste server). Voeg na installatie de bereikbare hosts toe via ⋯ → Toegestane hosts. Andere bereikt hij niet.',
'admin.plugins.noErrors': 'Geen fouten geregistreerd.',
'admin.plugins.uninstalled': 'Plugin verwijderd',
'admin.plugins.uninstallTitle': 'Plugin verwijderen?',
'admin.plugins.uninstallBody':
'Dit stopt de plugin, verwijdert de code en wist alle bijbehorende gegevens. Dit kan niet ongedaan worden gemaakt.',
'admin.plugins.status.starting': 'Wordt gestart…',
'admin.plugins.type.widget': 'Widget',
'admin.plugins.type.page': 'Pagina',
'admin.plugins.type.integration': 'Integratie',
'admin.plugins.type.trip-page': 'Reispagina',
'admin.plugins.reviewed': 'Beoordeeld',
'admin.plugins.unreviewed': 'Niet beoordeeld',
'admin.plugins.updated': 'Plugin bijgewerkt',
'admin.plugins.updateTo': 'Update → v{version}',
'admin.plugins.enabledToggle': 'Plugin inschakelen',
'plugins.notFound': 'Plugin niet gevonden',
'admin.plugins.title': 'Plugins',
'admin.plugins.subtitle': 'Installeer en beheer plugins van derden op je instantie.',
'admin.plugins.disabledTitle': 'Plugins zijn uitgeschakeld',
'admin.plugins.disabledBody':
'De plugin-runtime staat uit (TREK_PLUGINS_ENABLED). Er kan geen plugin draaien totdat een beheerder dit inschakelt in de serverconfiguratie.',
'admin.plugins.empty': 'Nog geen plugins geïnstalleerd.',
'admin.plugins.loadError': 'Plugins konden niet worden geladen.',
'admin.plugins.scaffoldNote':
'Plugininstallatie en -activatie volgen in een latere update. Dit paneel toont momenteel alleen geïnstalleerde plugins.',
'admin.plugins.status.active': 'Actief',
'admin.plugins.status.inactive': 'Inactief',
'admin.plugins.status.disabled': 'Uitgeschakeld',
'admin.plugins.status.error': 'Fout',
'admin.plugins.status.incompatible': 'Incompatibel',
'admin.plugins.details': 'Details',
'admin.plugins.detailError': 'Kan plugindetails niet laden.',
'admin.plugins.permissionsTitle': 'Machtigingen',
'admin.plugins.noPermissions': 'Deze plugin vraagt geen speciale machtigingen aan.',
'admin.plugins.egressNote': 'Kan verbinding maken met: {hosts}',
'admin.plugins.setupTitle': 'Configuratie',
'admin.plugins.noSetup': 'Werkt direct — er hoeft niets geconfigureerd te worden.',
'admin.plugins.scope.instance': 'Instantiebreed',
'admin.plugins.scope.user': 'Per gebruiker',
'admin.plugins.fieldRequired': 'Verplicht',
'admin.plugins.sourceRepo': 'Broncoderepository',
'admin.plugins.reportIssue': 'Probleem melden',
'admin.plugins.homepage': 'Homepage',
'admin.plugins.requiresTrek': 'Vereist TREK {version}+',
'admin.plugins.reviewedOn': 'Beoordeeld op {date}',
'admin.plugins.perm.db:own': 'Eigen gegevens opslaan in een geïsoleerde database',
'admin.plugins.perm.db:read:trips': 'Reizen lezen waartoe de actieve gebruiker toegang heeft',
'admin.plugins.perm.db:read:users': 'Basisprofielgegevens lezen (naam, avatar — nooit inloggegevens)',
'admin.plugins.perm.db:read:costs': 'Uitgaven (budgetposten) lezen waartoe de actieve gebruiker toegang heeft',
'admin.plugins.perm.db:read:packing': 'Paklijsten lezen van reizen waartoe de actieve gebruiker toegang heeft',
'admin.plugins.perm.db:write:packing':
'Paklijst-items aanmaken, bewerken en verwijderen op reizen die de actieve gebruiker mag bewerken',
'admin.plugins.perm.weather:read': 'De gecachte weersvoorspelling van de host lezen (op coördinaten)',
'admin.plugins.perm.db:read:categories': 'De globale lijst met plaatscategorieën lezen',
'admin.plugins.perm.db:read:tags': 'De tags van de actieve gebruiker lezen',
'admin.plugins.perm.db:write:tags': 'De tags van de actieve gebruiker aanmaken, bewerken en verwijderen',
'admin.plugins.perm.db:read:todos': 'De taken lezen van reizen waartoe de actieve gebruiker toegang heeft',
'admin.plugins.perm.db:write:todos':
'Taken aanmaken, bewerken en verwijderen op reizen die de actieve gebruiker mag bewerken',
'admin.plugins.perm.db:write:atlas':
"Landen en regio's als bezocht markeren en de bucketlist van de actieve gebruiker beheren (vereist de Atlas-add-on)",
'admin.plugins.perm.db:write:vacay':
'Vakantiedagen en bedrijfssluitingen op het actieve plan van de actieve gebruiker omschakelen (vereist de Vacay-add-on)',
'admin.plugins.perm.db:write:journal':
'Dagboekvermeldingen aanmaken, bewerken en verwijderen in dagboeken die de actieve gebruiker mag bewerken (vereist de Journey-add-on)',
'admin.plugins.perm.db:write:collections':
'Collecties aanmaken en bewerken en er plaatsen in opslaan, met de collectierol van de actieve gebruiker (vereist de Collections-add-on)',
'admin.plugins.perm.db:write:files':
'Bestanden toevoegen aan reizen die de actieve gebruiker mag bewerken (limiet 10 MB, geblokkeerde extensies geweigerd) en hun koppelingen beheren',
'admin.plugins.perm.db:write:collab':
'Notities, peilingen en chatberichten plaatsen op reizen die de actieve gebruiker mag bewerken (vereist de Collab-add-on)',
'admin.plugins.perm.db:write:members':
'Gebruikers aan reizen toevoegen — verleent reistoegang; beveiligd door het ledenbeheer-recht van de actieve gebruiker',
'admin.plugins.perm.notify:send':
'Een melding (belinbox + e-mail/ntfy) sturen naar de actieve gebruiker of een reis waartoe hij behoort — nooit naar een willekeurige ontvanger',
'admin.plugins.perm.ai:invoke':
'De door de beheerder geconfigureerde AI-provider (tekstaanvulling + documentextractie) namens de actieve gebruiker gebruiken — de plug-in bezit nooit een sleutel',
'admin.plugins.perm.oauth:client':
'Namens jou verbinden met een externe dienst via host-bemiddelde OAuth (de host bewaart de tokens, de plug-in ziet ze nooit)',
'admin.plugins.perm.db:read:files': 'Bestanden lezen van reizen waartoe de actieve gebruiker toegang heeft',
'admin.plugins.perm.db:write:reservations':
'Boekingen (reserveringen) aanmaken, bewerken en verwijderen op reizen die de actieve gebruiker mag bewerken',
'admin.plugins.perm.db:write:accommodations':
'Accommodaties (verblijven) aanmaken, bewerken en verwijderen op reizen die de actieve gebruiker mag bewerken',
'admin.plugins.perm.db:read:journal': 'De reisdagboeken van de actieve gebruiker lezen (vereist de Journey-add-on)',
'admin.plugins.perm.db:read:atlas':
"De door de actieve gebruiker bezochte landen en regio's lezen (vereist de Atlas-add-on)",
'admin.plugins.perm.db:read:vacay': 'Het vakantieplan van de actieve gebruiker lezen (vereist de Vacay-add-on)',
'admin.plugins.perm.db:read:daynotes': 'De dagnotities lezen van reizen waartoe de actieve gebruiker toegang heeft',
'admin.plugins.perm.db:read:collections':
'De collecties met opgeslagen plaatsen van de actieve gebruiker lezen (vereist de Collections-add-on)',
'admin.plugins.perm.db:write:daynotes':
'Dagnotities aanmaken, bewerken en verwijderen op reizen die de actieve gebruiker mag bewerken',
'admin.plugins.perm.jobs:run':
'De gedeclareerde achtergrondtaken volgens een schema uitvoeren (zonder gebruikerscontext — kan geen gebruikersgegevens lezen)',
'admin.plugins.perm.db:write:costs':
'Uitgaven (budgetposten) aanmaken op reizen die de actieve gebruiker mag bewerken',
'admin.plugins.perm.db:write:places':
'Plaatsen toevoegen, bewerken en verwijderen op reizen die de actieve gebruiker mag bewerken',
'admin.plugins.perm.db:write:days':
'Dagen toevoegen, bewerken en verwijderen op reizen die de actieve gebruiker mag bewerken',
'admin.plugins.perm.db:write:itinerary':
'Plaatsen toewijzen aan en verwijderen uit dagen van reizen die de actieve gebruiker mag bewerken',
'admin.plugins.perm.db:write:trips':
'Reisgegevens bewerken (titel, datums, valuta…) op reizen die de actieve gebruiker mag bewerken',
'admin.plugins.perm.db:meta':
'Eigen privégegevens koppelen aan reizen, plaatsen en dagen waartoe de actieve gebruiker toegang heeft',
'admin.plugins.perm.ws:broadcast:trip': 'Realtime updates sturen naar reisleden',
'admin.plugins.perm.ws:broadcast:user': 'Realtime updates sturen naar individuele gebruikers',
'admin.plugins.perm.hook:photo-provider': "Foto's aanleveren voor de Foto's-sectie",
'admin.plugins.perm.hook:calendar-source': 'Evenementen aanleveren voor de kalender',
'admin.plugins.perm.hook:place-detail-provider':
'Extra details (reviews, beoordelingen, links) aan een plek toevoegen',
'admin.plugins.perm.hook:trip-warning-provider':
'Validatiewaarschuwingen tonen bij een reis (zichtbaar in de planner)',
'admin.plugins.perm.hook:table-contributor':
'Kolommen en acties toevoegen aan reisweergaven (boekingen, plaatsen, dagen)',
'admin.plugins.perm.hook:map-marker-provider':
"Markeringen aan de reiskaart toevoegen (bijv. boekingen of POI's tonen)",
'admin.plugins.perm.hook:pdf-section-provider': 'Tekstsecties toevoegen aan de PDF-export van de reis',
'admin.plugins.perm.hook:atlas-layer-provider':
'Landen markeren op de Atlas-wereldkaart (bijv. wensenlijsten of reisadviezen)',
'admin.plugins.perm.hook:journal-entry-provider': 'Extra regels (links, statistieken) toevoegen aan dagboek-items',
'admin.plugins.perm.hook:user-data':
'De gegevens die het over een gebruiker opslaat wissen of exporteren (AVG: accountverwijdering en inzageverzoeken)',
'admin.plugins.perm.hook:trip-card-provider':
'Kleine badges (status, aantallen) aan je dashboard-reiskaarten toevoegen',
'admin.plugins.perm.hook:notification-channel': 'Je meldingen via een extra kanaal bezorgen',
'admin.plugins.perm.events:subscribe':
'Reageert op kernactiviteitsgebeurtenissen (alleen gebeurtenisnaam + reis, nooit de inhoud)',
'admin.plugins.perm.http:outbound': 'Uitgaande verzoeken doen naar de opgegeven hosts',
'admin.plugins.perm.db:read:collab':
'Notities, peilingen en chatberichten lezen van reizen waartoe de actieve gebruiker toegang heeft (vereist de Collab-add-on)',
'admin.plugins.perm.db:read:files:content':
'De inhoud (bytes) van bestanden lezen van reizen waartoe de actieve gebruiker toegang heeft',
'admin.plugins.perm.db:create:trips': 'Nieuwe reizen aanmaken die eigendom zijn van de actieve gebruiker',
'admin.plugins.perm.rates:read': 'De gecachte wisselkoersen van de host lezen',
'admin.plugins.updateConsentTitle': 'Deze update vereist nieuwe machtigingen',
'admin.plugins.updateConsentBody':
'{name} v{version} vraagt om rechten die je nog niet hebt verleend. De nieuwe versie is geïnstalleerd, maar blijft uit totdat je deze goedkeurt.',
'admin.plugins.updateNewPermissions': 'Nieuw aangevraagde machtigingen',
'admin.plugins.updateNewEgress': 'Nieuwe uitgaande verbindingen',
'admin.plugins.updateApprove': 'Goedkeuren en inschakelen',
'admin.plugins.updateLater': 'Voorlopig uit laten',
'admin.plugins.updateKeptOff': 'Update geïnstalleerd — uit gelaten totdat je de nieuwe machtigingen goedkeurt',
'admin.plugins.reviewedMeaning':
'"Beoordeeld" betekent dat een TREK-beheerder deze plugin bij elke versie op malware heeft gecontroleerd — niet op kwaliteit of op de vraag of hij werkt. Het is geen garantie dat een plugin ongevaarlijk is.',
'admin.plugins.security.title': 'Hoe plugins worden ingeperkt — en de grenzen',
'admin.plugins.security.isolationTitle': 'Elke plugin draait afgeschermd',
'admin.plugins.security.isolationBody':
"Een plugin draait als een eigen afgeschermd proces dat alleen zijn eigen bestanden kan lezen. Hij kan je database, je login-geheim of je versleutelingssleutel niet lezen, kan geen andere programma's starten en kan nergens bestanden wegschrijven. De interface draait in een afgesloten browserframe dat je sessiecookie niet kan lezen en de TREK-pagina eromheen niet kan aanraken.",
'admin.plugins.security.permsTitle': 'Wat de machtigingen betekenen',
'admin.plugins.security.permsBody':
'De machtigingen die vóór het installeren worden getoond, zijn een harde grens die TREK afdwingt zolang de plugin draait — hij kan letterlijk niets doen wat niet op de lijst staat. Maar ze vertellen je wat een plugin kán doen, niet wat hij daadwerkelijk doet. Een plugin die je reizen mag lezen en een server mag bereiken, kan je reizen naar die server sturen; lees dus de machtigingen en de uitgaande hosts, niet alleen de beschrijving.',
'admin.plugins.security.limitsTitle': 'Wat we niet kunnen beloven',
'admin.plugins.security.limitsBody':
'De isolatie is een sterke softwaregrens, geen absolute. Een plugin handelt met precies de rechten die je goedkeurt, dus binnen die rechten kan hij zich anders gedragen dan zijn beschrijving suggereert, en kan hij gegevens die hij rechtmatig bezit naar de door hem opgegeven hosts sturen. TREK leest of beoordeelt niet wat de code van een plugin daadwerkelijk doet.',
'admin.plugins.security.worstTitle': 'Het ergste geval',
'admin.plugins.security.worstBody':
"Een kwaadwillende plugin die je inschakelt, kan de gegevens en verbindingen die je hebt verleend misbruiken — bijvoorbeeld de reizen lekken die hij mag lezen. Hij kan geen wachtwoorden stelen, geen beheerderslogin vervalsen, geen commando's op je server uitvoeren en geen gegevens bereiken die je niet hebt verleend. De schade blijft binnen wat je hebt goedgekeurd, en de plugin uitschakelen stopt hem.",
'admin.plugins.security.reviewedTitle': 'Wat "Beoordeeld" betekent',
'admin.plugins.security.reviewedBody':
'Een beoordeelde plugin is bij elke versie handmatig door een TREK-beheerder op malware gecontroleerd — gecontroleerd op kwaadaardige code, niet op de vraag of hij goed werkt. Het is geen belofte dat de plugin ongevaarlijk is.',
'admin.plugins.security.signedTitle': 'Wat "Ondertekend" betekent',
'admin.plugins.security.signedBody':
'De controlesom die TREK bij elke installatie verifieert, bewijst dat de bestanden precies zijn waarvoor de registry instaat. Een handtekening bewijst iets anders: dat ze van de auteur komen, ondertekend met een sleutel die alleen hij heeft. Een ondertekende plugin heeft beide. Een niet-ondertekende plugin is niet onveilig — hij draagt simpelweg één garantie minder, en de meeste plugins in de registry zijn vandaag niet ondertekend.',
'admin.plugins.signed': 'Ondertekend',
'admin.plugins.signedHint': 'Bij de installatie geverifieerd tegen de ondertekeningssleutel van de auteur',
'admin.plugins.unsigned': 'Niet ondertekend',
'admin.plugins.unsignedHint':
'De bestanden komen overeen met waarvoor de registry instaat, maar niets koppelt ze aan de auteur. Eén garantie minder — niet onveilig.',
'admin.plugins.updateBlocked': 'Update geblokkeerd — {reason}',
'admin.plugins.reviewBlock': 'Bekijken',
'admin.plugins.retrusted': 'Nieuwe ondertekeningssleutel vertrouwd — de plugin is bijgewerkt',
'admin.plugins.sig.title': 'De handtekening van {name} kon niet worden geverifieerd',
'admin.plugins.sig.keyChangedBody':
'De ondertekeningssleutel van de auteur is niet die waarmee deze plugin is geïnstalleerd. Auteurs wisselen weleens van sleutel — maar een aanvaller die de plugin heeft overgenomen, zou er precies zo uitzien.',
'admin.plugins.sig.invalidBody':
'De bestanden komen niet overeen met de handtekening van de auteur. Ze zijn niet wat de auteur heeft ondertekend — ze zijn beschadigd geraakt, of er is mee geknoeid. Dit kan niet worden genegeerd.',
'admin.plugins.sig.missingBody':
'Deze plugin was ondertekend toen je hem installeerde, maar de nieuwe versie levert geen handtekening mee. TREK aanvaardt die stap terug niet stilzwijgend. Dit kan niet worden genegeerd.',
'admin.plugins.sig.incompleteBody':
'Het registry-item is half ondertekend: het vermeldt een auteurssleutel, maar de versie draagt geen handtekening (of andersom). Dat is een fout aan de kant van de plugin. Dit kan niet worden genegeerd.',
'admin.plugins.sig.pinnedKey': 'Sleutel waarmee hij is geïnstalleerd',
'admin.plugins.sig.newKey': 'Sleutel die hij nu aanbiedt',
'admin.plugins.sig.confirmOutOfBand':
'TREK kan een legitieme sleutelwissel niet onderscheiden van een overname — van hieruit zien ze er identiek uit. Bevestig de nieuwe sleutel bij de auteur via een kanaal dat je al vertrouwt voordat je hem accepteert. Zodra je dat doet, wordt de plugin bijgewerkt en wordt de nieuwe sleutel onthouden.',
'admin.plugins.sig.retrustConfirm': 'Nieuwe sleutel vertrouwen en bijwerken',
'admin.plugins.sig.cancel': 'Niet vertrouwen',
'admin.plugins.sig.consentUnsigned':
'Niets koppelt deze versie aan de auteur — de bestanden komen overeen met de registry, maar dragen geen handtekening van de auteur.',
'admin.plugins.security.trustTitle': 'De conclusie',
'admin.plugins.security.trustBody':
'Een plugin installeren is als het installeren van elke andere app van derden: voeg alleen code toe van auteurs die je vertrouwt en controleer deze bij twijfel eerst zelf. TREK aanvaardt geen enkele verantwoordelijkheid voor plugins van derden.',
'admin.plugins.runtimeOn': 'Runtime aan',
'admin.plugins.tabDiscover': 'Ontdekken',
'admin.plugins.searchPlaceholder': 'Plugins zoeken…',
'admin.plugins.filterType': 'Type',
'admin.plugins.filterStatus': 'Status',
'admin.plugins.sortBy': 'Sorteren',
'admin.plugins.allTypes': 'Alle typen',
'admin.plugins.allStatuses': 'Alle',
'admin.plugins.stateOff': 'Uit',
'admin.plugins.filterUpdate': 'Update beschikbaar',
'admin.plugins.sortName': 'Naam',
'admin.plugins.sortRecent': 'Onlangs bijgewerkt',
'admin.plugins.sortUpdates': 'Updates eerst',
'admin.plugins.sortDownloads': 'Meest gedownload',
'admin.plugins.updatesAvailable': '{count} updates beschikbaar voor je plugins.',
'admin.plugins.updateAll': 'Alles bijwerken',
'admin.plugins.noMatchInstalled': 'Geen geïnstalleerde plugins komen overeen met je zoekopdracht.',
'admin.plugins.noMatchRegistry': 'Geen plugins in het register komen overeen met je zoekopdracht.',
'admin.plugins.restart': 'Opnieuw starten',
'admin.plugins.restarted': 'Plugin opnieuw gestart',
'admin.plugins.cap.readsTrips': 'Leest je reizen',
'admin.plugins.cap.readsUsers': 'Leest basisprofielen',
'admin.plugins.cap.readsCosts': 'Leest je uitgaven',
'admin.plugins.cap.readsPacking': 'Leest paklijsten',
'admin.plugins.cap.readsFiles': 'Leest reisbestanden',
'admin.plugins.cap.writesCosts': 'Voegt uitgaven toe',
'admin.plugins.cap.writesPlaces': 'Bewerkt plaatsen',
'admin.plugins.cap.writesDays': 'Bewerkt dagen',
'admin.plugins.cap.writesItinerary': 'Bewerkt reisschema',
'admin.plugins.cap.writesTrips': 'Bewerkt reizen',
'admin.plugins.cap.metadata': 'Voegt metadata toe',
'admin.plugins.cap.widget': 'Dashboardwidget',
'admin.plugins.cap.heroWidget': 'Instapkaartwidget',
'admin.plugins.cap.placeSlot': 'Plaatsdetail',
'admin.plugins.cap.daySlot': 'Dagdetail',
'admin.plugins.cap.reservationSlot': 'Reserveringsdetail',
'admin.plugins.cap.replacesTabs': 'Vervangt planner-tabbladen',
'admin.plugins.cap.realtime': 'Realtime updates',
'admin.plugins.cap.notificationChannel': 'Meldingskanaal',
'admin.plugins.cap.photos': "Levert foto's",
'admin.plugins.cap.calendar': 'Levert kalenderevenementen',
'admin.plugins.cap.placeDetails': 'Verrijkt plekken',
'admin.plugins.cap.warnings': 'Signaleert problemen',
'admin.plugins.cap.events': 'Reageert op activiteit',
'admin.plugins.cap.requiresAddon': 'Vereist {addon}',
'admin.plugins.cap.dependsOn': 'Vereist {id} {version}',
'admin.plugins.dep.addonDisabledToast': 'Schakel eerst de vereiste add-on(s) in: {addons}',
'admin.plugins.dep.autoEnabled': 'Eerst vereiste plug-in(s) ingeschakeld: {plugins}',
'admin.plugins.dep.downloaded': '{id} gedownload',
'admin.plugins.dep.resolveTitle': 'Ontbrekende afhankelijkheden',
'admin.plugins.dep.resolveBody': '“{name}” heeft deze plug-ins nodig voordat het kan worden ingeschakeld.',
'admin.plugins.dep.requires': 'Vereist {version}',
'admin.plugins.dep.mismatch': 'Vereist {wanted} — {installed} is geïnstalleerd',
'admin.plugins.dep.download': 'Downloaden',
'admin.plugins.dep.update': 'Bijwerken',
'admin.plugins.dep.resolveHint': 'Downloadt de nieuwste compatibele versie, inclusief de eigen afhankelijkheden.',
'admin.plugins.dep.trekIncompatible': 'Vereist TREK {range} — deze server draait {host}',
'admin.plugins.dep.trekUnknown': 'Geeft niet aan welke TREK-versies worden ondersteund',
'admin.plugins.installCompatible': '{version} installeren',
'admin.plugins.incompatible': 'Niet compatibel',
'admin.plugins.accessTitle': 'Waartoe hij toegang heeft',
'admin.plugins.connectsTitle': 'Maakt verbinding met',
'admin.plugins.detailsTitle': 'Details',
'admin.plugins.noAccess': 'Heeft geen speciale toegang nodig.',
'admin.plugins.metaVersion': 'Versie',
'admin.plugins.metaSize': 'Grootte',
'admin.plugins.metaRequires': 'Vereist',
'admin.plugins.metaReviewed': 'Beoordeeld op',
'admin.plugins.downloads': 'Downloads',
'admin.addons.title': 'Add-ons',
'admin.addons.subtitle': 'Schakel functies in of uit om je TREK-ervaring aan te passen.',
'admin.addons.catalog.memories.name': "Foto's (Immich)",
'admin.addons.catalog.memories.description': "Deel reisfoto's via je Immich-instantie",
'admin.addons.catalog.mcp.name': 'MCP',
'admin.addons.catalog.mcp.description': 'Model Context Protocol voor AI-assistent integratie',
'admin.addons.catalog.packing.name': 'Lijsten',
'admin.addons.catalog.packing.description': 'Paklijsten en to-dotaken voor je reizen',
'admin.addons.catalog.budget.name': 'Onkosten',
'admin.addons.catalog.budget.description': 'Houd uitgaven bij en plan je reisbudget',
'admin.addons.catalog.documents.name': 'Documenten',
'admin.addons.catalog.documents.description': 'Bewaar en beheer reisdocumenten',
'admin.addons.catalog.vacay.name': 'Vakantie',
'admin.addons.catalog.vacay.description': 'Persoonlijke vakantieplanner met kalenderweergave',
'admin.addons.catalog.atlas.name': 'Atlas',
'admin.addons.catalog.atlas.description': 'Wereldkaart met bezochte landen en reisstatistieken',
'admin.addons.catalog.collab.name': 'Samenwerking',
'admin.addons.catalog.collab.description': 'Realtime notities, polls en chat voor het plannen van reizen',
'admin.addons.subtitleBefore': 'Schakel functies in of uit om je ',
'admin.addons.subtitleAfter': '-ervaring aan te passen.',
'admin.addons.enabled': 'Ingeschakeld',
'admin.addons.disabled': 'Uitgeschakeld',
'admin.addons.type.trip': 'Reis',
'admin.addons.type.global': 'Globaal',
'admin.addons.type.integration': 'Integratie',
'admin.addons.tripHint': 'Beschikbaar als tabblad binnen elke reis',
'admin.addons.globalHint': 'Beschikbaar als zelfstandig onderdeel in de hoofdnavigatie',
'admin.addons.integrationHint': 'Backenddiensten en API-integraties zonder eigen pagina',
'admin.addons.toast.updated': 'Add-on bijgewerkt',
'admin.addons.toast.error': 'Add-on bijwerken mislukt',
'admin.addons.noAddons': 'Geen add-ons beschikbaar',
'admin.weather.title': 'Weergegevens',
'admin.weather.badge': 'Sinds 24 maart 2026',
'admin.weather.description':
'TREK gebruikt Open-Meteo als weerbron. Open-Meteo is een gratis, open-source weerdienst — geen API-sleutel vereist.',
'admin.weather.forecast': '16-daagse voorspelling',
'admin.weather.forecastDesc': 'Voorheen 5 dagen (OpenWeatherMap)',
'admin.weather.climate': 'Historische klimaatgegevens',
'admin.weather.climateDesc': 'Gemiddelden over de afgelopen 85 jaar voor dagen buiten de 16-daagse voorspelling',
'admin.weather.requests': '10.000 verzoeken / dag',
'admin.weather.requestsDesc': 'Gratis, geen API-sleutel vereist',
'admin.weather.locationHint':
'Het weer is gebaseerd op de eerste plaats met coördinaten op elke dag. Als er geen plaats aan een dag is toegewezen, wordt een plaats uit de lijst als referentie gebruikt.',
'admin.tabs.mcpTokens': 'MCP-toegang',
'admin.mcpTokens.title': 'MCP-toegang',
'admin.mcpTokens.subtitle': 'OAuth-sessies en API-tokens van alle gebruikers beheren',
'admin.mcpTokens.sectionTitle': 'API-tokens',
'admin.mcpTokens.owner': 'Eigenaar',
'admin.mcpTokens.tokenName': 'Tokennaam',
'admin.mcpTokens.created': 'Aangemaakt',
'admin.mcpTokens.lastUsed': 'Laatst gebruikt',
'admin.mcpTokens.never': 'Nooit',
'admin.mcpTokens.empty': 'Er zijn nog geen MCP-tokens aangemaakt',
'admin.mcpTokens.deleteTitle': 'Token verwijderen',
'admin.mcpTokens.deleteMessage':
'Dit token wordt onmiddellijk ingetrokken. De gebruiker verliest MCP-toegang via dit token.',
'admin.mcpTokens.deleteSuccess': 'Token verwijderd',
'admin.mcpTokens.deleteError': 'Token kon niet worden verwijderd',
'admin.mcpTokens.loadError': 'Tokens konden niet worden geladen',
'admin.oauthSessions.sectionTitle': 'OAuth-sessies',
'admin.oauthSessions.clientName': 'Client',
'admin.oauthSessions.owner': 'Eigenaar',
'admin.oauthSessions.scopes': 'Rechten',
'admin.oauthSessions.created': 'Aangemaakt',
'admin.oauthSessions.empty': 'Geen actieve OAuth-sessies',
'admin.oauthSessions.revokeTitle': 'Sessie intrekken',
'admin.oauthSessions.revokeMessage':
'Deze OAuth-sessie wordt onmiddellijk ingetrokken. De client verliest MCP-toegang.',
'admin.oauthSessions.revokeSuccess': 'Sessie ingetrokken',
'admin.oauthSessions.revokeError': 'Sessie kon niet worden ingetrokken',
'admin.oauthSessions.loadError': 'OAuth-sessies konden niet worden geladen',
'admin.tabs.github': 'GitHub',
'admin.audit.subtitle':
'Beveiligingsgevoelige en beheerdersgebeurtenissen (back-ups, gebruikers, MFA, instellingen).',
'admin.audit.empty': 'Nog geen auditregistraties.',
'admin.audit.refresh': 'Vernieuwen',
'admin.audit.loadMore': 'Meer laden',
'admin.audit.showing': '{count} geladen · {total} totaal',
'admin.audit.col.time': 'Tijd',
'admin.audit.col.user': 'Gebruiker',
'admin.audit.col.action': 'Actie',
'admin.audit.col.resource': 'Bron',
'admin.audit.col.ip': 'IP',
'admin.audit.col.details': 'Details',
'admin.github.title': 'Release-geschiedenis',
'admin.github.subtitle': 'Laatste updates van {repo}',
'admin.github.latest': 'Nieuwste',
'admin.github.prerelease': 'Pre-release',
'admin.github.showDetails': 'Details tonen',
'admin.github.hideDetails': 'Details verbergen',
'admin.github.loadMore': 'Meer laden',
'admin.github.loading': 'Laden...',
'admin.github.support': 'Helpt mij TREK verder te ontwikkelen',
'admin.github.error': 'Releases laden mislukt',
'admin.github.by': 'door',
'admin.update.available': 'Update beschikbaar',
'admin.update.text': 'TREK {version} is beschikbaar. Je draait {current}.',
'admin.update.button': 'Bekijk op GitHub',
'admin.update.install': 'Update installeren',
'admin.update.confirmTitle': 'Update installeren?',
'admin.update.confirmText': 'TREK wordt bijgewerkt van {current} naar {version}. De server herstart automatisch.',
'admin.update.dataInfo':
'Al je gegevens (reizen, gebruikers, API-sleutels, uploads, Vacay, Atlas, budgetten) worden bewaard.',
'admin.update.warning': 'De app is kort niet beschikbaar tijdens het herstarten.',
'admin.update.confirm': 'Nu bijwerken',
'admin.update.installing': 'Bijwerken…',
'admin.update.success': 'Update geïnstalleerd! Server herstart…',
'admin.update.failed': 'Update mislukt',
'admin.update.backupHint': 'We raden aan een back-up te maken voordat je bijwerkt.',
'admin.update.backupLink': 'Naar back-up',
'admin.update.howTo': 'Hoe bij te werken',
'admin.update.dockerText':
"Je TREK-instantie draait in Docker. Om bij te werken naar {version}, voer de volgende commando's uit op je server:",
'admin.update.nonDockerText':
'Deze TREK-instantie draait niet in Docker. Om bij te werken naar {version}, voer de installatie- of updatemethode die je hebt gebruikt opnieuw uit — bij Proxmox Community Scripts voer je de update bijvoorbeeld uit vanuit de LXC-console:',
'admin.update.wikiLink': 'Open de updatehandleiding',
'admin.update.reloadHint': 'Herlaad de pagina over een paar seconden.',
'admin.tabs.permissions': 'Rechten',
'admin.notifications.emailPanel.title': 'Email (SMTP)',
'admin.notifications.webhookPanel.title': 'Webhook',
'admin.notifications.inappPanel.title': 'In-App',
'admin.notifications.inappPanel.hint':
'In-app-meldingen zijn altijd actief en kunnen niet globaal worden uitgeschakeld.',
'admin.notifications.adminWebhookPanel.title': 'Admin-webhook',
'admin.notifications.adminWebhookPanel.hint':
'Deze webhook wordt uitsluitend gebruikt voor admin-meldingen (bijv. versie-updates). Hij staat los van gebruikerswebhooks en verstuurt automatisch als er een URL is ingesteld.',
'admin.notifications.adminWebhookPanel.saved': 'Admin-webhook-URL opgeslagen',
'admin.notifications.adminWebhookPanel.testSuccess': 'Test-webhook succesvol verzonden',
'admin.notifications.adminWebhookPanel.testFailed': 'Test-webhook mislukt',
'admin.notifications.adminWebhookPanel.alwaysOnHint':
'Admin-webhook verstuurt automatisch als er een URL is ingesteld',
'admin.notifications.ntfy': 'Ntfy',
'admin.ntfy.hint':
'Hiermee kunnen gebruikers hun eigen ntfy-onderwerpen instellen voor pushmeldingen. Stel de standaardserver hieronder in om de gebruikersinstellingen vooraf in te vullen.',
'admin.notifications.testNtfy': 'Test-Ntfy verzenden',
'admin.notifications.testNtfySuccess': 'Test-Ntfy succesvol verzonden',
'admin.notifications.testNtfyFailed': 'Test-Ntfy mislukt',
'admin.notifications.adminNtfyPanel.title': 'Admin-Ntfy',
'admin.notifications.adminNtfyPanel.hint':
'Dit Ntfy-onderwerp wordt uitsluitend gebruikt voor admin-meldingen (bijv. versie-updates). Het staat los van onderwerpen per gebruiker en verstuurt altijd wanneer het geconfigureerd is.',
'admin.notifications.adminNtfyPanel.serverLabel': 'Ntfy-server-URL',
'admin.notifications.adminNtfyPanel.serverHint':
'Wordt ook gebruikt als standaardserver voor ntfy-meldingen van gebruikers. Laat leeg om ntfy.sh te gebruiken. Gebruikers kunnen dit aanpassen in hun eigen instellingen.',
'admin.notifications.adminNtfyPanel.serverPlaceholder': 'https://ntfy.sh',
'admin.notifications.adminNtfyPanel.topicLabel': 'Admin-onderwerp',
'admin.notifications.adminNtfyPanel.topicPlaceholder': 'trek-admin-alerts',
'admin.notifications.adminNtfyPanel.tokenLabel': 'Toegangstoken (optioneel)',
'admin.notifications.adminNtfyPanel.tokenCleared': 'Admin-toegangstoken gewist',
'admin.notifications.adminNtfyPanel.saved': 'Admin-Ntfy-instellingen opgeslagen',
'admin.notifications.adminNtfyPanel.test': 'Test-Ntfy verzenden',
'admin.notifications.adminNtfyPanel.testSuccess': 'Test-Ntfy succesvol verzonden',
'admin.notifications.adminNtfyPanel.testFailed': 'Test-Ntfy mislukt',
'admin.notifications.adminNtfyPanel.alwaysOnHint':
'Admin-Ntfy verstuurt altijd wanneer een onderwerp is geconfigureerd',
'admin.notifications.adminNotificationsHint':
'Stel in via welke kanalen admin-meldingen worden bezorgd (bijv. versie-updates). De webhook verstuurt automatisch als er een admin-webhook-URL is ingesteld.',
'admin.notifications.tripReminders.title': 'Reisherinneringen',
'admin.notifications.tripReminders.hint':
'Stuurt een herinneringsmelding voor de start van een reis (vereist ingestelde herinneringsdagen bij de reis).',
'admin.notifications.tripReminders.enabled': 'Reisherinneringen ingeschakeld',
'admin.notifications.tripReminders.disabled': 'Reisherinneringen uitgeschakeld',
'admin.tabs.notifications': 'Meldingen',
'admin.addons.catalog.journey.name': 'Reisverslag',
'admin.addons.catalog.journey.description': "Reistracking & reisdagboek met check-ins, foto's en dagelijkse verhalen",
'admin.addons.catalog.collections.name': 'Collecties',
'admin.addons.catalog.collections.description':
'Persoonlijke plekkenbibliotheek — sla plekken uit je reizen op in benoemde lijsten, kopieer ze naar elke reis en deel ze met anderen',
'admin.passkey.title': 'Inloggen met passkey',
'admin.passkey.cardHint': 'Laat gebruikers inloggen met passkeys (WebAuthn). Standaard uit.',
'admin.passkey.login': 'Inloggen met passkey inschakelen',
'admin.passkey.loginHint':
'Toon een optie "Inloggen met een passkey" en laat gebruikers passkeys registreren in hun instellingen.',
'admin.passkey.notConfigured':
'Voor deze installatie wordt nog geen WebAuthn-domein herleid. Stel APP_URL of de Relying Party ID hieronder in — tot dan blijven passkeys verborgen.',
'admin.passkey.rpId': 'Relying Party ID (domein)',
'admin.passkey.rpIdHint':
'Het kale domein waaraan passkeys zijn gebonden, bijv. trek.example.org. Laat leeg om het af te leiden uit APP_URL. Als je dit later wijzigt, worden bestaande passkeys ongeldig.',
'admin.passkey.origins': 'Toegestane origins',
'admin.passkey.originsHint':
"Volledige origins, gescheiden door komma's, bijv. https://trek.example.org. Laat leeg om APP_URL te gebruiken.",
'admin.passkey.reset': 'Passkeys resetten',
'admin.passkey.resetHint':
'Verwijder alle passkeys van deze gebruiker (bijv. bij een verloren apparaat). Ze kunnen nog steeds inloggen met hun wachtwoord.',
'admin.passkey.resetConfirm': 'Alle passkeys voor {name} verwijderen?',
'admin.passkey.resetDone': '{count} passkey(s) verwijderd',
'admin.defaultSettings.mapProvider': 'Kaartmotor',
'admin.defaultSettings.mapProviderHint':
'De standaardkaart voor iedereen op deze instantie. Elke gebruiker kan dit nog steeds aanpassen in zijn eigen instellingen.',
'admin.defaultSettings.providerLeaflet': 'Standaard (gratis)',
'admin.defaultSettings.providerMapbox': 'Mapbox (3D)',
'admin.defaultSettings.providerMapLibre': 'MapLibre (OpenFreeMap)',
'admin.defaultSettings.mapboxToken': 'Gedeeld Mapbox-token',
'admin.defaultSettings.mapboxTokenHint':
'Wordt gebruikt voor elke gebruiker die nog geen eigen token heeft ingevoerd — zo krijgt de hele instantie Mapbox zonder de sleutel apart te delen. Versleuteld opgeslagen.',
'admin.defaultSettings.mapboxStyle': 'Kaartstijl',
'admin.defaultSettings.mapboxStylePlaceholder': 'Kies een stijl…',
'admin.defaultSettings.mapbox3d': '3D-gebouwen & terrein',
'admin.defaultSettings.mapboxQuality': 'Hogekwaliteitsmodus',
'admin.group.users': 'Users',
'admin.group.config': 'Configuration',
'admin.group.integration': 'Integrations',
'admin.group.maintenance': 'Maintenance',
'admin.invite.tripLabel': 'Toevoegen aan reis (optioneel)',
'admin.invite.tripNone': 'Geen reis',
'admin.invite.tripHint':
'De nieuwe gebruiker wordt automatisch aan deze reis toegevoegd wanneer hij zich via de link registreert.',
'admin.invite.boundTo': 'voegt toe aan {trip}',
};
export default admin;