Browse Source

Add Dutch to the interface languages (issue #2891)

    The translation was contributed as a file on the issue and needed three
    corrections before it could be wired up, all of which the parity gate
    found.

    The nine stats.timeframe.* entries had their keys translated along with
    their values -- 'today' had become 'vandaag'. Code resolves those by the
    English key, so the Statistics timeframe selector would have found
    nothing and rendered raw key names for every Dutch user. The values are
    kept and the keys restored.

    The file was translated against an older en.ts and was 84 leaves short:
    the Filament Track Switch feed prompts, the AI-detection status strings,
    the no-3MF internal-history banner, the batch-order stranded-plate
    notices, the Avery starting-position field, and the whole
    locationHaSensors section from #2824. Rather than splice those in, nl.ts
    is regenerated from the en.ts skeleton with the contributor's strings
    carried over by key, so its structure, key order and section comments
    match the reference exactly and a later diff against en.ts reads as
    content rather than as reordering. The generator fails rather than emit a
    key it has no translation for, so nothing fell back to English silently.

    229 leaves are identical to English. Each was checked and all are kept:
    Dutch takes most technical UI vocabulary verbatim -- printer, filament,
    status, nozzle, timelapse, dashboard -- and Dutch slicer users use the
    English feature names untranslated, so support, ironing, prime tower and
    gap fill stay as they are. The 123 distinct values are enumerated in a
    NL_COGNATES list in check-i18n-parity.mjs, the same shape the other
    twelve locales use, so the exemption is a listed decision per string
    rather than a blanket skip for the locale.

    Backend app/i18n still carries English and German only, so push
    notification text falls back to English for Dutch. That is true of the
    eleven other non-German locales too and is left alone here.

    Parity green at 6264 leaves across 14 locales.
MartinNYHC 1 week ago
parent
commit
c6c732ea39

File diff suppressed because it is too large
+ 0 - 7
CHANGELOG.md


+ 40 - 0
frontend/scripts/check-i18n-parity.mjs

@@ -415,6 +415,45 @@ const UK_COGNATES = [
   '(25%, 50%, 75%)',
 ];
 
+// Dutch cognates. Dutch shares most technical UI vocabulary with English
+// verbatim -- printer, filament, status, nozzle, timelapse, dashboard --
+// and the slicer feature names (support, ironing, prime tower, gap fill)
+// are used untranslated by Dutch slicer users. Each entry below was
+// checked individually against the Dutch translation in #2891.
+const NL_COGNATES = [
+  '1 week', '(25%, 50%, 75%)', 'Accent', 'AMS Filament Backup',
+  '{{ams}} Slot {{slot}}', 'Auto', 'Auto Home', 'Bambu Cloud',
+  'Batch', 'Batches', 'Branch', 'Budget',
+  'Camera', 'China', 'Client Secret', 'Cloud',
+  'Commit', 'Compact', '({{count}}/8)', '{{count}} downloads',
+  '{{count}} filament', '({{count}} item)', '{{count}} item', '({{count}} items)',
+  '{{count}} items', '{{count}} object', 'Dashboard', 'Diagnose',
+  'Diameter', 'EC984C,#6CD4BC,A66EB9,D87694', 'Effect', 'email',
+  'ETA {{minutes}} min', 'Expert', 'Extruder', 'Extruders',
+  'Factor', 'Filament', 'Filament {{index}} ({{type}})', 'Filament {{n}}',
+  '{{filament}} @ {{temp}}°C', 'Filters', 'Firmware', 'Flow',
+  'Galaxy', 'Gap fill', 'Hex', 'Hex: #{{hex}}',
+  'Host', 'In browser', 'Interval', 'Interval (min)',
+  'Interval ({{type}})', 'Ironing', 'Jade White, Fire Red...', 'Label',
+  'Label...', 'LAN Developer Mode', 'Live', 'Long-Lived Access Token',
+  'Ludicrous (166%)', 'MakerWorld: {{designer}}', 'Metadata', 'Min',
+  'Model', '{{name}} — {{stage}} ({{percent}}%) — {{elapsed}}', '{{name}} - Timelapse', 'Nozzle',
+  'Nozzle 2', '{{n}} run', '{{n}} runs', 'Offline',
+  'Online', 'Open', 'Orca Cloud', 'parallel',
+  'Personal Access Token', 'Pipeline', 'Pipelines', 'Platform',
+  'Pressure Advance', 'Prime tower', 'Printer', 'Printer:',
+  '{{printer}}: {{error}}', '{{printer}} is offline', 'Printer is offline', 'Printer offline',
+  'Printers', 'Project', 'Project: {{name}}', 'Protocol',
+  'Providers', 'Provider: {{type}}', 'Proxy', 'Recent',
+  'Records', 'round robin', 'Scopes', 'Score',
+  'sets', 'Skirt / brim', 'Slicer', 'SpoolBuddy is offline',
+  'Sport (124%)', 'Start', 'Status', 'Subtype',
+  'Support', 'Tag', 'Tags', 'Temp',
+  'Timelapse', 'Topic', 'Trend', 'Type',
+  'Type:', 'Updates', 'Uptime', 'Urgent',
+  'Warm', '{{weight}}g', 'Workflow',
+];
+
 const IDENTICAL_TO_EN_ALLOWED = {
   de: new Set(DE_COGNATES),
   fr: new Set(FR_COGNATES),
@@ -428,6 +467,7 @@ const IDENTICAL_TO_EN_ALLOWED = {
   tr: new Set(TR_COGNATES),
   ru: new Set(RU_COGNATES),
   uk: new Set(UK_COGNATES),
+  nl: new Set(NL_COGNATES),
 };
 
 // Pure comparison logic, exported so tests can verify each failure mode

+ 4 - 1
frontend/src/i18n/index.ts

@@ -9,6 +9,7 @@ import es from './locales/es';
 import fr from './locales/fr';
 import ja from './locales/ja';
 import it from './locales/it';
+import nl from './locales/nl';
 import ko from './locales/ko';
 import ptBR from './locales/pt-BR';
 import zhCN from './locales/zh-CN';
@@ -24,6 +25,7 @@ const resources = {
   fr: { translation: fr },
   ja: { translation: ja },
   it: { translation: it },
+  nl: { translation: nl },
   ko: { translation: ko },
   'pt-BR': { translation: ptBR },
   'zh-CN': { translation: zhCN },
@@ -33,7 +35,7 @@ const resources = {
   uk: { translation: uk },
 };
 
-const SUPPORTED_LNGS = ['en', 'de', 'es', 'fr', 'ja', 'it', 'ko', 'pt-BR', 'ru', 'tr', 'uk', 'zh-CN', 'zh-TW'];
+const SUPPORTED_LNGS = ['en', 'de', 'es', 'fr', 'ja', 'it', 'ko', 'nl', 'pt-BR', 'ru', 'tr', 'uk', 'zh-CN', 'zh-TW'];
 const APPLIANCE_CONSUMED_KEY = 'bambuddy_appliance_locale_consumed';
 
 i18n
@@ -107,6 +109,7 @@ export const availableLanguages = [
   { code: 'ja', name: 'Japanese', nativeName: '日本語' },
   { code: 'it', name: 'Italian', nativeName: 'Italiano' },
   { code: 'ko', name: 'Korean', nativeName: '한국어' },
+  { code: 'nl', name: 'Dutch', nativeName: 'Nederlands' },
   { code: 'pt-BR', name: 'Portuguese (Brazil)', nativeName: 'Português (Brasil)' },
   { code: 'zh-CN', name: 'Chinese (Simplified)', nativeName: '简体中文' },
   { code: 'zh-TW', name: 'Chinese (Traditional)', nativeName: '繁體中文' },

+ 7630 - 0
frontend/src/i18n/locales/nl.ts

@@ -0,0 +1,7630 @@
+export default {
+  // Navigation
+  nav: {
+    printers: 'Printers',
+    archives: 'Archieven',
+    queue: 'Afdrukwachtrij',
+    stats: 'Statistieken',
+    profiles: 'Profielen',
+    maintenance: 'Onderhoud',
+    projects: 'Projecten',
+    finance: 'Financiën',
+    inventory: 'Filament',
+    files: 'Bestandsbeheer',
+    makerworld: 'MakerWorld',
+    notifications: 'Meldingen',
+    settings: 'Instellingen',
+    system: 'Systeem',
+    collapseSidebar: 'Zijbalk inklappen',
+    expandSidebar: 'Zijbalk uitklappen',
+    update: 'Bijwerken',
+    updateAvailable: 'Update beschikbaar: v{{version}}',
+    updateAvailableBanner: 'Versie {{version}} is beschikbaar!',
+    viewUpdate: 'Update bekijken',
+    viewOnGithub: 'Bekijken op GitHub',
+    keyboardShortcuts: 'Sneltoetsen (?)',
+    switchToLight: 'Overschakelen naar lichte modus',
+    switchToDark: 'Overschakelen naar donkere modus',
+    switchToSystem: 'Overschakelen naar systeemmodus',
+    smartSwitches: 'Slimme schakelaars',
+    logout: 'Uitloggen',
+    installApp: 'App installeren',
+    installAppSuccess: 'Bambuddy is geïnstalleerd',
+  },
+
+  // Common
+  common: {
+    plusNMore: '+{{count}} meer',
+    save: 'Opslaan',
+    saving: 'Opslaan...',
+    cancel: 'Annuleren',
+    delete: 'Verwijderen',
+    edit: 'Bewerken',
+    add: 'Toevoegen',
+    close: 'Sluiten',
+    confirm: 'Bevestigen',
+    loading: 'Laden...',
+    error: 'Fout',
+    errorLoading: 'Fout bij laden van gegevens',
+    retry: 'Opnieuw proberen',
+    success: 'Geslaagd',
+    warning: 'Waarschuwing',
+    enabled: 'Ingeschakeld',
+    disabled: 'Uitgeschakeld',
+    yes: 'Ja',
+    no: 'Nee',
+    on: 'Aan',
+    off: 'Uit',
+    all: 'Alle',
+    none: 'Geen',
+    search: 'Zoeken',
+    filter: 'Filteren',
+    sort: 'Sorteren',
+    refresh: 'Vernieuwen',
+    download: 'Downloaden',
+    upload: 'Uploaden',
+    uploading: 'Uploaden...',
+    uploadFailed: 'Upload mislukt',
+    actions: 'Acties',
+    status: 'Status',
+    name: 'Naam',
+    user: 'Gebruiker',
+    description: 'Beschrijving',
+    date: 'Datum',
+    time: 'Tijd',
+    hours: 'uren',
+    minutes: 'minuten',
+    seconds: 'seconden',
+    days: 'dagen',
+    enable: 'Inschakelen',
+    disable: 'Uitschakelen',
+    permissions: 'Machtigingen',
+    noPrinters: 'Geen printers geconfigureerd',
+    noData: 'Geen gegevens beschikbaar',
+    linkNotFound: 'Link niet gevonden',
+    required: 'Verplicht',
+    optional: 'Optioneel',
+    dismiss: 'Negeren',
+    apply: 'Toepassen',
+    reset: 'Resetten',
+    export: 'Exporteren',
+    import: 'Importeren',
+    clear: 'Wissen',
+    selectAll: 'Alles selecteren',
+    deselectAll: 'Alles deselecteren',
+    noChange: '— Geen wijziging —',
+    unchanged: 'Ongewijzigd',
+    unassigned: 'Niet toegewezen',
+    unknown: 'Onbekend',
+    unknownError: 'Onbekende fout',
+    today: 'Vandaag',
+    tomorrow: 'Morgen',
+    asap: 'Zo snel mogelijk',
+    overdue: 'Te laat',
+    now: 'Nu',
+    collapse: 'Inklappen',
+    expand: 'Uitklappen',
+    previous: 'Vorige',
+    next: 'Volgende',
+    viewArchive: 'Archief bekijken',
+    viewInFileManager: 'Bekijken in Bestandsbeheer',
+    addedBy: 'Toegevoegd door {{username}}',
+    prints: 'afdrukken',
+    more: '+{{count}} meer',
+    ascending: 'Oplopend',
+    descending: 'Aflopend',
+    back: 'Terug',
+    copy: 'Kopiëren',
+    copied: 'Gekopieerd!',
+    printer: 'Printer',
+    remove: 'Verwijderen',
+    type: 'Type',
+    print: 'Afdrukken',
+    rename: 'Naam wijzigen',
+    move: 'Verplaatsen',
+    create: 'Maken',
+    duplicate: 'Dupliceren',
+    left: 'Links',
+    right: 'Rechts',
+    run: 'Uitvoeren',
+    running: 'Bezig met uitvoeren...',
+  },
+
+  finance: {
+    allTypes: 'Alle typen',
+    allCostCenters: 'Alle kostenplaatsen',
+    title: 'Financiën',
+    subtitle: 'Portemonnee, persoonlijke transacties en kostenplaatsen',
+    noAccess: 'Je hebt geen toestemming om financiële gegevens te bekijken.',
+    personalView: 'Persoonlijke weergave',
+    adminView: 'Beheerdersweergave',
+    createCostCenter: 'Kostenplaats maken',
+    adjustWallet: 'Portemonnee aanpassen',
+    addManualPrint: 'Handmatige afdruk toevoegen',
+    manageMembers: 'Leden van kostenplaats beheren',
+    currentBalance: 'Persoonlijk saldo',
+    transactions: 'Transacties',
+    personalTransactions: 'Persoonlijke transacties',
+    costCenters: 'Kostenplaatsen',
+    availableForPrinting: 'Beschikbaar voor toewijzing aan afdruk',
+    costCenterName: 'Naam',
+    budgetType: 'Budgettype',
+    monthlyBudget: 'Maandbudget',
+    totalBudget: 'Totaalbudget',
+    noBudget: 'Geen budget',
+    create: 'Maken',
+    selectUser: 'Gebruiker selecteren',
+    transactionType: 'Type',
+    amount: 'Bedrag',
+    noCostCenter: 'Geen kostenplaats',
+    descriptionOptional: 'Beschrijving (optioneel)',
+    applyAdjustment: 'Aanpassing toepassen',
+    memberCanPrint: 'Lid mag afdrukken',
+    addMember: 'Lid toevoegen',
+    canPrint: 'Mag afdrukken',
+    noMembers: 'Geen leden toegewezen.',
+    editCostCenter: 'Kostenplaats bewerken',
+    myCostCenters: 'Mijn kostenplaatsen',
+    costCentersHint: 'Controleer budgetlimieten en houd kosten onder controle',
+    noCostCenters: 'Geen kostenplaatsen gevonden.',
+    owner: 'Eigenaar',
+    balance: 'Rekeningsaldo',
+    unlimited: 'Onbeperkt',
+    budgetPolicyHint: 'Rekeningsaldi houden kosten bij. Afdrukken wordt alleen beperkt door het budget van de geselecteerde kostenplaats; zonder budget is afdrukken onbeperkt.',
+    budget: 'Budget',
+    shared: 'Gedeeld',
+    cannotEditPrivateCostCenter: 'Privé-kostenplaatsen kunnen hier niet worden bewerkt',
+    recentTransactions: 'Recente transacties',
+    transactionsHint: 'Filteren op type en kostenplaats',
+    first: 'Eerste',
+    prev: 'Vorige',
+    next: 'Volgende',
+    last: 'Laatste',
+    pageNumberOf: 'Pagina {{page}} van {{total}}',
+    noTransactions: 'Geen transacties beschikbaar.',
+    noTransactionsForFilter: 'Geen transacties komen overeen met de geselecteerde filters.',
+    costCenter: 'Kostenplaats',
+    balanceAfter: 'Saldo na',
+    userWithId: 'Gebruiker #{{id}}',
+    partial: 'Gedeeltelijk',
+    editTransaction: 'Transactie bewerken',
+    selectCostCenter: 'Kostenplaats selecteren...',
+    costCenterRequired: 'Selecteer een kostenplaats',
+    amountExample: 'bijv. 4,00',
+    manualAdjustmentExample: 'bijv. Handmatige aanpassing',
+    userRequired: 'Selecteer een gebruiker',
+    amountInvalid: 'Bedrag moet een geldig getal zijn',
+    createdCostCenter: 'Kostenplaats gemaakt',
+    createCostCenterFailed: 'Kostenplaats maken mislukt',
+    transactionDeleted: 'Transactie verwijderd',
+    deleteTransactionFailed: 'Transactie verwijderen mislukt',
+    transactionEdited: 'Transactie bijgewerkt en grootboek opnieuw berekend',
+    editTransactionFailed: 'Transactie bewerken mislukt',
+    manualPrintCreated: 'Handmatige afdrukkosten toegevoegd en grootboek opnieuw berekend',
+    manualPrintFailed: 'Handmatige afdruk maken mislukt',
+    memberSaved: 'Lid opgeslagen',
+    memberSaveFailed: 'Lid opslaan mislukt',
+    memberRemoved: 'Lid verwijderd',
+    memberRemoveFailed: 'Lid verwijderen mislukt',
+    costCenterNameRequired: 'Naam van kostenplaats is verplicht',
+    costCenterUpdated: 'Kostenplaats bijgewerkt',
+    costCenterUpdateFailed: 'Kostenplaats bijwerken mislukt',
+    confirmDeleteCostCenter: 'Kostenplaats "{{name}}" verwijderen?',
+    costCenterDeleted: 'Kostenplaats verwijderd',
+    costCenterDeleteFailed: 'Kostenplaats verwijderen mislukt',
+    deleteTransactionConfirm: 'Deze transactie verwijderen? Saldi worden automatisch opnieuw berekend.',
+    deposit: 'Storten',
+    withdraw: 'Opnemen',
+    printCharge: 'Afdrukkosten',
+    deleteTransaction: 'Transactie verwijderen',
+  },
+
+  // Printers page
+  printers: {
+    title: 'Printers',
+    addPrinter: 'Printer toevoegen',
+    addPreflight: {
+      checking: 'Verbinding controleren...',
+      warning: 'Sommige verbindingscontroles zijn mislukt. Deze printer kan als offline worden weergegeven. Controleer de onderstaande controles, herstel wat je kunt of sla toch op.',
+      back: 'Terug',
+      saveAnyway: 'Toch opslaan',
+    },
+    editPrinter: 'Printer bewerken',
+    deletePrinter: 'Printer verwijderen',
+    printerName: 'Printernaam',
+    serialNumber: 'Serienummer',
+    ipAddress: 'IP-adres / hostnaam',
+    accessCode: 'Toegangscode',
+    model: 'Model',
+    nozzleCount: 'Aantal nozzles',
+    autoArchive: 'Automatisch archiveren',
+    status: {
+      available: 'Beschikbaar',
+      idle: 'Inactief',
+      printing: 'Bezig met afdrukken',
+      paused: 'Gepauzeerd',
+      offline: 'Offline',
+      problem: 'Probleem',
+      error: 'Fout',
+      finished: 'Voltooid',
+      unknown: 'Onbekend',
+    },
+    temperatures: {
+      nozzle: 'Nozzle',
+      bed: 'Printbed',
+      chamber: 'Kamer',
+    },
+    heaterHistory: {
+      title: 'Verwarmingsgeschiedenis',
+      openLabel: 'Verwarmingsgeschiedenis bekijken',
+      nozzle: 'Nozzle',
+      nozzle2: 'Nozzle 2',
+      bed: 'Printbed',
+      chamber: 'Kamer',
+      error: 'Geschiedenis laden mislukt',
+      empty: 'Nog geen gegevens vastgelegd',
+    },
+    progress: '{{percent}}% voltooid',
+    timeRemaining: '{{time}} resterend',
+    deleteConfirm: 'Weet je zeker dat je "{{name}}" wilt verwijderen?',
+    maintenanceOk: 'Onderhoud OK',
+    maintenanceWarning: '{{count}} waarschuwing',
+    maintenanceWarning_plural: '{{count}} waarschuwingen',
+    maintenanceDue: '{{count}} te doen',
+    maintenanceDue_plural: '{{count}} te doen',
+    // Sort options
+    sort: {
+      name: 'Naam',
+      status: 'Status',
+      model: 'Model',
+      location: 'Locatie',
+      eta: 'ETA',
+      ascending: 'Oplopend sorteren',
+      descending: 'Aflopend sorteren',
+    },
+    // Card size
+    cardSize: {
+      small: 'Kleine kaarten',
+      medium: 'Middelgrote kaarten',
+      large: 'Grote kaarten',
+      extraLarge: 'Extra grote kaarten',
+    },
+    pageView: {
+      openCamWallPage: 'Camerawand als pagina openen',
+      cards: 'Kaarten',
+      camWall: 'Camerawand',
+    },
+    camWall: {
+      page: {
+        tokenRejected:
+          'Deze camerawandlink is niet meer geldig. Het token is mogelijk verlopen of ingetrokken.',
+        loadFailed: 'Printers konden niet worden geladen.',
+      },
+      noPrinters: 'Geen printers om weer te geven',
+      noSignal: 'Geen signaal',
+      live: 'Live',
+      snap: 'Momentopname',
+      off: 'Uit',
+      summary: '{{live}} live, {{snap}} momentopnamen, {{total}} totaal',
+      layer: 'Laag {{cur}}/{{total}}',
+      timeLeft: '{{time}} resterend',
+      statusMode: {
+        off: 'Uit',
+        compact: 'Compact',
+        full: 'Volledig',
+      },
+      settings: {
+        title: 'Instellingen camerawand',
+        maxLive: 'Max. live streams',
+        maxLiveHint: 'Hoeveel tegels tegelijk live streamen. De overige worden vernieuwd als momentopnamen.',
+        snapshotInterval: 'Interval momentopnamen (seconden)',
+        snapshotIntervalHint: 'Hoe vaak niet-live tegels een nieuwe momentopname ophalen.',
+        statusOverlay: 'Statusoverlay',
+        statusOverlayHint: 'Compact: alleen statusbadge. Volledig: + voortgang, laag, resterende tijd.',
+      },
+    },
+    // Controls
+    hideOffline: 'Offline verbergen',
+    nextAvailable: 'Volgende beschikbare',
+    powerOn: 'Inschakelen',
+    offlinePrintersWithPlugs: 'Offline printers met slimme stekkers',
+    noPrintersConfigured: 'Nog geen printers geconfigureerd',
+    search: 'Printers zoeken...',
+    noSearchResults: 'Geen printers komen overeen met je zoekopdracht of filters',
+    filter: {
+      allStatuses: 'Alle statussen',
+      allLocations: 'Alle locaties',
+    },
+    toolbar: {
+      filters: 'Filters',
+      view: 'Weergave',
+      actions: 'Acties',
+    },
+    // Printer card
+    readyToPrint: 'Klaar om af te drukken',
+    external: 'Extern',
+    extL: 'Ext-L',
+    extR: 'Ext-R',
+    deleteArchives: 'Afdrukarchieven verwijderen',
+    noLabel: 'Geen label',
+    printPreview: 'Afdrukvoorbeeld',
+    width: 'Breedte',
+    height: 'Hoogte',
+    noObjectsFound: 'Geen objecten gevonden',
+    objectsLoadedOnPrintStart: 'Objecten worden geladen wanneer een afdruk start',
+    willBeSkipped: 'Wordt overgeslagen',
+    name: 'Naam',
+    serialCannotBeChanged: 'Serienummer kan niet worden gewijzigd',
+    locationHelp: 'Gebruikt om printers te groeperen en wachtrijtaken te filteren',
+    // WiFi signal strength
+    wifiSignal: {
+      veryWeak: 'Zeer zwak',
+      weak: 'Zwak',
+      fair: 'Redelijk',
+      good: 'Goed',
+      excellent: 'Uitstekend',
+    },
+    // Maintenance
+    maintenanceUpToDate: 'Al het onderhoud is bijgewerkt - Klik om te bekijken',
+    // Maintenance Mode (#1476) — operator-flipped "out of service" state.
+    // Distinct from the scheduled-maintenance dashboard above; this one
+    // wraps the backend is_active flag and stops MQTT + queue dispatch.
+    maintenance: {
+      title: 'In onderhoud',
+      subtitle: 'Deze printer is gepauzeerd — niet verbonden, niet beschikbaar voor de wachtrij en verzendt geen meldingen.',
+      pillLabel: 'Onderhoud',
+      exitButton: 'Onderhoud afsluiten',
+      menuEnter: 'Onderhoudsmodus inschakelen',
+      menuExit: 'Onderhoudsmodus afsluiten',
+      toastEntered: '{{name}} staat nu in onderhoudsmodus',
+      toastExited: '{{name}} is weer online',
+      confirmMidPrintTitle: 'Onderhoudsmodus inschakelen tijdens afdrukken?',
+      confirmMidPrintMessage: '{{name}} is momenteel aan het afdrukken. Als je de onderhoudsmodus inschakelt, wordt MQTT verbroken en stoppen voortgangsregistratie en voltooiingsmeldingen voor deze taak. Doorgaan?',
+      editFieldLabel: 'Onderhoudsmodus',
+      editFieldHelp: 'Als dit is ingeschakeld, wordt deze printer gepauzeerd voor MQTT, wachtrijverwerking en meldingen — handig voor reparatie, parallelle Bambuddy-installaties of tijdelijke buitengebruikstelling.',
+    },
+    // Chamber light
+    chamberLightOn: 'Kamerverlichting inschakelen',
+    chamberLightOff: 'Kamerverlichting uitschakelen',
+    // Files
+    files: 'Bestanden',
+    browseFiles: 'Printerbestanden bekijken',
+    // Smart plug
+    autoOffAfterPrint: 'Automatisch uitschakelen na afdruk',
+    autoOffExecuted: 'Automatisch uitschakelen is uitgevoerd - schakel de printer in om te resetten',
+    // HMS errors
+    hmsErrors: 'HMS-fouten',
+    viewHmsErrors: '{{count}} HMS-fout(en) bekijken',
+    // Actions
+    resume: 'Hervatten',
+    pause: 'Pauzeren',
+    stop: 'Stoppen',
+    camera: 'Camera',
+    skipObject: 'Object overslaan',
+    reconnect: 'Opnieuw verbinden',
+    forceRefresh: 'Geforceerd vernieuwen',
+    forceRefreshSuccess: 'Vernieuwing aangevraagd',
+    mqttDebug: 'MQTT-debug',
+    printerInformation: 'Printerinformatie',
+    copyToClipboard: 'Kopiëren',
+    copied: 'Gekopieerd!',
+    state: 'Status',
+    wifiSignalLabel: 'Wifi-signaal',
+    developerMode: 'Ontwikkelaarsmodus',
+    enabled: 'Ingeschakeld',
+    disabled: 'Uitgeschakeld',
+    addedOn: 'Toegevoegd',
+    sdCard: 'SD-kaart',
+    inserted: 'Geplaatst',
+    notInserted: 'Niet geplaatst',
+    totalPrintHours: 'Afdrukuren',
+    activeNozzle: 'Actief: {{nozzle}}-nozzle',
+    amsSwitchInletTooltip: 'Filament Track Switch IN-{{inlet}} ({{side}}) — voedt beide nozzles',
+    nozzleRack: 'Nozzlerek',
+    nozzleDocked: 'Gedockt',
+    nozzleMounted: 'Gemonteerd',
+    nozzleActive: 'Actief',
+    nozzleIdle: 'Inactief',
+    nozzleDiameter: 'Diameter',
+    nozzleType: 'Type',
+    nozzleStatus: 'Status',
+    nozzleFilament: 'Filament',
+    nozzleWear: 'Slijtage',
+    nozzleMaxTemp: 'Max. temp.',
+    nozzleSerial: 'Serienummer',
+    nozzleHardenedSteel: 'Gehard staal',
+    nozzleStainlessSteel: 'Roestvast staal',
+    nozzleTungstenCarbide: 'Wolfraamcarbide',
+    nozzleFlow: 'Flow',
+    nozzleHighFlow: 'Hoge flow',
+    nozzleStandardFlow: 'Standaard',
+    // Firmware
+    firmwareUpdate: 'Firmware-update',
+    firmwareInstructions: 'Ga op het touchscreen van de printer naar',
+    firmwareNav: 'Navigeer naar',
+    settings: 'Instellingen',
+    firmware: 'Firmware',
+    // Discovery
+    discoverPrinters: 'Printers ontdekken',
+    searching: 'Zoeken...',
+    manualEntry: 'Handmatige invoer',
+    addFromCloud: 'Toevoegen vanuit cloud',
+    // Toast messages
+    toast: {
+      printerDeleted: 'Printer verwijderd',
+      missingSpoolAssignment: 'Afdruk gestart op {{printer}}. Spoeltoewijzing ontbreekt voor: {{slots}}',
+      killSwitchTriggered: 'De facturerings-noodstop heeft een ongeautoriseerde afdruk op {{printer}} gestopt: {{filename}}',
+      billingChargeFailed: 'Facturering mislukt voor {{filename}} op {{printer}}. De budgetreservering is behouden; controleer de serverlogboeken.',
+      assignmentVerified: 'Filament geladen in sleuf {{slot}} ({{printer}})',
+      assignmentVerifiedNoKprofile: 'Sleuf {{slot}} op {{printer}} is geladen, maar de flowkalibratie (K-profiel) is niet toegepast',
+      assignmentNotConfirmed: 'De toewijzing voor sleuf {{slot}} op {{printer}} kon niet worden bevestigd — controleer de AMS-sleuf',
+      printerAdded: 'Printer toegevoegd',
+      printerUpdated: 'Printer bijgewerkt',
+      failedToDelete: 'Printer verwijderen mislukt',
+      failedToAdd: 'Printer toevoegen mislukt',
+      connectionFailedNotAdded: 'Kan geen verbinding maken met de printer. Controleer het IP-adres, serienummer en de toegangscode en bevestig dat LAN-only-modus is ingeschakeld. De printer is niet toegevoegd.',
+      failedToUpdate: 'Printer bijwerken mislukt',
+      commandSent: 'Opdracht verzonden',
+      failedToSendCommand: 'Opdracht verzenden mislukt',
+      turnedOn: '{{name}} ingeschakeld',
+      failedToPowerOn: 'Inschakelen van {{name}} mislukt',
+      scriptTriggered: 'Script geactiveerd',
+      printStopped: 'Afdruk gestopt',
+      printPaused: 'Afdruk gepauzeerd',
+      printResumed: 'Afdruk hervat',
+      referenceDeleted: 'Referentie verwijderd',
+      detectionAreaSaved: 'Detectiegebied opgeslagen',
+      failedToRunScript: 'Script uitvoeren mislukt',
+      failedToStopPrint: 'Afdruk stoppen mislukt',
+      failedToPausePrint: 'Afdruk pauzeren mislukt',
+      failedToResumePrint: 'Afdruk hervatten mislukt',
+      failedToControlChamberLight: 'Kamerverlichting bedienen mislukt',
+      failedToSetSpeed: 'Afdruksnelheid instellen mislukt',
+      failedToUpdateSetting: 'Instelling bijwerken mislukt',
+      failedToSkipObjects: 'Objecten overslaan mislukt',
+      failedToRereadRfid: 'RFID opnieuw uitlezen mislukt',
+      failedToCheckPlate: 'Plaat controleren mislukt',
+      failedToUpdateLabel: 'Label bijwerken mislukt',
+      failedToDeleteReference: 'Referentie verwijderen mislukt',
+      failedToSaveDetectionArea: 'Detectiegebied opslaan mislukt',
+      plateCheckEnabled: 'Plaatcontrole ingeschakeld',
+      plateCheckDisabled: 'Plaatcontrole uitgeschakeld',
+      calibrationSaved: 'Kalibratie opgeslagen!',
+      calibrationFailed: 'Kalibratie mislukt',
+      rfidRereadInitiated: 'Opnieuw uitlezen van RFID gestart',
+      loadInitiated: 'Filament laden…',
+      unloadInitiated: 'Filament ontladen…',
+      failedToLoad: 'Filament laden mislukt',
+      failedToUnload: 'Filament ontladen mislukt',
+    },
+    // Connection status
+    connection: {
+      connected: 'Verbonden',
+      offline: 'Offline',
+    },
+    plateStatus: {
+      markCleared: 'Plaat als leeg markeren',
+      cleared: 'Plaat leeg',
+      notCleared: 'Plaat niet leeg',
+      inUse: 'Plaat in gebruik',
+    },
+    // Queue info
+    queue: {
+      inQueue: '{{count}} afdruk in wachtrij',
+      inQueue_plural: '{{count}} afdrukken in wachtrij',
+    },
+    // Controls section
+    controls: 'Bediening',
+    // RFID
+    rfid: {
+      reread: 'RFID opnieuw uitlezen',
+    },
+    // AMS load/unload (#891)
+    ams: {
+      load: 'Laden',
+      unload: 'Ontladen',
+      feedTitle: '{{slot}} naar welke nozzle laden?',
+      feedPrompt: 'De Filament Track Switch kan dit slot naar beide hotends leiden. Kies welke je wilt voeden.',
+      feedLeft: 'Linker nozzle',
+      feedRight: 'Rechter nozzle',
+      feedAlreadyLoaded: 'Al geladen',
+      switchNotReady: 'De Filament Track Switch is nog niet ingesteld. Wijs elke AMS toe aan een inlaat op de printer en probeer het opnieuw.',
+    },
+    bedJog: {
+      limitWarning: 'Bewegingslimieten worden niet afgedwongen tijdens handmatige verplaatsingen — door een bug in Bambu-firmware worden software-eindstops bij externe opdrachten genegeerd. Verplaats voorzichtig om een botsing te voorkomen.',
+      title: 'Jog-bediening',
+      bed: 'Printbed',
+      step: 'Stap (mm)',
+      up: 'Plaat omhoog bewegen',
+      down: 'Plaat omlaag bewegen',
+      disabledWhilePrinting: 'Uitgeschakeld tijdens afdrukken',
+      notHomedTitle: 'Printer is niet gehomed',
+      notHomedMessage: 'De printer is sinds de laatste afdruk niet gehomed. Voer eerst auto-home uit voor een veilige positionering (parkeert de toolhead en homet daarna X, Y en Z), of verplaats toch — soft endstops worden omzeild.',
+      homeZ: 'Auto Home',
+      moveAnyway: 'Toch verplaatsen',
+      homingStarted: 'Printer automatisch homen…',
+    },
+    // Permissions
+    permission: {
+      noAdd: 'Je hebt geen toestemming om printers toe te voegen',
+      noEdit: 'Je hebt geen toestemming om printers te bewerken',
+      noDelete: 'Je hebt geen toestemming om printers te verwijderen',
+      noControl: 'Je hebt geen toestemming om printers te bedienen',
+      noFiles: 'Je hebt geen toestemming om printerbestanden te openen',
+      noAmsRfid: 'Je hebt geen toestemming om AMS-RFID opnieuw uit te lezen',
+      noSmartPlugControl: 'Je hebt geen toestemming om slimme stekkers te bedienen',
+      noCamera: 'Je hebt geen toestemming om camera\'s te bekijken',
+    },
+    // Add/Edit modal
+    modal: {
+      addTitle: 'Printer toevoegen',
+      editTitle: 'Printer bewerken',
+      myPrinter: 'Mijn printer',
+      selectModel: 'Model selecteren...',
+      locationGroup: 'Locatie / groep (optioneel)',
+      locationPlaceholder: 'bijv. Werkplaats, kantoor, kelder',
+      autoArchiveLabel: 'Voltooide afdrukken automatisch archiveren',
+      fromPrinterSettings: 'Uit printerinstellingen',
+      modelOptional: 'Model (optioneel)',
+      saveChanges: 'Wijzigingen opslaan',
+    },
+    // Skip objects
+    skipObjects: {
+      tooltip: 'Objecten overslaan',
+      onlyWhilePrinting: 'Objecten overslaan (alleen tijdens afdrukken)',
+      requiresMultiple: 'Objecten overslaan (vereist 2+ objecten)',
+      title: 'Objecten overslaan',
+      matchIdsInfo: 'Stem ID\'s af met het printerdisplay',
+      printerShowsIds: 'Het printerscherm toont object-ID\'s op de bouwplaat',
+      skipSelected: 'Geselecteerde overslaan',
+      skipping: 'Overslaan...',
+      noObjectsSelected: 'Geen objecten geselecteerd',
+      selectObjectsToSkip: 'Selecteer objecten die je in de huidige afdruk wilt overslaan',
+      skipped: 'overgeslagen',
+      objectsSkipped: 'Objecten overgeslagen',
+      activeCount: '{{count}} actief',
+      waitForLayer: 'Wacht tot laag 2+ om objecten over te slaan (momenteel laag {{layer}})',
+      skip: 'Overslaan',
+      confirmTitle: 'Object overslaan?',
+      confirmMessage: 'Weet je zeker dat je "{{name}}" wilt overslaan? Dit kan niet ongedaan worden gemaakt.',
+      confirmAllMessage: 'Alle resterende objecten zijn geselecteerd. Hierdoor wordt de afdruktaak gestopt. Doorgaan?',
+      confirmMultipleMessage: '{{count}} geselecteerde objecten overslaan? Dit kan niet ongedaan worden gemaakt.',
+    },
+    // Confirm modals
+    confirm: {
+      deleteTitle: 'Printer verwijderen',
+      deleteMessage: 'Weet je zeker dat je "{{name}}" wilt verwijderen? Hiermee worden alle verbindingsinstellingen verwijderd.',
+      deleteArchivesNote: 'De volledige afdrukgeschiedenis van deze printer wordt permanent verwijderd.',
+      keepArchivesNote: 'De afdrukgeschiedenis blijft bewaard, maar is niet langer aan deze printer gekoppeld.',
+      stopTitle: 'Afdruk stoppen',
+      stopMessage: 'Weet je zeker dat je de huidige afdruk op "{{name}}" wilt stoppen? Hiermee wordt de afdruktaak geannuleerd.',
+      stopButton: 'Afdruk stoppen',
+      pauseTitle: 'Afdruk pauzeren',
+      pauseMessage: 'Weet je zeker dat je de huidige afdruk op "{{name}}" wilt pauzeren?',
+      pauseButton: 'Afdruk pauzeren',
+      resumeTitle: 'Afdruk hervatten',
+      resumeMessage: 'Weet je zeker dat je de afdruk op "{{name}}" wilt hervatten?',
+      resumeButton: 'Afdruk hervatten',
+      powerOnTitle: 'Printer inschakelen',
+      powerOnMessage: 'Weet je zeker dat je de stroom voor "{{name}}" wilt INSCHAKELEN?',
+      powerOnButton: 'Inschakelen',
+      powerOffTitle: 'Printer uitschakelen',
+      powerOffMessage: 'Weet je zeker dat je de stroom voor "{{name}}" wilt UITSCHAKELEN?',
+      powerOffWarning: 'WAARSCHUWING: "{{name}}" is momenteel aan het afdrukken! Weet je zeker dat je de stroom wilt UITSCHAKELEN? Dit onderbreekt de afdruk en kan de printer beschadigen.',
+      powerOffButton: 'Uitschakelen',
+      haToggleTitle: '"{{name}}" omschakelen',
+      haToggleMessage: 'Home Assistant-entiteit {{entity}} omschakelen? Hierdoor kan de stroom worden uitgeschakeld als deze momenteel aan staat.',
+      haToggleWarning: 'WAARSCHUWING: "{{name}}" is momenteel aan het afdrukken! Het omschakelen van {{entity}} kan de stroom onderbreken en de afdruk stoppen. Doorgaan?',
+      haToggleButton: 'Omschakelen',
+    },
+    // Bulk actions
+    bulk: {
+      select: 'Selecteren',
+      selectAll: 'Alles selecteren',
+      selectByLocation: 'Selecteren op locatie',
+      selected: '{{count}} geselecteerd',
+      actions: {
+        stop: 'Stoppen',
+        pause: 'Pauzeren',
+        resume: 'Hervatten',
+        clearPlate: 'Printbed vrijgeven',
+        clearHMS: 'Meldingen wissen',
+      },
+      confirm: {
+        stopTitle: '{{count}} afdrukken stoppen',
+        stopMessage: 'Hiermee worden actieve afdrukken op {{count}} printer(s) geannuleerd. Deze actie kan niet ongedaan worden gemaakt.',
+        stopButton: 'Alles stoppen',
+        pauseTitle: '{{count}} afdrukken pauzeren',
+        pauseMessage: 'Hiermee worden actieve afdrukken op {{count}} printer(s) gepauzeerd.',
+        pauseButton: 'Alles pauzeren',
+        clearPlateTitle: '{{count}} printbedden vrijgeven',
+        clearPlateMessage: 'Hiermee wordt het printbed op {{count}} printer(s) vrijgegeven en kunnen wachtrijtaken worden gestart.',
+        clearPlateButton: 'Alles wissen',
+      },
+      success: '{{action}} voltooid op {{count}} printer(s)',
+      partial: '{{succeeded}} geslaagd, {{failed}} mislukt',
+      noneApplicable: 'Geen van de geselecteerde printers heeft de juiste status voor deze actie',
+      selectByState: 'Selecteren op status',
+    },
+    // Discovery
+    discovery: {
+      title: 'Printers ontdekken',
+      searching: 'Zoeken...',
+      scanning: 'Scannen...',
+      scanProgress: 'Scannen... {{scanned}}/{{total}}',
+      foundPrinters: '{{count}} printer(s) gevonden',
+      noPrintersFound: 'Geen printers gevonden',
+      noPrintersFoundSubnet: 'Geen printers gevonden in het opgegeven subnet.',
+      noPrintersFoundNetwork: 'Geen printers gevonden op het netwerk.',
+      allConfigured: 'Alle ontdekte printers zijn al geconfigureerd.',
+      alreadyAdded: 'Al toegevoegd',
+      select: 'Selecteren',
+      manualEntry: 'Handmatige invoer',
+      addFromCloud: 'Toevoegen vanuit cloud',
+      subnetToScan: 'Te scannen subnet',
+      dockerNote: 'Docker gedetecteerd. Voer het subnet van je printer in CIDR-notatie in. Vereist network_mode: host in docker-compose.yml.',
+      scanSubnet: 'Subnet scannen op printers',
+      discoverNetwork: 'Printers op netwerk ontdekken',
+      scanningSubnet: 'Subnet scannen op Bambu-printers...',
+      scanningNetwork: 'Netwerk scannen...',
+      serialRequired: 'Serienummer verplicht',
+      unknown: 'Onbekend',
+      failedToStart: 'Ontdekking starten mislukt',
+      customSubnetOption: 'Aangepast subnet...',
+      customSubnetLabel: 'Aangepast subnet (CIDR)',
+      customSubnetNote: 'Gebruik een aangepast subnet als je printer zich op een ander netwerk bevindt dan deze server. De FTP-poort (990) en MQTT-poort (8883) moeten bereikbaar zijn over de routeringsgrens.',
+    },
+    // AMS Drying
+    drying: {
+      start: 'Drogen starten',
+      stop: 'Drogen stoppen',
+      temperature: 'Temperatuur',
+      duration: 'Duur',
+      hours: 'uren',
+      timeRemaining: '{{time}} resterend',
+      active: 'Drogen',
+      targetSummary: '{{filament}} @ {{temp}}°C',
+      notSupported: 'Drogen niet ondersteund',
+      powerRequired: 'Sluit de AMS-voedingsadapter aan om drogen in te schakelen',
+      cannotDryNow: 'AMS kan op dit moment niet beginnen met drogen',
+      startingDrying: 'Drogen starten...',
+      toastCommandSent: 'Droogopdracht verzonden',
+      toastStopped: 'Drogen gestopt',
+      toastNotStarted: 'De printer heeft de opdracht geaccepteerd, maar de AMS is niet begonnen met drogen. Controleer of de AMS-voedingsadapter is aangesloten en de printer inactief is.',
+      screenOnly: 'AMS-drogen op deze printer kan alleen via het eigen scherm van de printer worden bediend (Bambu-beperking)',
+      stoppingDrying: 'Drogen stoppen...',
+      rotateTray: 'Spoel draaien tijdens drogen',
+      rotateUnavailableReason: 'Niet beschikbaar — een sleuf in deze AMS is naar de toolhead geladen. De spoel wordt door de toevoerbuis geblokkeerd en kan niet draaien. Trek eerst het filament terug.',
+      startMode: 'Starttijd',
+      modeNow: 'Nu',
+      modeDelay: 'Na vertraging',
+      modeAtTime: 'Op tijdstip',
+      // Verb (button label), not the noun.
+      schedule: 'Plannen',
+      scheduledFor: 'Drogen gepland voor {{time}}',
+      scheduledAsap: 'Drogen gepland (wachten op printer)',
+      cancelScheduled: 'Gepland drogen annuleren',
+      scheduleFailed: 'Drogen plannen mislukt',
+      retractFilament: 'Trek het filament bij de AMS-uitgang terug om het drogen te starten',
+      waitingAmsNotFound: 'Wachten tot de AMS wordt gedetecteerd',
+      waitingOffline: 'Wachten tot de printer online komt',
+      waitingPrinterBusy: 'Wachten tot de printer vrij is',
+      waitingAlreadyDrying: 'Wachten tot de huidige droogcyclus is voltooid',
+      waitingInterrupted: 'Onderbroken, wordt opnieuw gestart zodra de printer vrij is',
+      scheduleFailedReason: 'Gepland drogen mislukt: {{reason}}',
+      scheduleFailedUnknown: 'Onbekende fout',
+      dismissFailed: 'Negeren',
+    },
+    // AMS Filament Backup status badge (printer-wide auto-switch to another spool)
+    amsBackup: {
+      titleOn: 'AMS Filament Backup staat AAN. Klik om uit te schakelen.',
+      titleOff: 'AMS Filament Backup staat UIT. Klik om in te schakelen.',
+      titleUnknown: 'Status van AMS Filament Backup is niet beschikbaar op deze printer.',
+      toastEnabled: 'AMS Filament Backup ingeschakeld',
+      toastDisabled: 'AMS Filament Backup uitgeschakeld',
+      modalTitle: 'AMS Filament Backup',
+      modalHelp: 'Wanneer de actieve sleuf leeg raakt, doorloopt de printer in deze volgorde alle overeenkomende sleuven met hetzelfde preset en dezelfde kleur.',
+      modalNoSlots: 'Geen filament geladen.',
+      modalNoPairs: 'Geen back-upparen — geen twee sleuven gebruiken hetzelfde filamentprofiel en dezelfde kleur.',
+      extruderRightShort: 'R',
+      extruderLeftShort: 'L',
+      stateOn: 'Ingeschakeld',
+      stateOff: 'Uitgeschakeld',
+      stateUnknown: 'Niet ondersteund op deze printer',
+    },
+    activeJobSlot: {
+      title: 'Deze sleuf is filament {{n}} in de actieve afdruk',
+      ariaLabel: 'Actieve afdruksleuf {{n}}',
+    },
+    expectedSlot: {
+      title: 'De printer wacht op filament in deze sleuf',
+      ariaLabel: 'Verwachte filamentsleuf {{n}}',
+      label: '{{ams}} · Sleuf {{slot}}',
+      external: 'Externe spoel',
+    },
+    // Filaments section
+    filaments: 'Filamenten',
+    externalSpool: {
+      hide: 'Externe spoel verbergen',
+      show: 'Externe spoel weergeven',
+    },
+    // Camera
+    openCameraOverlay: 'Camera-overlay openen',
+    openCameraWindow: 'Camera in nieuw venster openen',
+    // Firmware
+    firmwareUpdateAvailable: 'Firmware-update beschikbaar: {{current}} → {{latest}}',
+    firmwareUpToDate: 'Firmware {{version}} — up-to-date',
+    firmwareUpdateButton: 'Bijwerken',
+    // Plate detection
+    plateDetection: {
+      noPermission: 'Je hebt geen toestemming om printers bij te werken',
+      enabledClick: 'Plaatcontrole ingeschakeld - Klik om uit te schakelen',
+      disabledClick: 'Plaatcontrole uitgeschakeld - Klik om in te schakelen',
+      manageCalibration: 'Kalibratie voor plaatdetectie beheren',
+      calibrationRequired: 'Kalibratie vereist',
+      calibrationInstructions: 'Zorg dat de bouwplaat <strong>volledig leeg</strong> is en klik daarna op Kalibreren.',
+      calibrationDescription: 'Bij kalibratie wordt een referentiebeeld van de lege plaat vastgelegd. Toekomstige controles vergelijken hiermee om objecten te detecteren.',
+      calibrationTip: '<strong>Tip:</strong> Je kunt maximaal 5 kalibraties voor verschillende platen opslaan. Het systeem gebruikt bij een controle automatisch de beste overeenkomst.',
+      plateEmpty: 'Plaat lijkt leeg',
+      objectsDetected: 'Objecten op plaat gedetecteerd',
+      confidence: 'Betrouwbaarheid',
+      difference: 'Verschil',
+      analysisPreview: 'Analysevoorbeeld:',
+      analysisLegend: 'Groen kader = detectiegebied, rode overlay = verschillen ten opzichte van kalibratie',
+      savedReferences: 'Opgeslagen referenties ({{count}}/{{max}})',
+      deleteReference: 'Referentie verwijderen',
+      labelPlaceholder: 'Label...',
+      clickToEdit: '{{label}} - Klik om te bewerken',
+      clickToAddLabel: 'Klik om label toe te voegen',
+    },
+    // Speed
+    speed: {
+      title: 'Afdruksnelheid',
+      silent: 'Stil (50%)',
+      standard: 'Standaard (100%)',
+      sport: 'Sport (124%)',
+      ludicrous: 'Ludicrous (166%)',
+    },
+    airduct: {
+      title: 'Luchtkanaalmodus',
+      cooling: 'Koelen',
+      heating: 'Verwarmen',
+    },
+    noSdCard: 'Geen SD',
+    door: {
+      open: 'Open',
+      closed: 'Gesloten',
+    },
+    // Fans
+    fans: {
+      partCooling: 'Onderdeelkoelventilator',
+      auxiliary: 'Hulpventilator',
+      leftAuxiliary: 'Linker hulpventilator',
+      exhaust: 'Afvoer',
+      chamber: 'Kamerventilator',
+    },
+    // HMS errors
+    clickToViewHmsErrors: 'Klik om HMS-fouten te bekijken',
+    aiDetection: {
+      safe: 'Veilig',
+      warning: 'Waarschuwing',
+      failure: 'Mislukking',
+      idle: 'Inactief',
+      error: 'Controleert niet',
+      unknown: 'Bezig met starten',
+      tooltipError: 'AI-storingsdetectie controleert deze print niet: {{reason}} - klik voor details',
+      tooltipUnknown: 'AI-storingsdetectie: wachten op het eerste resultaat - klik voor details',
+      errorHint: 'Deze print wordt niet gecontroleerd. De detectie hervat automatisch zodra het probleem hieronder is opgelost.',
+      tooltip: 'AI-foutdetectie: {{status}} (score {{score}}) - klik voor details',
+      tooltipIdle: 'AI-foutdetectie ingeschakeld - bewaking start bij de volgende afdruk - klik voor details',
+      modalTitle: 'AI-foutdetectie - {{name}}',
+      currentStatus: 'Status',
+      score: 'Score',
+      framesAnalyzed: 'Geanalyseerde frames',
+      idleHint: 'Er wordt momenteel geen afdruk bewaakt. De bewaking start automatisch bij de volgende afdruk.',
+      lastError: 'Laatste fout',
+      openSettings: 'Instellingen openen',
+    },
+    estimatedCompletion: 'Geschatte voltooiingstijd',
+    plateNumber: 'Plaat {{number}}',
+    slotOptions: 'Sleufopties',
+    // AMS hover popup
+    amsPopup: {
+      friendlyName: 'AMS-naam',
+      friendlyNamePlaceholder: 'bijv. AMS-naam',
+      serialNumber: 'Serienummer',
+      firmwareVersion: 'Firmware',
+      save: 'Opslaan',
+      clear: 'Wissen',
+      noEditPermission: 'Je hebt geen toestemming om AMS-eenheden te hernoemen',
+    },
+    // Firmware modal
+    firmwareModal: {
+      title: 'Firmware-update',
+      titleUpToDate: 'Firmware-informatie',
+      currentVersion: 'Huidig:',
+      latestVersion: 'Nieuwste:',
+      releaseNotes: 'Releaseopmerkingen',
+      checkingPrereqs: 'Vereisten controleren...',
+      sdCardReady: 'SD-kaart gereed. Klik hieronder om firmware te uploaden.',
+      uploadedSuccess: 'Firmware geüpload naar SD-kaart!',
+      applyInstructions: 'Om de update op je printer toe te passen:',
+      step1: 'Ga op het touchscreen van de printer naar <strong>Instellingen</strong>',
+      step2: 'Navigeer naar <strong>Firmware</strong>',
+      step3: 'Selecteer <strong>Update vanaf SD-kaart</strong>',
+      step4: 'De update duurt 10-20 minuten',
+      done: 'Klaar',
+      starting: 'Starten...',
+      uploadFirmware: 'Firmware uploaden',
+      uploadFailed: 'Upload starten mislukt: {{error}}',
+      uploadedToast: 'Firmware geüpload! Start de update vanaf het printerscherm.',
+      availableVersions: 'Beschikbare versies',
+      usable: 'Bruikbaar',
+      unavailable: 'Niet beschikbaar',
+      installed: 'Geïnstalleerd',
+      newerBadge: 'nieuwer',
+      olderBadge: 'ouder',
+      currentBadge: 'huidig',
+    },
+    accessCodePlaceholder: 'Laat leeg om huidige waarde te behouden',
+    // ROI editor
+    roi: {
+      title: 'Detectiegebied (ROI)',
+      xStart: 'X-start',
+      yStart: 'Y-start',
+      width: 'Breedte',
+      height: 'Hoogte',
+      instruction: 'Pas het detectiegebied aan zodat het op de bouwplaat is gericht. Het groene kader in het voorbeeld toont het huidige gebied.',
+    },
+    developerModeWarning: 'Developer LAN-modus is niet ingeschakeld op: {{names}}. Sommige functies werken mogelijk niet.',
+    howToEnable: 'Inschakelen',
+    incompatibleFile: 'Dit bestand is gesliced voor {{slicedFor}}, maar deze printer is een {{printerModel}}',
+    dropNotPrintable: 'Alleen .gcode- en .gcode.3mf-bestanden kunnen worden afgedrukt',
+    dropToPrint: 'Sleep hierheen om af te drukken',
+    dropToQueue: 'Sleep hierheen voor wachtrij',
+  },
+
+  // Archives page
+  archives: {
+    media: {
+      title: 'Afdrukvideo\'s',
+      download: 'Afdrukvideo\'s downloaden',
+      searching: 'Printer controleren op video\'s…',
+      searchFailed: 'Printer kon niet op video\'s worden gecontroleerd.',
+      none: 'Geen timelapse- of IP-camerafragmenten gevonden voor deze afdruk.',
+      attachedTimelapse: 'Gekoppelde timelapse',
+      printerFiles: 'Bestanden die nog op de printer staan',
+      downloadSelected: 'Geselecteerde downloaden',
+      printerMissing: 'Printer is niet meer geconfigureerd',
+      timelapseUnavailable: 'De timelapse-map kon niet worden gelezen',
+      ipcamUnavailable: 'De IP-camera-map kon niet worden gelezen',
+    },
+    title: 'Afdrukarchieven',
+    no3mfBanner: {
+      title: 'Sommige recente afdrukken konden niet met miniaturen worden gearchiveerd',
+      body: 'De slicer heeft het .gcode.3mf-bestand niet op de SD-kaart van de printer achtergelaten, waardoor Bambuddy de miniatuur of slicermetadata niet kon ophalen. Dit komt meestal doordat "Verzonden bestanden opslaan op externe opslag" is uitgeschakeld in de slicer (tabblad Apparaat in Bambu Studio / OrcaSlicer).',
+      docsLink: 'Zie installatiestap 4',
+      dismissLabel: 'Deze melding sluiten',
+      docsLinkInternalStorage: 'Waarom dit gebeurt',
+      titleInternalStorage: 'Sommige recente afdrukken bleven op de interne opslag van de printer staan',
+      bodyInternalStorage: 'Bambu Studio plaatste het geslicede bestand op de interne opslag van de printer in plaats van op de kaart, waardoor Bambuddy via FTP niets kon uitlezen. Op H2-serie en P2S doet de knop Afdrukken dit altijd — alleen Verzenden biedt een keuze, en ook daar is Cache standaard. Die afdrukken worden nog steeds gearchiveerd met naam en timing, maar zonder miniatuur of slicermetadata. Voor volledige archieven start je de afdruk vanuit Bambuddy, slice je in OrcaSlicer, of gebruik je in Bambu Studio Verzenden met Extern geselecteerd en start je daarna de afdruk. Voor al deze opties is een kaart of USB-stick in de printer nodig.',
+      titleNoExternalStorage: 'Sommige recente afdrukken konden niet worden gearchiveerd — geen opslag in de printer',
+      bodyNoExternalStorage: 'De printer meldt dat er geen kaart of USB-stick in de sleuf zit, waardoor het geslicede bestand nergens kon worden opgeslagen en Bambuddy niets kon uitlezen. Plaats er een en de volgende afdruk wordt volledig gearchiveerd.',
+      titleInternalHistory: 'Sommige recente prints zijn gestart vanaf een bestand dat al op de printer stond',
+      bodyInternalHistory: 'Die prints liepen vanuit de eigen bibliotheek van de printer — opnieuw afdrukken via het scherm, starten vanuit Handy, of een bestand dat eerder is verstuurd en later is afgedrukt. Bambuddy leest printbestanden via FTP, dat alleen de kaart of stick aanbiedt, terwijl de printer die bibliotheek bewaart op een plek die FTP niet kan bereiken. Er was dus geen 3MF om te lezen. Geen enkele slicer-instelling verandert dit, omdat er voor deze prints niets is verstuurd. Ze worden nog steeds gearchiveerd met naam en tijden, en via Archief bewerken kun je het gebruikte filament handmatig invullen. Start de print vanuit Bambuddy of vanuit je slicer voor een volledig archief.',
+    },
+    searchPlaceholder: 'Archieven zoeken...',
+    filterByPrinter: 'Filteren op printer',
+    filterByStatus: 'Filteren op status',
+    sortBy: 'Sorteren op',
+    sortNewest: 'Nieuwste eerst',
+    sortOldest: 'Oudste eerst',
+    sortName: 'Naam',
+    sortDuration: 'Duur',
+    sortLargest: 'Grootste eerst',
+    sortSmallest: 'Kleinste eerst',
+    sortSize: 'Grootte',
+    noArchives: 'Geen archieven gevonden',
+    noArchivesSearch: 'Geen archieven komen overeen met je zoekopdracht',
+    originalPrintNotVisible: 'Originele afdruk niet zichtbaar - probeer filters te wissen',
+    noArchivesYet: 'Nog geen archieven',
+    prints: 'afdrukken',
+    pagination: {
+      showing: 'Weergegeven',
+      to: 'tot',
+      of: 'van',
+      show: 'Tonen',
+      page: 'Pagina',
+      all: 'Alle',
+    },
+    loadingArchives: 'Archieven laden...',
+    releaseToUpload: 'Loslaten om te uploaden',
+    showAll: 'Alles tonen',
+    showFavoritesOnly: 'Alleen favorieten tonen',
+    gridView: 'Rasterweergave',
+    listView: 'Lijstweergave',
+    calendarView: 'Kalenderweergave',
+    logView: 'Afdruklogboek',
+    manageTags: 'Tags beheren',
+    showFailedPrints: 'Mislukte afdrukken tonen',
+    hideFailedPrints: 'Mislukte afdrukken verbergen',
+    hideDuplicates: 'Duplicaten verbergen',
+    viewOriginalPrint: 'Klik om originele afdruk te bekijken (#{{id}})',
+    printTime: 'Afdruktijd',
+    filamentUsed: 'Filament gebruikt',
+    cost: 'Kosten',
+    preview: 'Voorbeeld',
+    deleteArchive: 'Archief verwijderen',
+    deleteConfirm: 'Weet je zeker dat je dit archief wilt verwijderen?',
+    favorite: 'Favoriet',
+    unfavorite: 'Uit favorieten verwijderen',
+    viewDetails: 'Details bekijken',
+    status: {
+      completed: 'Voltooid',
+      failed: 'Mislukt',
+      stopped: 'Gestopt',
+    },
+    toast: {
+      source3mfAttached: 'Bron-3MF gekoppeld: {{filename}}',
+      failedUploadSource3mf: 'Uploaden van bron-3MF mislukt',
+      source3mfRemoved: 'Bron-3MF verwijderd',
+      failedRemoveSource3mf: 'Verwijderen van bron-3MF mislukt',
+      f3dAttached: 'F3D gekoppeld: {{filename}}',
+      failedUploadF3d: 'Uploaden van F3D mislukt',
+      f3dRemoved: 'F3D verwijderd',
+      failedRemoveF3d: 'Verwijderen van F3D mislukt',
+      timelapseAttached: 'Timelapse gekoppeld: {{filename}}',
+      timelapseAlreadyAttached: 'Timelapse is al gekoppeld',
+      noMatchingTimelapse: 'Geen overeenkomende timelapse gevonden',
+      failedScanTimelapse: 'Scannen naar timelapse mislukt',
+      failedAttachTimelapse: 'Timelapse koppelen mislukt',
+      timelapseRemoved: 'Timelapse verwijderd',
+      failedRemoveTimelapse: 'Timelapse verwijderen mislukt',
+      timelapseUploaded: 'Timelapse geüpload: {{filename}}',
+      failedUploadTimelapse: 'Timelapse uploaden mislukt',
+      archiveDeleted: 'Archief verwijderd',
+      failedDeleteArchive: 'Archief verwijderen mislukt',
+      addedToFavorites: 'Toegevoegd aan favorieten',
+      removedFromFavorites: 'Uit favorieten verwijderd',
+      projectUpdated: 'Project bijgewerkt',
+      failedUpdateProject: 'Project bijwerken mislukt',
+      linkCopied: 'Link naar klembord gekopieerd',
+      failedCopyLink: 'Link kopiëren mislukt',
+      photoDeleted: 'Foto verwijderd',
+      failedDeletePhoto: 'Foto verwijderen mislukt',
+      failedDeleteArchives: 'Archieven verwijderen mislukt',
+      failedUpdateFavorites: 'Favorieten bijwerken mislukt',
+      exportDownloaded: 'Export gedownload',
+      exportFailed: 'Export mislukt',
+    },
+    menu: {
+      print: 'Afdrukken',
+      openInBambuStudio: 'Openen in slicer',
+      slice: 'Slicen',
+      externalLink: 'Externe link',
+      viewOnMakerWorld: 'Bekijken op MakerWorld',
+      preview3d: '3D-voorbeeld',
+      viewTimelapse: 'Timelapse bekijken',
+      scanForTimelapse: 'Zoeken naar timelapse',
+      uploadTimelapse: 'Timelapse uploaden',
+      removeTimelapse: 'Timelapse verwijderen',
+      downloadSource3mf: 'Bron-3MF downloaden',
+      uploadSource3mf: 'Bron-3MF uploaden',
+      replaceSource3mf: 'Bron-3MF vervangen',
+      removeSource3mf: 'Bron-3MF verwijderen',
+      uploadF3d: 'F3D uploaden',
+      replaceF3d: 'F3D vervangen',
+      downloadF3d: 'F3D downloaden',
+      removeF3d: 'F3D verwijderen',
+      download: 'Downloaden',
+      copyDownloadLink: 'Downloadlink kopiëren',
+      qrCode: 'QR-code',
+      viewPhotos: 'Foto\'s bekijken',
+      viewPhotosCount: 'Foto\'s bekijken ({{count}})',
+      projectPage: 'Projectpagina',
+      addToFavorites: 'Toevoegen aan favorieten',
+      removeFromFavorites: 'Uit favorieten verwijderen',
+      edit: 'Bewerken',
+      printLog: 'Afdruklogboek',
+      goToProject: 'Ga naar project: {{name}}',
+      addToProject: 'Toevoegen aan project',
+      removeFromProject: 'Uit project verwijderen',
+      loading: 'Laden...',
+      noProjectsAvailable: 'Geen projecten beschikbaar',
+      searchProjects: 'Projecten zoeken…',
+      select: 'Selecteren',
+      deselect: 'Deselecteren',
+      delete: 'Verwijderen',
+    },
+    permission: {
+      noReprint: 'Je hebt geen toestemming om dit archief opnieuw af te drukken',
+      noAddToQueue: 'Je hebt geen toestemming om aan de wachtrij toe te voegen',
+      noUpdateArchives: 'Je hebt geen toestemming om archieven bij te werken',
+      noUploadFiles: 'Je hebt geen toestemming om bestanden te uploaden',
+      noDownload: 'Je hebt geen toestemming om archieven te downloaden',
+      noCopyLink: 'Je hebt geen toestemming om downloadlinks te kopiëren',
+      noDelete: 'Je hebt geen toestemming om dit archief te verwijderen',
+      noEdit: 'Je hebt geen toestemming om dit item te bewerken',
+      noCreate: 'Je hebt geen toestemming om archieven te maken',
+    },
+    platePicker: {
+      title: 'Selecteer plaat voor voorbeeld',
+      hint: 'Dit archief bevat meerdere platen. Kies er een om in de GCode-viewer te openen.',
+      plateLabel: 'Plaat {{index}}',
+      objectCount: '{{count}} object',
+      objectCount_plural: '{{count}} objecten',
+      noGcode: 'Dit archief bevat geen geslicede G-code voor een voorbeeld. Open het eerst in Bambu Studio om te slicen.',
+    },
+    card: {
+      previousPlate: 'Vorige plaat',
+      nextPlate: 'Volgende plaat',
+      plateNumber: 'Plaat {{index}}',
+      moreOptions: 'Klik met rechts voor meer opties',
+      addToFavorites: 'Toevoegen aan favorieten',
+      removeFromFavorites: 'Uit favorieten verwijderen',
+      cancelled: 'geannuleerd',
+      failed: 'mislukt',
+      duplicate: 'duplicaat',
+      duplicateTitle: 'Dit model is eerder afgedrukt',
+      openSource3mf: 'Bron-3MF openen in Bambu Studio (klik met rechts voor meer opties)',
+      downloadF3d: 'Fusion 360-ontwerpbestand downloaden',
+      viewTimelapse: 'Timelapse bekijken',
+      viewPhoto: '1 foto bekijken',
+      viewPhotos: '{{count}} foto\'s bekijken',
+      openFolder: 'Map openen: {{name}}',
+      slicedFile: 'Gesliced bestand - klaar om af te drukken',
+      sourceFile: 'Alleen bronbestand - geen AMS-toewijzing beschikbaar',
+      gcode: 'GCODE',
+      source: 'BRON',
+      project: 'Project: {{name}}',
+      runsBadge: '{{count}} afdrukken',
+      runsBadgeTitle: '{{count}} afdrukken totaal — {{successful}} geslaagd, {{failed}} mislukt. Klik om het volledige afdruklogboek te bekijken.',
+      estimated: 'Geschat: {{time}}',
+      actual: 'Werkelijk: {{time}}',
+      accuracy: 'Nauwkeurigheid: {{percent}}%',
+      filament: '{{weight}}g',
+      layer: '{{count}} laag',
+      layers: '{{count}} lagen',
+      object: '{{count}} object',
+      objects: '{{count}} objecten',
+      slicedFor: 'Gesliced voor {{model}}',
+      uploadedBy: 'Geüpload door',
+      noPermissionReprint: 'Je hebt geen toestemming om opnieuw af te drukken',
+      noFileForReprint: 'Geen 3MF-bestand beschikbaar — het bestand kon niet van de printer worden gedownload toen de afdruk werd geregistreerd',
+      slicerAmsMapping: 'AMS-toewijzing opgeslagen ({{printer}})',
+      slicerAmsMappingTooltip: 'De eigen AMS-sleufkeuze van de slicer is opgeslagen voor {{printer}}. Tray-nummers hebben alleen betekenis op die printer, dus bij opnieuw afdrukken worden ze alleen hergebruikt als opnieuw naar {{printer}} wordt afgedrukt.',
+      noPermissionEdit: 'Je hebt geen toestemming om archieven te bewerken',
+      noPermissionDelete: 'Je hebt geen toestemming om archieven te verwijderen',
+      openInBambuStudio: 'Openen in slicer',
+      openInBambuStudioToSlice: 'Openen in slicer om te slicen',
+      slice: 'Slicen',
+      externalLink: 'Externe link',
+      makerWorld: 'MakerWorld: {{designer}}',
+      viewProject: 'Project bekijken',
+      noExternalLink: 'Geen externe link',
+      preview3d: '3D-voorbeeld',
+      download: 'Downloaden',
+      edit: 'Bewerken',
+      delete: 'Verwijderen',
+    },
+    runLog: {
+      title: 'Afdruklogboek',
+      modalTitle: 'Afdruklogboek — {{name}}',
+      modalTitleFallback: 'dit archief',
+      empty: 'Nog geen afdrukgebeurtenissen geregistreerd voor dit archief.',
+      col: {
+        date: 'Datum',
+        status: 'Status',
+        duration: 'Duur',
+        filament: 'Filament',
+        cost: 'Kosten',
+      },
+      status: {
+        completed: 'Voltooid',
+        failed: 'Mislukt',
+        cancelled: 'Geannuleerd',
+        stopped: 'Gestopt',
+        skipped: 'Overgeslagen',
+        printing: 'Bezig met afdrukken',
+      },
+    },
+    modal: {
+      deleteArchive: 'Archief verwijderen',
+      deleteConfirm: 'Weet je zeker dat je "{{name}}" wilt verwijderen? Deze actie kan niet ongedaan worden gemaakt.',
+      deleteButton: 'Verwijderen',
+      deletePurgeStats: 'Deze afdruk ook verwijderen uit Snelle statistieken (filament, tijd, kosten, energie)',
+      deleteQueueItemsWarning: '{{count}} wachtrij-item(s) gekoppeld aan dit archief worden ook verwijderd.',
+      deleteBlockedByPrinting: 'Kan niet verwijderen — {{count}} wachtrij-item(s) zijn momenteel aan het afdrukken. Stop eerst de afdruk en probeer het daarna opnieuw.',
+      removeSource3mf: 'Bron-3MF verwijderen',
+      removeSource3mfConfirm: 'Weet je zeker dat je het bron-3MF-bestand uit "{{name}}" wilt verwijderen? Hiermee wordt het originele slicerprojectbestand verwijderd.',
+      removeButton: 'Verwijderen',
+      removeF3d: 'F3D verwijderen',
+      removeF3dConfirm: 'Weet je zeker dat je het Fusion 360-ontwerpbestand uit "{{name}}" wilt verwijderen?',
+      removeTimelapse: 'Timelapse verwijderen',
+      removeTimelapseConfirm: 'Weet je zeker dat je de timelapsevideo uit "{{name}}" wilt verwijderen?',
+      timelapse: '{{name}} - Timelapse',
+      selectTimelapse: 'Timelapse selecteren',
+      selectTimelapseDesc: 'Geen automatische overeenkomst gevonden. Selecteer de timelapse voor deze afdruk:',
+      deleteArchives: 'Archieven verwijderen',
+      deleteArchivesConfirm: 'Weet je zeker dat je {{count}} archief/archieven wilt verwijderen? Deze actie kan niet ongedaan worden gemaakt.',
+      deleteCount: '{{count}} verwijderen',
+    },
+    page: {
+      title: 'Archieven',
+      printsCount: '{{filtered}} van {{total}} afdrukken',
+      dropFilesHere: 'Sleep .3mf-bestanden hierheen',
+      releaseToUpload: 'Loslaten om te uploaden',
+      only3mfSupported: 'Alleen .3mf-bestanden worden ondersteund',
+      close: 'Sluiten',
+      selected: '{{count}} geselecteerd',
+      selectAll: 'Alles selecteren',
+      tags: 'Tags',
+      project: 'Project',
+      favorite: 'Favoriet',
+      delete: 'Verwijderen',
+      toggledFavorites: 'Favorieten omgeschakeld voor {{count}} archief/archieven',
+      failedUpdateFavorites: 'Favorieten bijwerken mislukt',
+      archivesDeleted: '{{count}} archief/archieven verwijderd',
+      failedDeleteArchives: 'Archieven verwijderen mislukt',
+      photoDeleted: 'Foto verwijderd',
+      failedDeletePhoto: 'Foto verwijderen mislukt',
+    },
+    list: {
+      name: 'Naam',
+      printer: 'Printer',
+      date: 'Datum',
+      size: 'Grootte',
+      actions: 'Acties',
+      hasTimelapse: 'Heeft timelapse',
+    },
+    log: {
+      date: 'Datum',
+      printName: 'Afdruknaam',
+      printer: 'Printer',
+      user: 'Gebruiker',
+      status: 'Status',
+      duration: 'Duur',
+      filament: 'Filament',
+      filamentUsed: 'Filament gebruikt',
+      cost: 'Kosten',
+      energy: 'Energie',
+      energyCost: 'Energiekosten',
+      completedAt: 'Voltooid',
+      columns: 'Kolommen',
+      sortBy: 'Sorteren op {{column}}',
+      allPrinters: 'Alle printers',
+      allUsers: 'Alle gebruikers',
+      allStatuses: 'Alle statussen',
+      cancelled: 'Geannuleerd',
+      skipped: 'Overgeslagen',
+      dateFrom: 'Van',
+      dateTo: 'Tot',
+      noEntries: 'Geen afdruklogboekitems gevonden',
+      showing: '{{count}} van {{total}} items weergegeven',
+      rowsPerPage: 'Rijen',
+      page: 'Pagina',
+      prev: 'Vorige',
+      next: 'Volgende',
+      clearLog: 'Logboek wissen',
+      clearLogTitle: 'Afdruklogboek wissen',
+      clearLogConfirm: 'Alle items in het afdruklogboek worden permanent verwijderd. Archieven en wachtrij-items blijven intact. Deze actie kan niet ongedaan worden gemaakt. Weet je het zeker?',
+      clearLogButton: 'Alles wissen',
+      cleared: '{{count}} logboekitems gewist',
+      clearFailed: 'Afdruklogboek wissen mislukt',
+      deleteEntryTitle: 'Afdruklogboekitem verwijderen',
+      deleteEntryConfirm: 'Dit item wordt uit het logboek verwijderd en de bijdragen aan filament, tijd en kosten verdwijnen uit Snelle statistieken. Het bijbehorende archief (indien aanwezig) blijft intact. Deze actie kan niet ongedaan worden gemaakt.',
+      entryDeleted: 'Afdruklogboekitem verwijderd',
+      entryDeleteFailed: 'Afdruklogboekitem verwijderen mislukt',
+      editEntryTitle: 'Afdruklogboekitem bewerken',
+      editEntryDescription: 'Classificeer deze afdrukrun. De widget Foutenanalyse groepeert op deze waarden, zodat wijzigingen direct in de statistieken worden verwerkt.',
+      entryUpdated: 'Afdruklogboekitem bijgewerkt',
+      entryUpdateFailed: 'Afdruklogboekitem bijwerken mislukt',
+      statuses: {
+        completed: 'Voltooid',
+        failed: 'Mislukt',
+        stopped: 'Gestopt',
+        cancelled: 'Geannuleerd',
+        skipped: 'Overgeslagen',
+      },
+    },
+  },
+
+  // Sticky upload-progress toast (#1625 follow-up — restored the
+  // legacy bg-dispatch toast UI for the scheduler-driven dispatch path).
+  // Strings mirror 0b43ac0d's backgroundDispatch namespace.
+  dispatchToast: {
+    untitled: 'Afdruktaak',
+    startingPrints: 'Afdrukken starten',
+    progressSummary: '{{complete}}/{{total}} voltooid • Verwerken: {{processing}}',
+    expandDetails: 'Verzenddetails uitklappen',
+    collapseDetails: 'Verzenddetails inklappen',
+    awaitingPrinter: 'Wachten op printer…',
+    status: {
+      processing: 'Verwerken',
+      completed: 'Voltooid',
+      failed: 'Mislukt',
+    },
+    failed: {
+      generic: 'Verzenden mislukt',
+      upload_failed: 'Uploaden naar printer mislukt',
+      start_command_failed: 'Printer heeft startopdracht geweigerd',
+    },
+    dismiss: 'Negeren',
+  },
+
+  // Pipeline Runs dashboard (#1425 PR C). Lists every Slicer Pipeline run
+  // across every pipeline with status + pipeline filters and pagination.
+  // Each row expands to per-copy job status; partial-failure runs get a
+  // Retry-failed button; in-flight runs get a Cancel button.
+  pipelineRuns: {
+    title: 'Pipeline-runs',
+    loading: 'Laden…',
+    empty: 'Nog geen pipeline-runs.',
+    filter: {
+      pipeline: 'Pipeline',
+      status: 'Status',
+      target: 'Doel',
+      all: 'Alle',
+      allPipelines: 'Alle pipelines',
+      allStatus: 'Alle statussen',
+      allTargets: 'Alle doelen',
+      clear: 'Filters wissen',
+      noMatches: 'Geen runs komen overeen met de huidige filters.',
+    },
+    totalCount_one: '{{n}} run',
+    totalCount_other: '{{n}} runs',
+    copies: '{{n}} kopieën',
+    failedCount: '{{n}} mislukt',
+    copyN: 'Kopie {{n}}',
+    retryFailed: 'Mislukte opnieuw proberen',
+    retryOf: 'herhaling van #{{n}}',
+    pagination: '{{start}}–{{end}} van {{total}}',
+    cancelledByUser: 'Geannuleerd door gebruiker',
+    toast: {
+      cancelled: 'Run geannuleerd',
+      cancelFailed: 'Annuleren mislukt',
+      retryStarted: 'Opnieuw proberen gestart',
+      retryFailed: 'Mislukte opnieuw proberen',
+      cleared: '{{n}} runs gewist',
+      clearFailed: 'Wissen mislukt',
+    },
+    clearLog: 'Logboek wissen',
+    clearConfirmTitle: 'Logboek wissen?',
+    clearConfirmBody: 'Alle voltooide, mislukte, geannuleerde en gedeeltelijk mislukte pipeline-runs verwijderen? Lopende runs blijven behouden. Dit kan niet ongedaan worden gemaakt.',
+    clearConfirmAction: 'Wissen',
+    jobStatus: {
+      pending: 'in afwachting',
+      awaiting_printer: 'wachten op printer',
+      queued: 'in wachtrij',
+      printing: 'bezig met afdrukken',
+      completed: 'voltooid',
+      failed: 'mislukt',
+      cancelled: 'geannuleerd',
+    },
+  },
+
+  // Queue page
+  queue: {
+    title: 'Afdrukwachtrij',
+    subtitle: 'Plan en beheer je afdruktaken',
+    filamentShort: {
+      rowBadge: 'Onvoldoende filament op de toegewezen spoel',
+      rowTooltip: 'De verzendscheduler heeft dit item gemarkeerd. Klik op Afspelen om het tekort per sleuf te bekijken en te bepalen of je toch wilt afdrukken.',
+      confirmTitle: 'Onvoldoende filament',
+      confirmIntro: 'De toegewezen spoel kan minstens één sleuf niet voorzien. Toch afdrukken?',
+      lineItem: 'Sleuf {{slot}}: vereist {{required}} g, {{remaining}} g resterend',
+      unknown: 'onbekend',
+      printAnyway: 'Toch afdrukken',
+    },
+    slicerAmsMapping: {
+      rowBadge: 'AMS-sleuven opgeslagen voor deze printer',
+      rowTooltip: 'Dit archief bevat de exacte AMS-sleuven die de slicer heeft gekozen, opgeslagen voor de printer waarop dit item is gericht. Bij opnieuw afdrukken op deze printer kunnen die trays worden hergebruikt in plaats van opnieuw op type en kleur te matchen.',
+    },
+    // Print modal
+    editQueueItem: 'Wachtrij-item bewerken',
+    selectAllPlates: 'Alle {{count}} platen selecteren',
+    deselectAll: 'Alles deselecteren',
+    printQueued: 'Afdruk in wachtrij geplaatst',
+    printQueuedWillStartWhenIdle: 'Start zodra de printer inactief is',
+    itemsQueued: '{{count}} items in wachtrij',
+    sending: 'Verzenden...',
+    sendingProgress: '{{current}}/{{total}} verzenden...',
+    adding: 'Toevoegen...',
+    addingProgress: '{{current}}/{{total}} toevoegen...',
+    savingProgress: '{{current}}/{{total}} opslaan...',
+    clearQueue: 'Wachtrij wissen',
+    clearHistory: 'Geschiedenis wissen',
+    emptyQueue: 'Wachtrij is leeg',
+    position: 'Positie',
+    scheduledTime: 'Geplande tijd',
+    moveUp: 'Omhoog verplaatsen',
+    moveDown: 'Omlaag verplaatsen',
+    startNow: 'Nu starten',
+    printingInProgress: 'Afdrukken bezig...',
+    viewArchive: 'Archief bekijken',
+    viewInFileManager: 'Bekijken in Bestandsbeheer',
+    itemCount: '{{count}} item',
+    itemCount_plural: '{{count}} items',
+    dragToReorder: 'Sleep om volgorde te wijzigen (alleen ZSM)',
+    reorderHint: 'Positie heeft alleen invloed op ZSM-items. Geplande items draaien op hun ingestelde tijd.',
+    sjf: {
+      label: 'SJF',
+      tooltip: 'Shortest Job First — scheduler geeft voorrang aan kortere afdrukken',
+    },
+    addedBy: 'Toegevoegd door {{name}}',
+    nextInQueue: 'Volgende in wachtrij',
+    clearPlateSuccess: 'Plaat vrijgegeven — klaar voor volgende afdruk',
+    plateNumber: 'Plaat {{index}}',
+    // Batch / quantity
+    quantity: 'Aantal',
+    quantityHint: 'Maakt {{count}} wachtrij-items',
+    plateQuantityLabel: 'Runs van {{plate}}',
+    plateQuantityTotal_one: '{{count}} run in totaal',
+    plateQuantityTotal_other: '{{count}} runs in totaal',
+    batchOrders: {
+      title: 'Batchorders',
+      emptyTitle: 'Geen batchorders',
+      emptyDescription: 'Plaats een bestand met meerdere platen, of meer dan één kopie van een plaat, in de wachtrij en de order verschijnt hier met de voortgang.',
+      filter: {
+        active: 'Actief',
+        completed: 'Voltooid',
+        cancelled: 'Geannuleerd',
+        all: 'Alle',
+      },
+      status: {
+        active: 'Actief',
+        completed: 'Voltooid',
+        cancelled: 'Geannuleerd',
+      },
+      noTargets: 'Alleen groepering',
+      noTargetsHint: 'Aangemaakt voordat doelen per plaat bestonden, dus dit volgt de runs die in de wachtrij zijn gezet in plaats van een doel.',
+      dispatchRemaining: '{{count}} resterende in wachtrij plaatsen',
+      dispatchPlate: 'Resterende in wachtrij plaatsen',
+      strandedPlate: 'Geen instellingen meer om van te kopiëren',
+      strandedNotice: 'Kan {{runs}} van de {{owed}} nog openstaande runs niet in de wachtrij zetten: die plaat heeft geen run in de wachtrij of voltooide run meer om instellingen van te kopiëren. Zet de plaat één keer vanuit het bestand in de wachtrij, of annuleer de order.',
+      dispatched: 'Resterende runs voor {{name}} in wachtrij geplaatst',
+      plateProgress: '{{completed}} van {{target}} klaar',
+      remaining: '{{count}} nog te doen',
+      failed: '{{count}} mislukt',
+      printing: '{{count}} bezig met afdrukken',
+      wholeFile: 'Hele bestand',
+      due: 'Vervalt {{date}}',
+      costSoFar: '{{amount}} tot nu toe',
+      costRemaining: '({{amount}} te gaan)',
+    },
+    activeBatches: 'Actieve batches',
+    batchProgress: '{{completed}} van {{total}} voltooid',
+    cancelBatch: 'Resterende annuleren',
+    batchCancelled: 'Resterende batchitems geannuleerd',
+    cancelBatchConfirmTitle: 'Batch annuleren',
+    cancelBatchConfirmMessage: 'Alle resterende openstaande items in deze batch annuleren?',
+    batch: {
+      defaultName: 'Batch',
+      label: '{{count}} item',
+      label_plural: '{{count}} items',
+      pendingCount: '{{count}} in afwachting',
+      pendingCount_plural: '{{count}} in afwachting',
+      expand: 'Batch uitklappen',
+      collapse: 'Batch inklappen',
+      groupAsBatch: 'Als batch groeperen…',
+      groupAsBatchDescription: 'Combineer de {{count}} geselecteerde items tot één inklapbare batch.',
+      nameLabel: 'Batchnaam',
+      namePlaceholder: 'bijv. Vrijdagcadeaus',
+      create: 'Batch maken',
+      ungroup: 'Groepering opheffen',
+      ungroupConfirmTitle: 'Batchgroepering opheffen?',
+      ungroupConfirmMessage: 'De items blijven in de wachtrij staan, maar worden niet langer als groep weergegeven.',
+      dragGroup: 'Groep slepen',
+    },
+    // Tabs
+    tabs: {
+      batches: 'Batches',
+      queue: 'Wachtrij',
+      history: 'Geschiedenis',
+      timeline: 'Tijdlijn',
+      pipelines: 'Pipelines',
+    },
+    // Layout toggle on the Queue tab — distinct from the sort dropdown
+    // (those control order; these control whether items render as one flat
+    // list or grouped under per-printer section headers).
+    layout: {
+      flatList: 'Lijst',
+      byPrinter: 'Per printer',
+      groupByPrinter: 'Groeperen per printer',
+    },
+    // History tab empty state
+    history: {
+      emptyTitle: 'Nog geen geschiedenis',
+      emptyDescription: 'Voltooide, geannuleerde en mislukte afdrukken verschijnen hier.',
+      showMore: 'Meer tonen',
+      showingCount: '{{shown}} van {{total}} weergegeven',
+    },
+    // Drag ghost label when multi-dragging
+    dragGhost: {
+      multiCount: '{{count}} items',
+      batch: '{{name}} ({{count}} kopie)',
+      batch_plural: '{{name}} ({{count}} kopieën)',
+    },
+    // Sections
+    sections: {
+      currentlyPrinting: 'Momenteel aan het afdrukken',
+      queued: 'In wachtrij',
+      history: 'Geschiedenis',
+    },
+    // Status
+    status: {
+      pending: 'In afwachting',
+      waiting: 'Wachten',
+      printing: 'Bezig met afdrukken',
+      paused: 'Gepauzeerd',
+      completed: 'Voltooid',
+      failed: 'Mislukt',
+      skipped: 'Overgeslagen',
+      cancelled: 'Geannuleerd',
+    },
+    // Summary cards
+    summary: {
+      printing: 'Bezig met afdrukken',
+      queued: 'In wachtrij',
+      totalTime: 'Totale wachtrijtijd',
+      totalWeight: 'Totaal wachtrijgewicht',
+      history: 'Geschiedenis',
+    },
+    // Filters
+    filter: {
+      allPrinters: 'Alle printers',
+      unassigned: 'Niet toegewezen',
+      allStatus: 'Alle statussen',
+      allLocations: 'Alle locaties',
+      any: 'Elke',
+    },
+    // Sort
+    sort: {
+      byPosition: 'Sorteren op positie',
+      byName: 'Sorteren op naam',
+      byPrinter: 'Sorteren op printer',
+      bySchedule: 'Sorteren op planning',
+      byDate: 'Sorteren op datum',
+      ascendingOldest: 'Oplopend (oudste eerst)',
+      descendingNewest: 'Aflopend (nieuwste eerst)',
+    },
+    // Badges
+    badges: {
+      staged: 'Gereed voor handmatige start',
+      requiresPrevious: 'Vereist dat vorige taak slaagt',
+      autoPowerOff: 'Automatisch uitschakelen',
+      gcodeInjection: 'G-code',
+    },
+    // Empty state
+    empty: {
+      title: 'Geen afdrukken gepland',
+      description: 'Plan een afdruk vanaf de pagina Archieven met de optie "Plannen" in het contextmenu, of sleep bestanden hierheen om te beginnen.',
+    },
+    // Time
+    time: {
+      etaIfStartedNow: 'Voltooiingstijd als deze taak nu zou starten',
+      asap: 'Zo snel mogelijk',
+      overdue: 'Te laat',
+      now: 'Nu',
+      lessThanMinute: 'Binnen een minuut',
+      inMinutes: 'Over {{count}} min',
+      inHours: 'Over {{count}} uur',
+    },
+    // Actions
+    actions: {
+      startPrint: 'Afdruk starten',
+      stopPrint: 'Afdruk stoppen',
+      requeue: 'Opnieuw in wachtrij plaatsen',
+    },
+    // Bulk edit
+    bulkEdit: {
+      title: '{{count}} item bewerken',
+      title_plural: '{{count}} items bewerken',
+      description: 'Alleen gewijzigde instellingen worden toegepast op de geselecteerde items.',
+      printer: 'Printer',
+      noChange: '— Geen wijziging —',
+      queueOptions: 'Wachtrijopties',
+      staged: 'Gereed voor handmatige start',
+      autoPowerOff: 'Automatisch uitschakelen na afdruk',
+      requirePrevious: 'Vereis dat vorige taak slaagt',
+      printOptions: 'Afdrukopties',
+      bedLevelling: 'Bednivellering',
+      flowCalibration: 'Flowkalibratie',
+      vibrationCalibration: 'Trillingskalibratie',
+      layerInspection: 'Inspectie eerste laag',
+      timelapse: 'Timelapse',
+      useAms: 'AMS gebruiken',
+      nozzleOffsetCali: 'Nozzle-offsetkalibratie',
+      applyChanges: 'Wijzigingen toepassen',
+      selectAll: 'Alles selecteren',
+      deselectAll: 'Alles deselecteren',
+      selected: '{{count}} geselecteerd',
+      editSelected: 'Geselecteerde bewerken',
+      cancelSelected: 'Geselecteerde annuleren',
+    },
+    // Confirmations
+    confirm: {
+      cancelTitle: 'Geplande afdruk annuleren',
+      cancelMessage: 'Weet je zeker dat je "{{name}}" wilt annuleren?',
+      stopTitle: 'Afdruk stoppen',
+      stopMessage: 'Weet je zeker dat je de huidige afdruk "{{name}}" wilt stoppen? Hiermee wordt de afdruktaak op de printer geannuleerd.',
+      removeTitle: 'Uit geschiedenis verwijderen',
+      removeMessage: 'Weet je zeker dat je "{{name}}" uit de wachtrijgeschiedenis wilt verwijderen?',
+      clearHistoryTitle: 'Geschiedenis wissen',
+      clearHistoryMessage: 'Weet je zeker dat je alle {{count}} item(s) uit de geschiedenis wilt verwijderen?',
+      cancelButton: 'Afdruk annuleren',
+      stopButton: 'Afdruk stoppen',
+      thisPrint: 'deze afdruk',
+      thisItem: 'dit item',
+    },
+    // Toast messages
+    toast: {
+      cancelled: 'Wachtrij-item geannuleerd',
+      cancelFailed: 'Item annuleren mislukt',
+      removed: 'Wachtrij-item verwijderd',
+      keptForOrder: 'Geannuleerd in plaats van verwijderd: dit is de enige run waaruit de batchorder deze plaat opnieuw in de wachtrij kan zetten',
+      historyKeptForOrders: '{{kept}} bewaard die batchorders nog nodig hebben.',
+      removeFailed: 'Item verwijderen mislukt',
+      stopped: 'Afdruk gestopt',
+      stopFailed: 'Afdruk stoppen mislukt',
+      released: 'Afdruk vrijgegeven aan wachtrij',
+      startFailed: 'Afdruk starten mislukt',
+      reorderFailed: 'Wachtrij herschikken mislukt',
+      historyCleared: '{{count}} geschiedenisitem(s) gewist',
+      clearHistoryFailed: 'Geschiedenis wissen mislukt',
+      updateFailed: 'Items bijwerken mislukt',
+      bulkCancelled: '{{count}} item(s) geannuleerd',
+      bulkCancelFailed: 'Items annuleren mislukt',
+      batchCreated: 'Batch "{{name}}" gemaakt',
+      batchCreateFailed: 'Batch maken mislukt',
+      batchUngrouped: 'Groepering van {{count}} item(s) opgeheven',
+      batchUngroupFailed: 'Batchgroepering opheffen mislukt',
+      resumedAfterFailure: 'Wachtrij hervat — {{restored}} taak/taken teruggezet naar in afwachting',
+      resumeAfterFailureFailed: 'Wachtrij hervatten mislukt',
+    },
+    resumeAfterFailure: {
+      banner: '{{printer}} is geblokkeerd door een eerdere afdrukfout — {{count}} taak/taken overgeslagen',
+      bannerHint: 'Los het printerprobleem op en hervat daarna om de overgeslagen taken terug te zetten en de blokkering op te heffen.',
+      button: 'Hervatten na fout',
+      confirmTitle: 'Wachtrij hervatten na fout?',
+      confirmMessage: 'Zet {{count}} overgeslagen taak/taken op {{printer}} terug naar in afwachting en hef de blokkering door de vorige afdruk op. Zorg dat de printer gereed is voordat je doorgaat.',
+    },
+    // Timeline view
+    timeline: {
+      listView: 'Lijst',
+      timelineView: 'Tijdlijn',
+      unassigned: 'Niet toegewezen',
+      noData: 'Geen geplande afdrukken voor deze dag',
+      nothingCommitted: 'Geen vastgelegde planningen in dit venster. Gereedstaande items, wachtende items en ZSM-taken op inactieve printers worden niet getoond — stel een geplande tijd in of geef een gereedstaand item vrij om het hier te zien.',
+      allDoneBy: 'Alle afdrukken naar verwachting klaar om {{time}}',
+      staged: 'Gereed voor handmatige start',
+      filterAll: 'Alles tonen',
+      filterPrinting: 'Bezig met afdrukken',
+      filterQueued: 'In wachtrij',
+      time: {
+        anyMoment: 'elk moment',
+        minutesLeft: '{{minutes}}m resterend',
+        hoursLeft: '{{hours}}u resterend',
+        hoursMinutesLeft: '{{hours}}u {{minutes}}m resterend',
+      },
+      day: {
+        previous: 'Vorige dag',
+        next: 'Volgende dag',
+        today: 'Vandaag',
+      },
+      // Rolling-24h Gantt window
+      window: {
+        back12h: '12 uur terug',
+        forward12h: '12 uur vooruit',
+        now: 'Nu',
+      },
+      printerColumnHeader: 'Printer',
+    },
+    // Permissions
+    permissions: {
+      noStopPrint: 'Je hebt geen toestemming om afdrukken te stoppen',
+      noStartPrint: 'Je hebt geen toestemming om afdrukken te starten',
+      noEdit: 'Je hebt geen toestemming om dit wachtrij-item te bewerken',
+      noCancel: 'Je hebt geen toestemming om dit wachtrij-item te annuleren',
+      noRequeue: 'Je hebt geen toestemming om items opnieuw in de wachtrij te plaatsen',
+      noRemove: 'Je hebt geen toestemming om dit wachtrij-item te verwijderen',
+      noClearHistory: 'Je hebt geen toestemming om de volledige geschiedenis te wissen',
+      noEditItems: 'Je hebt geen toestemming om wachtrij-items te bewerken',
+      noCancelItems: 'Je hebt geen toestemming om wachtrij-items te annuleren',
+    },
+  },
+
+  // Statistics page
+  stats: {
+    title: 'Statistieken',
+    subtitle: 'Sleep widgets om ze opnieuw te rangschikken. Klik op het oogpictogram om te verbergen.',
+    overview: 'Overzicht',
+    totalPrints: 'Totaal aantal afdrukken',
+    successRate: 'Slagingspercentage',
+    totalPrintTime: 'Totale afdruktijd',
+    printTime: 'Afdruktijd',
+    totalFilament: 'Totaal filamentverbruik',
+    filamentUsed: 'Filament gebruikt',
+    filamentCost: 'Filamentkosten',
+    totalCost: 'Totale kosten',
+    energyUsed: 'Energieverbruik',
+    energyCost: 'Energiekosten',
+    energyWarmingUpTooltip: 'Energietracking verzamelt nog steeds uurlijkse momentopnamen. Totalen over een datumbereik worden nauwkeurig zodra er minstens één momentopname van vóór het geselecteerde bereik bestaat. Vroege waarden kunnen te laag uitvallen.',
+    averagePrintTime: 'Gemiddelde afdruktijd',
+    printsPerDay: 'Afdrukken per dag',
+    byPrinter: 'Per printer',
+    printsByPrinter: 'Afdrukken per printer',
+    byMaterial: 'Per materiaal',
+    byMonth: 'Per maand',
+    last7Days: 'Laatste 7 dagen',
+    last30Days: 'Laatste 30 dagen',
+    last90Days: 'Laatste 90 dagen',
+    allTime: 'Altijd',
+    // Widgets
+    quickStats: 'Snelle statistieken',
+    printActivity: 'Afdrukactiviteit',
+    filamentTypes: 'Filamenttypen',
+    filamentTrends: 'Filamenttrends',
+    failureAnalysis: 'Foutenanalyse',
+    timeAccuracy: 'Tijdnauwkeurigheid',
+    successful: 'Geslaagd:',
+    failed: 'Mislukt:',
+    cancelled: 'Geannuleerd:',
+    perfectEstimate: '100% = perfecte schatting',
+    noTimeAccuracyData: 'Nog geen gegevens over tijdnauwkeurigheid',
+    noFilamentData: 'Geen filamentgegevens beschikbaar',
+    noPrinterData: 'Geen printergegevens beschikbaar',
+    noPrintData: 'Geen afdrukgegevens beschikbaar',
+    noPrintDataLast30Days: 'Geen afdrukgegevens in de laatste 30 dagen',
+    failureReasons: 'Oorzaken van fouten',
+    topFailureReasons: 'Meest voorkomende foutoorzaken',
+    failedPrintsCount: '{{failed}} / {{total}} afdrukken mislukt',
+    lastWeekRate: 'Vorige week: {{rate}}%',
+    // Actions
+    resetLayout: 'Indeling resetten',
+    recalculateCosts: 'Kosten opnieuw berekenen',
+    recalculateCostsHint: 'Alle archiefkosten opnieuw berekenen met de huidige filamentprijzen',
+    exportStats: 'Statistieken exporteren',
+    exportAsCsv: 'Exporteren als CSV',
+    exportAsExcel: 'Exporteren als Excel',
+    hiddenCount: '{{count}} verborgen',
+    // Toast
+    exportDownloaded: 'Export gedownload',
+    exportFailed: 'Export mislukt',
+    layoutReset: 'Indeling gereset',
+    recalculatedCosts: 'Kosten voor {{count}} archieven opnieuw berekend',
+    recalculateFailed: 'Kosten opnieuw berekenen mislukt',
+    // Loading
+    loadingStats: 'Statistieken laden...',
+    // Permissions
+    noPermissionResetLayout: 'Je hebt geen toestemming om de indeling te resetten',
+    noPermissionRecalculate: 'Je hebt geen toestemming om kosten opnieuw te berekenen',
+    noPrintDataInRange: 'Geen afdrukgegevens in het geselecteerde bereik',
+    periodFilament: 'Filament in periode',
+    periodCost: 'Kosten in periode',
+    avgPerPrint: 'Gem. per afdruk',
+    usageOverTime: 'Verbruik in de tijd',
+    energyOverTime: 'Energie in de tijd',
+    filamentByWeight: 'Gewicht',
+    printDuration: 'Afdrukduur',
+    printerUtilization: 'Printerbenutting',
+    filamentSuccess: 'Succes per materiaal',
+    printHabits: 'Afdrukgewoonten',
+    printTimeOfDay: 'Afdruktijdstip',
+    colorDistribution: 'Kleurverdeling',
+    noColorData: 'Geen kleurgegevens beschikbaar',
+    records: 'Records',
+    longestPrint: 'Langste afdruk',
+    heaviestPrint: 'Zwaarste afdruk',
+    mostExpensivePrint: 'Duurste',
+    busiestDay: 'Drukste dag',
+    successStreak: 'Succesreeks',
+    streakPrint: 'opeenvolgende afdruk',
+    streakPrints: '{{count}} opeenvolgende afdrukken',
+    printerStats: 'Printerstatistieken',
+    hours: 'uren',
+    avgPrints: 'Gem. afdrukken',
+    noArchiveData: 'Geen afdrukgegevens beschikbaar',
+    filamentByTime: 'Tijd',
+    avgWeight: 'Gem. gewicht',
+    avgTime: 'Gem. tijd',
+    filamentByPrints: 'Afdrukken',
+    timeframe: {
+      'today': 'Vandaag',
+      'this-week': 'Deze week',
+      'this-month': 'Deze maand',
+      'last-7': 'Laatste 7 dagen',
+      'last-30': 'Laatste 30 dagen',
+      'last-90': 'Laatste 90 dagen',
+      'this-year': 'Dit jaar',
+      'all-time': 'Altijd',
+      'custom': 'Aangepast bereik',
+      from: 'Van',
+      to: 'Tot',
+    },
+    // User filter
+    allUsers: 'Alle gebruikers',
+    noUser: 'Geen gebruiker (systeem)',
+    filterByUser: 'Filteren op gebruiker',
+  },
+
+  // Maintenance page
+  maintenance: {
+    title: 'Onderhoud',
+    overview: 'Overzicht',
+    allOk: 'Al het onderhoud is bijgewerkt',
+    dueCount: '{{count}} item te doen',
+    dueCount_plural: '{{count}} items te doen',
+    warningCount: '{{count}} waarschuwing',
+    warningCount_plural: '{{count}} waarschuwingen',
+    totalPrintTime: 'Totale afdruktijd',
+    nextMaintenance: 'Volgend onderhoud',
+    nothingDue: 'Niets te doen',
+    tasks: 'Taken',
+    lastPerformed: 'Laatst uitgevoerd',
+    interval: 'Interval',
+    hoursRemaining: '{{hours}}u resterend',
+    hoursOverdue: '{{hours}}u te laat',
+    markDone: 'Als voltooid markeren',
+    performMaintenance: 'Onderhoud uitvoeren',
+    history: 'Geschiedenis',
+    noHistory: 'Geen onderhoudsgeschiedenis',
+    editPrintHours: 'Afdrukuren bewerken',
+    currentHours: 'Huidige uren',
+    // Tabs
+    statusTab: 'Status',
+    settingsTab: 'Instellingen',
+    // Status
+    overdueCount: '{{count}} te laat',
+    dueSoonCount: '{{count}} binnenkort te doen',
+    dueSoon: 'Binnenkort te doen',
+    allGood: 'Alles goed',
+    overdueBy: '{{duration}} te laat',
+    dueIn: 'Over {{duration}} te doen',
+    timeLeft: '{{duration}} resterend',
+    // Duration formats
+    day: '1 dag',
+    days: '{{count}} dagen',
+    week: '1 week',
+    weeks: '{{count}} weken',
+    month: '1 maand',
+    months: '{{count}} maanden',
+    year: '1 jaar',
+    // Settings
+    maintenanceTypes: 'Onderhoudstypen',
+    maintenanceTypesDescription: 'Systeemtaken en je aangepaste onderhoudstaken',
+    addCustomType: 'Aangepast type toevoegen',
+    restoreDefaults: 'Standaardtaken herstellen',
+    intervalType: 'Intervaltype',
+    intervalValue: 'Interval ({{type}})',
+    icon: 'Pictogram',
+    documentationLink: 'Documentatielink (optioneel)',
+    assignToPrinters: 'Aan printers toewijzen',
+    selectAtLeastOnePrinter: 'Selecteer minstens één printer',
+    addType: 'Type toevoegen',
+    custom: 'Aangepast',
+    printHours: 'Afdrukuren',
+    calendarDays: 'Kalenderdagen',
+    exampleName: 'bijv. HEPA-filter vervangen',
+    viewDocumentation: 'Documentatie bekijken',
+    timeBasedInterval: 'Tijdsgebaseerd interval',
+    // Interval overrides
+    intervalOverrides: 'Intervaloverschrijvingen',
+    intervalOverridesDescription: 'Intervallen aanpassen voor specifieke printers',
+    // Printer assignment
+    assignedToPrinters: 'Toegewezen aan printers:',
+    noPrintersAssigned: 'Geen printers toegewezen',
+    addPrinterShort: 'Toevoegen:',
+    printersAssignedClick: '{{count}} printer(s) toegewezen - klik om te beheren',
+    removeFromPrinter: 'Van deze printer verwijderen',
+    // Types
+    types: {
+      lubricateCarbonRods: 'Koolstofstaven smeren',
+      lubricateRails: 'Lineaire rails smeren',
+      cleanNozzle: 'Nozzle/hotend reinigen',
+      checkBelts: 'Riemspanning controleren',
+      cleanBuildPlate: 'Bouwplaat reinigen',
+      checkExtruder: 'Extrudertandwielen controleren',
+      checkCooling: 'Koelventilatoren controleren',
+      generalInspection: 'Algemene inspectie',
+      cleanCarbonRods: 'Koolstofstaven reinigen',
+      lubricateSteelRods: 'Stalen staven smeren',
+      cleanSteelRods: 'Stalen staven reinigen',
+      cleanLinearRails: 'Lineaire rails reinigen',
+      checkPtfeTube: 'PTFE-buis controleren',
+      replaceHepaFilter: 'HEPA-filter vervangen',
+      replaceCarbonFilter: 'Koolstoffilter vervangen',
+      lubricateLeftNozzleRail: 'Linker nozzle-rail smeren',
+    },
+    // Toast
+    maintenanceComplete: 'Onderhoud als voltooid gemarkeerd',
+    typeUpdated: 'Onderhoudstype bijgewerkt',
+    typeDeleted: 'Onderhoudstype verwijderd',
+    defaultsRestored: '{{count}} standaardtaak/taken hersteld',
+    printHoursUpdated: 'Afdrukuren bijgewerkt',
+    printerAssigned: 'Printer toegewezen',
+    printerRemoved: 'Printer verwijderd',
+    // Confirmation
+    deleteTypeConfirm: '"{{name}}" verwijderen?',
+    deleteSystemTypeTitle: 'Standaard onderhoudstaak verwijderen?',
+    deleteSystemTypeMessage: 'Weet je zeker dat je de standaard onderhoudstaak "{{name}}" wilt verwijderen?',
+    // Permissions
+    noPermissionUpdate: 'Je hebt geen toestemming om onderhoudsitems bij te werken',
+    noPermissionPerform: 'Je hebt geen toestemming om onderhoud uit te voeren',
+    noPermissionEditTypes: 'Je hebt geen toestemming om onderhoudstypen te bewerken',
+    noPermissionDeleteTypes: 'Je hebt geen toestemming om onderhoudstypen te verwijderen',
+    noPermissionEditHours: 'Je hebt geen toestemming om afdrukuren te bewerken',
+    noPermissionRemovePrinter: 'Je hebt geen toestemming om printertoewijzingen te verwijderen',
+    noPermissionAssignPrinter: 'Je hebt geen toestemming om printers toe te wijzen',
+    noPermissionEditIntervals: 'Je hebt geen toestemming om intervallen te bewerken',
+    // Configure link
+    configureSettings: 'Onderhoudstypen en intervallen configureren',
+  },
+
+  // Settings page
+  settings: {
+    sliceEngine: 'Slice-engine',
+    sliceEngineSidecar: 'Server-sidecar',
+    sliceEngineSidecarHint: 'Slicen gebeurt op de server, in de slicer-sidecarcontainer.',
+    sliceEngineBrowser: 'In browser',
+    sliceEngineBrowserHint: 'Slicen gebeurt op dit apparaat, zonder tussenkomst van de server.',
+    title: 'Instellingen',
+    general: 'Algemeen',
+    // Tab names
+    tabs: {
+      general: 'Algemeen',
+      smartPlugs: 'Slimme stekkers',
+      sensors: 'Sensoren',
+      notifications: 'Meldingen',
+      queue: 'Workflow',
+      queueDispatch: 'Wachtrij en verzending',
+      queuePipelines: 'Pipelines',
+      filament: 'Filament',
+      network: 'Netwerk',
+      apiKeys: 'API-sleutels',
+      virtualPrinter: 'Virtuele printer',
+      spoolbuddy: 'SpoolBuddy',
+      failureDetection: 'Foutdetectie',
+      users: 'Authenticatie',
+      backup: 'Back-up',
+      emailAuth: 'E-mailauthenticatie',
+      ldap: 'LDAP',
+      twoFa: 'Tweefactorauthenticatie',
+      oidc: 'SSO / OIDC',
+      security: 'Beveiliging',
+    },
+    spoolbuddy: {
+      infoTitle: 'SpoolBuddy-apparaten',
+      infoBody: 'SpoolBuddy-kiosken registreren zichzelf automatisch via heartbeat. Registreer een apparaat hier af als het niet meer wordt gebruikt of als een verouderde duplicaatregistratie is achtergebleven na een daemoncrash.',
+      duplicatesTitle: '{{count}} apparaten geregistreerd',
+      duplicatesBody: 'Alleen het eerst geregistreerde apparaat wordt gebruikt door de kioskinterface. Als een van deze apparaten een verouderd duplicaat na een crash is, registreer het dan af — een online apparaat registreert zichzelf opnieuw bij de volgende heartbeat.',
+      empty: 'Nog geen SpoolBuddy-apparaten geregistreerd.',
+      online: 'Online',
+      offline: 'Offline',
+      unregister: 'Registratie opheffen',
+      unregisterSuccess: 'Apparaat afgemeld',
+      unregisterError: 'Apparaat afmelden mislukt',
+      confirmTitle: 'SpoolBuddy-apparaat afmelden?',
+      confirmBody: 'Hiermee wordt "{{hostname}}" ({{deviceId}}) uit de database verwijderd. Als het apparaat online is, registreert het zichzelf opnieuw bij de volgende heartbeat.',
+      ipAddress: 'IP-adres',
+      firmware: 'Firmware',
+      lastSeen: 'Laatst gezien',
+      daemonUptime: 'Daemon-uptime',
+      systemUptime: 'Systeem-uptime',
+      never: 'nooit',
+      nfc: 'NFC',
+      scale: 'Weegschaal',
+      cpuTemp: 'CPU-temp.',
+      cpuLoad: 'CPU-belasting',
+      memory: 'Geheugen',
+      disk: 'Schijf',
+      // Device actions
+      update: 'Bijwerken',
+      updateConfirmTitle: 'SpoolBuddy-daemon bijwerken?',
+      updateConfirmBody: 'Een software-update starten op "{{hostname}}"? De daemon wordt opnieuw gestart zodra de update is toegepast.',
+      restartBrowser: 'Browser herstarten',
+      restartBrowserConfirmTitle: 'Kioskbrowser herstarten?',
+      restartBrowserConfirmBody: 'De kioskbrowser op "{{hostname}}" herstarten? Het scherm wordt kort zwart.',
+      restartDaemon: 'Daemon herstarten',
+      restartDaemonConfirmTitle: 'SpoolBuddy-daemon herstarten?',
+      restartDaemonConfirmBody: 'De SpoolBuddy-daemon op "{{hostname}}" herstarten? Het apparaat is enkele seconden offline.',
+      reboot: 'Opnieuw opstarten',
+      rebootConfirmTitle: 'Apparaat opnieuw opstarten?',
+      rebootConfirmBody: '"{{hostname}}" opnieuw opstarten? Het apparaat is ongeveer een minuut offline.',
+      shutdown: 'Afsluiten',
+      shutdownConfirmTitle: 'Apparaat afsluiten?',
+      shutdownConfirmBody: '"{{hostname}}" afsluiten? Je hebt fysieke toegang nodig om het weer in te schakelen.',
+      commandConfirm: 'Bevestigen',
+      commandQueued: 'Opdracht in wachtrij geplaatst',
+      commandError: 'Opdracht verzenden mislukt',
+    },
+    // LDAP settings
+    ldap: {
+      title: 'LDAP-authenticatie',
+      enabledDesc: 'LDAP-authenticatie is ingeschakeld',
+      disabledDesc: 'LDAP-authenticatie is uitgeschakeld',
+      disabledHint: 'Configureer en sla de LDAP-instellingen hieronder op en schakel daarna in.',
+      enabled: 'LDAP-authenticatie ingeschakeld',
+      disabled: 'LDAP-authenticatie uitgeschakeld',
+      feature1: 'Gebruikers kunnen inloggen met LDAP-inloggegevens',
+      feature2: 'Lokaal beheerdersaccount blijft beschikbaar als terugvaloptie',
+      feature3: 'LDAP-groepen worden bij inloggen gekoppeld aan BamBuddy-groepen',
+      serverConfig: 'LDAP-serverconfiguratie',
+      serverUrl: 'Server-URL',
+      serverUrlHint: 'Gebruik ldaps:// voor SSL of ldap:// met StartTLS',
+      security: 'Beveiliging',
+      securityHint: 'StartTLS zet een gewone verbinding om naar TLS. LDAPS gebruikt TLS vanaf het begin.',
+      bindDn: 'Bind-DN (serviceaccount)',
+      bindPassword: 'Bind-wachtwoord',
+      searchBase: 'Zoekbasis-DN',
+      userFilter: 'Gebruikerszoekfilter',
+      userFilterHint: '{username} wordt vervangen door de inloggebruikersnaam. Gebruik (uid={username}) voor OpenLDAP.',
+      advanced: 'Geavanceerd',
+      autoProvision: 'Gebruikers automatisch aanmaken',
+      autoProvisionHint: 'Automatisch een BamBuddy-account maken bij de eerste LDAP-login',
+      defaultGroup: 'Standaardgroep',
+      defaultGroupNone: '— Geen (geen terugval) —',
+      defaultGroupHint: 'Terugvalgroep die wordt toegewezen wanneer een LDAP-gebruiker zich authenticeert maar niet voorkomt in een gekoppelde LDAP-groep. Laat leeg om niet-gekoppelde gebruikers zonder machtigingen te laten.',
+      groupMapping: 'Groepskoppeling (JSON)',
+      groupMappingHint: 'Koppel LDAP-groep-DN\'s aan BamBuddy-groepen. Beschikbare groepen: ',
+      testConnection: 'Verbinding testen',
+      settingsSaved: 'LDAP-instellingen opgeslagen',
+      errors: {
+        serverRequired: 'LDAP-server-URL is verplicht',
+        searchBaseRequired: 'Zoekbasis-DN is verplicht',
+        enableAuthFirst: 'Schakel authenticatie eerst in',
+        configureLdapFirst: 'Sla de LDAP-instellingen eerst op',
+      },
+    },
+    // Email settings
+    email: {
+      smtpSettings: 'SMTP-configuratie',
+      smtpHost: 'SMTP-server',
+      smtpPort: 'SMTP-poort',
+      security: 'Beveiliging',
+      authentication: 'Authenticatie',
+      username: 'Gebruikersnaam',
+      password: 'Wachtwoord',
+      fromEmail: 'Afzender-e-mail',
+      fromName: 'Afzendernaam',
+      testConnection: 'SMTP-verbinding testen',
+      testRecipient: 'E-mailadres testontvanger',
+      sendTest: 'Testmail verzenden',
+      sending: 'Verzenden...',
+      save: 'Instellingen opslaan',
+      saving: 'Opslaan...',
+      advancedAuth: 'Geavanceerde authenticatie',
+      advancedAuthEnabled: 'Geavanceerde authenticatie is ingeschakeld',
+      advancedAuthEnabledDesc: 'E-mailgebaseerde functies voor gebruikersbeheer zijn actief. Nieuwe gebruikers ontvangen automatisch gegenereerde wachtwoorden per e-mail en gebruikers kunnen hun wachtwoord resetten via de functie Wachtwoord vergeten.',
+      advancedAuthDisabled: 'Geavanceerde authenticatie is uitgeschakeld',
+      advancedAuthDisabledDesc: 'Schakel geavanceerde authenticatie in om e-mailgebaseerde functies voor gebruikersbeheer te activeren.',
+      enable: 'Inschakelen',
+      disable: 'Uitschakelen',
+      feature1: 'Wachtwoorden worden automatisch gegenereerd en naar nieuwe gebruikers gemaild',
+      feature2: 'Gebruikers kunnen inloggen met gebruikersnaam of e-mailadres',
+      feature3: 'De functie Wachtwoord vergeten is beschikbaar',
+      feature4: 'Beheerders kunnen gebruikerswachtwoorden via e-mail resetten',
+      // Error messages
+      errors: {
+        requiredFields: 'Vul alle verplichte velden in',
+        usernameRequired: 'Gebruikersnaam is verplicht wanneer authenticatie is ingeschakeld',
+        enterTestEmail: 'Voer een test-e-mailadres in',
+        smtpServerAndEmail: 'Vul SMTP-server en Afzender-e-mail in voordat je test',
+        usernamePasswordRequired: 'Gebruikersnaam en wachtwoord zijn verplicht wanneer authenticatie is ingeschakeld',
+        configureSmtpFirst: 'Configureer en test eerst de SMTP-instellingen',
+        enableAuthFirst: 'Schakel authenticatie eerst in om e-mailfuncties te gebruiken.',
+      },
+      // Success messages
+      success: {
+        settingsSaved: 'SMTP-instellingen succesvol opgeslagen',
+      },
+      // Security options
+      securityOptions: {
+        starttls: 'STARTTLS (poort 587)',
+        ssl: 'SSL/TLS (poort 465)',
+        none: 'Geen (poort 25)',
+      },
+      // Authentication options
+      authOptions: {
+        enabled: 'Ingeschakeld',
+        disabled: 'Uitgeschakeld',
+      },
+    },
+    appearance: 'Uiterlijk',
+    notifications: 'Meldingen',
+    smartPlugs: 'Slimme stekkers',
+    spoolman: 'Spoolman',
+    updates: 'Updates',
+    language: 'Taal',
+    languageDescription: 'Selecteer je voorkeurstaal',
+    theme: 'Thema',
+    themeLight: 'Licht',
+    themeDark: 'Donker',
+    themeSystem: 'Systeem',
+    defaultView: 'Standaardweergave',
+    defaultViewDescription: 'Pagina die wordt getoond bij het openen van de app',
+    checkForUpdates: 'Controleren op updates',
+    autoUpdate: 'Automatisch bijwerken',
+    currentVersion: 'Huidige versie',
+    latestVersion: 'Nieuwste versie',
+    upToDate: 'Je bent up-to-date',
+    updateAvailable: 'Update beschikbaar',
+    // Notifications
+    notificationLanguage: 'Taal voor meldingen',
+    notificationLanguageDescription: 'Taal voor pushmeldingen',
+    bedCooledThreshold: 'Drempel voor afgekoeld bed',
+    bedCooledThresholdDescription: 'Temperatuur waaronder het bed na een afdruk als afgekoeld wordt beschouwd',
+    userNotificationsEnabled: 'Gebruikersmeldingen',
+    userNotificationsEnabledDescription: 'Schakel het gebruikersmeldingenmenu en e-mailmeldingen voor afdrukgebeurtenissen in. Vereist Geavanceerde authenticatie.',
+    userNotificationsDisabledHint: 'Schakel Geavanceerde authenticatie in om gebruikersmeldingen te gebruiken.',
+    notificationProviders: 'Meldingsproviders',
+    addProvider: 'Provider toevoegen',
+    editProvider: 'Provider bewerken',
+    providerType: 'Providertype',
+    testNotification: 'Testmelding',
+    testSuccess: 'Testmelding succesvol verzonden',
+    testFailed: 'Testmelding verzenden mislukt',
+    quietHours: 'Stille uren',
+    quietHoursDescription: 'Niet storen tijdens deze uren',
+    quietHoursStart: 'Start',
+    quietHoursEnd: 'Einde',
+    events: {
+      title: 'Meldingsgebeurtenissen',
+      printStart: 'Afdruk gestart',
+      printComplete: 'Afdruk voltooid',
+      printFailed: 'Afdruk mislukt',
+      printStopped: 'Afdruk gestopt',
+      printProgress: 'Voortgangsmijlpalen',
+      printProgressDescription: 'Melden bij 25%, 50%, 75%',
+      printerOffline: 'Printer offline',
+      printerError: 'Printerfout',
+      filamentLow: 'Weinig filament',
+      maintenanceDue: 'Onderhoud nodig',
+      maintenanceDueDescription: 'Melden wanneer onderhoud nodig is',
+    },
+    // Smart Plugs
+    smartPlug: {
+      title: 'Slimme stekkers',
+      add: 'Slimme stekker toevoegen',
+      edit: 'Slimme stekker bewerken',
+      name: 'Naam',
+      ipAddress: 'IP-adres',
+      linkedPrinter: 'Gekoppelde printer',
+      autoOn: 'Automatisch inschakelen',
+      autoOnDescription: 'Inschakelen wanneer afdruk start',
+      autoOff: 'Automatisch uitschakelen',
+      autoOffDescription: 'Uitschakelen nadat afdruk is voltooid',
+      offDelay: 'Uitschakelvertraging',
+      offDelayMinutes: 'Minuten na afdruk',
+      offDelayTemp: 'Wanneer nozzle onder temperatuur is',
+      currentState: 'Huidige status',
+      turnOn: 'Inschakelen',
+      turnOff: 'Uitschakelen',
+    },
+    // Filament Tracking Mode
+    filamentTracking: 'Filamenttracking',
+    filamentTrackingDesc: 'Kies hoe je filamentspoelen wilt bijhouden. Je kunt de ingebouwde voorraad gebruiken of een externe Spoolman-server koppelen.',
+    autoAddUnknownRfid: 'Onbekende RFID-spoelen automatisch toevoegen',
+    autoAddUnknownRfidDesc: 'Maak automatisch een voorraaditem wanneer een spoel met een onbekende RFID-tag wordt gedetecteerd. Schakel dit uit als je nieuwe spoelen handmatig vooraf registreert om duplicaten te voorkomen.',
+    filamentChecks: 'Filamentcontroles',
+    disableFilamentWarnings: 'Filamentwaarschuwingen uitschakelen',
+    disableFilamentWarningsDesc: 'Geen waarschuwingen tonen over onvoldoende filament bij afdrukken of in wachtrij plaatsen',
+    preferLowestFilament: 'Voorkeur voor laagste resterende hoeveelheid filament',
+    preferLowestFilamentDesc: 'Als meerdere spoelen overeenkomen, gebruik dan de spoel met de minste resterende hoeveelheid filament',
+    preferLowestFilamentBackupNote: 'Dit werkt alleen wanneer AMS Filament Backup op de printer is ingeschakeld — anders kan de printer niet overschakelen naar een tweede spoel wanneer de gekozen spoel leeg raakt.',
+    trackingModeBuiltIn: 'Ingebouwde voorraad',
+    trackingModeBuiltInDesc: 'Automatische RFID-koppeling en verbruikstracking inbegrepen',
+    trackingModeSpoolmanDesc: 'Externe filamentbeheerserver',
+    builtInFeatureRfid: 'Detecteert automatisch Bambu Lab RFID-spoelen in AMS',
+    builtInFeatureUsage: 'Houdt filamentverbruik per afdruk bij',
+    builtInFeatureCatalog: 'Spoelen, kleuren en K-factorprofielen beheren',
+    builtInFeatureThirdParty: 'Spoelen van derden kunnen aan voorraadspoelen worden toegewezen',
+    amsSyncButton: 'Gewichten synchroniseren vanuit AMS',
+    amsSyncTitle: 'Spoelgewichten synchroniseren vanuit AMS',
+    amsSyncMessage: 'Hiermee worden alle gewichten van voorraadspoelen overschreven met de huidige AMS remain%-waarden van verbonden printers. Gebruik dit om beschadigde gewichtsgegevens te herstellen. Printers moeten online zijn.',
+    amsSyncing: 'Synchroniseren...',
+    amsSyncSuccess: '{{synced}} spoel(en) gesynchroniseerd, {{skipped}} overgeslagen',
+    amsSyncError: 'Gewichten synchroniseren vanuit AMS mislukt',
+    spoolmanAmsSyncButton: 'Spoolman-gewichten synchroniseren vanuit AMS',
+    spoolmanAmsSyncTitle: 'Spoolman-spoelgewichten synchroniseren vanuit AMS',
+    spoolmanAmsSyncMessage: 'Hiermee worden alle Spoolman-spoelgewichten bijgewerkt op basis van de huidige AMS remain%-waarden van verbonden printers. Printers moeten online zijn.',
+    spoolmanAmsSyncing: 'Synchroniseren...',
+    spoolmanAmsSyncSuccess: '{{synced}} spoel(en) gesynchroniseerd, {{skipped}} overgeslagen',
+    spoolmanAmsSyncError: 'Spoolman-gewichten synchroniseren vanuit AMS mislukt',
+    spoolmanAmsSyncErrorUnreachable: 'Spoolman-gewichten synchroniseren mislukt (Spoolman niet bereikbaar)',
+    spoolmanAmsSyncErrorNotConfigured: 'Spoolman-gewichten synchroniseren mislukt (Spoolman niet geconfigureerd)',
+    spoolmanNotConfigured: 'Spoolman niet geconfigureerd',
+    // Spoolman filament catalog section in spool catalog settings
+    spoolmanFilamentCatalogTitle: 'Spoolman-filamentcatalogus',
+    spoolmanFilamentCatalogDesc: 'Filamentnamen en taragewichten uit je Spoolman-instantie. Naam en spoelgewicht zijn hier bewerkbaar; alle andere eigenschappen worden rechtstreeks in Spoolman beheerd.',
+    // Spoolman settings
+    spoolmanUrl: 'Spoolman-URL',
+    spoolmanUrlHint: 'URL van je Spoolman-server (bijv. http://localhost:7912)',
+    spoolmanConnected: 'Verbonden',
+    spoolmanDisconnected: 'Niet verbonden',
+    status: 'Status',
+    connect: 'Verbinden',
+    disconnect: 'Verbinding verbreken',
+    howSyncWorks: 'Hoe synchronisatie werkt',
+    syncInfoRfidOnly: 'Alleen officiële Bambu Lab-spoelen met RFID worden gesynchroniseerd',
+    syncInfoAutoCreate: 'Nieuwe spoelen worden bij de eerste synchronisatie automatisch in Spoolman aangemaakt',
+    syncInfoThirdPartySkipped: 'Niet-Bambu Lab-spoelen (derden, hervuld) worden overgeslagen',
+    linkingExistingSpools: 'Bestaande spoelen koppelen',
+    linkingExistingSpoolsDesc: 'Om bestaande Spoolman-spoelen aan je AMS te koppelen, beweeg je over een AMS-sleuf en klik je op "Koppelen aan Spoolman".',
+    syncMode: 'Synchronisatiemodus',
+    syncModeAuto: 'Automatisch',
+    syncModeManual: 'Alleen handmatig',
+    syncModeAutoDesc: 'AMS-gegevens worden automatisch gesynchroniseerd wanneer wijzigingen worden gedetecteerd',
+    syncModeManualDesc: 'Alleen synchroniseren wanneer handmatig gestart',
+    syncAmsData: 'AMS-gegevens synchroniseren',
+    syncAmsDataDesc: 'Printer-AMS-gegevens handmatig synchroniseren met Spoolman',
+    allPrinters: 'Alle printers',
+    // Default printer
+    noDefaultPrinter: 'Geen standaard (elke keer vragen)',
+    // Sidebar
+    sidebarOrder: 'Volgorde zijbalk',
+    // Camera
+    saveThumbnails: 'Miniaturen opslaan',
+    captureFinishPhoto: 'Eindfoto vastleggen',
+    noPrintersConfigured: 'Geen printers geconfigureerd',
+    // Archive settings
+    archiveMode: {
+      always: 'Altijd archiefitem maken',
+      never: 'Nooit archiefitem maken',
+      ask: 'Elke keer vragen',
+    },
+    // Updates
+    checkForUpdatesLabel: 'Controleren op updates',
+    checkPrinterFirmware: 'Printerfirmware controleren',
+    includeBetaUpdates: 'Bètaversies opnemen',
+    includeBetaUpdatesDesc: 'Melden over bèta- en prereleaseversies bij controle op updates',
+    localLogin: {
+      disable: 'Lokale login met gebruikersnaam/wachtwoord uitschakelen',
+      disableHint: 'Wanneer ingeschakeld kunnen alleen SSO-providers inloggen. LDAP blijft onaangetast. Stel BAMBUDDY_LOCAL_LOGIN=true in op de server om een herstelmogelijkheid te behouden.',
+    },
+    // Queue
+    enableRetry: 'Opnieuw proberen inschakelen',
+    // Home Assistant
+    homeAssistantDescription: 'Slimme stekkers bedienen via Home Assistant',
+    environmentManagedLabel: '(Omgevingsvariabele beheerd)',
+    autoEnabledViaEnv: 'Automatisch ingeschakeld via omgevingsvariabelen',
+    urlFromEnvReadOnly: 'Waarde ingesteld door omgevingsvariabele HA_URL (alleen-lezen)',
+    tokenFromEnvReadOnly: 'Waarde ingesteld door omgevingsvariabele HA_TOKEN (alleen-lezen)',
+    // MQTT
+    mqttConnectedTo: 'Verbonden met',
+    // Prometheus
+    prometheusDescription: 'Printergegevens beschikbaar stellen in Prometheus-formaat',
+    // Smart plugs empty state
+    noSmartPlugsTitle: 'Geen slimme stekkers geconfigureerd',
+    noSmartPlugsDescription: 'Voeg een slimme stekker op basis van Tasmota toe om energieverbruik te volgen en stroomregeling te automatiseren.',
+    // Notifications empty state
+    noProvidersTitle: 'Geen providers geconfigureerd',
+    noProvidersDescription: 'Voeg een provider toe om waarschuwingen te ontvangen.',
+    noTemplatesAvailable: 'Geen sjablonen beschikbaar. Start de backend opnieuw om standaardsjablonen aan te maken.',
+    // API permissions
+    apiPermissionView: 'Printerstatus en wachtrij bekijken',
+    apiPermissionEdit: 'Items toevoegen aan en verwijderen uit de afdrukwachtrij',
+    // API keys
+    apiKeysEmptyTitle: 'Geen API-sleutels',
+    apiKeysEmptyDescription: 'Maak een API-sleutel om te integreren met externe services.',
+    // Users
+    noUsersFound: 'Geen gebruikers gevonden',
+    noGroupsFound: 'Geen groepen gevonden',
+    noGroupsAvailable: 'Geen groepen beschikbaar',
+    passwordsDoNotMatch: 'Wachtwoorden komen niet overeen',
+    systemGroupWarning: 'Namen van systeemgroepen kunnen niet worden gewijzigd',
+    // Auth disabled
+    authDisabledTitle: 'Authenticatie is uitgeschakeld',
+    authDisabledFeature1: 'Login vereisen voor toegang tot het systeem',
+    authDisabledFeature2: 'Meerdere gebruikers maken met groepsgebaseerde machtigingen',
+    authDisabledFeature3: 'Toegang regelen met meer dan 50 gedetailleerde machtigingen',
+    // User deletion
+    userHasCreated: 'Deze gebruiker heeft gemaakt:',
+    userItemsQuestion: 'Wat wil je met deze items doen?',
+    deleteUserConfirm: 'Weet je zeker dat je deze gebruiker wilt verwijderen?',
+    actionCannotBeUndone: 'Deze actie kan niet ongedaan worden gemaakt.',
+    // Smart plugs
+    addFirstSmartPlug: 'Je eerste slimme stekker toevoegen',
+    // Notifications
+    providers: 'Providers',
+    log: 'Logboek',
+    testAll: 'Alles testen',
+    testResults: 'Testresultaten',
+    testPassedCount: '{{count}} geslaagd',
+    testFailedCount: '{{count}} mislukt',
+    messageTemplates: 'Berichtsjablonen',
+    messageTemplatesDescription: 'Meldingsberichten voor elke gebeurtenis aanpassen.',
+    // API Keys section
+    apiKeys: 'API-sleutels',
+    apiKeysDescription: 'API-sleutels maken voor externe integraties en webhooks.',
+    createKey: 'Sleutel maken',
+    apiKeyCreated: 'API-sleutel succesvol gemaakt',
+    apiKeyCopyWarning: 'Kopieer deze sleutel nu - hij wordt niet opnieuw getoond!',
+    useInApiBrowser: 'Gebruiken in API-browser',
+    apiKeyQrButton: 'QR-code',
+    apiKeyQrTitle: 'Scannen om te configureren',
+    apiKeyQrCaption: 'Scan met je mobiele app om deze server en API-sleutel toe te voegen.',
+    apiKeyQrWarning: 'Bevat je geheime API-sleutel — deel deze niet en maak geen screenshot waar anderen hem kunnen zien.',
+    createNewApiKey: 'Nieuwe API-sleutel maken',
+    keyName: 'Sleutelnaam',
+    keyNamePlaceholder: 'bijv. Home Assistant, OctoPrint',
+    readStatus: 'Status lezen',
+    readStatusDescription: 'Printerstatus, wachtrij, bibliotheek, archieven en slicerpipelines bekijken',
+    manageQueue: 'Wachtrij beheren',
+    manageQueueDescription: 'Items toevoegen aan en verwijderen uit de afdrukwachtrij. Met Bibliotheek beheren kun je slicerpipelines uitvoeren',
+    controlPrinter: 'Printer bedienen',
+    controlPrinterDescription: 'Afdrukken pauzeren, hervatten en stoppen',
+    manageLibrary: 'Bibliotheek beheren',
+    manageLibraryDescription: 'Bibliotheekbestanden uploaden, hernoemen, verwijderen en slicen; modellen importeren uit MakerWorld. Met Wachtrij beheren kun je slicerpipelines uitvoeren',
+    manageInventory: 'Voorraad beheren',
+    manageInventoryDescription: 'Spoelen en voorraadrecords maken, bijwerken en verwijderen. Vereist voor SpoolBuddy-kiosken (NFC-scan, weegschaalmetingen, kiosksysteemopdrachten).',
+    manageMaintenance: 'Onderhoud beheren',
+    manageMaintenanceDescription: 'Voltooid onderhoud registreren, tellers resetten, intervallen bewerken en de catalogus met onderhoudstypen beheren. Geschikt voor Home Assistant-automatiseringen die bijvoorbeeld registreren "Ik heb de nozzle gereinigd" zonder bredere printerbediening toe te staan.',
+    manageArchives: 'Archieven beheren',
+    manageArchivesDescription: 'Afdrukarchieven bewerken en verwijderen, inclusief oude afdrukken en hun bijdrage aan statistieken. Geschikt voor automatiseringen die de afdrukgeschiedenis opschonen.',
+    manageProjects: 'Projecten beheren',
+    manageProjectsDescription: 'Projecten maken, bijwerken en verwijderen en archieven eraan toevoegen. Geschikt voor automatiseringen die afdrukken in projecten organiseren.',
+    libraryBadge: 'Bibliotheek',
+    inventoryBadge: 'Voorraad',
+    maintenanceBadge: 'Onderhoud',
+    archivesBadge: 'Archieven',
+    projectsBadge: 'Projecten',
+    cloudAccess: 'Cloudtoegang toestaan',
+    cloudAccessDescription: 'Bambu Cloud-presets en filamenten namens jou lezen. Vereist dat je bent ingelogd bij Bambu Cloud.',
+    cloudBadge: 'Cloud',
+    updateEnergyCost: 'Elektriciteitsprijs bijwerken',
+    updateEnergyCostDescription: 'Sta toe dat deze sleutel via POST een nieuwe elektriciteitsprijs per kWh naar /settings/electricity-price stuurt. Handig voor Home Assistant-automatiseringen met dynamische tarieven (Tibber, Octopus, enz.). Dit is het enige instellingenveld dat via een API-sleutel schrijfbaar is.',
+    energyCostBadge: 'Energie',
+    legacyKey: 'Verouderd',
+    legacyKeyTooltip: 'Aangemaakt vóór eigendom per gebruiker; maak opnieuw om cloudtoegang te gebruiken',
+    unnamedKey: 'Naamloze sleutel',
+    lastUsed: 'Laatst gebruikt',
+    read: 'Lezen',
+    control: 'Bedienen',
+    createFirstKey: 'Je eerste sleutel maken',
+    webhookEndpoints: 'Webhook-eindpunten',
+    webhookApiKeyHint: 'Gebruik je API-sleutel in de X-API-Key-header.',
+    webhook: {
+      getAllStatus: 'Status van alle printers ophalen',
+      getSpecificStatus: 'Status van specifieke printer ophalen',
+      addToQueue: 'Toevoegen aan afdrukwachtrij',
+      pausePrint: 'Afdruk pauzeren',
+      resumePrint: 'Afdruk hervatten',
+      stopPrint: 'Afdruk stoppen',
+    },
+    apiBrowser: 'API-browser',
+    apiBrowserDescription: 'Alle beschikbare API-eindpunten verkennen en testen.',
+    apiKeyForTesting: 'API-sleutel voor testen',
+    apiKeyPlaceholder: 'Plak hier je API-sleutel om geauthenticeerde eindpunten te testen...',
+    apiKeyHint: 'Deze sleutel wordt als X-API-Key-header met verzoeken meegestuurd.',
+    deleteApiKeyTitle: 'API-sleutel verwijderen',
+    deleteApiKeyMessage: 'Weet je zeker dat je deze API-sleutel wilt verwijderen? Integraties die deze sleutel gebruiken stoppen dan met werken.',
+    deleteKey: 'Sleutel verwijderen',
+    // Filament tab
+    amsDisplayThresholds: 'AMS-weergavedrempels',
+    amsThresholdsDescription: 'Kleurdrempels voor AMS-indicatoren voor luchtvochtigheid en temperatuur configureren.',
+    humidity: 'Luchtvochtigheid',
+    goodGreen: 'Goed (groen)',
+    fairOrange: 'Redelijk (oranje)',
+    aboveFairBad: 'Boven de redelijke drempel wordt rood (slecht) weergegeven',
+    fairAlsoDryingThreshold: 'Deze drempel wordt ook gebruikt om automatisch drogen te starten wanneer dit is ingeschakeld',
+    fairBelowDryingFloor: 'Automatisch drogen kan deze waarde niet bereiken: een AMS leest {{floor}}% of hoger terwijl de verwarming draait, waardoor elke cyclus direct zou eindigen en opnieuw starten. Verhoog dit tot boven {{floor}}% als automatisch drogen is ingeschakeld.',
+    temperature: 'Temperatuur',
+    goodBlue: 'Goed (blauw)',
+    aboveFairHot: 'Boven de redelijke drempel wordt rood (heet) weergegeven',
+    tempAlarmThreshold: 'Alarm boven',
+    tempAlarmSeparateFromBand: 'Alleen de alarmdrempel verstuurt meldingen — Goed en Redelijk kleuren de weergave. Laat het leeg om te alarmeren bij de drempel Redelijk, zoals voorheen.',
+    tempAlarmMustBePositive: 'Een drempel van 0 of lager wordt genegeerd — het alarm valt terug op de drempel Redelijk.',
+    historyRetention: 'Bewaartermijn geschiedenis',
+    keepSensorHistory: 'Sensorgeschiedenis bewaren gedurende',
+    historyRetentionDescription: 'Oudere gegevens over luchtvochtigheid en temperatuur worden automatisch verwijderd',
+    defaultPrintOptions: 'Standaard afdrukopties',
+    defaultPrintOptionsDescription: 'Stel standaardwaarden in voor afdrukopties bij het starten van nieuwe afdrukken. Deze kunnen per afdruk in het afdrukdialoogvenster worden overschreven.',
+    defaultBedLevelling: 'Bednivellering',
+    defaultBedLevellingDesc: 'Bed automatisch nivelleren voor afdruk',
+    defaultFlowCali: 'Flowkalibratie',
+    defaultFlowCaliDesc: 'Extrusieflow kalibreren',
+    defaultVibrationCali: 'Trillingskalibratie',
+    defaultVibrationCaliDesc: 'Ringing-artefacten verminderen',
+    defaultLayerInspect: 'Inspectie eerste laag',
+    defaultLayerInspectDesc: 'AI-inspectie van de eerste laag',
+    defaultTimelapse: 'Timelapse',
+    defaultTimelapseDesc: 'Timelapsevideo opnemen',
+    defaultNozzleOffsetCali: 'Nozzle-offsetkalibratie',
+    defaultNozzleOffsetCaliDesc: 'Nozzle-offsets tussen extruders kalibreren',
+    tempFanPresetsTitle: 'Voorinstellingen temperatuur en ventilator',
+    tempFanPresetsDescription: 'Pas de snelkeuzewaarden aan die worden weergegeven in pop-overs voor temperatuur en ventilatorsnelheid op printerkaarten. De knop Uit wordt altijd weergegeven.',
+    tempFanPresetsNozzle: 'Nozzletemperatuur',
+    tempFanPresetsBed: 'Bedtemperatuur',
+    tempFanPresetsChamber: 'Kamertemperatuur',
+    tempFanPresetsFan: 'Ventilatorsnelheid',
+    tempFanPresetsReset: 'Standaardwaarden herstellen',
+    concurrentUploadsTitle: 'Gelijktijdige uploads',
+    concurrentUploadsDescription: 'Hoeveel printers de wachtrij tegelijk bestanden mag sturen. Printers ontvangen bestanden langzaam (een grote afdruk kan enkele minuten duren) en elke printer wacht op zijn beurt — bij een groter printerpark voorkomt een hogere waarde dat de laatste printer in een batch op alle eerdere overdrachten moet wachten. Verlaag dit als je netwerk of Bambuddy-host moeite heeft met parallelle overdrachten.',
+    concurrentUploadsLabel: 'Printers tegelijk uploaden',
+    concurrentUploadsHelp: '1 verstuurt naar één printer tegelijk (het oude gedrag). Standaard is 4.',
+    staggeredStart: 'Gespreide start',
+    staggeredStartDescription: 'Standaard groepsgrootte en interval bij gespreid starten van batches over meerdere printers. Kan per batch in het afdrukvenster worden overschreven.',
+    preheatTitle: 'Voorverwarmen en warmtestabilisatie',
+    preheatDescription: 'Verwarm het bed (en de kamer, indien ondersteund) en houd de temperatuur vast voordat elke afdruk uit de wachtrij start. Handig voor technische filamenten (PA, ABS) op printers zonder actieve kamerverwarming — het bed verwarmt de kamer via straling terwijl de stabilisatietimer loopt. Het beddoel wordt uit het afdrukbestand gelezen; kamerwerking hangt af van het printermodel.',
+    preheatEnabled: 'Voorverwarmen en stabiliseren inschakelen',
+    preheatEnabledDesc: 'Wanneer uitgeschakeld worden afdrukken uit de wachtrij direct verzonden. Elk wachtrij-item kan dit per afdruk overschrijven.',
+    preheatFilamentTargetsLabel: 'Kamerdoel per filament (°C)',
+    preheatFilamentTargetsHint: 'Bambuddy kiest het hoogste doel van de geladen AMS-sleuven; afdrukken met alleen PLA krijgen 0 en slaan de kamerfase automatisch over.',
+    preheatFilamentTargetsReset: 'Standaardwaarden herstellen',
+    preheatFilamentTargetsDefaultRow: 'Overig / niet gekoppeld',
+    preheatMaxWait: 'Max. wachttijd (seconden)',
+    preheatMaxWaitHelp: 'Maximale duur van de opwarmfase van de kamer voordat wordt doorgegaan.',
+    preheatSoak: 'Stabiliseren (seconden)',
+    preheatSoakHelp: 'Wachttijd nadat het doel is bereikt of de maximale wachttijd is verstreken.',
+    preheatHardwareTitle: 'Gedrag per printer:',
+    preheatHardwareDetail: 'H2C/H2D/H2D Pro/H2S/X2D/X1E verwarmen de kamer actief via M141. X1C/P2S lezen de kamertemperatuur maar vertrouwen op stralingswarmte van het bed. P1S/P1P/A1/A1 Mini hebben geen kamersensor — alleen de stabilisatietimer is van toepassing.',
+    preheatPerItemDesc: 'Verwarm bed en kamer voordat deze afdruk start. Standaard wordt de globale schakelaar Instellingen → Workflow gebruikt.',
+    preheatOverride_inherit: 'Overnemen',
+    preheatOverride_on: 'Aan',
+    preheatOverride_off: 'Uit',
+    calibrationMode_off: 'Uit',
+    calibrationMode_on: 'Aan',
+    calibrationMode_auto: 'Auto',
+    preheatTargetOverride: 'Kamerdoel overschrijven (°C, leeg = filamentstandaard)',
+    plateClear: 'Bevestiging plaat vrij',
+    requirePlateClear: 'Bevestiging vereisen dat plaat vrij is',
+    requirePlateClearDescription: 'Wanneer ingeschakeld wacht de scheduler op een bevestiging per printer dat de plaat vrij is voordat afdrukken uit de wachtrij starten op printers met voltooide taken. Als je dit uitschakelt, worden ook de plaatstatusbadge en de knop "Plaat als leeg markeren" op printerkaarten verborgen.',
+    keepBedWarm: 'Bed warm houden tussen afdrukken',
+    keepBedWarmDesc: 'Tijdens het wachten op het vrijgeven van de plaat wordt het bed op de onderstaande warmhoudtemperatuur gehouden zodat de kamer warm blijft — of op de eigen bedtemperatuur van de volgende afdruk als die hoger is. Alleen van toepassing wanneer de volgende afdruk kamerverwarming nodig heeft (ASA, ABS, PA, PC enz.). Vereist dat bevestiging plaat vrij is ingeschakeld.',
+    keepWarmBedTemp: 'Warmhoudtemperatuur bed (°C)',
+    keepWarmBedTempHelp: 'Wordt gebruikt wanneer het bed de kamer moet verwarmen — tijdens warmhouden en bij voorverwarmen wanneer het afdrukbestand geen bedtemperatuur bevat. Een hogere bedtemperatuur uit het afdrukbestand heeft altijd voorrang.',
+    keepWarmMaxMinutes: 'Warmhouden stoppen na (minuten)',
+    keepWarmMaxMinutesHelp: 'Als de plaat binnen deze tijd niet is vrijgegeven, worden de verwarmingselementen uitgeschakeld in plaats van onbeperkt warm gehouden.',
+    gcodeInjection: 'G-code-injectie',
+    gcodeInjectionDescription: 'Configureer aangepaste G-code die aan het begin en/of einde van afdrukken wordt ingevoegd voor automatische afdruksystemen zoals Farmloop, SwapMod, AutoClear en Printflow 3D. Fragmenten worden per printermodel ingesteld en toegepast wanneer "G-code injecteren" is ingeschakeld op een wachtrij-item.',
+    gcodeInjectionNoPrinters: 'Geen printers gevonden. Voeg printers toe om G-codefragmenten te configureren.',
+    gcodeStartLabel: 'Start-G-code',
+    gcodeEndLabel: 'Eind-G-code',
+    gcodeStartPlaceholder: 'G-code die vóór de start van de afdruk wordt toegevoegd...',
+    gcodeEndPlaceholder: 'G-code die na afloop van de afdruk wordt toegevoegd...',
+    staggerGroupSize: 'Groepsgrootte',
+    staggerGroupSizeHelp: 'Aantal printers dat per groep gelijktijdig start',
+    staggerInterval: 'Interval (minuten)',
+    staggerIntervalHelp: 'Vertraging tussen het starten van elke groep',
+    queueDrying: 'Automatisch drogen in wachtrij',
+    queueDryingDescription: 'Droog AMS-filament automatisch wanneer de printer tussen afdrukken in de wachtrij inactief is. Gebruikt de bovenstaande luchtvochtigheidsdrempel om het drogen te starten.',
+    queueDryingEnabled: 'Automatisch drogen inschakelen',
+    queueDryingEnabledDescription: 'Start AMS-drogen automatisch wanneer de printer inactief is en de luchtvochtigheid boven de drempel ligt',
+    queueDryingBlock: 'Wachten tot drogen is voltooid',
+    queueDryingBlockDescription: 'Blokkeer de afdrukwachtrij tot het drogen is voltooid. Wanneer uitgeschakeld hebben afdrukken voorrang op drogen.',
+    ambientDryingEnabled: 'Drogen zonder wachtrij',
+    ambientDryingEnabledDescription: 'Droog filament automatisch op inactieve printers wanneer de luchtvochtigheid boven de drempel komt, ook zonder afdrukken in de wachtrij.',
+    printDryingEnabled: 'Doorgaan met drogen tijdens afdrukken',
+    printDryingEnabledDescription: 'Sta toe dat automatisch drogen tijdens een afdruk blijft draaien op ondersteunde hardware (H2D, H2C, H2S, P2S, H2D Pro, X2D, X1C, A2L met recente firmware). De droogtemperatuur wordt automatisch begrensd op 5°C onder de inactieve preset om spoelen te beschermen.',
+    dryingPresets: 'Droogpresets',
+    dryingPresetsDescription: 'Temperatuur en duur per filamenttype. AMS 2 Pro gebruikt lagere temperaturen, AMS-HT ondersteunt hogere temperaturen.',
+    dryingFilament: 'Filament',
+    humidityThresholds: 'Luchtvochtigheidsdrempels',
+    humidityThresholdsDescription: 'Luchtvochtigheidsdrempel per filament voor automatisch drogen en alarmen. Bij gemengde lading in één AMS wordt de laagste drempel gebruikt.',
+    humidityThresholdCol: 'Drempel',
+    humidityThresholdDefault: 'Standaard (onbekende typen)',
+    printModal: 'Afdrukvenster',
+    expandCustomMapping: 'Aangepaste toewijzing standaard uitklappen',
+    expandCustomMappingDescription: 'Bij afdrukken naar meerdere printers de AMS-toewijzing per printer uitgeklapt tonen',
+    // User management
+    authentication: 'Authenticatie',
+    authEnabledDescription: 'Je instantie is beveiligd met gebruikersauthenticatie',
+    authDisabledDescription: 'Schakel in om inloggen te vereisen en gebruikerstoegang te beheren',
+    authDisabledMessage: 'Schakel authenticatie in om gebruikersaccounts te maken, machtigingen te beheren en je Bambuddy-instantie te beveiligen.',
+    enableAuthentication: 'Authenticatie inschakelen',
+    currentUser: 'Huidige gebruiker',
+    changePassword: 'Wachtwoord wijzigen',
+    admin: 'Beheerder',
+    users: 'Gebruikers',
+    addUser: 'Gebruiker toevoegen',
+    groups: 'Groepen',
+    addGroup: 'Groep toevoegen',
+    system: 'Systeem',
+    noDescription: 'Geen beschrijving',
+    userCount: '{{count}} gebruikers',
+    permissionCount: '{{count}} machtigingen',
+    createUser: 'Gebruiker maken',
+    username: 'Gebruikersnaam',
+    enterUsername: 'Voer gebruikersnaam in',
+    password: 'Wachtwoord',
+    enterPassword: 'Voer wachtwoord in',
+    passwordRequirements: 'Minstens 8 tekens, met één hoofdletter, één kleine letter, één cijfer en één speciaal teken.',
+    confirmPassword: 'Wachtwoord bevestigen',
+    confirmPasswordPlaceholder: 'Bevestig wachtwoord',
+    // Title tooltips
+    viewReleaseOnGitHub: 'Release bekijken op GitHub',
+    turnAllPlugsOn: 'Alle stekkers inschakelen',
+    turnAllPlugsOff: 'Alle stekkers uitschakelen',
+    // Modal: Clear logs
+    clearNotificationLogs: 'Meldingslogboeken wissen',
+    clearLogsMessage: 'Hiermee worden alle meldingslogboeken ouder dan 30 dagen permanent verwijderd. Deze actie kan niet ongedaan worden gemaakt.',
+    clearLogs: 'Logboeken wissen',
+    // Modal: Reset UI
+    resetUiPreferences: 'UI-voorkeuren resetten',
+    resetUiPreferencesMessage: 'Hiermee worden alle UI-voorkeuren teruggezet naar de standaardwaarden: volgorde zijbalk, thema, dashboardindeling, weergavemodi en sorteervoorkeuren. Je printers, archieven en serverinstellingen worden NIET beïnvloed. De pagina wordt na het wissen opnieuw geladen.',
+    resetPreferences: 'Voorkeuren resetten',
+    // Modal: Delete group
+    deleteGroupTitle: 'Groep verwijderen',
+    deleteGroupMessage: 'Weet je zeker dat je deze groep wilt verwijderen? Gebruikers in deze groep verliezen deze machtigingen.',
+    deleteGroup: 'Groep verwijderen',
+    // Modal: Disable auth
+    disableAuthenticationTitle: 'Authenticatie uitschakelen',
+    disableAuthenticationMessage: 'Weet je zeker dat je authenticatie wilt uitschakelen? Hierdoor wordt je Bambuddy-instantie toegankelijk zonder login. Alle gebruikers blijven in de database, maar authenticatie wordt uitgeschakeld.',
+    disableAuthentication: 'Authenticatie uitschakelen',
+    // Additional settings
+    configureBambuddy: 'Bambuddy configureren',
+    systemDefault: 'Systeemstandaard',
+    archiveSettings: 'Archiefinstellingen',
+    newWindow: 'Nieuw venster',
+    embeddedOverlay: 'Ingebedde overlay',
+    preferredSlicer: 'Voorkeursslicer',
+    preferredSlicerDescription: 'Slicer die wordt gebruikt voor slicen in de app via de API-sidecar',
+    openInSlicerLabel: 'Openen in slicer',
+    openInSlicerInherit: 'Zelfde als API-slicer',
+    openInSlicerDescription: 'Desktop-slicer die wordt gebruikt door de knop \'Openen in slicer\'. Laat op \'Zelfde als API-slicer\' staan om deze over te nemen, of kies een andere slicer voor lokaal gebruik.',
+    orcaslicerKnownIssuesWarning: 'OrcaSlicer 2.3.2 / 2.4.0-dev heeft bekende CLI-bugs die het slicen van veel door Bambu gemaakte 3MF\'s blokkeren — zie upstream issues #12426 (segfault bij gekleurde multi-extruderbestanden) en #13386 (afwijzing door strikte validatie van parameterbereik). Bambu Studio wordt aanbevolen tot de upstream fixes beschikbaar zijn.',
+    useSlicerApi: 'Slicer-API gebruiken',
+    useSlicerApiDescription: 'Wanneer ingeschakeld openen acties "Slicen" het slicervenster in de app en roepen ze de slicer-API-sidecar aan. Wanneer uitgeschakeld (standaard) worden ze via een URI-schema aan de desktopslicer doorgegeven.',
+    slicerCard: 'Slicer',
+    orcaslicerApiUrl: 'OrcaSlicer-sidecar-URL',
+    bambuStudioApiUrl: 'Bambu Studio-sidecar-URL',
+    slicerStallTimeout: 'Time-out slicerstagnatie (minuten)',
+    slicerStallTimeoutDescription: 'Stop een slice na deze tijd als er geen voortgang van de sidecar is. Zware modellen die voortgang blijven melden worden nooit afgebroken, hoe lang ze ook duren. Sidecars die geen voortgang melden gebruiken dit in plaats daarvan als totale tijdslimiet.',
+    slicerApiUrlDescription: 'URL van de slicer-API-sidecarcontainer. Laat leeg om de standaardwaarden van de omgevingsvariabelen SLICER_API_URL / BAMBU_STUDIO_API_URL te gebruiken.',
+    externalCameras: 'Externe camera\'s',
+    costTracking: 'Kostentracking',
+    billingEnabled: 'Facturering inschakelen',
+    billingEnabledDescription: 'Gebruikers kosten voor afdrukken in rekening brengen en financiële functies inschakelen',
+    printerKillSwitch: 'Noodstop ongeautoriseerde afdruk',
+    printerKillSwitchDescription: 'Stop afdrukken direct wanneer ze zonder autorisatie starten.',
+    financeBudgetReset: 'Maandelijkse reset financieel budget',
+    financeBudgetResetDay: 'Resetdag',
+    financeBudgetResetDayHelp: 'Voor korte maanden wordt op de laatste dag van de maand gereset.',
+    financeBudgetResetTimezone: 'Tijdzone voor reset',
+    financeBudgetResetTimezoneHelp: 'Start van budgetperiode wordt in deze tijdzone berekend.")',
+    rebuildLedger: 'Portemonnee-grootboek opnieuw opbouwen',
+    rebuildLedgerStarted: 'Opnieuw opbouwen van grootboek gestart',
+    rebuildLedgerConfirmTitle: 'Portemonnee-grootboek opnieuw opbouwen?',
+    rebuildLedgerConfirmMessage:
+      'Hiermee wordt het portemonnee-grootboek opnieuw opgebouwd om historische saldowaarden te herstellen. Voer dit alleen uit als je weet wat je doet.',
+    rebuildLedgerInProgress: 'Opnieuw opbouwen starten...',
+    printsOnly: 'Alleen afdrukken',
+    totalConsumption: 'Totaal verbruik',
+    dataManagement: 'Gegevensbeheer',
+    storageUsage: 'Opslaggebruik',
+    storageUsageDescription: 'Verdeling van gegevensgebruik per categorie',
+    storageUsageTotal: 'Totaal',
+    storageUsageErrors: 'Fouten',
+    storageUsageOtherBreakdown: 'Overig (inclusief statische assets, scripts en configuratiebestanden)',
+    storageUsageSystem: 'Systeem',
+    storageUsageData: 'Gegevens',
+    storageUsageUnavailable: 'Informatie over opslaggebruik niet beschikbaar',
+    clearNotificationLogsDescription: 'Meldingslogboeken ouder dan 30 dagen verwijderen',
+    resetUiPreferencesDescription: 'Reset volgorde zijbalk, thema, weergavemodi en indelingsvoorkeuren. Printers, archieven en instellingen worden niet beïnvloed.',
+    enableHomeAssistant: 'Home Assistant inschakelen',
+    enableMqtt: 'MQTT inschakelen',
+    useTls: 'TLS gebruiken',
+    enableMetricsEndpoint: 'Metrics-eindpunt inschakelen',
+    availableMetrics: 'Beschikbare metrics',
+    editUser: 'Gebruiker bewerken',
+    deleteUserTitle: 'Gebruiker verwijderen',
+    groupName: 'Groepsnaam',
+    // Placeholders
+    leaveEmptyForAnonymous: 'Laat leeg voor anoniem',
+    leaveEmptyForNoAuth: 'Laat leeg voor geen authenticatie',
+    enterNewPassword: 'Voer nieuw wachtwoord in',
+    confirmNewPassword: 'Bevestig nieuw wachtwoord',
+    enterGroupName: 'Voer groepsnaam in',
+    enterDescriptionOptional: 'Voer beschrijving in (optioneel)',
+    enterCurrentPassword: 'Voer huidig wachtwoord in',
+    enterNewPasswordMin6: 'Voer nieuw wachtwoord in (min. 6 tekens)',
+    toast: {
+      keyCopied: 'Sleutel naar klembord gekopieerd',
+      copyFailed: 'Sleutel kopiëren mislukt',
+      keyAddedToBrowser: 'Sleutel toegevoegd aan API-browser',
+      clearLogsFailed: 'Logboeken wissen mislukt',
+      uiPreferencesReset: 'UI-voorkeuren gereset. Vernieuwen...',
+      authDisabled: 'Authenticatie succesvol uitgeschakeld',
+      authDisableFailed: 'Authenticatie uitschakelen mislukt',
+      apiKeyCreated: 'API-sleutel gemaakt',
+      apiKeyDeleted: 'API-sleutel verwijderd',
+      userCreated: 'Gebruiker succesvol gemaakt',
+      userUpdated: 'Gebruiker succesvol bijgewerkt',
+      userDeleted: 'Gebruiker succesvol verwijderd',
+      groupCreated: 'Groep succesvol gemaakt',
+      groupUpdated: 'Groep succesvol bijgewerkt',
+      groupDeleted: 'Groep succesvol verwijderd',
+      fillRequiredFields: 'Vul alle verplichte velden in',
+      passwordsDoNotMatch: 'Wachtwoorden komen niet overeen',
+      passwordTooShort: 'Wachtwoord moet minstens 8 tekens bevatten',
+      passwordNeedsUppercase: 'Wachtwoord moet minstens één hoofdletter bevatten',
+      passwordNeedsLowercase: 'Wachtwoord moet minstens één kleine letter bevatten',
+      passwordNeedsDigit: 'Wachtwoord moet minstens één cijfer bevatten',
+      passwordNeedsSpecial: 'Wachtwoord moet minstens één speciaal teken bevatten',
+      enterGroupName: 'Voer een groepsnaam in',
+      settingsSaved: 'Instellingen opgeslagen',
+      noPermissionUpdate: 'Je hebt geen toestemming om instellingen te wijzigen',
+      cameraSettingsSaved: 'Camera-instellingen opgeslagen',
+      enterCameraUrl: 'Voer een camera-URL in',
+      passwordChanged: 'Wachtwoord succesvol gewijzigd',
+      connectionFailed: 'Verbinding mislukt',
+      testFailed: 'Test mislukt',
+      cameraConnected: 'Camera verbonden{{resolution}}',
+      cameraConnectedCoalesced: 'Camera verbonden{{resolution}} (gedeeld met een opname die al draait)',
+    },
+    testConnection: 'Verbinding testen',
+    catalog: {
+      spoolCatalog: 'Spoelcatalogus',
+      spoolCatalogDescription: 'Lege spoelgewichten per merk/type. Gebruikt voor automatisch opzoeken van gewicht bij het toevoegen van spoelen.',
+      searchCatalog: 'Catalogus zoeken...',
+      addNewEntry: 'Nieuw item toevoegen',
+      namePlaceholder: 'Naam (bijv. Bambu Lab - Plastic)',
+      weight: 'Gewicht',
+      type: 'Type',
+      default: 'Standaard',
+      custom: 'Aangepast',
+      noMatch: 'Geen items komen overeen met je zoekopdracht',
+      empty: 'Geen items in catalogus',
+      deleteEntry: 'Item verwijderen',
+      deleteConfirm: 'Weet je zeker dat je "{{name}}" wilt verwijderen?',
+      resetCatalog: 'Catalogus resetten',
+      resetConfirm: 'Catalogus terugzetten naar standaardwaarden? Hiermee worden alle aangepaste items verwijderd.',
+      loadFailed: 'Spoelcatalogus laden mislukt',
+      nameWeightRequired: 'Naam en gewicht zijn verplicht',
+      entryAdded: 'Item toegevoegd',
+      addFailed: 'Item toevoegen mislukt',
+      entryUpdated: 'Item bijgewerkt',
+      updateFailed: 'Item bijwerken mislukt',
+      entryDeleted: 'Item verwijderd',
+      deleteFailed: 'Item verwijderen mislukt',
+      resetSuccess: 'Catalogus teruggezet naar standaardwaarden',
+      resetFailed: 'Catalogus resetten mislukt',
+      exported: '{{count}} items geëxporteerd',
+      imported: '{{added}} items geïmporteerd ({{skipped}} overgeslagen)',
+      importFailed: 'Importeren mislukt: ongeldig JSON-formaat',
+      exportTooltip: 'Catalogus exporteren naar JSON',
+      importTooltip: 'Catalogus importeren uit JSON',
+      resetTooltip: 'Standaardwaarden herstellen',
+      selectedCount: '{{count}} geselecteerd',
+      deleteSelected: 'Geselecteerde verwijderen',
+      bulkDeleteConfirm: 'Weet je zeker dat je {{count}} items wilt verwijderen?',
+      bulkDeleted: '{{count}} items verwijderd',
+      bulkDeleteFailed: 'Items verwijderen mislukt',
+      material: 'Materiaal',
+      spoolWeight: 'Spoelgewicht',
+      color: 'Kleur',
+      updateSpoolWeight: 'Spoelgewicht bijwerken',
+      filamentUpdated: 'Filament bijgewerkt',
+      filamentUpdateFailed: 'Filament bijwerken mislukt',
+      filamentUpdateInvalid: 'Ongeldige filamentgegevens',
+      keepExistingSpoolWeight: 'Oud gewicht behouden voor bestaande spoelen',
+      keepExistingSpoolWeightDesc: 'Spoelen die al met dit filamenttype zijn gemaakt behouden het oude taragewicht. Nieuwe spoelen gebruiken de bijgewerkte waarde.',
+      applyToAllSpools: 'Toepassen op alle spoelen',
+      applyToAllSpoolsDesc: 'Alle gewichtsberekeningen voor dit filamenttype gebruiken direct het nieuwe taragewicht.',
+    },
+    colorCatalog: {
+      title: 'Kleurcatalogus',
+      description: 'Filamentkleuren per fabrikant/materiaal. Gebruikt voor automatisch opzoeken van kleur bij het toevoegen van spoelen.',
+      searchColors: 'Kleuren zoeken...',
+      allManufacturers: 'Alle fabrikanten',
+      addNewColor: 'Nieuwe kleur toevoegen',
+      manufacturer: 'Fabrikant',
+      colorName: 'Kleurnaam',
+      hex: 'Hex',
+      materialOptional: 'Materiaal (optioneel)',
+      showing: '{{filtered}} van {{total}} kleuren weergegeven',
+      noMatch: 'Geen kleuren komen overeen met je zoekopdracht',
+      empty: 'Geen kleuren in catalogus',
+      deleteColor: 'Kleur verwijderen',
+      deleteConfirm: 'Weet je zeker dat je "{{name}}" wilt verwijderen?',
+      resetCatalog: 'Kleurcatalogus resetten',
+      resetConfirm: 'Catalogus terugzetten naar standaardwaarden? Hiermee worden alle aangepaste kleuren verwijderd.',
+      sync: 'Synchroniseren',
+      starting: 'Starten...',
+      syncTooltip: 'Synchroniseren vanaf FilamentColors.xyz (2000+ kleuren, kan een minuut duren)',
+      loadFailed: 'Kleurcatalogus laden mislukt',
+      fieldsRequired: 'Fabrikant, kleurnaam en hexkleur zijn verplicht',
+      colorAdded: 'Kleur toegevoegd',
+      addFailed: 'Kleur toevoegen mislukt',
+      colorUpdated: 'Kleur bijgewerkt',
+      updateFailed: 'Kleur bijwerken mislukt',
+      colorDeleted: 'Kleur verwijderd',
+      deleteFailed: 'Kleur verwijderen mislukt',
+      resetSuccess: 'Kleurcatalogus teruggezet naar standaardwaarden',
+      resetFailed: 'Catalogus resetten mislukt',
+      syncUpToDate: 'Al up-to-date ({{count}} kleuren gecontroleerd)',
+      syncComplete: '{{added}} nieuwe kleuren toegevoegd ({{skipped}} bestonden al)',
+      syncError: 'Synchronisatiefout',
+      syncFailed: 'Synchroniseren vanaf FilamentColors.xyz mislukt',
+      exported: '{{count}} kleuren geëxporteerd',
+      imported: '{{added}} kleuren geïmporteerd ({{skipped}} overgeslagen)',
+      importFailed: 'Importeren mislukt: ongeldig JSON-formaat',
+      selectedCount: '{{count}} geselecteerd',
+      deleteSelected: 'Geselecteerde verwijderen',
+      bulkDeleteConfirm: 'Weet je zeker dat je {{count}} kleuren wilt verwijderen?',
+      bulkDeleted: '{{count}} kleuren verwijderd',
+      bulkDeleteFailed: 'Kleuren verwijderen mislukt',
+    },
+    // General tab
+    dateFormat: 'Datumnotatie',
+    dateFormatUs: 'VS (MM/DD/JJJJ)',
+    dateFormatEu: 'EU (DD/MM/JJJJ)',
+    dateFormatIso: 'ISO (JJJJ-MM-DD)',
+    timeFormat: 'Tijdnotatie',
+    timeFormat12: '12-uurs (3:30 PM)',
+    timeFormat24: '24-uurs (15:30)',
+    defaultPrinter: 'Standaardprinter',
+    defaultPrinterDescription: 'Selecteer deze printer vooraf voor uploads, opnieuw afdrukken en andere bewerkingen.',
+    slicerBambuStudio: 'Bambu Studio',
+    slicerOrcaSlicer: 'OrcaSlicer',
+    sidebarOrderDescription: 'Gebruik Zijbalk om items opnieuw te ordenen, zichtbaarheid te resetten en aangepaste links te beheren.',
+    setDefault: 'Als standaard instellen',
+    sidebarOrderSetDefaultHint: 'Als standaard instellen past de huidige menuvolgorde toe op gebruikers die hun menu nog niet hebben aangepast.',
+    sidebarDefaultSet: 'Standaard menuvolgorde is ingesteld.',
+    sidebarDefaultCleared: 'Standaard menuvolgorde is gewist.',
+    sidebarDefaultFailed: 'Standaard menuvolgorde instellen mislukt.',
+    reset: 'Resetten',
+    // Appearance
+    darkMode: 'Donkere modus',
+    lightMode: 'Lichte modus',
+    active: '(actief)',
+    background: 'Achtergrond',
+    accent: 'Accent',
+    style: 'Stijl',
+    bgNeutral: 'Neutraal',
+    bgWarm: 'Warm',
+    bgCool: 'Koel',
+    bgOled: 'OLED-zwart',
+    bgSlate: 'Leisteenblauw',
+    bgForest: 'Bosgroen',
+    accentGreen: 'Groen',
+    accentTeal: 'Blauwgroen',
+    accentBlue: 'Blauw',
+    accentOrange: 'Oranje',
+    accentPurple: 'Paars',
+    accentRed: 'Rood',
+    styleClassic: 'Klassiek',
+    styleGlow: 'Gloed',
+    styleVibrant: 'Levendig',
+    themeToggleHint: 'Schakel met het pictogram in de zijbalk tussen donkere, lichte en systeemmodus.',
+    progressInTitle: 'Afdrukvoortgang in tabblad',
+    progressInTitleDescription: 'Toon het percentage van de actieve afdruk en een voortgangsring in het browsertabblad.',
+    // Archive
+    autoArchivePrints: 'Afdrukken automatisch archiveren',
+    autoArchiveDescription: '3MF-bestanden automatisch opslaan wanneer afdrukken zijn voltooid',
+    saveThumbnailsDescription: 'Voorbeeldafbeeldingen uit 3MF-bestanden extraheren en opslaan',
+    captureFinishPhotoDescription: 'Maak een foto met de printercamera wanneer de afdruk is voltooid. Bambuddy neemt tijdens de afdruk een korte timelapse op zodat de foto kan worden genomen van het moment voordat het bed zakt; het timelapsebestand blijft behouden als je timelapse voor deze afdruk hebt ingeschakeld, anders wordt het automatisch verwijderd nadat de foto is vastgelegd.',
+    finishPhotoRestorePlate: 'Plaat herstellen voor eindfoto',
+    finishPhotoRestorePlateDescription: 'De printer laat de bouwplaat ongeveer 100 mm zakken wanneer een afdruk eindigt, waardoor de voltooide afdruk onder het camerabeeld komt. Bambuddy brengt de plaat terug tot net boven de laatst geprinte laag, maakt de foto en laat hem daarna weer zakken. Wordt overgeslagen wanneer de afdrukhoogte onbekend is of er een andere taak in de wachtrij staat.',
+    ffmpegNotInstalled: 'ffmpeg niet geïnstalleerd',
+    ffmpegRequired: 'Camera-opname vereist ffmpeg. Installeer dit via <brew>brew install ffmpeg</brew> (macOS) of <apt>apt install ffmpeg</apt> (Linux).',
+    // Camera
+    camera: 'Camera',
+    cameraViewMode: 'Cameraweergavemodus',
+    cameraOverlayDescription: 'Camera opent in een schaalbare overlay op het hoofdscherm',
+    cameraWindowDescription: 'Camera opent in een apart browservenster',
+    externalCamerasDescription: 'Configureer externe camera\'s ter vervanging van de ingebouwde printercamera. Ondersteunt MJPEG-streams, RTSP, HTTP-momentopnamen en USB-camera\'s (V4L2). Wanneer ingeschakeld wordt de externe camera gebruikt voor liveweergave en eindfoto\'s.',
+    cameraPlaceholderUsb: 'Apparaatpad (/dev/video0)',
+    cameraPlaceholderUrl: 'Camera-URL (rtsp://... of http://...)',
+    cameraTypeMjpeg: 'MJPEG-stream',
+    cameraTypeRtsp: 'RTSP-stream',
+    cameraTypeSnapshot: 'HTTP-momentopname',
+    cameraTypeUsb: 'USB-camera (V4L2)',
+    cameraSnapshotUrl: 'Momentopname-URL (optioneel)',
+    cameraSnapshotUrlPlaceholder: 'http://192.168.1.61:1984/api/frame.jpeg?src=printer',
+    cameraSnapshotUrlHelp: 'URL voor één frame, gebruikt voor miniaturen in meldingen, eindfoto\'s, frames van laagtimelapses en plaatdetectie. Timelapse en plaatdetectie vereisen elk hun eigen schakelaar per printer — deze URL is alleen de afbeeldingsbron die ze gebruiken wanneer actief. Laat leeg om beelden uit de bovenstaande livestream vast te leggen. Handig voor go2rtc (/api/frame.jpeg) en IP-camera\'s met een speciaal snapshot-eindpunt.',
+    cameraRotation: 'Rotatie',
+    test: 'Testen',
+    connected: 'Verbonden',
+    disconnected: 'Niet verbonden',
+    // Cost tracking
+    currency: 'Valuta',
+    defaultFilamentCost: 'Standaard filamentkosten (per kg)',
+    electricityCost: 'Elektriciteitskosten per kWh',
+    energyDisplayMode: 'Weergavemodus energie',
+    energyModePrintDescription: 'Dashboard toont de som van energie die tijdens afdrukken is gebruikt',
+    energyModeTotalDescription: 'Dashboard toont het totale energieverbruik van slimme stekkers over de hele levensduur',
+    // File Manager
+    fileManager: 'Bestandsbeheer',
+    createArchiveEntry: 'Archiefitem maken bij afdrukken',
+    createArchiveEntryDescription: 'Bij afdrukken vanuit Bestandsbeheer optioneel een archiefitem maken',
+    lowDiskSpaceWarning: 'Waarschuwing weinig schijfruimte',
+    lowDiskSpaceDescription: 'Waarschuwing tonen wanneer vrije schijfruimte onder deze drempel komt',
+    // Updates
+    printerFirmware: 'Printerfirmware',
+    checkFirmwareDescription: 'Controleren op printerfirmware-updates van Bambu Lab',
+    bambuddySoftware: 'Bambuddy-software',
+    autoCheckDescription: 'Automatisch controleren op nieuwe versies bij opstarten',
+    checkNow: 'Nu controleren',
+    updateAvailableVersion: 'Update beschikbaar: v{{version}}',
+    releaseNotes: 'Releaseopmerkingen',
+    updateViaDocker: 'Bijwerken via Docker Compose:',
+    composeDirectory: 'Compose-map',
+    composeDirectoryHint: 'Map met je docker-compose.yml. Laat leeg om cd weg te laten.',
+    copyUpdateCommand: 'Update-opdracht kopiëren',
+    updateViaHomeAssistant: 'Updates worden beheerd door de Home Assistant Supervisor. Open Instellingen → Add-ons → Bambuddy in Home Assistant om de nieuwe versie te installeren.',
+    updateViaWindowsInstaller: 'Windows-installaties worden bijgewerkt door het installatieprogramma opnieuw uit te voeren. Download hieronder de nieuwe versie — je gegevens, instellingen en printers blijven behouden.',
+    downloadWindowsInstaller: 'Installatieprogramma voor v{{version}} downloaden',
+    installUpdate: 'Update installeren',
+    latestVersionRunning: 'Je gebruikt de nieuwste versie',
+    failedToCheckUpdates: 'Controleren op updates mislukt: {{error}}',
+    // Data Management
+    backupRestore: 'Back-up en herstel',
+    backupRestoreDescription: 'Instellingen exporteren/importeren en GitHub-back-up configureren',
+    goToBackup: 'Ga naar Back-up',
+    // Network tab
+    externalUrl: 'Externe URL',
+    externalUrlDescription: 'De externe URL waarop Bambuddy bereikbaar is. Wordt gebruikt voor afbeeldingen in meldingen en externe integraties.',
+    bambuddyUrl: 'Bambuddy-URL',
+    externalUrlHint: 'Inclusief protocol en poort (bijv. http://192.168.1.100:8000)',
+    ftpRetry: 'FTP opnieuw proberen',
+    ftpRetryDescription: 'FTP-bewerkingen opnieuw proberen wanneer de wifi van de printer onbetrouwbaar is. Geldt voor 3MF-downloads, afdrukuploads, timelapse-downloads en firmware-updates.',
+    autoRetryDescription: 'Mislukte FTP-bewerkingen automatisch opnieuw proberen',
+    retryAttempts: 'Aantal pogingen',
+    retryDelay: 'Vertraging tussen pogingen',
+    connectionTimeout: 'Verbindingstime-out',
+    time_one: '{{count}} keer',
+    time_other: '{{count}} keer',
+    second_one: '{{count}} seconde',
+    second_other: '{{count}} seconden',
+    nSeconds: '{{count}} seconden',
+    increaseForWeakWifi: 'Verhoog dit voor printers met zwakke wifi',
+    // Home Assistant
+    homeAssistant: 'Home Assistant',
+    homeAssistantFullDescription: 'Verbind met Home Assistant om slimme stekkers via de REST-API van HA te bedienen. Ondersteunt switch-, light-, input_boolean- en script-entiteiten.',
+    homeAssistantUrl: 'Home Assistant-URL',
+    longLivedAccessToken: 'Long-Lived Access Token',
+    haTokenHint: 'Maak een token in HA: Profiel → Long-Lived Access Tokens → Token maken',
+    connectionSuccessful: 'Verbinding geslaagd',
+    connectionFailed: 'Verbinding mislukt',
+    haConnectionSuccess: 'Succesvol verbonden met Home Assistant.',
+    haConnectionFailed: 'Verbinding met Home Assistant mislukt.',
+    // MQTT
+    mqttPublishing: 'MQTT-publicatie',
+    mqttDescription: 'Publiceer BamBuddy-gebeurtenissen naar een externe MQTT-broker voor integratie met Node-RED, Home Assistant en andere automatiseringssystemen.',
+    mqttEnableDescription: 'Gebeurtenissen publiceren naar externe MQTT-broker',
+    brokerHostname: 'Hostnaam broker',
+    port: 'Poort',
+    usernameOptional: 'Gebruikersnaam (optioneel)',
+    passwordOptional: 'Wachtwoord (optioneel)',
+    topicPrefix: 'Topic-prefix',
+    topicPrefixHint: 'Topics worden: {{prefix}}/printers/<serial>/status, enz.',
+    // Prometheus
+    prometheusMetrics: 'Prometheus-metrics',
+    prometheusEndpointDescription: 'Printermetrics beschikbaar stellen op <code>/api/v1/metrics</code> voor bewaking met Prometheus/Grafana.',
+    bearerTokenOptional: 'Bearer-token (optioneel)',
+    bearerTokenHint: 'Indien ingesteld moeten verzoeken <code>Authorization: Bearer <token></code> bevatten',
+    metricsConnectionStatus: 'Verbindingsstatus',
+    metricsPrinterState: 'Printerstatus (inactief/afdrukken/enz.)',
+    metricsPrintProgress: 'Afdrukvoortgang 0-100%',
+    metricsBedTemp: 'Bedtemperatuur',
+    metricsNozzleTemp: 'Nozzletemperatuur',
+    metricsPrintsTotal: 'Totaal aantal afdrukken per resultaat',
+    metricsMore: '...en meer (lagen, ventilatoren, wachtrij, filamentverbruik)',
+    // Smart Plugs
+    smartPlugsDescription: 'Verbind slimme stekkers (Tasmota of Home Assistant) om stroomregeling te automatiseren en energieverbruik van je printers te volgen.',
+    allOn: 'Alles aan',
+    allOff: 'Alles uit',
+    addSmartPlug: 'Slimme stekker toevoegen',
+    energySummary: 'Energieoverzicht',
+    currentPower: 'Huidig vermogen',
+    plugsOnline: '{{reachable}}/{{total}} stekkers online',
+    today: 'Vandaag',
+    yesterday: 'Gisteren',
+    total: 'Totaal',
+    enablePlugsForSummary: 'Schakel stekkers in om energieoverzicht te zien',
+    addNotificationProvider: 'Toevoegen',
+    // Users
+    systemBadge: '(Systeem)',
+    creating: 'Maken...',
+    changing: 'Wijzigen...',
+    deleteUserAndItems: 'Gebruiker EN diens items verwijderen',
+    deleteUserKeepItems: 'Gebruiker verwijderen, items behouden (worden eigenaarloos)',
+    ok: 'OK',
+
+    // 2FA settings
+    twoFa: {
+      totpTitle: 'Authenticator-app (TOTP)',
+      totpDesc: 'Gebruik een authenticator-app zoals Google Authenticator, Aegis of Authy.',
+      emailOtpTitle: 'E-mail-OTP',
+      emailOtpDesc: 'Stuur bij inloggen een eenmalige code naar {{email}}.',
+      emailOtpNoEmail: 'Voeg een e-mailadres toe aan je account om deze methode in te schakelen.',
+      addEmailFirst: 'Je account heeft geen e-mailadres. Vraag een beheerder om er een toe te voegen voordat je E-mail-OTP inschakelt.',
+      setupTotp: 'Authenticator-app instellen',
+      setupAuthApp: 'Authenticator-app instellen',
+      setupInstructions: 'Scan onderstaande QR-code met je authenticator-app en bevestig daarna met een code.',
+      manualEntry: 'Kun je niet scannen? Voer dit geheim handmatig in:',
+      scannedContinue: 'Ik heb de code gescand — doorgaan',
+      enterCodeToConfirm: 'Voer de 6-cijferige code uit je authenticator-app in om de installatie te bevestigen.',
+      activate: 'Activeren',
+      disableTotp: 'Authenticator uitschakelen',
+      disableConfirmHint: 'Voer een geldige TOTP-code of back-upcode in om de authenticator uit te schakelen.',
+      totpDisabled: 'Authenticator-app uitgeschakeld.',
+      emailOtpEnabled: 'E-mail-OTP ingeschakeld.',
+      emailOtpDisabled: 'E-mail-OTP uitgeschakeld.',
+      smtpRequired: 'Configureer en test eerst de SMTP-instellingen.',
+      invalidCode: 'Ongeldige code. Probeer het opnieuw.',
+      enableEmailOtp: 'E-mail-OTP inschakelen',
+      disableEmailOtp: 'E-mail-OTP uitschakelen',
+      emailSetupEnterCode: 'Er is een verificatiecode naar je e-mailadres gestuurd. Voer deze hieronder in om te bevestigen dat deze inbox van jou is.',
+      verifyAndEnable: 'Verifiëren en inschakelen',
+      emailDisablePasswordHint: 'Voer je accountwachtwoord in om het uitschakelen van e-mail-OTP te bevestigen.',
+      passwordPlaceholder: 'Voer je wachtwoord in',
+      backupCodesTitle: 'Bewaar je back-upcodes',
+      backupCodesWarning: 'Bewaar deze codes op een veilige plek. Elke code kan maar één keer worden gebruikt en wordt niet opnieuw getoond.',
+      backupCodesRemaining: '{{count}} back-upcodes resterend',
+      savedCodes: 'Ik heb mijn codes opgeslagen',
+      regenBackup: 'Back-upcodes opnieuw genereren',
+      regenBackupHint: 'Voer je huidige TOTP-code in om 10 nieuwe back-upcodes te genereren. Alle bestaande back-upcodes worden ongeldig.',
+      newBackupCodes: 'Nieuwe back-upcodes',
+      linkedAccounts: 'Gekoppelde SSO-accounts',
+      linkedAccountsDesc: 'Deze externe identiteitsproviders zijn gekoppeld aan je account.',
+      oidcUnlinked: 'Account ontkoppeld.',
+    },
+
+    // Session Policy (#1706) — admin-configurable session lifetime ceiling.
+    sessionPolicy: {
+      title: 'Sessiebeleid',
+      description: 'Maximale sessieduur voor nieuwe gebruikerslogins. Reeds uitgegeven tokens behouden hun oorspronkelijke vervaldatum.',
+      preset24h: '24 uur',
+      preset7d: '7 dagen',
+      preset30d: '30 dagen',
+      customHoursLabel: 'Aangepaste sessieduur in uren',
+      hoursSuffix: 'uren',
+      warning: 'Langere sessies verminderen de bescherming door automatisch uitloggen. Alleen aanbevolen voor vertrouwde installaties met één gebruiker.',
+    },
+
+    // OIDC provider settings
+    oidc: {
+      title: 'SSO / OIDC-providers',
+      desc: 'Configureer OpenID Connect-providers om single sign-on via externe identiteitsproviders toe te staan.',
+      addProvider: 'Provider toevoegen',
+      newProvider: 'Nieuwe provider',
+      empty: 'Nog geen OIDC-providers geconfigureerd.',
+      created: 'Provider gemaakt.',
+      updated: 'Provider bijgewerkt.',
+      deleted: 'Provider verwijderd.',
+      refreshIcon: 'Pictogram vernieuwen',
+      removeIcon: 'Pictogram verwijderen',
+      iconRefreshed: 'Pictogram vernieuwd.',
+      iconRemoved: 'Pictogram verwijderd.',
+      iconFetchFailed: 'Pictogram kon niet worden opgehaald van de provider-URL.',
+      deleteTitle: 'Provider verwijderen',
+      deleteMessage: '"{{name}}" verwijderen? Alle gekoppelde gebruikersaccounts worden ontkoppeld.',
+      form: {
+        name: 'Weergavenaam',
+        issuerUrl: 'Issuer-URL',
+        clientId: 'Client-ID',
+        clientSecret: 'Client Secret',
+        scopes: 'Scopes',
+        iconUrl: 'Pictogram-URL (optioneel)',
+        enabled: 'Ingeschakeld',
+        autoCreate: 'Gebruikers automatisch maken',
+        autoCreateDesc: 'Automatisch een lokaal account maken bij de eerste login.',
+        autoLink: 'Bestaande accounts automatisch koppelen',
+        autoLinkDesc: 'Bestaande lokale accounts bij de eerste login koppelen op basis van een overeenkomend e-mailadres.',
+        secretHint: 'laat leeg om huidige waarde te behouden',
+        secretPlaceholder: 'nieuw geheim',
+        emailClaim: 'E-mailclaim',
+        emailClaimDesc: 'JWT-claim die als e-mailidentiteit wordt gebruikt. Gebruik \'preferred_username\' of \'upn\' voor Azure Entra ID (dat email_verified niet meestuurt). Gebruik alleen vertrouwde claimnamen.',
+        emailClaimPlaceholder: 'email',
+        emailClaimCustomClaimAutoLinkWarning: 'Aangepaste claims zijn alleen veilig voor automatische koppeling wanneer de waarde door de tenantbeheerder wordt beheerd (bijv. Azure Entra ID upn / preferred_username). Schakel automatisch koppelen niet in als je IdP gebruikers deze claim zelf laat opgeven.',
+        requireEmailVerified: 'Geverifieerd e-mailadres vereisen',
+        requireEmailVerifiedDesc: 'Accepteer de e-mailclaim alleen wanneer de provider deze als geverifieerd markeert.',
+        requireEmailVerifiedWarning: 'Waarschuwing: e-mail wordt ook zonder verificatie geaccepteerd. Gebruik dit alleen met vertrouwde providers.',
+        requireEmailVerifiedAutoLink: 'Schakel automatisch koppelen eerst uit om deze instelling te wijzigen.',
+        defaultGroup: 'Standaardgroep',
+        defaultGroupDesc: 'Groep die aan automatisch gemaakte gebruikers wordt toegewezen. Valt terug op Kijkers indien niet ingesteld.',
+        defaultGroupViewersFallback: 'Kijkers (standaard)',
+        autologin: 'Automatisch inloggen',
+        autologinDesc: 'Stuur niet-geauthenticeerde bezoekers rechtstreeks door naar deze provider. Slechts één provider kan deze vlag hebben.',
+      },
+    },
+
+    encryption: {
+      title: 'MFA-versleutelingsstatus',
+      enabledFromEnv: 'Versleuteling in opslag ingeschakeld (sleutel uit omgevingsvariabele MFA_ENCRYPTION_KEY)',
+      enabledFromFile: 'Versleuteling in opslag ingeschakeld (sleutel geladen uit gegevensmap)',
+      enabledGenerated: 'Versleuteling in opslag ingeschakeld met automatisch gegenereerde sleutel',
+      notConfigured: 'Versleuteling in opslag niet geconfigureerd',
+      notConfiguredDesc: 'TOTP-geheimen en OIDC client_secrets worden als platte tekst opgeslagen. Stel MFA_ENCRYPTION_KEY in of start Bambuddy opnieuw met een schrijfbare gegevensmap om automatisch een sleutel te genereren.',
+      allEncrypted: 'Alle MFA-geheimen zijn versleuteld opgeslagen.',
+      legacyRowsLabel: 'Verouderde rijen met platte tekst',
+      encryptedRowsLabel: 'Versleutelde rijen',
+      legacyRowsWarning: '{{count}} verouderde rij(en) met platte tekst gedetecteerd. Sla de OIDC-provider opnieuw op of registreer de authenticator-app van de gebruiker opnieuw om naar versleutelde opslag te migreren.',
+      backupHint: 'De automatisch gegenereerde sleutel wordt opgeslagen in DATA_DIR/.mfa_encryption_key en zit in lokale back-up-ZIP\'s. Bewaar je back-ups veilig of stel MFA_ENCRYPTION_KEY expliciet in.',
+      decryptionBrokenTitle: 'Versleutelingssleutel ontbreekt',
+      decryptionBrokenError: '{{count}} versleutelde record(s) kunnen niet worden ontsleuteld omdat de versleutelingssleutel niet meer beschikbaar is. Herstel de vorige MFA_ENCRYPTION_KEY of DATA_DIR/.mfa_encryption_key om te herstellen.',
+      migrationErrorWarning: '{{count}} verouderde rij(en) konden bij het opstarten niet opnieuw worden versleuteld. Controleer de serverlogboeken en start Bambuddy opnieuw om het opnieuw te proberen.',
+    },
+
+    // Slicer Pipeline limits (#1425 PR C). Admin-tunable cap that constrains
+    // the copies input in the Run-with-pipeline modal. Lives on the Workflow
+    // tab's Queue & Dispatch sub-tab.
+    pipelineLimits: {
+      title: 'Limieten slicerpipeline',
+      maxCopiesLabel: 'Max. kopieën per run',
+      maxCopiesDesc: 'Bovengrens voor het aantal kopieën dat operators kunnen aanvragen bij het uitvoeren van een pipeline. Harde serverlimiet is 1000.',
+    },
+
+    // Slicer Pipelines (#1425): list/edit/delete preset bundles users saved
+    // from the Slice dialog. Lives in Settings → Workflow → Pipelines sub-tab.
+    pipelines: {
+      title: 'Slicerpipelines',
+      subtitle: 'Herbruikbare bundels met presets (printer + proces + filamenten + bedtype). Sla er een op vanuit het Slice-dialoogvenster en pas deze met één klik toe op het volgende bestand.',
+      loading: 'Pipelines laden…',
+      loadError: 'Pipelines konden niet worden geladen.',
+      confirmDelete: 'Deze pipeline verwijderen? Dit kan niet ongedaan worden gemaakt.',
+      staleWarning: 'Een of meer gekoppelde presets bestaan niet meer. Sla deze pipeline opnieuw op vanuit het Slice-dialoogvenster om dit te herstellen.',
+      empty: {
+        title: 'Nog geen pipelines.',
+        howto: 'Open het Slice-dialoogvenster voor een bestand, kies printer / proces / filamenten / bedtype en klik daarna op "Opslaan als pipeline". Je opgeslagen pipelines verschijnen hier.',
+      },
+      field: {
+        name: 'Pipelinenaam',
+        description: 'Beschrijving',
+        targetPrinter: 'Doelprinter',
+        noTarget: '— Geen doel —',
+        // PR C
+        targetKind: 'Doeltype',
+        targetKindSpecific: 'Specifieke printer',
+        targetKindClass: 'Printerklasse',
+        targetModelClass: 'Printermodel',
+        fanoutStrategy: 'Verdelingsstrategie',
+        fanout: {
+          max_parallel: 'Maximaal parallel — verdelen over elke inactieve overeenkomende printer',
+          round_robin: 'Round robin — rouleren door geschikte printers',
+          fill_one_first: 'Eerst één vullen — alle kopieën aan één printer koppelen',
+        },
+        // Short labels for the inline target chip on each pipeline card.
+        // The verbose ones above explain the strategy in the editor; the
+        // card just needs a compact reminder of which one is in use.
+        fanoutShort: {
+          max_parallel: 'parallel',
+          round_robin: 'round robin',
+          fill_one_first: 'eerst één vullen',
+        },
+      },
+      action: {
+        save: 'Opslaan',
+        cancel: 'Annuleren',
+        rename: 'Naam wijzigen',
+        delete: 'Verwijderen',
+      },
+      slot: {
+        printer: 'Printer',
+        process: 'Proces',
+        filament: 'Filament',
+        filamentN: 'Filament {{n}}',
+        filamentAll: 'Alle {{n}} sleuven',
+        bed: 'Printbed',
+      },
+      // PR C polish — grouped sections in the card body, plus the
+      // panel-level search + filter row.
+      group: {
+        profiles: 'Profielen',
+        filaments: 'Filamenten',
+      },
+      searchPlaceholder: 'Pipelines zoeken…',
+      filterTargetType: 'Filteren op doeltype',
+      filterTarget: 'Filteren op doel',
+      filter: {
+        all: 'Alle doelen',
+        noTarget: 'Geen doel ingesteld',
+        count: '{{shown}} / {{total}}',
+        noMatches: 'Geen pipelines komen overeen met de huidige filters.',
+      },
+      toast: {
+        saved: 'Pipeline opgeslagen',
+        saveFailed: 'Opslaan mislukt',
+        deleted: 'Pipeline verwijderd',
+        deleteFailed: 'Verwijderen mislukt',
+      },
+      // PR B target binding + last-run summary
+      noTargetHint: 'Stel een doelprinter in om dit uit te voeren',
+      noTargetWarning: 'Stel een doelprinter in voordat je deze pipeline uitvoert.',
+      runs: {
+        lastRun: 'Laatste run',
+        status: {
+          queued: 'in wachtrij',
+          slicing: 'slicen',
+          dispatching: 'verzenden',
+          in_progress: 'bezig met afdrukken',
+          completed: 'voltooid',
+          failed: 'mislukt',
+          partial_failure: 'gedeeltelijke fout',
+          cancelled: 'geannuleerd',
+        },
+      },
+    },
+  },
+
+  // Notifications (for push notifications)
+  notification: {
+    printStarted: {
+      title: 'Afdruk gestart',
+      body: '{{printer}}: {{filename}} is begonnen met afdrukken',
+    },
+    printCompleted: {
+      title: 'Afdruk voltooid',
+      body: '{{printer}}: {{filename}} succesvol voltooid',
+    },
+    printFailed: {
+      title: 'Afdruk mislukt',
+      body: '{{printer}}: {{filename}} is mislukt',
+    },
+    printStopped: {
+      title: 'Afdruk gestopt',
+      body: '{{printer}}: {{filename}} is gestopt',
+    },
+    printProgress: {
+      title: 'Afdrukvoortgang',
+      body: '{{printer}}: {{filename}} is {{percent}}% voltooid',
+    },
+    printerOffline: {
+      title: 'Printer offline',
+      body: '{{printer}} is offline',
+    },
+    printerError: {
+      title: 'Printerfout',
+      body: '{{printer}}: {{error}}',
+    },
+    filamentLow: {
+      title: 'Weinig filament',
+      body: '{{printer}}: Filament raakt bijna op',
+    },
+    maintenanceDue: {
+      title: 'Onderhoud nodig',
+      body: '{{printer}}: {{items}} vereisen aandacht',
+    },
+  },
+
+  // Errors
+  errors: {
+    generic: 'Er is iets misgegaan',
+    networkError: 'Netwerkfout. Controleer je verbinding.',
+    notFound: 'Niet gevonden',
+    unauthorized: 'Niet geautoriseerd',
+    serverError: 'Serverfout',
+    validationError: 'Controleer je invoer',
+    printerConnectionFailed: 'Verbinding met printer mislukt',
+    saveFailed: 'Wijzigingen opslaan mislukt',
+    deleteFailed: 'Verwijderen mislukt',
+    loadFailed: 'Gegevens laden mislukt',
+  },
+
+  // HMS Errors modal
+  hmsErrors: {
+    title: 'Fouten - {{name}}',
+    noErrors: 'Geen fouten',
+    viewOnWiki: 'Bekijken op Bambu Lab Wiki',
+    mqttVerifyFailedRemedy: 'Schakel Developer Mode op de printer in, start de printer opnieuw en start daarna de taak opnieuw.',
+    unknownCode: 'Onbekende HMS-code — zie de Bambu Lab-wiki voor details.',
+    clearInstructions: 'Wis fouten op de printer om ze hier te verwijderen.',
+    clearErrors: 'Fouten wissen',
+    clearSuccess: 'HMS-fouten gewist',
+    clearFailed: 'HMS-fouten wissen mislukt',
+    actionSuccess: 'Actie naar printer verzonden',
+    actionFailed: 'Actie verzenden mislukt',
+    runoutExpectedSlot: 'Filament is op in {{ranOut}}. De printer wacht nu op compatibel filament in {{expected}}. Plaats een spoel in {{expected}} en selecteer daarna Opnieuw proberen.',
+    runoutExpectedSlotOnly: 'De printer wacht op compatibel filament in {{expected}}. Plaats daar een spoel en selecteer daarna Opnieuw proberen.',
+    runoutSlotUnknown: 'Filament is op en de afdruk is gepauzeerd. Bambuddy kon niet bepalen welke sleuf de printer nu verwacht — controleer het printerscherm voor de gevraagde sleuf.',
+    actions: {
+      RESUME_PRINTING: 'Afdrukken hervatten',
+      RESUME_PRINTING_DEFECTS: 'Hervatten (defecten acceptabel)',
+      RESUME_PRINTING_PROBELM_SOLVED: 'Hervatten (probleem opgelost)',
+      STOP_PRINTING: 'Afdrukken stoppen',
+      CHECK_ASSISTANT: 'Assistent controleren',
+      FILAMENT_EXTRUDED: 'Filament geëxtrudeerd, doorgaan',
+      RETRY_FILAMENT_EXTRUDED: 'Nog niet geëxtrudeerd, opnieuw proberen',
+      CONTINUE: 'Voltooid, doorgaan',
+      LOAD_VIRTUAL_TRAY: 'Filament laden',
+      OK_BUTTON: 'OK',
+      FILAMENT_LOAD_RESUME: 'Filament geladen, hervatten',
+      JUMP_TO_LIVEVIEW: 'Liveweergave bekijken',
+      NO_REMINDER_NEXT_TIME: 'Volgende keer niet herinneren',
+      REFRESH_NOZZLE: 'Opnieuw controleren',
+      IGNORE_NO_REMINDER_NEXT_TIME: 'Negeren. Volgende keer niet herinneren',
+      IGNORE_RESUME: 'Negeren en hervatten',
+      PROBLEM_SOLVED_RESUME: 'Probleem opgelost en hervatten',
+      TURN_OFF_FIRE_ALARM: 'Begrepen, brandalarm uitschakelen.',
+      RETRY_PROBLEM_SOLVED: 'Opnieuw proberen (probleem opgelost)',
+      CANCLE: 'Annuleren',
+      STOP_DRYING: 'Drogen stoppen',
+      PROCEED: 'Doorgaan',
+      OK_JUMP_RACK: 'OK',
+      ABORT: 'Afbreken',
+      DISABLE_PURIFICATION: 'Luchtreiniging voor deze afdruk uitschakelen',
+      DONT_REMIND_NEXT_TIME: 'Niet meer herinneren',
+      DBL_CHECK_CANCEL: 'Annuleren',
+      DBL_CHECK_DONE: 'Klaar',
+      DBL_CHECK_RETRY: 'Opnieuw proberen',
+      DBL_CHECK_RESUME: 'Hervatten',
+      DBL_CHECK_OK: 'Bevestigen',
+      REMOVE_CLOSE_BTN: 'Sluiten',
+    }
+  },
+
+  // MQTT Debug modal
+  mqttDebug: {
+    title: 'MQTT-debuglogboek',
+    searchPlaceholder: 'Topic of payload zoeken...',
+    noMessages: 'Nog geen berichten gelogd',
+    startLoggingHint: 'Klik op "Loggen starten" om MQTT-berichten vast te leggen',
+    noMessagesMatch: 'Geen berichten komen overeen met je filter',
+    adjustFilterHint: 'Probeer je zoekopdracht of filtercriteria aan te passen',
+    incoming: 'Inkomend',
+    outgoing: 'Uitgaand',
+    loggingStopped: 'Loggen gestopt',
+    loggingActive: 'Loggen actief - berichten worden automatisch vernieuwd',
+    startLogging: 'Loggen starten',
+    stopLogging: 'Loggen stoppen',
+    clearLog: 'Logboek wissen',
+    topic: 'Topic',
+    timestamp: 'Tijdstempel',
+    direction: 'Richting',
+    all: 'Alle',
+  },
+
+  // Printer File Manager modal (printer internal storage)
+  printerFiles: {
+    zipStarted_one: 'ZIP-download gestart voor {{count}} bestand',
+    zipStarted_other: 'ZIP-download gestart voor {{count}} bestanden',
+    zipPartial: 'ZIP-download gestart met {{successful}} van {{total}} bestanden; de overige konden niet worden opgehaald',
+    downloadFailed: 'Download mislukt: {{error}}',
+    unknownError: 'Onbekende fout',
+    shiftSelectHint: 'Shift-klik om een bereik te selecteren',
+    selectFile: '{{name}} selecteren',
+    deselectFile: '{{name}} deselecteren',
+    title: 'Bestandsbeheer',
+    storageUsed: 'Gebruikt:',
+    storageFree: 'Vrij:',
+    filterPlaceholder: 'Bestanden filteren...',
+    deleteButton: 'Verwijderen',
+    deleteFiles: '{{count}} bestanden verwijderen',
+    deleteFileConfirm: '"{{name}}" verwijderen? Dit kan niet ongedaan worden gemaakt.',
+    deleteFilesConfirm: '{{count}} geselecteerde bestanden verwijderen? Dit kan niet ongedaan worden gemaakt.',
+    noFiles: 'Geen bestanden op printer',
+    printerUnavailable: 'De bestandsservice van de printer is niet beschikbaar. Probeer het opnieuw wanneer de printer bereikbaar is.',
+    loadingFiles: 'Bestanden laden...',
+    failedToLoad: 'Bestanden laden mislukt',
+    toast: {
+      filesDeleted: '{{count}} bestand(en) verwijderd',
+      deleteFailed: 'Verwijderen mislukt: {{error}}',
+    },
+  },
+
+  // Confirmations
+  confirm: {
+    delete: 'Weet je zeker dat je dit wilt verwijderen?',
+    unsavedChanges: 'Je hebt niet-opgeslagen wijzigingen. Weet je zeker dat je wilt vertrekken?',
+    clearQueue: 'Weet je zeker dat je de wachtrij wilt wissen?',
+  },
+
+  // Login page
+  login: {
+    title: 'Bambuddy-login',
+    subtitle: 'Log in op je account',
+    username: 'Gebruikersnaam',
+    usernamePlaceholder: 'Voer je gebruikersnaam in',
+    usernameOrEmail: 'Gebruikersnaam of e-mail',
+    usernameOrEmailPlaceholder: 'Gebruikersnaam of @ e-mail',
+    password: 'Wachtwoord',
+    passwordPlaceholder: 'Voer je wachtwoord in',
+    signIn: 'Inloggen',
+    signingIn: 'Inloggen...',
+    rememberMe: 'Onthoud mij',
+    forgotPassword: 'Wachtwoord vergeten?',
+    autologinFailed: 'Automatisch inloggen via SSO is mislukt. Kies hieronder een provider om door te gaan.',
+    localDisabledNotice: 'Lokaal inloggen is uitgeschakeld. Gebruik een van de onderstaande SSO-providers.',
+    loginSuccess: 'Succesvol ingelogd',
+    loginFailed: 'Inloggen mislukt',
+    enterCredentials: 'Voer gebruikersnaam en wachtwoord in',
+    enterEmail: 'Voer je e-mailadres in',
+    oidcLoginFailed: 'OIDC-login mislukt',
+    oidcErrors: {
+      providerError: 'De identiteitsprovider heeft een fout geretourneerd',
+      missingParameters: 'OIDC-callback mist vereiste parameters',
+      invalidState: 'OIDC-status is ongeldig of is al gebruikt',
+      stateExpired: 'OIDC-inlogsessie verlopen — probeer het opnieuw',
+      providerNotFound: 'OIDC-provider niet gevonden',
+      discoveryFailed: 'OIDC-discoverydocument ophalen mislukt',
+      invalidDiscovery: 'OIDC-discoverydocument is ongeldig',
+      networkError: 'Netwerkfout tijdens OIDC-tokenuitwisseling',
+      badResponse: 'Onverwachte reactie tijdens OIDC-tokenuitwisseling',
+      noIdToken: 'OIDC-provider heeft geen ID-token geretourneerd',
+      validationFailed: 'OIDC-tokenvalidatie mislukt',
+      nonceMismatch: 'OIDC-nonce komt niet overeen — mogelijke replay-aanval',
+      missingSubClaim: 'OIDC-token mist de sub-claim',
+      noLinkedAccount: 'Er is geen lokaal account gekoppeld aan deze OIDC-identiteit',
+      accountInactive: 'Je account is inactief',
+      userResolutionFailed: 'Je account kon niet worden bepaald',
+      internalError: 'Er is een interne fout opgetreden tijdens OIDC-login',
+      tokenExchangeFailed: 'OIDC-tokenuitwisseling mislukt',
+    },
+    forgotPasswordTitle: 'Wachtwoord vergeten',
+    forgotPasswordMessage: 'Als je je wachtwoord bent vergeten, neem dan contact op met je systeembeheerder om het te laten resetten.',
+    forgotPasswordEmailMessage: 'Voer je e-mailadres in en we sturen je een nieuw wachtwoord.',
+    emailAddress: 'E-mailadres',
+    emailPlaceholder: 'jouw.email@voorbeeld.nl',
+    cancel: 'Annuleren',
+    sending: 'Verzenden...',
+    sendResetEmail: 'Resetmail verzenden',
+    howToReset: 'Zo reset je je wachtwoord:',
+    resetStep1: 'Neem contact op met je Bambuddy-beheerder',
+    resetStep2: 'Vraag om je wachtwoord te resetten in Gebruikersbeheer',
+    resetStep3: 'De beheerder kan een nieuw tijdelijk wachtwoord voor je instellen',
+    resetStep4: 'Log in met het nieuwe wachtwoord en wijzig het in Instellingen',
+    gotIt: 'Begrepen',
+    resetPassword: {
+      title: 'Nieuw wachtwoord instellen',
+      subtitle: 'Voer hieronder je nieuwe wachtwoord in en bevestig het.',
+      newPassword: 'Nieuw wachtwoord',
+      newPasswordPlaceholder: 'Minstens 8 tekens',
+      confirmPassword: 'Wachtwoord bevestigen',
+      confirmPasswordPlaceholder: 'Herhaal nieuw wachtwoord',
+      saving: 'Opslaan…',
+      submit: 'Nieuw wachtwoord instellen',
+      backToLogin: 'Terug naar login',
+      passwordsDoNotMatch: 'Wachtwoorden komen niet overeen',
+      passwordTooShort: 'Wachtwoord moet minstens 8 tekens bevatten',
+      resetFailed: 'Wachtwoord resetten mislukt. De link is mogelijk verlopen.',
+    },
+    twoFA: {
+      title: 'Tweefactorauthenticatie',
+      subtitle: 'Je account is beveiligd met 2FA. Voer hieronder de verificatiecode in.',
+      methodAuthenticator: 'Authenticator-app',
+      methodEmail: 'E-mailcode',
+      methodBackup: 'Back-upcode',
+      instructionsTotp: 'Open je authenticator-app en voer de 6-cijferige code voor Bambuddy in.',
+      instructionsEmail: 'Er is een 6-cijferige code naar je e-mailadres gestuurd. Deze verloopt over 10 minuten.',
+      instructionsEmailNotSent: 'Klik op de knop hieronder om een verificatiecode per e-mail te ontvangen.',
+      instructionsBackup: 'Voer een van je 8-teken lange back-upherstelcodes in. Elke code kan maar één keer worden gebruikt.',
+      sendCodeButton: 'Code per e-mail verzenden',
+      sendingCode: 'Verzenden...',
+      resendCode: 'Code opnieuw verzenden',
+      codeLabel: 'Verificatiecode',
+      backupCodeLabel: 'Back-upcode',
+      codePlaceholder: '000000',
+      backupCodePlaceholder: 'XXXXXXXX',
+      verifyButton: 'Verifiëren',
+      verifyingButton: 'Verifiëren...',
+      backToLogin: '← Terug naar login',
+      orContinueWith: 'of doorgaan met',
+      signInWith: 'Inloggen met {{provider}}',
+      enterCode: 'Voer de verificatiecode in',
+      sendCodeFailed: 'Verificatiecode verzenden mislukt',
+      invalidCode: 'Ongeldige code. Probeer het opnieuw.',
+    },
+
+  },
+
+  // Setup page
+  setup: {
+    title: 'Bambuddy-installatie',
+    subtitle: 'Authenticatie configureren voor je Bambuddy-instantie',
+    enableAuth: 'Authenticatie inschakelen',
+    adminAccount: 'Beheerdersaccount',
+    adminAccountDesc: 'Als er al beheerders bestaan, wordt authenticatie ingeschakeld met de bestaande beheerdersaccounts. Laat de onderstaande velden leeg om bestaande beheerders te gebruiken, of voer nieuwe gegevens in om een nieuwe beheerder te maken.',
+    adminUsername: 'Gebruikersnaam beheerder',
+    adminPassword: 'Wachtwoord beheerder',
+    optionalIfAdminExists: '(optioneel als er beheerders bestaan)',
+    adminUsernamePlaceholder: 'Voer gebruikersnaam beheerder in (optioneel)',
+    adminPasswordPlaceholder: 'Voer wachtwoord beheerder in (optioneel)',
+    confirmPassword: 'Wachtwoord bevestigen',
+    confirmPasswordPlaceholder: 'Bevestig wachtwoord beheerder',
+    settingUp: 'Instellen...',
+    completeSetup: 'Installatie voltooien',
+    toast: {
+      authEnabledAdminCreated: 'Authenticatie ingeschakeld en beheerdersgebruiker gemaakt',
+      authEnabledExistingAdmins: 'Authenticatie ingeschakeld met bestaande beheerders',
+      setupCompleted: 'Installatie voltooid',
+      enterBothCredentials: 'Voer zowel gebruikersnaam als wachtwoord van de beheerder in, of laat beide leeg om bestaande beheerders te gebruiken',
+      passwordsDoNotMatch: 'Wachtwoorden komen niet overeen',
+      passwordTooShort: 'Wachtwoord moet minstens 6 tekens bevatten',
+    },
+  },
+
+  // Password change
+  changePassword: {
+    title: 'Wachtwoord wijzigen',
+    currentPassword: 'Huidig wachtwoord',
+    currentPasswordPlaceholder: 'Voer huidig wachtwoord in',
+    newPassword: 'Nieuw wachtwoord',
+    newPasswordPlaceholder: 'Voer nieuw wachtwoord in (min. 6 tekens)',
+    confirmPassword: 'Nieuw wachtwoord bevestigen',
+    confirmPasswordPlaceholder: 'Bevestig nieuw wachtwoord',
+    passwordsDoNotMatch: 'Wachtwoorden komen niet overeen',
+    passwordTooShort: 'Wachtwoord moet minstens 6 tekens bevatten',
+    changing: 'Wijzigen...',
+    success: 'Wachtwoord succesvol gewijzigd',
+    failed: 'Wachtwoord wijzigen mislukt',
+  },
+
+  // Plate detection alert
+  plateAlert: {
+    title: 'Afdruk gepauzeerd!',
+    message: 'Objecten gedetecteerd op de bouwplaat. De afdruk is automatisch gepauzeerd. Maak de plaat vrij en hervat de afdruk.',
+    understand: 'Ik begrijp het',
+  },
+
+  // Camera page
+  camera: {
+    title: 'Cameraweergave',
+    invalidPrinterId: 'Ongeldige printer-ID',
+    live: 'Live',
+    snapshot: 'Momentopname',
+    restartStream: 'Stream opnieuw starten',
+    refreshSnapshot: 'Momentopname vernieuwen',
+    fullscreen: 'Volledig scherm',
+    exitFullscreen: 'Volledig scherm afsluiten',
+    connectingToCamera: 'Verbinding maken met camera...',
+    capturingSnapshot: 'Momentopname vastleggen...',
+    connectionLost: 'Verbinding verbroken',
+    connectionFailed: 'Cameraverbinding mislukt',
+    reconnecting: 'Opnieuw verbinden over {{countdown}}s... (poging {{attempt}}/{{max}})',
+    reconnectNow: 'Nu opnieuw verbinden',
+    cameraUnavailable: 'Camera niet beschikbaar',
+    cameraUnavailableDesc: 'Zorg dat de printer is ingeschakeld en verbonden.',
+    noCamera: 'Geen camera beschikbaar',
+    retry: 'Opnieuw proberen',
+    cameraStream: 'Camerastream',
+    zoomOut: 'Uitzoomen',
+    zoomIn: 'Inzoomen',
+    resetZoom: 'Zoom resetten',
+    recording: 'Opnemen',
+    startRecording: 'Opname starten',
+    stopRecording: 'Opname stoppen',
+    chamberLight: 'Kamerverlichting omschakelen',
+    unavailable: 'Camera niet beschikbaar',
+    diagnose: {
+      button: 'Diagnose',
+      modalTitle: 'Cameradiagnose',
+      running: 'Diagnose uitvoeren...',
+      runFailed: 'Diagnose kon niet worden uitgevoerd: {{error}}',
+      retry: 'Opnieuw uitvoeren',
+      stage: {
+        tcp_reachable: 'Netwerkbereikbaarheid',
+        first_frame: 'Frame vastleggen',
+        live_stream_active: 'Livestream actief',
+      },
+      summary: {
+        all_ok: 'Camera werkt. De diagnose heeft alle stappen succesvol voltooid.',
+        live_stream_active_healthy: 'Camera streamt momenteel met recente frames — geen test nodig.',
+        printer_unreachable: 'Printer is niet bereikbaar. Controleer het IP-adres, de netwerkverbinding en of de printer is ingeschakeld.',
+        camera_port_closed: 'Printer is bereikbaar maar de camerapoort is gesloten. Zorg dat LAN-only-modus en Developer Mode zijn ingeschakeld in de printerinstellingen.',
+        no_frame: 'Verbonden met de camera, maar er zijn geen frames ontvangen. Probeer opnieuw of controleer of de camera is ingeschakeld in de printerinstellingen.',
+        unknown_failure: 'Cameradiagnose is om onbekende reden mislukt. Controleer het supportlogboek voor details.',
+      },
+      meta: {
+        protocol: 'Protocol',
+        port: 'Poort',
+        profile: 'Profiel',
+      },
+    },
+  },
+
+  // Groups management
+  groups: {
+    title: 'Groepsbeheer',
+    subtitle: 'Machtigingsgroepen beheren voor toegangscontrole',
+    backToSettings: 'Terug naar Instellingen',
+    createGroup: 'Groep maken',
+    noPermission: 'Je hebt geen toestemming om deze pagina te openen.',
+    system: 'Systeem',
+    noDescription: 'Geen beschrijving',
+    usersCount: '{{count}} gebruikers',
+    permissionsCount: '{{count}} machtigingen',
+    edit: 'Bewerken',
+    delete: 'Verwijderen',
+    toast: {
+      created: 'Groep succesvol gemaakt',
+      updated: 'Groep succesvol bijgewerkt',
+      deleted: 'Groep succesvol verwijderd',
+      enterGroupName: 'Voer een groepsnaam in',
+    },
+    modal: {
+      editGroup: 'Groep bewerken',
+      createGroup: 'Groep maken',
+      cancel: 'Annuleren',
+      saving: 'Opslaan...',
+      creating: 'Maken...',
+      saveChanges: 'Wijzigingen opslaan',
+    },
+    form: {
+      groupName: 'Groepsnaam',
+      groupNamePlaceholder: 'Voer groepsnaam in',
+      systemGroupWarning: 'Namen van systeemgroepen kunnen niet worden gewijzigd',
+      description: 'Beschrijving',
+      descriptionPlaceholder: 'Voer beschrijving in (optioneel)',
+      permissions: 'Machtigingen ({{count}} geselecteerd)',
+    },
+    deleteModal: {
+      title: 'Groep verwijderen',
+      message: 'Weet je zeker dat je deze groep wilt verwijderen? Gebruikers in deze groep verliezen deze machtigingen.',
+      confirm: 'Groep verwijderen',
+    },
+    editor: {
+      title: 'Groep bewerken',
+      createTitle: 'Groep maken',
+      search: 'Machtigingen zoeken...',
+      selectAll: 'Alles selecteren',
+      clearAll: 'Alles wissen',
+      permissionsSelected: '{{count}} geselecteerd',
+      noResults: 'Geen machtigingen komen overeen met je zoekopdracht',
+      websocketHint: 'Vereist voor live-updates. Zonder dit valt de interface terug op periodieke polling.',
+    },
+  },
+
+  // Users management
+  users: {
+    title: 'Gebruikersbeheer',
+    subtitle: 'Gebruikers en hun toegang tot je Bambuddy-instantie beheren',
+    backToSettings: 'Terug naar Instellingen',
+    createUser: 'Gebruiker maken',
+    noPermission: 'Je hebt geen toestemming om deze pagina te openen.',
+    admin: 'Beheerder',
+    noGroups: 'Geen groepen',
+    active: 'Actief',
+    inactive: 'Inactief',
+    edit: 'Bewerken',
+    delete: 'Verwijderen',
+    system: 'Systeem',
+    noGroupsAvailable: 'Geen groepen beschikbaar',
+    table: {
+      username: 'Gebruikersnaam',
+      groups: 'Groepen',
+      status: 'Status',
+      actions: 'Acties',
+    },
+    toast: {
+      created: 'Gebruiker succesvol gemaakt',
+      updated: 'Gebruiker succesvol bijgewerkt',
+      deleted: 'Gebruiker succesvol verwijderd',
+      fillRequired: 'Vul alle verplichte velden in',
+      passwordsDoNotMatch: 'Wachtwoorden komen niet overeen',
+      passwordTooShort: 'Wachtwoord moet minstens 6 tekens bevatten',
+      ldapProvisioned: 'LDAP-gebruiker "{{username}}" ingericht',
+    },
+    modal: {
+      createUser: 'Gebruiker maken',
+      editUser: 'Gebruiker bewerken',
+      cancel: 'Annuleren',
+      creating: 'Maken...',
+      saving: 'Opslaan...',
+      saveChanges: 'Wijzigingen opslaan',
+      advancedAuthSubtitle: 'met Geavanceerde authenticatie',
+      // LDAP manual provisioning (#1298)
+      tabsAriaLabel: 'Gebruikersbron',
+      localTab: 'Lokaal',
+      ldapTab: 'LDAP',
+      ldapSearchLabel: 'Directory zoeken',
+      ldapSearchPlaceholder: 'Typ een gebruikersnaam, naam of e-mailadres...',
+      ldapMinChars: 'Typ minstens 2 tekens om te zoeken',
+      ldapTypeToSearch: 'Begin te typen om in de LDAP-directory te zoeken',
+      ldapSearching: 'Directory doorzoeken...',
+      ldapNoResults: 'Geen overeenkomende gebruikers in de directory',
+      ldapSearchError: 'Zoeken in directory mislukt. Controleer de status van de LDAP-server.',
+      ldapAlreadyProvisioned: 'Al ingericht',
+      ldapSelectedLabel: 'Geselecteerd',
+      ldapProvision: 'Gebruiker inrichten',
+      ldapProvisioning: 'Inrichten...',
+      ldapErrorProvision: 'Inrichten mislukt. Controleer de status van de LDAP-server en probeer opnieuw.',
+    },
+    form: {
+      username: 'Gebruikersnaam',
+      usernamePlaceholder: 'Voer gebruikersnaam in',
+      email: 'E-mail',
+      emailPlaceholder: 'user@example.com',
+      password: 'Wachtwoord',
+      passwordPlaceholder: 'Voer wachtwoord in',
+      confirmPassword: 'Wachtwoord bevestigen',
+      confirmPasswordPlaceholder: 'Bevestig wachtwoord',
+      newPasswordPlaceholder: 'Voer nieuw wachtwoord in',
+      confirmNewPasswordPlaceholder: 'Bevestig nieuw wachtwoord',
+      leaveBlankToKeep: 'laat leeg om huidige waarde te behouden',
+      groups: 'Groepen',
+      optional: 'optioneel',
+      autoGeneratedPassword: 'Een veilig wachtwoord wordt automatisch gegenereerd en per e-mail naar de gebruiker gestuurd.',
+      passwordManagedByAdvancedAuth: 'Wachtwoord wordt beheerd door Geavanceerde authenticatie. Gebruik "Wachtwoord resetten" om via e-mail een nieuw wachtwoord naar de gebruiker te sturen.',
+      resetPassword: 'Wachtwoord resetten',
+      resettingPassword: 'Wachtwoord resetten...',
+    },
+    deleteModal: {
+      title: 'Gebruiker verwijderen',
+      message: 'Weet je zeker dat je deze gebruiker wilt verwijderen? Deze actie kan niet ongedaan worden gemaakt.',
+      confirm: 'Gebruiker verwijderen',
+    },
+  },
+
+  // Stream overlay
+  streamOverlay: {
+    title: 'Streamoverlay',
+    invalidPrinterId: 'Ongeldige printer-ID',
+    cameraStream: 'Camerastream',
+    progress: 'Voortgang',
+    eta: 'ETA',
+    printerIdle: 'Printer is inactief',
+    printerOffline: 'Printer offline',
+    builder: {
+      title: 'Streamingoverlay',
+      description: 'Bouw de URL voor een streamingoverlay — een camerabeeld op volledig scherm met live afdrukgegevens eroverheen, voor OBS, een wanddisplay of elke browserbron. Kies de velden die je wilt en kopieer de URL.',
+      printer: 'Printer',
+      size: 'Tekstgrootte',
+      sizeSmall: 'Klein',
+      sizeMedium: 'Middelgroot',
+      sizeLarge: 'Groot',
+      fps: 'Framesnelheid',
+      fpsHint: 'A1- en P1-camera\'s halen maximaal ongeveer 5 fps, ongeacht wat je instelt.',
+      token: 'Token voor streamingoverlay (optioneel)',
+      tokenHint: 'Alleen nodig wanneer login is ingeschakeld: OBS heeft geen eigen sessie. Maak hierboven een token met het bereik Streamingoverlay.',
+      tokenWarning: 'Deze URL bevat een token — iedereen die hem kan lezen kan de stream bekijken en de bestandsnaam zien. Trek het token in om de toegang te stoppen.',
+      fields: 'Velden om te tonen',
+      fieldPrinter: 'Printernaam',
+      fieldFilename: 'Bestandsnaam',
+      fieldStatus: 'Status',
+      fieldProgress: 'Voortgangsbalk',
+      fieldLayers: 'Aantal lagen',
+      fieldEta: 'Resterende tijd en ETA',
+      fieldCamera: 'Camerabeeld',
+      chamberHint: 'Kamertemperatuur verschijnt alleen op modellen met een echte kamersensor — P1- en A1-printers melden een betekenisloze waarde, daarom wordt die daar weggelaten.',
+      urlTitle: 'Overlay-URL',
+      open: 'Open',
+      showPreview: 'Voorbeeld tonen',
+      hidePreview: 'Voorbeeld verbergen',
+      previewTitle: 'Overlayvoorbeeld',
+    },
+    status: {
+      printing: 'Bezig met afdrukken',
+      paused: 'Gepauzeerd',
+      finished: 'Voltooid',
+      failed: 'Mislukt',
+      idle: 'Inactief',
+      unknown: 'Onbekend',
+    },
+  },
+
+  // Profiles
+  profiles: {
+    title: 'Profielen',
+    subtitle: 'Beheer je slicerpresets en pressure-advance-kalibraties',
+    tabs: {
+      bambuCloud: 'Bambu Cloud',
+      orcaCloud: 'Orca Cloud',
+      local: 'Lokale profielen',
+      kprofiles: 'K-profielen',
+    },
+    orcaCloud: {
+      connectedAs: 'Verbonden als',
+      connectedShort: 'Verbonden met Orca Cloud',
+      logout: 'Verbinding verbreken',
+      noLogoutPermission: 'Je hebt geen toestemming om de verbinding te verbreken',
+      noConnectPermission: 'Je hebt geen toestemming om verbinding te maken met Orca Cloud',
+      retry: 'Opnieuw proberen',
+      connectButton: 'Orca Cloud verbinden',
+      connect: {
+        title: 'Verbinden met Orca Cloud',
+        description: 'Log in op je Orca Cloud-account om je slicerprofielen met Bambuddy te synchroniseren.',
+      },
+      device: {
+        title: 'Bambuddy goedkeuren in Orca Cloud',
+        instruction: 'Open Orca Cloud en keur deze code goed. Bambuddy maakt automatisch verbinding zodra je goedkeurt.',
+        codeLabel: 'Je koppelcode',
+        openButton: 'Orca Cloud-goedkeuringspagina openen',
+        manualHint: 'Of ga naar {{url}} en voer bovenstaande code in.',
+        waiting: 'Wachten op je goedkeuring…',
+        cancel: 'Annuleren',
+      },
+      profiles: {
+        title: 'Je Orca Cloud-profielen ({{count}})',
+        refresh: 'Vernieuwen',
+        empty: 'Nog geen profielen gevonden in je Orca Cloud-account.',
+      },
+      toast: {
+        disconnected: 'Verbinding met Orca Cloud verbroken',
+      },
+      errors: {
+        startFailed: 'Koppelen met Orca Cloud kon niet worden gestart.',
+        denied: 'De koppeling is geweigerd in Orca Cloud.',
+        expired: 'De koppelcode is verlopen. Klik op Verbinden om opnieuw te proberen.',
+        pollFailed: 'De verbinding ging verloren tijdens het wachten op goedkeuring. Probeer het opnieuw.',
+      },
+    },
+    localProfiles: {
+      title: 'Lokale profielen',
+      subtitle: 'Slicepresets importeren en beheren vanuit OrcaSlicer',
+      import: 'Profielen importeren',
+      importDesc: 'Sleep .bbscfg-, .bbsflmt-, .orca_filament-, .zip- of .json-bestanden hierheen',
+      importing: 'Importeren...',
+      search: 'Lokale presets zoeken...',
+      noPresets: 'Nog geen lokale presets',
+      noSearchResults: 'Geen presets komen overeen met je zoekopdracht',
+      badge: 'Lokaal',
+      edit: 'Bewerken',
+      delete: 'Verwijderen',
+      cancel: 'Annuleren',
+      deleteConfirmTitle: 'Preset verwijderen',
+      deleteConfirm: 'Weet je zeker dat je deze preset wilt verwijderen? Dit kan niet ongedaan worden gemaakt.',
+      source: 'Bron',
+      inheritsFrom: 'Erft van',
+      filamentType: 'Type',
+      vendor: 'Leverancier',
+      compatiblePrinters: 'Printers',
+      nozzleTemp: 'Nozzletemp.',
+      cost: 'Kosten',
+      density: 'Dichtheid',
+      pressureAdvance: 'Pressure Advance',
+      filament: 'Filament',
+      process: 'Proces',
+      printer: 'Printer',
+      toast: {
+        importSuccess: '{{count}} preset(s) geïmporteerd',
+        importSkipped: '{{count}} preset(s) overgeslagen (duplicaten)',
+        importError: '{{count}} fout(en) tijdens importeren',
+        deleted: 'Preset verwijderd',
+        updated: 'Preset bijgewerkt',
+      },
+    },
+    connectedAs: 'Verbonden als',
+    signInExpiredTitle: 'Bambu Cloud-login verlopen',
+    signInExpiredBody: 'Bambu Lab accepteert het opgeslagen token niet meer. Log opnieuw in om cloudprofielen, MakerWorld-imports en firmwarecontroles te herstellen.',
+    logout: 'Uitloggen',
+    noLogoutPermission: 'Je hebt geen toestemming om uit te loggen',
+    failedToLoad: 'Profielen laden mislukt',
+    retry: 'Opnieuw proberen',
+    time: {
+      justNow: 'Zojuist',
+      minsAgo: '{{count}}m geleden',
+      hoursAgo: '{{count}}u geleden',
+      daysAgo: '{{count}}d geleden',
+    },
+    toast: {
+      loggedOut: 'Uitgelogd',
+    },
+    login: {
+      title: 'Verbinden met Bambu Cloud',
+      subtitle: 'Synchroniseer je slicerpresets tussen apparaten',
+      email: 'E-mail',
+      password: 'Wachtwoord',
+      region: 'Regio',
+      regionGlobal: 'Wereldwijd',
+      regionChina: 'China',
+      verificationCode: 'Verificatiecode',
+      totpCode: 'Authenticatorcode',
+      checkEmail: 'Controleer je e-mail ({{email}}) op een 6-cijferige code',
+      enterTotpHint: 'Voer de 6-cijferige code uit je authenticator-app in',
+      accessToken: 'Toegangstoken',
+      accessTokenHint: 'Plak je Bambu Cloud-toegangstoken. Accounts in de China-regio moeten deze methode gebruiken (telefoongebonden — inloggen via e-mail niet beschikbaar). Zie de wiki voor het ophalen van het token uit MakerWorld-cookies.',
+      back: 'Terug',
+      loginButton: 'Inloggen',
+      verifyButton: 'Verifiëren',
+      setTokenButton: 'Token instellen',
+      useToken: 'In plaats daarvan toegangstoken gebruiken',
+      captchaTitle: 'Bambu Cloud vraagt om een CAPTCHA',
+      captchaBody: 'Bambu controleert je netwerk voordat inloggen wordt geaccepteerd en deze controle kan niet vanuit Bambuddy worden beantwoord. Je e-mailadres en wachtwoord zijn niet het probleem. De blokkering is gekoppeld aan je openbare IP-adres en verdwijnt normaal vanzelf binnen enkele uren — herhaaldelijk opnieuw proberen laat de blokkering langer duren. Gebruik om nu in te loggen een toegangstoken uit een browsersessie.',
+      useEmail: 'In plaats daarvan inloggen met e-mail',
+      toast: {
+        loggedIn: 'Succesvol ingelogd',
+        codeSent: 'Verificatiecode naar je e-mail gestuurd',
+        enterTotp: 'Voer code uit je authenticator-app in',
+        tokenSet: 'Token succesvol ingesteld',
+      },
+    },
+    presets: {
+      myPreset: 'Mijn preset (bewerkbaar)',
+      duplicate: 'Dupliceren',
+      editable: 'Bewerkbaar',
+      failedToLoadDetails: 'Presetdetails laden mislukt',
+      deleteConfirm: 'Deze preset verwijderen?',
+      deleteWarning: 'Hiermee wordt "{{name}}" permanent uit Bambu Cloud verwijderd. Dit kan niet ongedaan worden gemaakt.',
+      noDuplicatePermission: 'Je hebt geen toestemming om presets te dupliceren',
+      noEditPermission: 'Je hebt geen toestemming om presets te bewerken',
+      noDeletePermission: 'Je hebt geen toestemming om presets te verwijderen',
+      types: {
+        filament: 'Filamentpreset',
+        printer: 'Printerpreset',
+        process: 'Procespreset',
+      },
+      toast: {
+        deleted: 'Preset verwijderd',
+        created: 'Preset gemaakt',
+        updated: 'Preset bijgewerkt',
+        duplicated: 'Preset gedupliceerd',
+        fieldAdded: 'Veld "{{key}}" toegevoegd',
+        exported: 'Preset geëxporteerd',
+      },
+      baseLabel: 'Basis: {{name}}',
+      currentLabel: 'Huidig: {{name}}',
+      newPreset: 'Nieuwe preset',
+      editPreset: 'Preset bewerken',
+      duplicatePreset: 'Preset dupliceren',
+      createNewPreset: 'Nieuwe preset maken',
+      customizeSettings: 'Instellingen voor je nieuwe preset aanpassen',
+      compareWithBase: 'Vergelijken met basispreset',
+      compare: 'Vergelijken',
+      // CreatePresetModal - Basic Info
+      basePreset: 'Basispreset',
+      selectBasePreset: 'Basispreset selecteren...',
+      presetName: 'Presetnaam',
+      myCustomPreset: 'Mijn aangepaste preset',
+      inheritsFrom: 'Erft van',
+      dropJsonToImport: 'Sleep JSON om te importeren',
+      // CreatePresetModal - Tabs
+      tabs: {
+        common: 'Algemeen',
+        allFields: 'Alle velden',
+      },
+      // CreatePresetModal - All Fields Tab
+      availableFields: 'Beschikbare velden',
+      searchFieldsPlaceholder: 'Velden zoeken...',
+      noMatchingFields: 'Geen overeenkomende velden',
+      allFieldsAdded: 'Alle velden toegevoegd',
+      addCustomField: 'Aangepast veld toevoegen',
+      yourOverrides: 'Je overschrijvingen',
+      noOverridesYet: 'Nog geen overschrijvingen',
+      clickFieldsToAdd: 'Klik links op velden om ze toe te voegen',
+      saveAsTemplate: 'Opslaan als sjabloon',
+      jsonTip: 'Tip: sleep een .json-bestand ergens in dit venster om instellingen te importeren',
+    },
+    cloudView: {
+      searchPlaceholder: 'Presets zoeken...',
+      templates: 'Sjablonen',
+      refresh: 'Vernieuwen',
+      newPreset: 'Nieuwe preset',
+      clearFilters: 'Filters wissen',
+      // Compare mode
+      compareMode: 'Vergelijkingsmodus',
+      selectAnotherPreset: 'Selecteer een andere {{type}}-preset',
+      clickTwoPresets: 'Klik op twee presets van hetzelfde type om te vergelijken',
+      selectFirst: '1. Selecteer eerste',
+      selectSecond: '2. Selecteer tweede',
+      compareNow: 'Nu vergelijken',
+      // Status row
+      lastSynced: 'Laatst gesynchroniseerd:',
+      showingCount: '{{showing}} van {{total}} presets weergegeven',
+      noPresetsFound: 'Geen presets gevonden',
+      // Column headers
+      columns: {
+        filament: 'Filament',
+        process: 'Proces',
+        printer: 'Printer',
+      },
+      noFilamentPresets: 'Geen filamentpresets',
+      noProcessPresets: 'Geen procespresets',
+      noPrinterPresets: 'Geen printerpresets',
+      // Filters
+      filters: {
+        type: 'Type',
+        owner: 'Eigenaar',
+        printer: 'Printer',
+        nozzle: 'Nozzle',
+        filament: 'Filament',
+        layer: 'Laag',
+        all: 'Alle',
+        myPresets: 'Mijn presets',
+        builtIn: 'Ingebouwd',
+        process: 'Proces',
+      },
+      // Permissions
+      noTemplatesPermission: 'Je hebt geen toestemming om sjablonen te beheren',
+      noRefreshPermission: 'Je hebt geen toestemming om profielen te vernieuwen',
+      noCreatePermission: 'Je hebt geen toestemming om presets te maken',
+    },
+    templates: {
+      title: 'Snelle sjablonen',
+      noTemplates: 'Nog geen sjablonen',
+      createFirst: 'Maak sjablonen vanuit de preseteditor',
+      typeFilter: 'Type:',
+      deleteTitle: 'Sjabloon verwijderen',
+      deleteWarning: 'Deze actie kan niet ongedaan worden gemaakt',
+      deleteConfirm: 'Weet je zeker dat je "{{name}}" wilt verwijderen?',
+      namePlaceholder: 'Sjabloonnaam',
+      descriptionPlaceholder: 'Beschrijving',
+      settingsJson: 'Instellingen (JSON)',
+      fieldsCount: '{{count}} velden',
+      shownInModals: 'Getoond in vensters',
+      hiddenInModals: 'Verborgen in vensters',
+      apply: 'Toepassen',
+      toast: {
+        deleted: 'Sjabloon verwijderd',
+        updated: 'Sjabloon bijgewerkt',
+        created: 'Sjabloon gemaakt',
+        applied: 'Sjabloon toegepast',
+      },
+    },
+  },
+
+  // Support/Debug
+  support: {
+    debugLoggingActive: 'Debuglogboekregistratie is actief',
+    manageLogs: 'Beheren',
+    collectItem7: 'Printerverbinding en firmwareversies',
+    collectItem8: 'Integratiestatus (Spoolman, MQTT, HA)',
+    collectItem9: 'Netwerkinterfaces (alleen subnetten)',
+    collectItem10: 'Python-pakketversies',
+    collectItem11: 'Databasegezondheidscontroles',
+    collectItem12: 'Docker-omgevingsdetails',
+    bundleGenerating: 'Bundel genereren...',
+    bundleStepConnection: 'Printerverbindingscontroles uitvoeren',
+    bundleStepVirtualPrinters: 'Installatiecontroles virtuele printer uitvoeren',
+    bundleStepLogScan: 'Recente logboeken scannen op bekende problemen',
+    bundleStepBuild: 'Supportbundel-ZIP maken',
+  },
+
+  // File manager
+  fileManager: {
+    variants: {
+      badge: '{{count}} versies',
+      groupAction: 'Als versies groeperen',
+      groupTooltip: 'Markeer deze bestanden als dezelfde taak, gesliced voor verschillende printers',
+      grouped: '{{count}} bestanden als versies gegroepeerd',
+      printAlternatives: 'Afdrukken ({{count}} alternatieven)',
+    },
+    title: 'Bestandsbeheer',
+    subtitle: 'Organiseer en beheer je afdrukbestanden',
+    uploadFiles: 'Bestanden uploaden',
+    newFolder: 'Nieuwe map',
+    folderName: 'Mapnaam',
+    folderNamePlaceholder: 'bijv. Functionele onderdelen',
+    renameFile: 'Bestand hernoemen',
+    renameFolder: 'Map hernoemen',
+    invalidFilenameChar: 'Het teken "{{char}}" is niet toegestaan in bestandsnamen voor afdrukken. De SD-kaart van de printer weigert: < > : " / \\ | ? *',
+    moveFiles: '{{count}} bestand(en) verplaatsen',
+    rootNoFolder: 'Hoofdmap (geen map)',
+    current: 'huidig',
+    linkFolder: 'Map koppelen',
+    linkFolderDescription: 'Koppel "{{name}}" aan een project of archief voor snelle toegang.',
+    project: 'Project',
+    archive: 'Archief',
+    noProjectsFound: 'Geen projecten gevonden',
+    noArchivesFound: 'Geen archieven gevonden',
+    unlink: 'Ontkoppelen',
+    link: 'Koppelen',
+    dragDropFiles: 'Sleep bestanden hierheen',
+    dropFilesHere: 'Zet bestanden hier neer',
+    releaseToUpload: 'Loslaten om te uploaden',
+    orClickToBrowse: 'of klik om te bladeren',
+    allFileTypesSupported: 'Alle bestandstypen worden ondersteund. ZIP-bestanden worden uitgepakt.',
+    zipFilesDetected: 'ZIP-bestanden gedetecteerd',
+    zipExtractOptions: 'ZIP-bestanden worden uitgepakt. Kies hoe de mapstructuur moet worden verwerkt:',
+    preserveZipStructure: 'Mapstructuur uit ZIP behouden',
+    createFolderFromZip: 'Map maken op basis van ZIP-bestandsnaam',
+    stlThumbnailGeneration: 'STL-miniaturen genereren',
+    zipMayContainStl: 'ZIP-bestanden kunnen STL-bestanden bevatten. Miniaturen kunnen tijdens het uitpakken worden gegenereerd.',
+    thumbnailsCanBeGenerated: 'Voor STL-bestanden kunnen miniaturen worden gegenereerd. Grote modellen kunnen langer duren om te verwerken.',
+    generateThumbnailsForStl: 'Miniaturen genereren voor STL-bestanden',
+    threemfDetected: '3MF-bestanden gedetecteerd',
+    threemfExtractionInfo: 'Printermodel, materiaal, kleur en afdrukinstellingen worden automatisch uit 3MF-bestanden gehaald.',
+    willBeExtracted: 'Wordt uitgepakt',
+    filesExtracted: '{{count}} bestanden uitgepakt',
+    uploadComplete: 'Upload voltooid: {{succeeded}} geslaagd',
+    uploadFailed: 'Upload mislukt',
+    zipFilesFailed: '{{count}} bestanden mislukt',
+    uploading: 'Uploaden...',
+    changeLink: 'Koppeling wijzigen...',
+    linkTo: 'Koppelen aan...',
+    linkToProjectOrArchive: 'Koppelen aan project of archief',
+    generateThumbnail: 'Miniatuur genereren',
+    generateThumbnails: 'Miniaturen genereren',
+    generateThumbnailsForMissing: 'Miniaturen genereren voor STL-bestanden die ze missen',
+    gridView: 'Rasterweergave',
+    listView: 'Lijstweergave',
+    lowDiskSpaceWarning: 'Waarschuwing weinig schijfruimte',
+    lowDiskSpaceDetails: 'Slechts {{free}} vrij van {{total}} totaal. De drempel is in de instellingen ingesteld op {{threshold}} GB.',
+    files: 'Bestanden',
+    folders: 'Mappen',
+    size: 'Grootte',
+    free: 'Vrij',
+    allFiles: 'Alle bestanden',
+    allExternal: 'Extern',
+    externalIsEmpty: 'Geen externe bestanden',
+    externalEmptyDescription: 'Bestanden in je gekoppelde externe mappen verschijnen hier.',
+    wrap: 'Regelafbreking',
+    enableTextWrapping: 'Tekstterugloop inschakelen',
+    disableTextWrapping: 'Tekstterugloop uitschakelen',
+    collapse: 'Inklappen',
+    collapseFoldersByDefault: 'Mappen standaard inklappen',
+    expandFoldersByDefault: 'Mappen standaard uitklappen',
+    folderSort: 'Mappen sorteren',
+    folderSortByName: 'Op naam',
+    folderSortByActivity: 'Op recente activiteit',
+    dragToResizeTooltip: 'Sleep om grootte te wijzigen, dubbelklik om te resetten',
+    searchFiles: 'Bestanden zoeken...',
+    searchSubfoldersHint: 'Inclusief submappen',
+    readme: {
+      truncated: 'Afgekapt',
+      show: 'README tonen',
+      hide: 'README verbergen',
+      label: 'README',
+    },
+    tags: {
+      title: 'Tags',
+      subtitle: 'Label bestanden voor flexibele filtering — speelgoed, kindveilig, alleen PETG, wat je maar wilt.',
+      manage: 'Tags',
+      manageTitle: 'Tagcatalogus beheren',
+      add: 'Nieuwe tag',
+      edit: 'Tag hernoemen',
+      name: 'Naam',
+      fileCount: 'Bestanden',
+      empty: 'Nog geen tags. Maak er een om bestanden te gaan labelen.',
+      noMatches: 'Geen overeenkomende tags.',
+      createPlaceholder: 'bijv. speelgoed, kindveilig, petg',
+      createButton: 'Maken',
+      nameRequired: 'Naam is verplicht.',
+      searchPlaceholder: 'Tags filteren...',
+      created: 'Tag gemaakt.',
+      updated: 'Tag hernoemd.',
+      deleted: 'Tag verwijderd.',
+      saveFailed: 'Tag kon niet worden opgeslagen.',
+      deleteFailed: 'Tag kon niet worden verwijderd.',
+      applyFailed: 'Tags konden niet worden toegepast.',
+      applyAdd: 'Tags toevoegen',
+      applyRemove: 'Tags verwijderen',
+      applyAddSuccess: '{{count}} tag(s) toegevoegd aan {{files}} bestand(en).',
+      applyRemoveSuccess: '{{count}} tag(s) verwijderd van {{files}} bestand(en).',
+      actionAdd: 'Toevoegen aan geselecteerde bestanden',
+      actionRemove: 'Verwijderen van geselecteerde bestanden',
+      tagAction: 'Tag',
+      bulkTitle: '{{count}} geselecteerde bestand(en) taggen',
+      bulkTooltip: 'Tags toevoegen aan of verwijderen van elk geselecteerd bestand.',
+      noPermission: 'Je hebt geen toestemming om bestanden te taggen.',
+      filterLabel: 'Filteren op:',
+      clearAll: 'Alles wissen',
+      confirmDelete: 'Tag "{{name}}" verwijderen?',
+      confirmDeleteMessage: 'Hiermee wordt de tag uit de catalogus verwijderd. Bestanden behouden hun andere tags.',
+      confirmDeleteInUseMessage: 'Deze tag staat op {{count}} bestand(en). Bij verwijderen verdwijnt het label van al deze bestanden; de bestanden zelf blijven intact.',
+      editAria: '{{name}} bewerken',
+      deleteAria: '{{name}} verwijderen',
+    },
+    allTypes: 'Alle typen',
+    prints: 'Afdrukken',
+    ascending: 'Oplopend',
+    descending: 'Aflopend',
+    showModified: 'Wijzigingsdatums tonen',
+    hideModified: 'Wijzigingsdatums verbergen',
+    lastModified: 'Laatst gewijzigd',
+    lastActivity: 'Laatste activiteit',
+    resultsCount: '{{showing}} van {{total}} bestanden',
+    selectAll: 'Alles selecteren',
+    deselectAll: 'Alles deselecteren',
+    selected: '{{count}} geselecteerd',
+    adding: 'Toevoegen...',
+    loadingFiles: 'Bestanden laden...',
+    folderIsEmpty: 'Map is leeg',
+    noFilesYet: 'Nog geen bestanden',
+    folderEmptyDescription: 'Upload bestanden of verplaats bestanden naar deze map om te beginnen.',
+    noFilesDescription: 'Upload bestanden om je afdrukgerelateerde bestanden te organiseren.',
+    noMatchingFiles: 'Geen overeenkomende bestanden',
+    noMatchingFilesDescription: 'Geen bestanden komen overeen met je huidige zoek- of filtercriteria.',
+    clearFilters: 'Filters wissen',
+    printedCount: '{{count}}x afgedrukt',
+    uploadedBy: 'Geüpload door',
+    deleteFolder: 'Map verwijderen',
+    deleteFile: 'Bestand verwijderen',
+    deleteFilesCount: '{{count}} bestanden verwijderen',
+    deleteFolderConfirm: 'Weet je zeker dat je deze map wilt verwijderen? Alle bestanden erin worden ook verwijderd.',
+    deleteFileConfirm: 'Weet je zeker dat je dit bestand wilt verwijderen?',
+    deleteFilesConfirm: 'Weet je zeker dat je {{count}} geselecteerde bestanden wilt verwijderen? Deze actie kan niet ongedaan worden gemaakt.',
+    deleting: 'Verwijderen...',
+    noPermissionRenameFolder: 'Je hebt geen toestemming om mappen te hernoemen',
+    noPermissionLinkFolder: 'Je hebt geen toestemming om mappen te koppelen',
+    noPermissionDeleteFolder: 'Je hebt geen toestemming om mappen te verwijderen',
+    onlyEmptyFoldersDeletable: 'Je kunt alleen lege mappen verwijderen',
+    noPermissionPrint: 'Je hebt geen toestemming om af te drukken',
+    noPermissionAddToQueue: 'Je hebt geen toestemming om aan de wachtrij toe te voegen',
+    noPermissionSlice: 'Je hebt geen toestemming om bestanden te slicen',
+    noPermissionDownload: 'Je hebt geen toestemming om bestanden te downloaden',
+    noPermissionPreview: 'Je hebt geen toestemming om bestanden te bekijken',
+    preview3d: '3D-voorbeeld',
+    noPermissionRenameFile: 'Je hebt geen toestemming om dit bestand te hernoemen',
+    noPermissionGenerateThumbnail: 'Je hebt geen toestemming om miniaturen te genereren',
+    noPermissionDeleteFile: 'Je hebt geen toestemming om dit bestand te verwijderen',
+    noPermissionCreateFolder: 'Je hebt geen toestemming om mappen te maken',
+    noPermissionUpload: 'Je hebt geen toestemming om bestanden te uploaden',
+    noPermissionMoveFiles: 'Je hebt geen toestemming om bestanden te verplaatsen',
+    noPermissionDeleteFiles: 'Je hebt geen toestemming om bestanden te verwijderen',
+    // External folder
+    linkExternal: 'Extern koppelen',
+    linkExternalFolder: 'Externe map koppelen',
+    linkExternalFolderDescription: 'Koppel een hostmap (NAS, USB, netwerkshare) aan Bestandsbeheer. Bestanden worden niet gekopieerd — ze worden rechtstreeks vanaf het oorspronkelijke pad benaderd.',
+    externalFolderNamePlaceholder: 'bijv. NAS-afdrukken',
+    externalPath: 'Hostpad',
+    externalPathHelp: 'Absoluut pad naar de map op de Docker-host. Moet als bind-mount in de container zijn gekoppeld.',
+    readOnly: 'Alleen-lezen',
+    readOnlyHelp: 'voorkomt uploads en verwijderingen',
+    showHiddenFiles: 'Verborgen bestanden tonen (dotfiles)',
+    externalFolder: 'Externe map',
+    scanFolder: 'Scannen',
+    toast: {
+      folderCreated: 'Map gemaakt',
+      openInSlicerFailed: 'Kon niet openen in slicer',
+      folderDeleted: 'Map verwijderd',
+      fileDeleted: 'Bestand verwijderd',
+      filesDeleted: '{{count}} bestanden verwijderd',
+      filesMoved: 'Bestanden verplaatst',
+      folderLinked: 'Map gekoppeld',
+      folderUnlinked: 'Map ontkoppeld',
+      externalFolderLinked: 'Externe map gekoppeld en gescand',
+      folderScanned: 'Scan voltooid: {{added}} toegevoegd, {{removed}} verwijderd',
+      addedToQueue: '{{count}} bestand(en) aan wachtrij toegevoegd',
+      addedToQueuePartial: '{{added}} bestand(en) toegevoegd, {{failed}} mislukt',
+      failedToAddToQueue: 'Bestanden toevoegen mislukt: {{error}}',
+      fileRenamed: 'Bestand hernoemd',
+      folderRenamed: 'Map hernoemd',
+      thumbnailsGenerated: '{{count}} miniatuur/miniaturen gegenereerd',
+      thumbnailsGeneratedPartial: '{{succeeded}} miniatuur/miniaturen gegenereerd, {{failed}} mislukt',
+      noStlMissingThumbnails: 'Geen STL-bestanden zonder miniaturen',
+      failedToGenerateThumbnails: 'Miniaturen genereren mislukt: {{error}}',
+      thumbnailGenerated: 'Miniatuur gegenereerd',
+      failedToGenerateThumbnail: 'Miniatuur genereren mislukt: {{error}}',
+    },
+  },
+
+  // Projects
+  projects: {
+    parentLabel: 'Bovenliggend project',
+    parentNone: 'Geen (project op hoogste niveau)',
+    parentHint: 'Plaats dit project onder een ander project zodat de cijfers worden opgeteld in een hoofdproject',
+    partOf: 'Onderdeel van {{name}}',
+    subProjectCount: '{{count}} subprojecten',
+    subProjectsOf: 'Subprojecten van {{name}}',
+    title: 'Projecten',
+    subtitle: 'Organiseer en volg je 3D-printprojecten',
+    newProject: 'Nieuw project',
+    editProject: 'Project bewerken',
+    deleteProject: 'Project verwijderen',
+    projectName: 'Projectnaam',
+    description: 'Beschrijving',
+    noProjects: 'Nog geen projecten',
+    noProjectsFiltered: 'Geen {{status}} projecten',
+    noProjectsFilteredHelp: 'Je hebt geen {{status}} projecten. Projecten verschijnen hier wanneer hun status verandert.',
+    createFirst: 'Maak je eerste project om gerelateerde afdrukken te organiseren, voortgang bij te houden en je builds te beheren.',
+    createFirstButton: 'Maak je eerste project',
+    create: 'Maken',
+    files: 'Bestanden',
+    prints: 'Afdrukken',
+    plates: 'platen',
+    parts: 'onderdelen',
+    lastModified: 'Laatst gewijzigd',
+    deleteConfirm: 'Weet je zeker dat je dit project wilt verwijderen? Archieven en wachtrij-items worden ontkoppeld maar niet verwijderd.',
+    addFiles: 'Bestanden toevoegen',
+    removeFile: 'Bestand verwijderen',
+    viewDetails: 'Details bekijken',
+    // Modal fields
+    namePlaceholder: 'bijv. Voron 2.4 Build',
+    descriptionPlaceholder: 'Optionele beschrijving...',
+    urlLabel: 'URL',
+    urlPlaceholder: 'https://makerworld.com/...',
+    urlInvalid: 'URL moet beginnen met http:// of https://',
+    openExternalUrl: 'Project-URL openen',
+    coverImageLabel: 'Omslagfoto',
+    coverImageAlt: 'Projectomslagfoto',
+    coverImageUpload: 'Uploaden',
+    coverImageReplace: 'Vervangen',
+    coverImageRemove: 'Verwijderen',
+    color: 'Kleur',
+    targetPlates: 'Doelaantal platen',
+    targetPlatesPlaceholder: 'bijv. 25',
+    targetPlatesHelp: 'Aantal afdruktaken',
+    targetParts: 'Doelaantal onderdelen',
+    targetPartsPlaceholder: 'bijv. 150',
+    targetPartsHelp: 'Totaal aantal benodigde objecten',
+    targetSets: 'Kopieën per bestand',
+    targetSetsPlaceholder: 'bijv. 10',
+    targetSetsHelp: 'Aantal keren dat elk afdrukbaar bestand moet worden afgedrukt',
+    tagsLabel: 'Tags (door komma\'s gescheiden)',
+    tagsPlaceholder: 'bijv. voron, functioneel, cadeau',
+    dueDate: 'Einddatum',
+    priority: 'Prioriteit',
+    priorityLow: 'Laag',
+    priorityNormal: 'Normaal',
+    priorityHigh: 'Hoog',
+    priorityUrgent: 'Urgent',
+    // Status
+    statusActive: 'Actief',
+    statusCompleted: 'Voltooid',
+    statusArchived: 'Gearchiveerd',
+    done: 'Klaar',
+    completed: 'voltooid',
+    failed: 'mislukt',
+    inQueue: 'in wachtrij',
+    noPrintsYet: 'Nog geen afdrukken',
+    // Footer stats
+    printJobs: 'Afdruktaken (platen)',
+    partsPrinted: 'Afgedrukte onderdelen',
+    failedParts: 'Mislukte onderdelen',
+    // Actions
+    import: 'Importeren',
+    export: 'Exporteren',
+    importProject: 'Project importeren',
+    exportAll: 'Alle projecten exporteren',
+    loading: 'Projecten laden...',
+    // Permissions
+    noEditPermission: 'Je hebt geen toestemming om projecten te bewerken',
+    noDeletePermission: 'Je hebt geen toestemming om projecten te verwijderen',
+    noCreatePermission: 'Je hebt geen toestemming om projecten te maken',
+    noImportPermission: 'Je hebt geen toestemming om projecten te importeren',
+    noExportPermission: 'Je hebt geen toestemming om projecten te exporteren',
+    // Toast
+    toast: {
+      created: 'Project gemaakt',
+      updated: 'Project bijgewerkt',
+      deleted: 'Project verwijderd',
+      imported: 'Project geïmporteerd',
+      multipleImported: '{{count}} projecten geïmporteerd',
+      importFailed: 'Importeren mislukt',
+      exported: 'Projecten geëxporteerd (alleen metadata)',
+    },
+  },
+
+  // Project detail page
+  projectDetail: {
+    notFound: 'Project niet gevonden',
+    backToProjects: 'Terug naar Projecten',
+    export: 'Exporteren',
+    exportProject: 'Project exporteren',
+    noExportPermission: 'Je hebt geen toestemming om projecten te exporteren',
+    noEditPermission: 'Je hebt geen toestemming om projecten te bewerken',
+    partOf: 'Onderdeel van:',
+    priorityLabel: 'Prioriteit:',
+    noPrints: 'Nog geen afdrukken in dit project',
+    status: {
+      active: 'Actief',
+      completed: 'Voltooid',
+      archived: 'Gearchiveerd',
+    },
+    priority: {
+      low: 'Laag',
+      normal: 'Normaal',
+      high: 'Hoog',
+      urgent: 'Urgent',
+    },
+    dueDate: {
+      overdue: 'Te laat',
+      today: 'Vandaag te doen',
+      daysLeft: '{{count}} dagen resterend',
+    },
+    progress: {
+      platesProgress: 'Voortgang platen',
+      partsProgress: 'Voortgang onderdelen',
+      setsProgress: 'Volledige sets',
+      sets: 'sets',
+      setsHint: 'Een set is compleet zodra elk afdrukbaar bestand dit aantal heeft bereikt',
+      printJobs: 'afdruktaken',
+      parts: 'onderdelen',
+      percentComplete: '{{percent}}% voltooid',
+      remaining: '{{count}} resterend',
+    },
+    stats: {
+      printJobs: 'Afdruktaken',
+      total: 'totaal',
+      failed: '{{count}} mislukt',
+      partsPrinted: '{{count}} onderdelen afgedrukt',
+      printTime: 'Afdruktijd',
+      filamentUsed: 'Filament gebruikt',
+    },
+    cost: {
+      title: 'Kostentracking',
+      filamentCost: 'Filamentkosten',
+      energy: 'Energie',
+      totalCost: 'Geschatte kosten',
+      total: 'Totaal',
+      includesBom: 'incl. materiaallijst',
+      budget: 'Budget',
+      remaining: 'Resterend',
+    },
+    subProjects: {
+      title: 'Subprojecten ({{count}})',
+      jobs: '{{count}} taken',
+    },
+    rollup: {
+      title: 'Inclusief {{count}} subprojecten',
+      progress: 'Totale voortgang',
+      percentComplete: '{{percent}}% voltooid',
+    },
+    notes: {
+      title: 'Notities',
+      noEditPermission: 'Je hebt geen toestemming om notities te bewerken',
+      placeholder: 'Voeg notities over dit project toe...',
+      empty: 'Nog geen notities. Klik op Bewerken om notities toe te voegen.',
+    },
+    files: {
+      title: 'Bestanden',
+      linkFolders: 'Koppel mappen vanuit Bestandsbeheer',
+      forQuickAccess: 'aan dit project voor snelle toegang.',
+      fileCount: '{{count}} bestand(en)',
+      empty: 'Geen mappen gekoppeld. Ga naar Bestandsbeheer en koppel een map aan dit project.',
+      printedCount: '{{count}} keer afgedrukt',
+      progressTooltip: '{{done}} van {{target}} voltooide afdrukken',
+      noFiles: 'Geen bestanden in deze map.',
+    },
+    bom: {
+      title: 'Materiaallijst',
+      acquired: '{{completed}}/{{total}} aangeschaft',
+      showAll: 'Alles tonen',
+      hideDone: 'Voltooide verbergen',
+      addPart: 'Onderdeel toevoegen',
+      noAddPermission: 'Je hebt geen toestemming om onderdelen toe te voegen',
+      partNamePlaceholder: 'Onderdeelnaam (bijv. M3x8-schroeven)',
+      partName: 'Onderdeelnaam',
+      qty: 'Aantal',
+      price: 'Prijs ({{currency}})',
+      sourcingUrlPlaceholder: 'Inkoop-URL (optioneel)',
+      remarksPlaceholder: 'Opmerkingen (optioneel)',
+      deletePart: 'Onderdeel verwijderen',
+      deleteConfirm: 'Weet je zeker dat je "{{name}}" wilt verwijderen?',
+      noUpdatePermission: 'Je hebt geen toestemming om onderdelen bij te werken',
+      noEditPermission: 'Je hebt geen toestemming om onderdelen te bewerken',
+      noDeletePermission: 'Je hebt geen toestemming om onderdelen te verwijderen',
+      totalCost: 'Totale kosten:',
+      empty: 'Geen onderdelen in de materiaallijst. Voeg hardware, elektronica of andere componenten toe om bij te houden wat nog moet worden ingekocht.',
+    },
+    timeline: {
+      title: 'Activiteitentijdlijn',
+      empty: 'Nog geen activiteit.',
+    },
+    template: {
+      saveAsTemplate: 'Opslaan als sjabloon',
+      noCreatePermission: 'Je hebt geen toestemming om sjablonen te maken',
+    },
+    queue: {
+      title: 'Wachtrij',
+      viewAll: 'Alles bekijken',
+      printing: '{{count}} bezig met afdrukken',
+      queued: '{{count}} in wachtrij',
+    },
+    prints: {
+      title: 'Afdrukken ({{count}})',
+    },
+    toast: {
+      projectUpdated: 'Project bijgewerkt',
+      partAdded: 'Onderdeel toegevoegd',
+      partRemoved: 'Onderdeel verwijderd',
+      exportFailed: 'Export mislukt',
+      projectExported: 'Project geëxporteerd',
+      templateCreated: 'Sjabloon gemaakt',
+    },
+  },
+
+  // System info
+  system: {
+    title: 'Systeeminformatie',
+    version: 'Versie',
+    uptime: 'Uptime',
+    cpuUsage: 'CPU-gebruik',
+    memoryUsage: 'Geheugengebruik',
+    diskUsage: 'Schijfgebruik',
+    networkInfo: 'Netwerkinformatie',
+    logs: 'Logboeken',
+    debugMode: 'Debugmodus',
+    enableDebug: 'Debuglogboekregistratie inschakelen',
+    disableDebug: 'Debuglogboekregistratie uitschakelen',
+    downloadLogs: 'Logboeken downloaden',
+    clearLogs: 'Logboeken wissen',
+    dockerInfo: 'Docker-informatie',
+    containerName: 'Containernaam',
+    imageName: 'Imagenaam',
+    platform: 'Platform',
+    architecture: 'Architectuur',
+  },
+
+  // Sponsor surface (System page)
+  sponsors: {
+    sectionTitle: 'Onafhankelijk en door de community gefinancierd',
+    tagline: 'Bambuddy is gratis en blijft dat omdat mensen ervoor kiezen het te ondersteunen. Geen durfkapitaal, geen cloud-lock-in.',
+    viewSupporters: 'Supporters bekijken',
+    toastPrints: 'Je hebt {{count}} afdrukken voltooid met Bambuddy. Bambuddy blijft gratis dankzij zijn supporters.',
+    toastCost: 'Je hebt {{total}} aan filament bijgehouden met Bambuddy. Bekijk wie het project onafhankelijk houdt.',
+    toastArchives: '{{count}} afdrukken gearchiveerd met Bambuddy. Bekijk wie het onafhankelijk houdt.',
+    toastAnniversary: 'Eén jaar met Bambuddy! Bekijk wie het project onafhankelijk houdt.',
+    toastVersionUpdate: 'Bijgewerkt naar v{{version}}. Bambuddy blijft gratis dankzij zijn supporters.',
+    toastBusiness: 'Gebruik je Bambuddy met {{count}} printers? Er is een supportplan voor teams — prioriteitsoplossingen, facturering en direct contact met de beheerder.',
+    businessCta: 'Bambuddy voor bedrijven',
+    businessTitle: 'Bambuddy voor bedrijven',
+    businessTagline: 'Je gebruikt {{count}} printers. Prioriteitssupport, commerciële licenties en facturering zijn beschikbaar voor teams en printfarms.',
+  },
+
+  // Library (K Profiles)
+  library: {
+    title: 'Filamentbibliotheek',
+    addFilament: 'Filament toevoegen',
+    editFilament: 'Filament bewerken',
+    deleteFilament: 'Filament verwijderen',
+    vendor: 'Leverancier',
+    material: 'Materiaal',
+    color: 'Kleur',
+    kFactor: 'K-factor',
+    temperature: 'Temperatuur',
+    noFilaments: 'Geen filamenten in bibliotheek',
+    deleteConfirm: 'Weet je zeker dat je dit filament wilt verwijderen?',
+    importFromPrinter: 'Importeren vanaf printer',
+    exportToFile: 'Exporteren naar bestand',
+    // Slicer Pipelines run from the File Manager (#1425 PR B). The Run-with-pipeline
+    // modal is a two-step dialog: pick a pipeline, then either fire (eligibility ok)
+    // or confirm-and-fire (eligibility report shown). Lives in components/RunWithPipelineModal.tsx.
+    runWithPipeline: {
+      actionLabel: 'Uitvoeren met pipeline',
+      noPermission: 'Je hebt geen toestemming om pipelines uit te voeren',
+      modalTitle: 'Uitvoeren met pipeline',
+      confirmTitle: 'Run bevestigen',
+      confirmIntro: 'Pre-flight heeft problemen met deze run gevonden',
+      sourceHint: 'Bron',
+      pipelineHint: 'Pipeline',
+      targetHint: 'Doel',
+      pipelineListAria: 'Beschikbare pipelines',
+      runAnyway: 'Toch uitvoeren',
+      loading: 'Laden…',
+      empty: 'Nog geen pipelines opgeslagen. Open het Slice-dialoogvenster en klik op "Opslaan als pipeline" om er een te maken.',
+      noTarget: 'Geen doelprinter ingesteld',
+      noTargetMessage: 'Deze pipeline heeft geen doelprinter ingesteld. Open hem in Instellingen om er een te kiezen.',
+      // PR C — copies input + class-targeted pipelines.
+      copies: 'Kopieën',
+      copiesHint: 'max. {{n}}',
+      classTarget: 'Elke {{model}}',
+      toast: {
+        started: 'Pipeline-run gestart',
+        failed: 'Run starten mislukt',
+      },
+      issue: {
+        printerNotSet: 'Geen doelprinter ingesteld voor deze pipeline.',
+        printerNotFound: 'Doelprinter bestaat niet meer.',
+        printerDisabled: 'Doelprinter is uitgeschakeld.',
+        printerOffline: 'Doelprinter is offline.',
+        filamentType: 'Filamentsleuf {{slot}}: verwacht {{expected}}, AMS heeft {{actual}}',
+        filamentColor: 'Filamentsleuf {{slot}}: kleur wijkt af (verwacht {{expected}}, AMS heeft {{actual}})',
+        amsSlotMissing: 'AMS-sleuf {{slot}} is niet beschikbaar op deze printer',
+        filamentUnverified: 'Filamentsleuf {{slot}} komt uit een cloud-/standaardpreset en kon niet statisch worden geverifieerd.',
+        // PR C — class targeting
+        noClassMatches: 'Geen printers in deze installatie komen overeen met de doelmodelklasse van de pipeline ({{expected}}).',
+        classNotSet: 'Pipelinedoel is ingesteld op een printerklasse, maar er is geen model gekozen.',
+      },
+    },
+  },
+
+  // Slice (slicer-API integration via SliceModal)
+  slicerSettings: {
+    presetValuesOutdatedSidecar: 'Slicerstandaarden weergegeven: je slicer-sidecar is ouder dan deze functie en kan de waarden van een preset niet rapporteren. Werk de sidecar-image bij om ze te zien. Alles wat je niet wijzigt blijft de preset gebruiken.',
+    presetValuesNotConfigured: 'Slicerstandaarden weergegeven: er is geen slicer-sidecar geconfigureerd, dus de waarden van een preset kunnen niet worden gelezen. Alles wat je niet wijzigt blijft de preset gebruiken.',
+    presetValuesSidecarUnavailable: 'Slicerstandaarden weergegeven: de slicer-sidecar antwoordde niet, dus de waarden van een preset kunnen niet worden gelezen. Alles wat je niet wijzigt blijft de preset gebruiken.',
+    presetValuesUnavailable: 'Slicerstandaarden weergegeven: de eigen waarden van de gekozen preset konden niet worden gelezen. Alles wat je niet wijzigt blijft de preset gebruiken.',
+    filamentDefault: 'Standaard',
+    fromFile: 'uit bestand',
+    fromFileHint: 'De ontwerper heeft dit in het bronbestand gewijzigd. De waarde is {{value}}.',
+    fromFilePrinterCoupled: 'printer van ontwerper',
+    fromFilePrinterCoupledHint: 'Afgestemd op de printer waarvoor dit bestand is ontworpen -- kan op jouw printer verkeerd of buiten bereik zijn.',
+    fromFileOverridesPreset: 'overschrijft preset',
+    fromFileOverridesPresetHint:
+      'Het bestand stelt dit in op {{value}}, terwijl de gekozen preset {{preset}} gebruikt. Vink dit alleen aan als het bestand voorrang moet krijgen.',
+    useFromFile: 'Waarde uit het bronbestand gebruiken voor {{option}}',
+    otherFromFile: 'Andere instellingen uit dit bestand',
+    fromFileSummary: 'De ontwerper heeft {{count}} procesinstellingen in dit bestand gewijzigd. Alleen de instellingen die je aanvinkt worden gebruikt.',
+    fromFileUseAll: 'Instellingen van de ontwerper gebruiken',
+    fromFileUseAllHint: 'Vinkt de instellingen aan die tussen printers overdraagbaar zijn. Instellingen die zijn afgestemd op de eigen printer van de ontwerper en instellingen die de gekozen preset definiëren blijven uitgeschakeld.',
+    fromFileClear: '{{count}} wissen',
+    loading: 'Slicerinstellingen laden…',
+    mode: {
+      simple: 'Eenvoudig',
+      advanced: 'Geavanceerd',
+      expert: 'Expert',
+    },
+    searchPlaceholder: 'Instellingen zoeken',
+    resetAll: '{{count}} resetten',
+    resetOption: 'Terugzetten naar standaard',
+    noMatches: 'Geen instellingen komen overeen met deze zoekopdracht.',
+  },
+  slice: {
+    filamentSlotUnset: 'niet ingesteld',
+    processSettingsEmbedded: 'Niet gebruikt zolang "Ingebouwde instellingen van het bestand gebruiken" is ingeschakeld -- de eigen instellingen van het bestand sturen deze slice aan.',
+    processSettingsInactive: 'Inactief',
+    presetsHidden: '{{count}} verborgen',
+    showAllPresets: 'Alles tonen',
+    showFewerPresets: 'Minder tonen',
+    processSettings: 'Procesinstellingen',
+    processSettingsHint: 'Pas de gekozen preset aan voor deze slice. Alles wat je niet aanraakt blijft zoals de preset het definieert.',
+    processSettingsChanged: '{{count}} gewijzigd',
+    processSettingsUnchanged: 'Presetstandaarden',
+    title: 'Model slicen',
+    action: 'Slicen',
+    actionAll: 'Alle {{count}} platen slicen',
+    actionAllTitle: 'Slice elke plaat naar één uitvoer met meerdere platen (één archief). Filamentselectie omvat elke sleuf die het project definieert.',
+    allPlatesToggle: 'Alle {{count}} platen slicen',
+    slicing: 'Slicen…',
+    printer: 'Printerprofiel',
+    process: 'Procesprofiel',
+    filament: 'Filamentprofiel',
+    filamentSlot: 'Filament {{index}} ({{type}})',
+    selectPreset: '— Selecteer een preset —',
+    loadingPresets: 'Presets laden…',
+    analyzingPlateFilaments: 'Filamenten op plaat analyseren…',
+    analyzingPlateFilamentsHint: 'Een voorbeeldslice uitvoeren om te ontdekken welke AMS-sleuven deze plaat gebruikt. Daarna gecachet — opnieuw openen is direct.',
+    previewToast: '{{name}} analyseren — {{elapsed}}',
+    previewWithProgress: '{{name}} analyseren — {{stage}} ({{percent}}%) — {{elapsed}}',
+    notUsedByPlate: '— niet gebruikt door deze plaat',
+    noPresetsForSlot: 'Geen presets beschikbaar',
+    otherPrinters: 'Andere printers',
+    presetsLoadFailed: 'Presets laden mislukt. Open eerst Instellingen → Profielen om ze te importeren.',
+    refreshPresets: 'Vernieuwen',
+    refreshPresetsTitle: 'Presets vernieuwen — haal de nieuwste cloud- en meegeleverde lijsten op (gebruik na het verwijderen van een preset in Bambu Studio of Bambu Handy)',
+    allPresetsRequired: 'Alle presets moeten worden geselecteerd',
+    useEmbedded: 'Ingebouwde instellingen van het bestand gebruiken',
+    useEmbeddedHint: 'Slice zoals de ontwerper het heeft ingesteld (wanden, infill, filament) in plaats van met bovenstaande profielen. Beschikbaar omdat je printer overeenkomt met die van het bestand.',
+    autoOrient: 'Objecten automatisch oriënteren',
+    autoOrientHint: 'Laat de slicer elk object eerst naar de beste afdrukzijde draaien. Overschrijft de oriëntatie uit het bestand.',
+    autoArrange: 'Automatisch op de plaat rangschikken',
+    autoArrangeHint: 'Laat de slicer de objecten positioneren zodat ze niet meer overlappen. Vervangt de indeling uit het bestand.',
+    enqueuing: 'Slicetaak indienen…',
+    queued: 'In wachtrij…',
+    failed: 'Slicen mislukt. Controleer de logboeken van de slicer-sidecar.',
+    startedToast: '{{name}} op de achtergrond slicen…',
+    queuedToast: 'In wachtrij: {{name}} — {{elapsed}}',
+    runningToast: '{{name}} slicen — {{elapsed}}',
+    runningWithProgress: '{{name}} — {{stage}} ({{percent}}%) — {{elapsed}}',
+    runningWithProgressMultiPlate: 'Plaat {{plateIndex}} van {{plateCount}} • {{name}} — {{stage}} ({{percent}}%) — {{elapsed}}',
+    completedToast: '{{name}} gesliced',
+    externalWriteFallbackToast: 'Opgeslagen in de interne bibliotheek: er kon niet naar de externe map worden geschreven',
+    failedTitle: 'Slicen mislukt',
+    failedToast: 'Slicen van {{name}} mislukt: {{detail}}',
+    tier: {
+      local: 'Geïmporteerd',
+      cloud: 'Bambu Cloud',
+      orcaCloud: 'Orca Cloud',
+      standard: 'Standaard',
+    },
+    cloud: {
+      notAuthenticated: 'Log in op Bambu Cloud (Instellingen → Profielen → Bambu Cloud) om je cloudpresets te zien.',
+      expired: 'Bambu Cloud-sessie verlopen — log opnieuw in om je cloudpresets te vernieuwen.',
+      unreachable: 'Bambu Cloud is momenteel niet bereikbaar. Lokale en standaardpresets blijven werken.',
+    },
+    orcaCloud: {
+      notAuthenticated: 'Log in op Orca Cloud (Profielen → Orca Cloud) om je Orca-presets te zien.',
+      expired: 'Orca Cloud-sessie verlopen — log opnieuw in om je Orca-presets te vernieuwen.',
+      unreachable: 'Orca Cloud is momenteel niet bereikbaar. Andere presets blijven werken.',
+    },
+    bedType: {
+      label: 'Bouwplaat',
+      auto: 'Auto (procespreset gebruiken)',
+      coolPlate: 'Cool Plate',
+      coolPlateSuperTack: 'Cool Plate SuperTack',
+      engineering: 'Engineering Plate',
+      highTemp: 'High Temp Plate',
+      texturedPEI: 'Textured PEI Plate',
+      smoothPEI: 'Smooth PEI Plate',
+    },
+    // Slicer Pipelines (#1425) — apply a saved bundle or save the current pick.
+    pipelines: {
+      label: 'Pipeline',
+      applyAria: 'Pipeline toepassen',
+      applyPrompt: 'Pipeline toepassen…',
+      empty: 'Geen opgeslagen pipelines',
+      saveButton: 'Opslaan als pipeline',
+      saveTitle: 'Sla de huidige selectie van vier sleuven op als herbruikbare pipeline',
+      namePlaceholder: 'Pipelinenaam',
+      nameAria: 'Nieuwe pipelinenaam',
+      toast: {
+        applied: '"{{name}}" toegepast',
+        saved: 'Pipeline opgeslagen',
+        saveFailed: 'Opslaan mislukt',
+      },
+    },
+  },
+
+  // Spoolman
+  spoolman: {
+    title: 'Spoolman-integratie',
+    enabled: 'Spoolman ingeschakeld',
+    url: 'Spoolman-URL',
+    connected: 'Verbonden',
+    disconnected: 'Niet verbonden',
+    testConnection: 'Verbinding testen',
+    sync: 'Synchroniseren',
+    syncing: 'Synchroniseren...',
+    lastSync: 'Laatste synchronisatie',
+    linkToSpoolman: 'Koppelen aan Spoolman',
+    openInSpoolman: 'Openen in Spoolman',
+    unlinkSpool: 'Spoel ontkoppelen',
+    unlinkConfirmTitle: 'Spoeltoewijzing verwijderen?',
+    unlinkConfirmMessage: 'Hiermee wordt de spoel uit deze sleuf verwijderd. De spoelgegevens zelf blijven ongewijzigd.',
+    selectSpool: 'Spoel selecteren',
+    noUnlinkedSpools: 'Geen niet-toegewezen spoelen beschikbaar',
+    linkSuccess: 'Spoel succesvol toegewezen',
+    linkFailed: 'Spoel toewijzen mislukt',
+    unlinkSuccess: 'Spoeltoewijzing succesvol verwijderd',
+    unlinkFailed: 'Spoeltoewijzing verwijderen mislukt',
+    linkedSpool: 'Toegewezen spoel',
+    spoolId: 'Spoel-ID',
+    fillSourceLabel: '(Spoolman)',
+    weight: 'Gewicht',
+    remaining: 'Resterend',
+    disableWeightSync: 'Synchronisatie van geschat AMS-gewicht uitschakelen',
+    disableWeightSyncDesc: 'Werk de resterende capaciteit niet bij op basis van AMS-schattingen. Gebruik dit als je de verbruikstracking van Spoolman verkiest boven AMS-schattingen op basis van percentages. Nieuwe spoelen gebruiken nog steeds de AMS-schatting als startgewicht.',
+    reportPartialUsage: 'Gedeeltelijk verbruik rapporteren voor mislukte afdrukken',
+    reportPartialUsageDesc: 'Wanneer een afdruk mislukt of wordt geannuleerd, rapporteer dan het geschatte filamentverbruik tot dat moment op basis van de laagvoortgang.',
+  },
+
+  locations: {
+    title: 'Opslaglocaties',
+    subtitle: 'Beheer planken, laden en andere fysieke opslagplekken voor je spoelen',
+    add: 'Locatie toevoegen',
+    addShort: 'Toevoegen',
+    edit: 'Locatie bewerken',
+    name: 'Naam',
+    spools: 'Spoelen',
+    sensors: 'Sensoren',
+    empty: 'Nog geen opslaglocaties. Maak je eerste plank of lade.',
+    manage: 'Locaties',
+    createPlaceholder: 'bijv. Plank A, Lade 1',
+    nameRequired: 'Locatienaam is verplicht',
+    created: 'Locatie gemaakt',
+    updated: 'Locatie bijgewerkt',
+    deleted: 'Locatie verwijderd',
+    saveFailed: 'Locatie opslaan mislukt',
+    deleteFailed: 'Locatie verwijderen mislukt',
+    deleteBlocked: 'Verwijder alle spoelen uit deze locatie voordat je deze verwijdert',
+    confirmDelete: '"{{name}}" verwijderen?',
+    confirmDeleteMessage: 'Deze locatie wordt uit de catalogus verwijderd. Spoelen moeten eerst worden verplaatst.',
+    confirmDeleteMessageWithSensors:
+      'Deze locatie wordt uit de catalogus verwijderd. Spoelen moeten eerst worden verplaatst. Gekoppelde sensoren worden ook verwijderd.',
+  },
+
+  // Inventory
+  inventory: {
+    title: 'Spoelvoorraad',
+    subtitle: 'Beheer je spoelen',
+    addToInventory: 'Aan voorraad toevoegen',
+    addToInventoryPending: 'Toevoegen...',
+    addToInventorySuccess: 'Spoel aan voorraad toegevoegd',
+    addToInventoryFailed: 'Spoel aan voorraad toevoegen mislukt',
+    unknownSpoolTitle: 'Nieuw filament gedetecteerd',
+    unknownSpoolMessage: 'Een spoel met een onbekende RFID-tag is gedetecteerd bij {{location}}. Nu aan je voorraad toevoegen?',
+    unknownSpoolSlot: 'Sleuf',
+    bulk: {
+      selectAllVisible: 'Alle zichtbare selecteren',
+      selectRow: 'Rij selecteren',
+      selectGroup: 'Groep selecteren',
+      selectionCount: '{{count}} geselecteerd',
+      edit: 'Bewerken',
+      printLabels: 'Labels afdrukken',
+      resetUsage: 'Verbruik resetten',
+      restore: 'Herstellen',
+      archive: 'Archief',
+      delete: 'Verwijderen',
+      clearSelection: 'Selectie wissen',
+      editTitle: 'Spoelen in bulk bewerken',
+      editSubtitle: 'Van toepassing op {{count}} geselecteerde spoelen. Alleen velden die je aanvinkt worden bijgewerkt.',
+      editHint: 'Typ in een veld om het voor bijwerken te markeren — alleen aangevinkte rijen worden verzonden. Een leeg veld laat de spoelen ongewijzigd (velden wissen kan alleen per spoel).',
+      useCustom: '"{{value}}" gebruiken',
+      toggleField: 'Bijwerken voor dit veld omschakelen',
+      changeCount: '{{count}} velden worden bijgewerkt.',
+      applyPending: 'Toepassen...',
+      applyButton: 'Toepassen op {{count}} spoelen',
+      deleteTitle: 'Geselecteerde spoelen verwijderen',
+      archiveTitle: 'Geselecteerde spoelen archiveren',
+      restoreTitle: 'Geselecteerde spoelen herstellen',
+      resetUsageTitle: 'Verbruik van geselecteerde spoelen resetten',
+      deleteMessage: '{{count}} spoelen permanent verwijderen? Dit kan niet ongedaan worden gemaakt.',
+      archiveMessage: '{{count}} spoelen archiveren? Ze kunnen later worden hersteld.',
+      restoreMessage: '{{count}} gearchiveerde spoelen herstellen?',
+      resetUsageMessage: 'De teller "Totaal verbruikt" op {{count}} spoelen resetten? Resterend gewicht blijft behouden.',
+      updateSuccess: '{{count}} spoelen bijgewerkt',
+      updateFailed: 'Bulkupdate mislukt',
+      updatePartial: '{{ok}} spoelen bijgewerkt, {{failed}} mislukt',
+      updateAllFailed: 'Alle {{count}} spoelupdates zijn mislukt — selectie blijft behouden zodat je opnieuw kunt proberen',
+      deleteSuccess: '{{count}} spoelen verwijderd',
+      deleteFailed: 'Bulkverwijdering mislukt',
+      deletePartial: '{{ok}} spoelen verwijderd, {{failed}} mislukt',
+      deleteAllFailed: 'Alle {{count}} spoelverwijderingen zijn mislukt — selectie blijft behouden zodat je opnieuw kunt proberen',
+      archiveSuccess: '{{count}} spoelen gearchiveerd',
+      archiveFailed: 'Bulkarchivering mislukt',
+      archivePartial: '{{ok}} spoelen gearchiveerd, {{failed}} mislukt',
+      archiveAllFailed: 'Alle {{count}} spoelarchiveringen zijn mislukt — selectie blijft behouden zodat je opnieuw kunt proberen',
+      restoreSuccess: '{{count}} spoelen hersteld',
+      restoreFailed: 'Bulkherstel mislukt',
+      restorePartial: '{{ok}} spoelen hersteld, {{failed}} mislukt',
+      restoreAllFailed: 'Alle {{count}} spoelherstelacties zijn mislukt — selectie blijft behouden zodat je opnieuw kunt proberen',
+      invalidHex: 'Voer 6 hextekens (RRGGBB) of 8 (RRGGBBAA) in. Anders wordt het veld niet toegepast.',
+    },
+    spoolmanMixedContentTitle: 'Spoolman kan niet via HTTPS laden — mixed content wordt door je browser geblokkeerd',
+    spoolmanMixedContentBody: 'Bambuddy wordt via HTTPS aangeboden (via je reverse proxy), maar je Spoolman-URL gebruikt nog gewone HTTP. Browsers blokkeren mixed content om veiligheidsredenen, waardoor de ingebedde Spoolman-interface niet kan worden weergegeven. Spoolman moet via HTTPS bereikbaar zijn om dit te laten werken.',
+    spoolmanMixedContentFixReverseProxy: 'Plaats Spoolman achter dezelfde reverse proxy als Bambuddy (Traefik / Nginx / Caddy) met HTTPS en werk daarna de Spoolman-URL in Instellingen bij naar het nieuwe HTTPS-adres.',
+    spoolmanMixedContentFixOpenNewTab: 'Als tijdelijke oplossing kun je Spoolman in een nieuw browsertabblad via HTTP openen — mixed-contentregels gelden alleen voor ingebedde frames, dus een zelfstandig tabblad werkt wel.',
+    spoolmanOpenInNewTab: 'Spoolman in nieuw tabblad openen',
+    labels: {
+      title: 'Spoellabels afdrukken',
+      selectedCount: '{{count}} geselecteerd',
+      pickSpools: 'Kies voor welke spoelen je labels wilt afdrukken:',
+      monochrome: 'Monochroom (zwart-witprinter)',
+      monochromeHint: 'Verwijdert het kleurvlak en maakt de tekst breder',
+      startingPosition: 'Startpositie van etiket',
+      startingPositionRange: 'Alleen vellensjablonen: L7160 ondersteunt 1–21; 5160 ondersteunt 1–30.',
+      startingPositionInvalid: 'Voer een heel getal van 1 tot {{capacity}} in.',
+      startingPositionFirst: 'Het afdrukken begint op positie 1.',
+      startingPositionSkipped: 'Posities 1 tot en met {{lastPosition}} blijven leeg op het eerste vel.',
+      startingPositionRangeError: 'De startpositie moet voor dit vel tussen 1 en {{capacity}} liggen.',
+      searchPlaceholder: 'Zoek op naam, merk of #ID',
+      filterByMaterial: 'Materiaal:',
+      allMaterials: 'Alle',
+      selectVisible: 'Alle zichtbare selecteren ({{count}})',
+      deselectVisible: 'Zichtbare deselecteren',
+      clearAll: 'Alles wissen',
+      noSpoolsToShow: 'Geen spoelen om te tonen. Pas je filter aan en probeer opnieuw.',
+      noMatches: 'Geen spoelen komen overeen met de huidige zoekopdracht of het filter.',
+      printOne: 'Label voor deze spoel afdrukken',
+      printLabels: 'Labels afdrukken…',
+      bulkTitle: 'Kies spoelen voor labels uit de {{count}} die momenteel worden weergegeven',
+      noSpoolsTitle: 'Geen spoelen om te labelen',
+      error: 'Labels genereren mislukt: {{msg}}',
+      sortBy: {
+        label: 'Sorteren:',
+        id: 'Op ID',
+        color: 'Op kleur',
+      },
+      templates: {
+        amsHolderSmall: {
+          label: 'AMS-houder — klein (74 × 33 mm)',
+          hint: 'Eén label per pagina; past bij het printbare label van MakerWorld-model 752566 (AMS Filament Label Holder).',
+        },
+        amsHolderLarge: {
+          label: 'AMS-houder — groot (75 × 55 mm)',
+          hint: 'Eén label per pagina; past bij de kartonnen insteekvariant van de AMS Filament Label Holder. Ruim genoeg voor kleurvlak, merk, materiaal, ID en QR-code.',
+        },
+        box40x30: {
+          label: 'Dooslabel (40 × 30 mm)',
+          hint: 'Eén label per pagina; veelgebruikt DK/Brother-rolformaat, geschikt voor filamentzakken en opslagbakken.',
+        },
+        box: {
+          label: 'Dooslabel (62 × 29 mm)',
+          hint: 'Eén label per pagina; formaat voor kleine labels van Brother PT/QL en Dymo.',
+        },
+        averyL7160: {
+          label: 'Avery L7160 — A4-vel (38,1 × 63,5 mm × 21)',
+          hint: 'EU-vellen; 21 labels per A4-pagina.',
+        },
+        avery5160: {
+          label: 'Avery 5160 — US Letter-vel (25,4 × 66,7 mm × 30)',
+          hint: 'Amerikaanse vellen; 30 labels per Letter-pagina.',
+        },
+      },
+    },
+    csv: {
+      importButton: 'CSV importeren',
+      exportButton: 'CSV exporteren',
+      modalTitle: 'Spoelen importeren uit CSV',
+      selectFile: 'Kies een CSV-bestand of sleep het hierheen',
+      dragHint: 'Koptekst: material (verplicht), brand, subtype, color_name, rgba, …',
+      parsing: 'Bestand lezen…',
+      previewError: 'CSV-bestand kon niet worden gelezen',
+      validCount: '{{count}} geldig',
+      errorCount: '{{count}} fout',
+      skippedCount: '{{count}} overgeslagen',
+      colRow: 'Rij',
+      colStatus: 'Status',
+      colColor: 'Kleur',
+      colorResolved: 'Kleur ingevuld vanuit catalogus',
+      colorCrossMaterial: 'Kleur overgenomen van ander materiaal — geen exacte overeenkomst in catalogus',
+      duplicateExisting: 'Er bestaat al een spoel met dit materiaal, merk en deze kleur — deze wordt toch als nieuwe spoel geïmporteerd',
+      spoolmanHint: 'Gebruik in Spoolman-modus de ingebouwde CSV-import/-export van Spoolman.',
+      importValidRows: '{{count}} geldige rijen importeren',
+      noValidRows: 'Geen geldige rijen',
+      importing: 'Importeren…',
+      importSuccess: '{{count}} spoelen geïmporteerd',
+      importError: 'Importeren mislukt',
+      exportError: 'Export mislukt',
+    },
+    addSpool: 'Spoel toevoegen',
+    editSpool: 'Spoel bewerken',
+    copySpool: 'Spoel kopiëren',
+    material: 'Materiaal',
+    selectMaterial: 'Materiaal selecteren...',
+    subtype: 'Subtype',
+    brand: 'Merk',
+    searchBrand: 'Merk zoeken...',
+    useCustomBrand: '"{{brand}}" gebruiken',
+    useCustomMaterial: 'Aangepast materiaal gebruiken: {{material}}',
+    suggestedOptions: 'Voorgesteld',
+    allOptions: 'Alle',
+    colorName: 'Kleurnaam',
+    colorNamePlaceholder: 'Jade White, Fire Red...',
+    color: 'Kleur',
+    hexColor: 'Hexkleur',
+    pickColor: 'Aangepaste kleur kiezen',
+    labelWeight: 'Labelgewicht',
+    coreWeight: 'Leeg spoelgewicht',
+    searchSpoolWeight: 'Spoelgewicht zoeken...',
+    weightUsed: 'Gebruikt',
+    currentWeight: 'Resterend gewicht',
+    measuredWeight: 'Gemeten gewicht',
+    spoolName: 'Spoel',
+    costPerKg: 'Kosten per kg',
+    storageLocation: 'Opslaglocatie',
+    temperature: 'Temperatuur',
+    humidity: 'Luchtvochtigheid',
+    battery: 'Batterij',
+    storageLocationPlaceholder: 'bijv. Plank A, Lade 1',
+    openInInventory: 'Openen in Voorraad',
+    measuredWeightError: 'Gemeten gewicht moet tussen {{min}}g en {{max}}g liggen.',
+    slicerFilament: 'Slicerfilament',
+    slicerFilamentName: 'Naam slicerpreset',
+    slicerPreset: 'Slicer-preset',
+    searchPresets: 'Filamentpresets zoeken...',
+    selectedPreset: 'Geselecteerd',
+    noPresetsFound: 'Geen presets gevonden',
+    tempOverrides: 'Temperatuuroverschrijvingen',
+    note: 'Notitie',
+    notePlaceholder: 'Eventuele extra notities over deze spoel...',
+    // Per-spool category + low-stock threshold override (#729)
+    category: 'Categorie',
+    categoryPlaceholder: 'bijv. Productie, Prototype, Klant A',
+    categoryNone: 'Niet gecategoriseerd',
+    // #1400: storage-location filter chip — `storageLocation` label is
+    // already defined above for the spool-edit field, reused here for the
+    // dropdown header. `storageLocationNone` is new (the "no location set"
+    // group, mirrors `categoryNone`).
+    storageLocationNone: 'Geen locatie ingesteld',
+    lowStockThresholdOverride: 'Drempel lage voorraad (deze spoel)',
+    lowStockThresholdOverrideHelp: 'Laat leeg om de globale drempel ({{global}}%) te gebruiken.',
+    // RFID button rename (was "Delete Tag" — confusing because it sounds like a
+    // taxonomy delete; this clears the RFID tag/UUID off the spool record)
+    clearRfid: 'RFID-tag wissen',
+    rfidCleared: 'RFID-tag gewist',
+    archive: 'Archief',
+    restore: 'Herstellen',
+    noSpools: 'Nog geen spoelen. Voeg je eerste spoel toe om te beginnen.',
+    noAvailableSpools: 'Geen spoelen beschikbaar. Voeg eerst een spoel toe aan je voorraad of maak de toewijzing van een andere sleuf ongedaan.',
+    kProfiles: 'K-profielen',
+    addKProfile: 'K-profiel toevoegen',
+    assignSpool: 'Spoel toewijzen',
+    unassignSpool: 'Toewijzing opheffen',
+    assignSuccess: 'Spoel toegewezen en AMS-sleuf geconfigureerd',
+    assignPendingInsert: 'Toegewezen. Sleuf wordt geconfigureerd zodra je de spoel plaatst.',
+    assignFailed: 'Spoel toewijzen mislukt',
+    selectSpool: 'Selecteer een spoel om aan deze sleuf toe te wijzen',
+    assigned: 'Toegewezen',
+    assigning: 'Toewijzen...',
+    searchSpools: 'Spoelen zoeken...',
+    showAllSpools: 'Alle spoelen tonen',
+    spoolmanSpools: 'Spoolman-spoelen',
+    allMaterials: 'Alle materialen',
+    filterByBrand: 'Filteren op merk...',
+    showArchived: 'Gearchiveerde tonen',
+    quickAdd: 'Snel toevoegen (voorraad)',
+    quantity: 'Aantal',
+    stock: 'Voorraad',
+    configured: 'Geconfigureerd',
+    spoolsCreated: '{{count}} spoelen gemaakt',
+    spoolsPartiallyCreated: '{{created}} van {{total}} spoelen gemaakt (sommige mislukt)',
+    spoolCreated: 'Spoel gemaakt',
+    spoolUpdated: 'Spoel bijgewerkt',
+    spoolDeleted: 'Spoel verwijderd',
+    deepLinkSpoolNotFound: 'Spoel niet gevonden',
+    deepLinkFetchFailed: 'Spoel kon niet worden geladen — probeer opnieuw',
+    spoolArchived: 'Spoel gearchiveerd',
+    spoolRestored: 'Spoel hersteld',
+    kProfileSaveFailed: 'K-profielinstellingen konden niet worden opgeslagen',
+    syncWeightSpoolNotFound: 'Spoel niet gevonden — mogelijk verwijderd',
+    syncWeightSpoolmanUnreachable: 'Spoolman is niet bereikbaar — probeer het later opnieuw',
+    syncWeightFailed: 'Gewicht synchroniseren mislukt',
+    spoolmanUnreachable: 'Spoolman is niet bereikbaar — probeer het later opnieuw',
+    deleteSpoolNotFound: 'Spoel niet gevonden — mogelijk al verwijderd',
+    deleteFailed: 'Spoel verwijderen mislukt',
+    archiveSpoolNotFound: 'Spoel niet gevonden — mogelijk al verwijderd',
+    archiveFailed: 'Spoel archiveren mislukt',
+    restoreSpoolNotFound: 'Spoel niet gevonden — mogelijk al verwijderd',
+    restoreFailed: 'Spoel herstellen mislukt',
+    saveFailed: 'Wijzigingen opslaan mislukt',
+    tagClearFailed: 'Tag wissen mislukt',
+    deleteConfirm: 'Weet je zeker dat je deze spoel wilt verwijderen? Dit kan niet ongedaan worden gemaakt.',
+    archiveConfirm: 'Weet je zeker dat je deze spoel wilt archiveren?',
+    advancedSettings: 'Geavanceerde instellingen',
+    // Tabs
+    filamentInfoTab: 'Filamentinformatie',
+    paProfileTab: 'PA-profiel',
+    filamentInfo: 'Filament',
+    additional: 'Aanvullend',
+    // Cloud
+    loadingPresets: 'Cloudpresets laden...',
+    cloudConnected: 'Cloud verbonden',
+    cloudNotConnected: 'Cloud niet verbonden (standaardwaarden worden gebruikt)',
+    // Colors
+    recentColors: 'Recent',
+    searchColors: 'Kleuren zoeken...',
+    searchResults: 'Zoekresultaten',
+    allColors: 'Alle kleuren',
+    commonColors: 'Veelgebruikte kleuren',
+    showLess: 'Minder tonen',
+    showAll: 'Alles tonen',
+    noColorsFound: 'Geen kleuren komen overeen met je zoekopdracht',
+    noResults: 'Geen overeenkomsten gevonden',
+    // Multi-color gradient + visual effect (#1154)
+    extraColorsLabel: 'Extra kleuren',
+    extraColorsPlaceholder: 'EC984C,#6CD4BC,A66EB9,D87694',
+    extraColorsHint: 'Plak 2 tot 8 hexwaarden, gescheiden door komma\'s. Wordt als verloop weergegeven.',
+    extraColorsInvalid: 'Ongeldige hex genegeerd: {{tokens}}',
+    colorEffectLabel: 'Effect',
+    colorEffect: {
+      none: 'Geen',
+      // Surface effects
+      sparkle: 'Sprankelend',
+      wood: 'Hout',
+      marble: 'Marmer',
+      glow: 'Gloed',
+      matte: 'Mat',
+      // Sheen / finish variants
+      silk: 'Zijde',
+      galaxy: 'Galaxy',
+      rainbow: 'Regenboog',
+      metal: 'Metaal',
+      translucent: 'Transparant',
+      // Multi-color structural variants
+      gradient: 'Verloop',
+      dualColor: 'Tweekleurig',
+      triColor: 'Driekleurig',
+      multicolor: 'Meerkleurig',
+    },
+    // PA Profiles
+    selectMaterialFirst: 'Selecteer eerst een materiaal op het tabblad Filamentinformatie.',
+    noPrintersConfigured: 'Geen printers geconfigureerd. Voeg printers toe om PA-profielen te gebruiken.',
+    matchingFilter: 'Overeenkomend',
+    anyBrand: 'Elk merk',
+    anyVariant: 'Elke variant',
+    autoSelect: 'Automatisch selecteren',
+    matches: 'overeenkomsten',
+    match: 'overeenkomst',
+    noMatches: 'Geen overeenkomsten',
+    connected: 'Verbonden',
+    offline: 'Offline',
+    printerOffline: 'Printer is offline. Maak verbinding om kalibratieprofielen te bekijken.',
+    noKProfilesMatch: 'Geen K-profielen komen overeen met het geselecteerde filament.',
+    leftNozzle: 'Linker nozzle',
+    rightNozzle: 'Rechter nozzle',
+    profilesSelected: 'kalibratieprofiel(en) geselecteerd',
+    // Stats & enhanced table
+    totalInventory: 'Totale voorraad',
+    totalConsumed: 'Totaal verbruikt',
+    byMaterial: 'Per materiaal',
+    inPrinter: 'In printer',
+    lowStock: 'Lage voorraad',
+    sinceTracking: 'Sinds tracking is gestart',
+    resetConsumedCounter: 'Teller resetten',
+    resetConsumedCounterTooltip: 'Zet de teller voor verbruikte grammen van deze spoel op nul. Resterend gewicht verandert niet.',
+    resetConsumedCounterConfirm: 'Teller voor verbruikte grammen van deze spoel resetten naar 0? Toekomstige afdrukken tellen dan weer vanaf nul. De spoel zelf, de berekening van resterend gewicht en je instellingen veranderen niet.',
+    resetAllConsumedCounters: 'Alle tellers resetten',
+    resetAllConsumedCountersTooltip: 'Zet de teller voor verbruikte grammen op elke spoel op nul. Resterende gewichten veranderen niet.',
+    resetAllConsumedCountersConfirm: 'De teller voor verbruikte grammen op alle {{count}} spoelen (inclusief gearchiveerde) resetten naar 0? Hiermee wordt de statistiek "Totaal verbruikt" gewist zodat toekomstige afdrukken weer vanaf nul tellen. Spoelen en resterende gewichten veranderen niet.',
+    consumedCounterReset: 'Teller gereset',
+    allConsumedCountersReset: 'Teller gereset voor {{count}} spoel(en)',
+    resetConsumedCounterFailed: 'Teller resetten mislukt',
+    loadedInAms: 'Geladen in AMS/Ext',
+    remaining: 'Resterend',
+    weightCheck: 'Gewichtscontrole',
+    lastWeighed: 'Laatst gewogen',
+    neverWeighed: 'Nooit gewogen',
+    search: 'Spoelen zoeken...',
+    showing: 'Weergegeven',
+    to: 'tot',
+    of: 'van',
+    show: 'Tonen',
+    spools: 'spoelen',
+    spool: 'spoel',
+    page: 'Pagina',
+    noSpoolsMatch: 'Geen resultaten gevonden',
+    noSpoolsMatchDesc: 'Pas je zoekopdracht of filters aan om te vinden wat je zoekt.',
+    active: 'Actief',
+    archived: 'Gearchiveerd',
+    all: 'Alle',
+    used: 'Gebruikt',
+    new: 'Nieuw',
+    clearFilters: 'Filters wissen',
+    table: 'Tabel',
+    cards: 'Kaarten',
+    net: 'Netto',
+    // Grouping
+    groupSimilar: 'Groeperen',
+    groupedSpools: '{{count}} identieke spoelen',
+    groupedRows: 'rijen',
+    // Column config
+    columns: 'Kolommen',
+    configureColumns: 'Kolommen configureren',
+    configureColumnsDesc: 'Sleep om kolommen te herschikken of gebruik de pijlen. Schakel zichtbaarheid met het oogpictogram.',
+    visible: 'zichtbaar',
+    reset: 'Resetten',
+    cancel: 'Annuleren',
+    applyChanges: 'Wijzigingen toepassen',
+    moveUp: 'Omhoog verplaatsen',
+    moveDown: 'Omlaag verplaatsen',
+    hideColumn: 'Kolom verbergen',
+    showColumn: 'Kolom tonen',
+    // Tag linking
+    linkToSpool: 'Koppelen aan spoel',
+    tagLinked: 'Tag aan spoel gekoppeld',
+    tagLinkFailed: 'Tag koppelen mislukt',
+    tagAlreadyLinked: 'Tag is al aan een andere spoel gekoppeld',
+    unknownTag: 'Onbekende RFID-tag gedetecteerd',
+    // Usage history
+    usageHistory: 'Verbruiksgeschiedenis',
+    noUsageHistory: 'Nog geen verbruik geregistreerd',
+    printName: 'Afdruknaam',
+    weightConsumed: 'Verbruikt gewicht',
+    clearHistory: 'Wissen',
+    historyCleared: 'Verbruiksgeschiedenis gewist',
+    fillSourceLabel: '(Voorraad)',
+    lowStockThresholdError: 'Drempel moet tussen 0,1 en 99,9 liggen',
+    assignMismatchTitle: 'Materiaal komt niet overeen',
+    assignMismatchMessage: 'Het materiaal van de geselecteerde spoel "{{spoolMaterial}}" komt niet overeen met het materiaal van de tray "{{trayMaterial}}" voor {{location}}. Toch toewijzen?',
+    assignMismatchConfirm: 'Toch toewijzen',
+    assignPartialMismatchMessage: 'Het spoelmateriaal "{{spoolMaterial}}" lijkt op maar komt niet exact overeen met "{{trayMaterial}}" in {{location}}. Wil je doorgaan?',
+    assignProfileMismatchMessage: 'Het spoelprofiel "{{spoolProfile}}" komt niet overeen met het trayprofiel "{{trayProfile}}" in {{location}}. Wil je doorgaan?',
+    assignReconfigureNote: 'De AMS-sleuf wordt opnieuw geconfigureerd om het profiel van de spoel te gebruiken.',
+    // Spoolman filament catalog picker
+    spoolmanFilamentCatalog: 'Spoolman-filamentcatalogus',
+    pickFromSpoolmanCatalog: 'Kies uit Spoolman-catalogus…',
+    spoolmanFilamentSelected: 'Filament geselecteerd uit Spoolman-catalogus',
+    spoolmanFilamentUnlinked: 'Koppeling met filamentcatalogus gewist',
+    noSpoolmanFilaments: 'Geen filamenten gevonden in Spoolman-catalogus',
+    spoolmanFilamentColorSwatch: 'Filamentkleur',
+    spoolWeightManagedBySpoolman: 'Leeg spoelgewicht wordt per filamenttype beheerd in Spoolman',
+    spoolmanCatalogLoadFailed: 'Spoolman-filamentcatalogus laden mislukt',
+  },
+
+  // Timelapse
+  timelapse: {
+    title: 'Timelapse',
+    create: 'Timelapse maken',
+    download: 'Downloaden',
+    delete: 'Verwijderen',
+    preview: 'Voorbeeld',
+    frameRate: 'Framesnelheid',
+    quality: 'Kwaliteit',
+    processing: 'Verwerken...',
+    noTimelapses: 'Geen timelapses beschikbaar',
+  },
+
+  // AMS
+  ams: {
+    title: 'AMS',
+    slot: 'Sleuf',
+    empty: 'Leeg',
+    emptySlot: 'Lege sleuf',
+    slotEmpty: 'Leeg',
+    slotUnconfigured: '?',
+    emptySlotReset: 'Geen filament toegewezen',
+    unknown: 'Onbekend',
+    humidity: 'Luchtvochtigheid',
+    temperature: 'Temperatuur',
+    filamentType: 'Filamenttype',
+    filamentColor: 'Kleur',
+    remaining: 'Resterend',
+    history: 'AMS-geschiedenis',
+    noHistory: 'Geen geschiedenis beschikbaar',
+    configureSlot: 'Sleuf configureren',
+    externalSpool: 'Externe spoel',
+    profile: 'Profiel',
+    kFactor: 'K-factor',
+    kFactorShort: 'K',
+    fill: 'Vullen',
+    configure: 'Configureren',
+    used: 'gebruikt',
+    remainingUnit: 'resterend',
+  },
+
+  // Print modal
+  printModal: {
+    variants: {
+      editNote: 'Deze alternatieven zijn ingesteld toen de taak in de wachtrij werd geplaatst. Annuleer en plaats opnieuw in de wachtrij om ze te wijzigen.',
+      title: 'Printeralternatieven',
+      help: 'Eén taak, één wachtrijpositie. De eerste passende printer die vrijkomt voert zijn bestand uit.',
+      unknownModel: 'Onbekend model',
+      plateFor: 'Plaat voor {{filename}}',
+      moveUp: 'Omhoog verplaatsen',
+      moveDown: 'Omlaag verplaatsen',
+      queued: 'In wachtrij met {{count}} alternatieven',
+    },
+    selectPrinter: 'Printer selecteren',
+    selectPlate: 'Plaat selecteren',
+    filamentMapping: 'Filamenttoewijzing',
+    useArchiveMapping: 'Toewijzing',
+    useArchiveMappingTooltip: 'Selecteer elke sleuf uit de AMS-toewijzing die met dit archief is opgeslagen (vanuit de slicer), in plaats van te matchen op type/kleur.',
+    clickToChangeSlot: 'Klik om sleuftoewijzing te wijzigen',
+    reRead: 'Opnieuw uitlezen',
+    plateN: 'Plaat {{n}}',
+    plateFilamentsUnreadable: 'De filamenten van een geselecteerde plaat konden niet worden gelezen en kunnen daarom niet worden toegewezen. Deselecteer deze plaat om de andere in de wachtrij te plaatsen.',
+    totalCost: 'Totale kosten:',
+    slotRemainingShort: ' - {{grams}}g resterend',
+    printSettings: 'Afdrukinstellingen',
+    bedLeveling: 'Bednivellering',
+    flowCalibration: 'Flowkalibratie',
+    vibrationCalibration: 'Trillingskalibratie',
+    layerInspection: 'Inspectie eerste laag',
+    timelapse: 'Timelapse',
+    cancel: 'Annuleren',
+    noPrintersAvailable: 'Geen printers beschikbaar',
+    printerBusy: 'Printer is bezig',
+    printerOffline: 'Printer is offline',
+    sameTypeDifferentColor: 'Zelfde type, andere kleur',
+    filamentTypeNotLoaded: 'Filamenttype niet geladen',
+    statusReady: 'Gereed',
+    statusColorMismatch: 'Kleur komt niet overeen',
+    statusTypeNotFound: 'Type niet gevonden',
+    requiredTooltip: 'Vereist: {{name}} - {{color}}',
+    manuallySelected: 'Handmatig geselecteerd',
+    autoMatched: 'Automatisch gekoppeld',
+    selectSlot: 'Sleuf selecteren',
+    sameTypeDifferentColorDetail: 'Zelfde type, andere kleur: vereist {{required}}, sleuf bevat {{loaded}}',
+    requiredTypeNotInPrinter: 'Vereist filamenttype niet gevonden in printer.',
+    whenToPrint: 'Wanneer afdrukken',
+    asap: 'Zo snel mogelijk',
+    queue: 'Wachtrij',
+    schedule: 'Plannen',
+    dateTime: 'Datum en tijd',
+    invalidDateTime: 'Voer een geldige datum en tijd in',
+    openCalendar: 'Kalender openen',
+    requireManualStart: 'Handmatige start vereisen',
+    requirePreviousSuccess: 'Alleen starten als vorige afdruk is geslaagd',
+    autoOffAfter: 'Printer uitschakelen wanneer klaar',
+    helpAsap: 'Afdruk wordt bovenaan de wachtrij geplaatst en start zodra een geschikte printer inactief is.',
+    helpSchedule: 'Afdruk start op het geplande tijdstip als de printer inactief is. Als deze bezig is, wacht de taak tot de printer beschikbaar is.',
+    helpQueue: 'Afdruk wordt achteraan de wachtrij geplaatst.',
+    leftNozzle: 'L',
+    rightNozzle: 'R',
+    leftNozzleTooltip: 'Linker nozzle',
+    rightNozzleTooltip: 'Rechter nozzle',
+    ftsSameInletHint: 'Alle filamenten voor deze afdruk zitten op Filament Track Switch IN-{{inlet}}. Wisselen tussen filamenten op dezelfde ingang is langzamer — verplaats er één naar een AMS op de andere ingang om dit te versnellen.',
+    rackPosition: 'Rekpositie',
+    rackPositionTooltip: 'Welke nozzle op het rek dit filament afdrukt. Posities zijn genummerd zoals op de printer.',
+    rackEmptyPosition: 'Deze rekpositie is leeg',
+    rackWrongNozzle: 'Bevat een {{has}}-nozzle; dit filament vereist {{needs}}',
+    filamentOverride: 'Filament overschrijven',
+    filamentOverrideHint: 'Overschrijf filamenten optioneel voor modelgebaseerde toewijzing. De scheduler matcht op je geselecteerde filamenten in plaats van de oorspronkelijke 3MF-waarden.',
+    originalFilament: 'Origineel',
+    overrideWith: 'Overschrijven met',
+    resetToOriginal: 'Terugzetten naar origineel',
+    insufficientFilamentTitle: 'Niet genoeg filament',
+    waitingForAmsStatus: 'Wachten op AMS-status van {{printer}}…',
+    insufficientFilamentMessage: 'Sommige toegewezen spoelen bevatten minder filament dan deze afdruk nodig heeft:',
+    insufficientFilamentLine: '{{printer}} - {{slot}}: vereist {{required}}g, {{remaining}}g resterend',
+    insufficientFilamentLinePooled: '{{printer}} - {{slot}}: vereist {{required}}g, {{remaining}}g beschikbaar over overeenkomende spoelen (AMS Filament Backup)',
+    printAnyway: 'Toch afdrukken',
+    forceColorMatch: 'Kleurmatch afdwingen',
+    staggerPrinterStarts: 'Printerstarts spreiden',
+    staggerGroupSize: 'Groepsgrootte',
+    staggerInterval: 'Interval (min)',
+    staggerPreview: '{{printers}} printers → {{groups}} groepen van {{size}}, starten elke {{interval}} min',
+    staggerLastGroup: 'laatste groep: {{count}}',
+    staggerTotal: 'totaal: {{minutes}} min',
+    staggerToPrinters: 'Spreiden over {{count}} printers',
+    gcodeInjection: 'Auto-print-G-code injecteren',
+    insufficientBudget: 'Onvoldoende budget',
+    unlimitedNoBudget: 'Onbeperkt – er is geen budgetlimiet ingesteld.',
+    noPrintableCostCenters: 'Er is geen actieve kostenplaats beschikbaar voor afdrukken. Vraag een beheerder om je afdruktoegang te geven.',
+  },
+
+  // Backup
+  backup: {
+    includesEncryptionKey: 'Lokale back-ups bevatten het MFA-versleutelingssleutelbestand (DATA_DIR/.mfa_encryption_key), zodat een back-up-ZIP op zichzelf compleet is. Behandel de ZIP als gevoelig — iedereen met het bestand kan de opgeslagen OIDC-clientgeheimen en TOTP-geheimen ontsleutelen.',
+    title: 'Back-up en herstel',
+    createBackup: 'Back-up maken',
+    restoreBackup: 'Back-up herstellen',
+    restoreDescription: 'Alle gegevens vervangen vanuit een back-upbestand',
+    downloadBackup: 'Back-up downloaden',
+    uploadBackup: 'Back-up uploaden',
+    lastBackup: 'Laatste back-up',
+    autoBackup: 'Automatische back-up',
+    backupNow: 'Nu back-up maken',
+    restoreWarning: 'Waarschuwing: het herstellen van een back-up overschrijft alle huidige gegevens.',
+    includeArchives: 'Archieven opnemen',
+    includeSettings: 'Instellingen opnemen',
+    includeProfiles: 'Profielen opnemen',
+    backupSuccess: 'Back-up succesvol gemaakt',
+    restoreSuccess: 'Back-up succesvol hersteld',
+    backupFailed: 'Back-up mislukt',
+    restoreFailed: 'Herstellen mislukt',
+    restoreNote: 'Virtuele printer wordt tijdens het herstellen gestopt',
+
+    // GitHub Backup
+    githubBackup: 'Git-back-up',
+    enabled: 'Ingeschakeld',
+    cloudLoginRequired: 'Bambu Cloud-login vereist. Log in onder Profielen → Cloudprofielen om GitHub-back-up in te schakelen.',
+    cloudLoginRequiredShort: 'Cloudlogin vereist',
+    githubDescription: 'Synchroniseer je profielen automatisch met een privé-GitHub-repository voor back-up en versiegeschiedenis.',
+    repoIsPrivate: 'Repository is privé — veilig voor back-up.',
+    repoIsPublicWarning: 'Repository is OPENBAAR. Bambuddy-back-ups bevatten MQTT-inloggegevens, Home Assistant-tokens, Prometheus-tokens, je Bambu Cloud-e-mailadres en printertoegangscodes via K-profielen. Opslaan is geblokkeerd totdat je de repository privé maakt in de instellingen van je provider.',
+    repoVisibilityUnknown: 'De zichtbaarheid van de repository kon niet worden bepaald. Bambuddy weigert een back-up naar iets dat niet als privé is bevestigd; opslaan wordt geblokkeerd.',
+    repositoryUrl: 'Repository-URL',
+    repoUrlPlaceholderGitHub: 'https://github.com/username/repo-name',
+	repoUrlPlaceholderGitea: 'https://gitea.example.com/username/repo-name',
+    repoUrlPlaceholderForgejo: 'https://forgejo.example.com/username/repo-name',
+    repoUrlPlaceholderGitLab: 'https://gitlab.com/username/repo-name',
+    allowInsecureHttp: 'Onveilige HTTP toestaan',
+    allowInsecureHttpHint: 'Inschakelen voor zelfgehoste instanties op privénetwerken zonder TLS',
+    personalAccessToken: 'Personal Access Token',
+    tokenSaved: '(opgeslagen)',
+    enterNewToken: 'Voer nieuw token in om bij te werken',
+    tokenHintGitHub: 'Fine-grained token met lees- en schrijftoegang tot Contents, of een klassiek token met de repo-scope.',
+    tokenHintGitLab: 'Token met de api-scope, of read_repository en write_repository samen.',
+    tokenHintGitea: 'Token met de write:repository-scope.',
+    tokenHintForgejo: 'Token met de write:repository-scope. Een token beperkt tot alleen deze repository is voldoende.',
+    branch: 'Branch',
+    provider: 'Git-provider',
+    providerGitHub: 'GitHub',
+    providerGitLab: 'GitLab',
+	providerGitea: 'Gitea',
+    providerForgejo: 'Forgejo',
+    manualOnly: 'Alleen handmatig',
+    hourly: 'Elk uur',
+    daily: 'Dagelijks',
+    weekly: 'Wekelijks',
+    includeInBackup: 'Opnemen in back-up',
+    kProfiles: 'K-profielen',
+    kProfilesDescription: 'Pressure-advance-kalibratie van verbonden printers',
+    noPrintersConnected: 'Geen printers verbonden',
+    printersConnected: '{{connected}}/{{total}} verbonden',
+    cloudProfiles: 'Cloudprofielen',
+    cloudProfilesDescription: 'Filament-, printer- en procespresets uit Bambu Cloud en Orca Cloud',
+    cloudProfilesAccounts: 'Verbonden accounts — Bambu Cloud: {{bambu}}, Orca Cloud: {{orca}}',
+    appSettings: 'App-instellingen',
+    appSettingsDescription: 'Bambuddy-configuratie (volledige database)',
+    spoolInventory: 'Spoelvoorraad',
+    spoolInventoryDescription: 'Filamentspoelen, verbruiksgeschiedenis en kostentracking',
+    printArchives: 'Afdrukarchieven',
+    printArchivesDescription: 'Metadata van afdrukgeschiedenis (geen gcode/3MF-bestanden)',
+    lastBackupAt: 'Laatste back-up:',
+    noBackupsYet: 'Nog geen back-ups',
+    next: 'Volgende:',
+    startingBackup: 'Back-up starten...',
+    test: 'Testen',
+    enableBackup: 'Back-up inschakelen',
+    testConnection: 'Verbinding testen',
+    enterRepoUrl: 'Voer repository-URL in',
+    enterRepoAndToken: 'Voer repository-URL en toegangstoken in',
+    repoRequired: 'Repository-URL is verplicht',
+    tokenRequired: 'Toegangstoken is verplicht',
+    githubBackupEnabled: 'GitHub-back-up ingeschakeld',
+    tokenUpdated: 'Token bijgewerkt',
+    settingsSaved: 'Instellingen opgeslagen',
+    failedToSave: 'Opslaan mislukt: {{message}}',
+    backupCompleteFiles: 'Back-up voltooid - {{count}} bestanden bijgewerkt',
+    backupSkippedNoChanges: 'Back-up overgeslagen - geen wijzigingen',
+    backupFailed2: 'Back-up mislukt: {{message}}',
+    clearedLogs: '{{count}} logboeken gewist',
+    failedToClearLogs: 'Logboeken wissen mislukt: {{message}}',
+
+    // Restore from Git backup (#2656)
+    restoreFromGit: {
+      button: 'Herstellen vanuit Git',
+      title: 'Herstellen vanuit Git-back-up',
+      subtitle: 'Kies een commit en wat je wilt herstellen',
+      commitLabel: 'Back-upcommit',
+      latestCommit: 'Nieuwste back-up (top van branch)',
+      categoriesLabel: 'Wat herstellen',
+      inspecting: 'Inhoud van back-up lezen...',
+      itemCount: '{{count}} in back-up',
+      overwriteLabel: 'Bestaande items overschrijven',
+      overwriteOn: 'Bestaande items worden bijgewerkt vanuit de back-up.',
+      overwriteOff: 'Alleen ontbrekende items worden toegevoegd; bestaande blijven ongewijzigd.',
+      selectedCount: '{{count}} geselecteerd',
+      restoring: 'Herstellen...',
+      confirmTitle: 'Herstellen vanuit back-up?',
+      confirmMessage: 'De geselecteerde categorieën worden uit deze commit hersteld. Ontbrekende items worden toegevoegd; bestaande blijven zoals ze zijn.',
+      confirmMessageOverwrite: 'De geselecteerde categorieën worden uit deze commit hersteld en bestaande lokale items worden overschreven. Dit kan niet ongedaan worden gemaakt.',
+      kprofilesOverwriteCaveat: 'K-profielen zijn de uitzondering: het schrijven van een sleuf vervangt altijd de kalibratie op de printer.',
+      tally: '{{restored}} hersteld, {{skipped}} overgeslagen, {{failed}} mislukt',
+      reloadHint: 'Herlaad Bambuddy zodat de herstelde gegevens overal verschijnen.',
+      partialHint: 'De hierboven vermelde categorieën zijn voltooid en staan op schijf. Ontbrekende categorieën zijn niet uitgevoerd.',
+      failed: 'Herstellen mislukt.',
+      loadFailed: 'Back-uprepository kon niet worden gelezen.',
+      // Preview caveats. The server sends detail_code + detail_params and the
+      // English detail as defaultValue, same contract as backup.pathCheck.
+      details: {
+        notPresent: 'Niet aanwezig in deze back-upcommit',
+        unreadableJson: 'Onleesbare JSON: {{paths}}',
+        settingsNoPayload: 'Geen instellingen in payload',
+        settingsCredentialsWillSkip: '{{count}} sleutel(s) die op inloggegevens lijken worden overgeslagen',
+        settingsCompanionWillSkip: '{{count}} sleutel(s) die op inloggegevens lijken worden overgeslagen, en {{companion}} schakelaar(s) die daarvan afhankelijk zijn blijven uitgeschakeld',
+        settingsCompanionOnlyWillSkip: '{{companion}} schakelaar(s) blijven uitgeschakeld - de benodigde inloggegevens kunnen niet uit een back-up worden hersteld',
+        spoolsUsageCount: 'inclusief {{count}} verbruiksrecord(s)',
+        archivesMetadataOnly: 'Alleen metadata - 3MF-bestanden en miniaturen zitten niet in een Git-back-up',
+        kprofilesPrinterCount: 'over {{count}} printer(s)',
+      },
+      // Tally notes, same contract. noData is shared by all four categories:
+      // the category heading renders beside it, so naming the category again
+      // would be redundant.
+      notes: {
+        noData: 'Geen gegevens van dit type in deze back-up',
+        archivesPrinterMissing: 'Sommige archieven verwezen naar printers die niet meer bestaan - koppeling gewist',
+        archivesProjectMissing: 'Sommige archieven verwezen naar projecten die niet meer bestaan - koppeling gewist',
+        archivesOwnerCleared: 'Sommige archieven verwezen naar gebruikers die niet meer bestaan - eigenaar gewist, waardoor ze alleen zichtbaar zijn voor gebruikers met de machtiging archives:read_all totdat een beheerder ze opnieuw toewijst',
+        archivesOwnerUnmatched: 'Sommige archieven noemen een eigenaar die deze instantie niet heeft - eigenaar gewist in plaats van te gokken op basis van het gebruikers-ID uit de back-up, waardoor ze alleen zichtbaar zijn voor gebruikers met de machtiging archives:read_all totdat een beheerder ze opnieuw toewijst',
+        archivesOwnerUnknown: 'Sommige archieven zijn zonder eigenaar hersteld - deze back-up registreert er geen, waardoor ze alleen zichtbaar zijn voor gebruikers met de machtiging archives:read_all totdat een beheerder ze opnieuw toewijst',
+        archivesUndeleted: 'Archief/archieven die sinds de back-up waren verwijderd zijn weer zichtbaar - overschrijven stond aan',
+        archivesMetadataOnly: 'Herstelde archieven bevatten alleen metadata - de 3MF- en miniatuurbestanden zitten niet in een Git-back-up',
+        spoolUsageUnresolved: '{{count}} verbruiksrecord(s) overgeslagen - hun spoel staat niet in de spoellijst van deze back-up, dus er is niets om ze aan te koppelen.',
+        spoolUsageUnlinked: '{{count}} verbruiksrecord(s) hersteld zonder koppeling met afdrukgeschiedenis - selecteer Afdrukarchieven samen met Spoelvoorraad om deze te behouden.',
+        spoolTagKept: '{{count}} spoeltag(s) gelaten zoals ze zijn - de back-up zou een tag hebben gewist die sindsdien is gescand, of een tag naar een tweede spoel hebben verplaatst.',
+        settingsCredentialsSkipped: '{{count}} sleutel(s) die op inloggegevens lijken overgeslagen - voer geheimen handmatig opnieuw in',
+        settingsAuthSkipped: '{{count}} authenticatie-instelling(en) overgeslagen - wijzig deze onder Instellingen > Authenticatie zodat vergrendelingscontroles blijven werken',
+        settingsCompanionSkipped: '{{keys}} blijven uitgeschakeld - de benodigde inloggegevens kunnen niet uit een back-up worden hersteld en deze instantie heeft er geen opgeslagen, dus inschakelen zou de integratie zonder authenticatie achterlaten',
+        settingsMqttRelayFailed: 'MQTT-instellingen hersteld, maar de relay kon niet opnieuw verbinden - start Bambuddy opnieuw',
+        kprofilesAlwaysOverwrite: 'K-profielen overschrijven altijd de overeenkomende sleuf op de printer',
+        kprofilesAckUnreliable: 'Een printer die niet antwoordt telt nog steeds als hersteld - controleer de profielen op de printer',
+        kprofilesPrinterMissing: 'Geen printer met serienummer {{serial}} - overgeslagen',
+        kprofilesPrinterOffline: '{{printer}} ({{serial}}) is niet verbonden - overgeslagen',
+        kprofilesUnknownNozzle: 'Onverwachte nozzlediameter {{nozzle}} voor {{serial}} - ongewijzigd verzonden',
+        kprofilesUnmatched: '{{count}} profiel(en) voor {{nozzle}} hadden geen tegenhanger op {{printer}} - als nieuwe profielen toegevoegd',
+        kprofilesSendFailed: 'Verzenden van {{nozzle}}-profielen naar {{printer}} ({{serial}}) mislukt',
+        kprofilesRefused: '{{printer}} ({{serial}}) weigerde de {{nozzle}}-profielen: {{reason}}',
+        kprofilesStepFailed: 'De K-profielstap kon niet worden voltooid - {{reason}}. Alles wat daarvoor is hersteld blijft opgeslagen.',
+      },
+    },
+
+    // History
+    history: 'Geschiedenis',
+    clear: 'Wissen',
+    date: 'Datum',
+    status: 'Status',
+    trigger: 'Type',
+    triggers: {
+      manual: 'Back-up (handmatig)',
+      scheduled: 'Back-up (gepland)',
+      restore: 'Herstellen',
+    },
+    commit: 'Commit',
+
+    // Local Backup
+    localBackup: 'Lokale back-up',
+    localBackupDescription: 'Maak een volledige back-up van je Bambuddy-gegevens inclusief database, archieven, uploads en alle bestanden.',
+    downloadBackupLabel: 'Back-up downloaden',
+    completeBackupZip: 'Volledige back-up: database + alle bestanden (ZIP)',
+    download: 'Downloaden',
+    preparingBackup: 'Back-up voorbereiden...',
+    creatingArchive: 'Back-uparchief maken... Dit kan even duren bij grote archieven.',
+    downloadingFile: 'Back-upbestand downloaden...',
+    backupDownloaded: 'Back-up succesvol gedownload',
+    failedToCreateBackup: 'Back-up maken mislukt: {{message}}',
+    restore: 'Herstellen',
+    restoreReplacesAll: 'Herstellen vervangt alle gegevens.',
+    restoreReplacesAllDetail: 'Je huidige database en bestanden worden volledig vervangen. Na herstel is een herstart vereist.',
+    restoreConfirmTitle: 'Back-up herstellen',
+    restoreConfirmMessage: 'Weet je zeker dat je wilt herstellen vanuit "{{filename}}"? Hiermee worden je huidige database en alle bestanden volledig vervangen. De applicatie moet daarna opnieuw worden gestart.',
+    restoreConfirmButton: 'Back-up herstellen',
+    uploadingFile: 'Back-upbestand uploaden...',
+    backupRestoredRestart: 'Back-up hersteld. Start Bambuddy opnieuw.',
+    failedToRestore: 'Back-up herstellen mislukt. Controleer het bestandsformaat.',
+    reloadNow: 'Nu herladen',
+    creatingBackup: 'Back-up maken',
+    restoringBackup: 'Back-up herstellen',
+    preparing: 'Voorbereiden...',
+    processing: 'Verwerken...',
+    doNotClosePage: 'Sluit deze pagina niet en navigeer niet weg. Deze bewerking kan bij grote back-ups enkele minuten duren.',
+
+    // RestoreModal
+    restoring: 'Herstellen...',
+    restoreComplete: 'Herstel voltooid',
+    restoreFailed2: 'Herstel mislukt',
+    importSettings: 'Instellingen importeren uit een back-upbestand',
+    pleaseWaitRestoring: 'Wacht terwijl je gegevens worden hersteld',
+    selectBackupFile: 'Klik om een back-upbestand te selecteren (.json of .zip)',
+    duplicateHandling: 'Zo werkt afhandeling van duplicaten:',
+    matchPrinters: 'Printers',
+    matchPrintersBy: 'gekoppeld op serienummer',
+    matchSmartPlugs: 'Slimme stekkers',
+    matchSmartPlugsBy: 'gekoppeld op IP-adres',
+    matchNotificationProviders: 'Meldingsproviders',
+    matchNotificationProvidersBy: 'gekoppeld op naam',
+    matchFilaments: 'Filamenten',
+    matchFilamentsBy: 'gekoppeld op naam + type + merk',
+    matchArchives: 'Archieven',
+    matchArchivesBy: 'gekoppeld op inhoudshash (altijd overgeslagen)',
+    matchPendingUploads: 'Openstaande uploads',
+    matchPendingUploadsBy: 'gekoppeld op bestandsnaam',
+    matchSettingsTemplates: 'Instellingen en sjablonen',
+    matchSettingsTemplatesBy: 'altijd overschreven',
+    replaceExisting: 'Bestaande gegevens vervangen',
+    keepExisting: 'Bestaande gegevens behouden',
+    overwriteDescription: 'Items die al bestaan overschrijven met gegevens uit de back-up',
+    keepDescription: 'Alleen items herstellen die nog niet bestaan',
+    overwriteCaution: 'Let op:',
+    overwriteWarning: 'Overschrijven vervangt je huidige configuraties met gegevens uit de back-up. Printertoegangscodes worden om veiligheidsredenen nooit overschreven.',
+    cancel: 'Annuleren',
+    processingBackup: 'Back-upbestand verwerken...',
+    itemsRestored: 'Items hersteld',
+    itemsSkipped: 'Items overgeslagen',
+    restored: 'Hersteld',
+    skippedAlreadyExist: 'Overgeslagen (bestaan al)',
+    filesCategory: 'Bestanden (3MF, miniaturen, enz.)',
+    andMore: '...en {{count}} meer',
+    newApiKeysGenerated: 'Nieuwe API-sleutels gegenereerd',
+    keysShownOnce: 'Deze sleutels worden maar één keer getoond. Kopieer ze nu!',
+    copy: 'Kopiëren',
+    noDataFound: 'Er zijn geen gegevens gevonden om uit het back-upbestand te herstellen.',
+    close: 'Sluiten',
+
+    // Scheduled local backups (#884)
+    scheduledBackup: 'Geplande back-ups',
+    scheduledBackupDescription: 'Maak automatisch back-upsnapshots volgens een schema. De uitvoermap kan aan een NAS of externe opslag worden gekoppeld.',
+    frequency: 'Frequentie',
+    backupTime: 'Tijd',
+    retention: 'Bewaartermijn',
+    retentionDescription: 'Aantal back-ups om te bewaren',
+    outputPath: 'Uitvoerpad',
+    outputPathPlaceholder: 'Standaard: {{path}}',
+    outputPathDescription: 'Laat leeg voor standaardlocatie',
+    runNow: 'Nu uitvoeren',
+    backupFiles: 'Back-upbestanden',
+    noScheduledBackups: 'Nog geen back-ups',
+    deleteBackup: 'Verwijderen',
+    deleteBackupConfirm: 'Dit back-upbestand verwijderen?',
+    backupRunning: 'Back-up bezig...',
+    scheduledBackupComplete: 'Back-up succesvol voltooid',
+    scheduledBackupFailed: 'Back-up mislukt',
+    nextBackup: 'Volgende back-up',
+    backupSize: 'Grootte',
+    localTimeHint: 'Lokale tijd ({{tz}})',
+    defaultPathLabel: 'Standaard:',
+    // Backup output-path probe (#2544)
+    pathCheck: {
+      title: 'Bambuddy kan niet naar deze map schrijven',
+      howToFix: 'Oplossing:',
+      sandboxed: 'De Bambuddy-service kan niet naar {{path}} schrijven. De systemd-unit draait met ProtectSystem=strict, waardoor elke map buiten de installatie-, data- en logmappen alleen-lezen is voor de service - zelfs als je er vanuit je eigen shell wel naar kunt schrijven.',
+      read_only: '{{path}} staat op een alleen-lezen bestandssysteem.',
+      permission_denied: 'Bambuddy mag niet schrijven naar {{path}}. Controleer de eigenaar en machtigingen van de map.',
+      no_space: 'Het bestandssysteem waarop {{path}} staat is vol.',
+      not_a_directory: '{{path}} bestaat, maar is geen map.',
+      missing: '{{path}} bestaat niet en kon niet worden aangemaakt.',
+      error: 'Bambuddy kan niet schrijven naar {{path}}.',
+      ephemeralTitle: 'Deze back-ups blijven niet behouden na een herstart van de container',
+      container_ephemeral: '{{path}} bevindt zich in de Bambuddy-container, niet op de host. Back-ups die daar worden opgeslagen gaan verloren wanneer de container opnieuw wordt aangemaakt. Koppel de map vanaf de host:',
+    },
+
+    // Category labels
+    categories: {
+      settings: 'Instellingen',
+      notification_providers: 'Meldingsproviders',
+      notification_templates: 'Meldingssjablonen',
+      smart_plugs: 'Slimme stekkers',
+      printers: 'Printers',
+      filaments: 'Filamenten',
+      maintenance_types: 'Onderhoudstypen',
+      archives: 'Archieven',
+      projects: 'Projecten',
+      pending_uploads: 'Openstaande uploads',
+      external_links: 'Externe links',
+      api_keys: 'API-sleutels',
+    },
+  },
+
+  // Tags
+  tags: {
+    title: 'Tags',
+    addTag: 'Tag toevoegen',
+    editTag: 'Tag bewerken',
+    deleteTag: 'Tag verwijderen',
+    tagName: 'Tagnaam',
+    tagColor: 'Tagkleur',
+    noTags: 'Geen tags',
+    deleteConfirm: 'Weet je zeker dat je deze tag wilt verwijderen?',
+    manageTags: 'Tags beheren',
+  },
+
+  // Upload modal (archives)
+  uploadModal: {
+    title: '3MF-bestanden uploaden',
+    dragDrop: 'Sleep .3mf-bestanden hierheen',
+    or: 'of',
+    browseFiles: 'Bestanden bladeren',
+    extractionInfo: 'Het printermodel wordt automatisch uit de metadata van het 3MF-bestand gehaald.',
+    uploaded: 'geüpload',
+    failed: 'mislukt',
+    uploading: 'Uploaden...',
+    upload: 'Uploaden',
+    uploadFailed: 'Upload mislukt',
+  },
+
+  // Edit archive modal
+  // Edit Archive Modal
+  editArchive: {
+    title: 'Archief bewerken',
+    name: 'Naam',
+    namePlaceholder: 'Afdruknaam',
+    printer: 'Printer',
+    noPrinter: 'Geen printer',
+    project: 'Project',
+    noProject: 'Geen project',
+    itemsPrinted: 'Afgedrukte items',
+    itemsPrintedHelp: 'Aantal items dat met deze afdruktaak is geproduceerd',
+    filamentUsed: 'Gebruikt filament (g)',
+    filamentUsedPlaceholder: 'bijv. 46.16',
+    filamentUsedHelp: 'Stel dit handmatig in wanneer een afdruk zonder 3MF is gearchiveerd, zodat deze toch meetelt in de filamenttotalen. Bij het opnieuw scannen van een archief met een 3MF wordt de waarde weer uit het bestand gelezen.',
+    notes: 'Notities',
+    notesPlaceholder: 'Voeg notities over deze afdruk toe...',
+    externalLink: 'Externe link',
+    externalLinkPlaceholder: 'https://printables.com/model/...',
+    externalLinkHelp: 'Link naar Printables, Thingiverse of een andere bron',
+    tags: 'Tags',
+    tagsPlaceholder: 'Tags toevoegen...',
+    addMoreTags: 'Meer tags toevoegen...',
+    matchingTags: 'Komt overeen met "{{query}}"',
+    existingTags: 'Bestaande tags',
+    clickToAdd: '(klik om toe te voegen)',
+    status: 'Status',
+    failureReason: 'Reden van mislukking',
+    selectReason: 'Selecteer reden...',
+    photos: 'Foto\'s van het afdrukresultaat',
+    photosHelp: 'Klik op + om foto\'s van je afdrukresultaat toe te voegen',
+    printResult: 'Afdrukresultaat',
+    saving: 'Opslaan...',
+    // Failure reasons
+    failureReasons: {
+      adhesionFailure: 'Hechtingsprobleem',
+      spaghettiDetached: 'Spaghetti / losgeraakt',
+      layerShift: 'Laagverschuiving',
+      cloggedNozzle: 'Verstopte nozzle',
+      filamentRunout: 'Filament op',
+      warping: 'Kromtrekken',
+      stringing: 'Draadvorming',
+      underExtrusion: 'Onderextrusie',
+      powerFailure: 'Stroomuitval',
+      userCancelled: 'Door gebruiker geannuleerd',
+      other: 'Overig',
+    },
+    // Archive statuses
+    statuses: {
+      completed: 'Voltooid',
+      failed: 'Mislukt',
+      aborted: 'Geannuleerd',
+      printing: 'Bezig met afdrukken',
+    },
+  },
+
+  // K-Profiles
+  kProfiles: {
+    title: 'K-profielen',
+    noPrintersConfigured: 'Geen printers geconfigureerd',
+    addPrinterInSettings: 'Voeg in Instellingen een printer toe om K-profielen te beheren',
+    noActivePrinters: 'Geen actieve printers',
+    enablePrinterConnection: 'Schakel een printerverbinding in om de K-profielen te bekijken',
+    loadingProfiles: 'K-profielen laden...',
+    printerOffline: 'Printer offline',
+    printerOfflineDesc: 'De geselecteerde printer is niet verbonden. Schakel hem in om de K-profielen te bekijken.',
+    noMatchingProfiles: 'Geen overeenkomende profielen',
+    noMatchingProfilesDesc: 'Geen profielen komen overeen met je zoekcriteria',
+    noKProfiles: 'Geen K-profielen',
+    noKProfilesDesc: 'Geen pressure-advance-profielen gevonden voor een nozzle van {{diameter}} mm',
+    createFirstProfile: 'Eerste profiel aanmaken',
+    // Controls
+    printer: 'Printer',
+    nozzle: 'Nozzle',
+    refresh: 'Vernieuwen',
+    addProfile: 'Profiel toevoegen',
+    export: 'Exporteren',
+    import: 'Importeren',
+    select: 'Selecteren',
+    selectAll: 'Alles selecteren',
+    delete: 'Verwijderen',
+    // Filters
+    searchPlaceholder: 'Zoeken op naam of filament...',
+    allExtruders: 'Alle extruders',
+    leftOnly: 'Alleen links',
+    rightOnly: 'Alleen rechts',
+    allFlow: 'Alle flowtypen',
+    hfOnly: 'Alleen HF',
+    sOnly: 'Alleen S',
+    sortName: 'Sorteren: naam',
+    sortKValue: 'Sorteren: K-waarde',
+    sortFilament: 'Sorteren: filament',
+    // Dual extruder labels
+    leftExtruder: 'Linker extruder',
+    rightExtruder: 'Rechter extruder',
+    // Modal
+    modal: {
+      addTitle: 'K-profiel toevoegen',
+      editTitle: 'K-profiel bewerken',
+      profileName: 'Profielnaam',
+      profileNamePlaceholder: 'Mijn PLA-profiel',
+      kValue: 'K-waarde',
+      kValuePlaceholder: '0.020',
+      kValueHelp: 'Typisch bereik: 0.01 - 0.06 voor PLA, 0.02 - 0.10 voor PETG',
+      filament: 'Filament',
+      selectFilament: 'Selecteer filament...',
+      source: {
+        local: 'Geïmporteerd',
+        orcaCloud: 'Orca Cloud',
+        bambuCloud: 'Bambu Cloud',
+        builtin: 'Ingebouwd',
+      },
+      noFilamentsHelp: 'Geen filamenten beschikbaar. Meld je aan bij Bambu Cloud of importeer presets via Profielen → Lokale profielen.',
+      searchFilaments: 'Filamenten zoeken...',
+      noFilamentMatches: 'Geen filament komt overeen met die zoekopdracht',
+      flowType: 'Flowtype',
+      highFlow: 'Hoge flow',
+      standard: 'Standaard',
+      nozzleSize: 'Nozzlegrootte',
+      extruder: 'Extruder',
+      extruders: 'Extruders',
+      left: 'Links',
+      right: 'Rechts',
+      notes: 'Notities (lokaal opgeslagen)',
+      notesPlaceholder: 'Voeg notities over dit profiel toe...',
+      notesHelp: 'Notities worden opgeslagen in Bambuddy, niet op de printer',
+      syncing: 'Synchroniseren met printer...',
+      savingExtruder: 'Opslaan naar extruder {{current}}/{{total}}...',
+      pleaseWait: 'Even geduld',
+    },
+    // Delete confirmation
+    deleteConfirm: {
+      title: 'Profiel verwijderen',
+      cannotUndo: 'Dit kan niet ongedaan worden gemaakt',
+      message: 'Weet je zeker dat je "{{name}}" van de printer wilt verwijderen?',
+    },
+    // Bulk delete
+    bulkDelete: {
+      title: 'Profielen verwijderen',
+      cannotUndo: 'Dit kan niet ongedaan worden gemaakt',
+      message: 'Weet je zeker dat je {{count}} geselecteerde profielen van de printer wilt verwijderen?',
+    },
+    // Toast
+    toast: {
+      profileSaved: 'K-profiel opgeslagen',
+      profilesSaved: 'K-profiel opgeslagen naar {{count}} extruders',
+      selectAtLeastOneExtruder: 'Selecteer ten minste één extruder',
+      selectFilament: 'Selecteer eerst een filament',
+      filamentNotResolvable: 'Geen Bambu-filament-ID voor {{name}} — de printer kan hiervoor geen profiel opslaan',
+      profileDeleted: 'K-profiel verwijderd',
+      profilesDeleted: '{{count}} profielen verwijderd',
+      exportedProfiles: '{{count}} profielen geëxporteerd',
+      importedProfiles: '{{count}} van {{total}} profielen geïmporteerd',
+      noProfilesToExport: 'Geen profielen om te exporteren',
+      invalidFileFormat: 'Ongeldig bestandsformaat',
+      failedToParseImport: 'Importbestand kon niet worden verwerkt',
+      failedToSaveBatch: 'K-profielen opslaan mislukt',
+      noteSaved: 'Notitie opgeslagen',
+      failedToSaveNote: 'Notitie opslaan mislukt',
+    },
+    // Permissions
+    permission: {
+      noRead: 'Je hebt geen toestemming om profielen te vernieuwen',
+      noCreate: 'Je hebt geen machtiging om profielen toe te voegen',
+      noUpdate: 'Je hebt geen machtiging om K-profielen bij te werken',
+      noDelete: 'Je hebt geen machtiging om K-profielen te verwijderen',
+      noExport: 'Je hebt geen machtiging om profielen te exporteren',
+      noImport: 'Je hebt geen machtiging om profielen te importeren',
+    },
+  },
+
+  // Virtual Printer
+  virtualPrinter: {
+    title: 'Virtuele printer',
+    running: 'Actief',
+    stopped: 'Gestopt',
+    description: {
+      default: 'Schakel een virtuele printer in die zichtbaar wordt in Bambu Studio en OrcaSlicer. Bestanden die naar deze printer worden gestuurd, worden direct gearchiveerd zonder te worden afgedrukt.',
+      proxy: 'Schakel een proxy in die slicerverkeer doorstuurt naar een echte printer, zodat je via elk netwerk op afstand kunt afdrukken.',
+    },
+    enable: {
+      title: 'Virtuele printer inschakelen',
+      visibleInSlicer: 'Zichtbaar als "Bambuddy" in slicerdetectie',
+      proxyingTo: 'Proxy naar {{name}}',
+      notActive: 'Niet actief',
+    },
+    model: {
+      title: 'Printermodel',
+      description: 'Selecteer welk printermodel moet worden geëmuleerd.',
+      restartWarning: 'Als je het model wijzigt, wordt de virtuele printer opnieuw gestart',
+    },
+    accessCode: {
+      title: 'Toegangscode',
+      isSet: 'Toegangscode is ingesteld',
+      notSet: 'Geen toegangscode ingesteld - vereist om in te schakelen',
+      placeholder: 'Voer een code van 8 tekens in',
+      placeholderChange: 'Voer een nieuwe code in om te wijzigen',
+      hint: 'Moet precies 8 tekens zijn. Wordt door slicers gebruikt voor authenticatie.',
+      charCount: '({{count}}/8)',
+      inheritedFromTarget: 'Overgenomen van doelprinter',
+      derivedFromTargetHint: 'Gebruikt de toegangscode van de doelprinter. De bridge stuurt de slicerauthenticatie door naar de echte printer, dus de codes moeten overeenkomen — bewerk de toegangscode van de printer om deze waarde te wijzigen.',
+      reveal: 'Toegangscode tonen',
+      hide: 'Toegangscode verbergen',
+    },
+    targetPrinter: {
+      title: 'Doelprinter',
+      configured: 'Proxydoel geconfigureerd',
+      notConfigured: 'Geen doelprinter geselecteerd - vereist voor proxymodus',
+      placeholder: 'Selecteer een printer...',
+      hint: 'Selecteer de printer waarnaar slicerverkeer via de proxy moet worden doorgestuurd. De printer moet in LAN-modus staan.',
+      noPrinters: 'Geen printers geconfigureerd. Voeg eerst een printer toe om de proxymodus te gebruiken.',
+    },
+    remoteInterface: {
+      title: 'Overschrijving netwerkinterface',
+      configured: 'Interface-overschrijving actief',
+      optional: 'Optioneel - gebruik dit als het automatisch gedetecteerde IP-adres onjuist is (bijv. meerdere NIC\'s, Docker, VPN)',
+      placeholder: 'Automatisch detecteren (standaard)...',
+      hint: 'Overschrijf het IP-adres dat via SSDP wordt geadverteerd en in het TLS-certificaat wordt gebruikt. Handig wanneer Bambuddy meerdere netwerkinterfaces heeft.',
+    },
+    mode: {
+      title: 'Modus',
+      archive: 'Archief',
+      archiveDesc: 'Bestanden direct archiveren',
+      review: 'Beoordelen',
+      reviewDesc: 'Beoordelen vóór archiveren',
+      queue: 'Wachtrij',
+      queueDesc: 'Archiveren en aan wachtrij toevoegen',
+      proxy: 'Proxy',
+      proxyDesc: 'Doorsturen naar echte printer',
+    },
+    autoDispatch: {
+      title: 'Automatisch verzenden',
+      description: 'Start afdrukken automatisch wanneer ze aan de wachtrij worden toegevoegd. Als dit uitstaat, wachten afdrukken op handmatige verzending.',
+    },
+    queueForceColorMatch: {
+      title: 'Kleurmatch afdwingen',
+      description: 'Weiger een taak te verzenden naar een printer waarop niet exact het juiste filamenttype en de juiste kleur zijn geladen. Standaard uit — zonder dit gebruikt de wachtrij alleen modelmatching en kan een printer worden gekozen waarop de verkeerde kleur is geladen.',
+    },
+    saveAmsMapping: {
+      title: 'AMS-toewijzing opslaan',
+      description: 'Sla de AMS-slotkeuze van de slicer zelf (uit de MQTT-opdracht project_file) op in het archief, zodat bij later opnieuw afdrukken exact dezelfde fysieke spoel wordt gebruikt in plaats van opnieuw een keuze af te leiden uit type/kleur van het bestand. Standaard uit.',
+    },
+    gcodeInjection: {
+      title: 'G-code-injectie',
+      description: 'Pas de per model ingestelde G-codefragmenten uit Instellingen toe op taken van deze VP. Standaard uit.',
+    },
+    tailscaleDisabled: {
+      title: 'Tailscale-integratie',
+      description: 'Schakel dit in om deze VP als via Tailscale beschikbaar te markeren. Toont het Tailscale-adres van de host, zodat je weet welk IP-adres je in de slicer moet invoeren. De stap voor het importeren van de CA blijft hetzelfde — deze schakelaar heeft geen invloed op certificaten.',
+    },
+    setupRequired: {
+      title: 'Installatie vereist',
+      description: 'Voor de functie virtuele printer is aanvullende systeemconfiguratie nodig voordat deze werkt. Dit omvat port forwarding, firewallregels en platformspecifieke instellingen.',
+      readGuide: 'Lees de installatiehandleiding voordat je dit inschakelt',
+    },
+    archiveNameSource: {
+      title: 'Bron voor archiefnaam',
+      description: 'Kies hoe nieuwe archieven worden genoemd wanneer bestanden via de virtuele printer binnenkomen. "Metadata" gebruikt de in de slicer opgenomen Titel uit de 3MF (standaard). "Bestandsnaam" gebruikt de bestandsnaam die Bambu Studio via FTP heeft verzonden. Let op: Bambu Studio overschrijft de naam die je in het dialoogvenster "naar printer verzenden" invoert met het Titel-veld van de 3MF wanneer dat aanwezig is, waardoor beide modi vaak dezelfde tekst opleveren.',
+      metadata: 'Metadata',
+      filename: 'Bestandsnaam',
+    },
+    caCert: {
+      title: 'Slicercertificaat',
+      description: 'Virtuele printers gebruiken een TLS-certificaat dat is ondertekend door de Bambuddy-CA. Importeer dit CA-certificaat eenmalig in de trust store van je slicer zodat de verbinding wordt geaccepteerd — je hoeft het niet via de opdrachtregel te kopiëren.',
+      copy: 'Kopiëren',
+      copied: 'Gekopieerd',
+      download: 'Downloaden',
+      fingerprint: 'SHA-256',
+    },
+    howItWorks: {
+      title: 'Hoe het werkt',
+      step1: 'Op hetzelfde LAN verschijnen virtuele printers automatisch in je slicer (Bambu Studio / OrcaSlicer) via detectie. Vanaf andere netwerken voeg je ze handmatig toe met IP-adres en toegangscode.',
+      step2: 'Gebruik in de modi Archief, Beoordelen en Wachtrij de knop "Verzenden" in je slicer om 3MF-bestanden naar Bambuddy te uploaden. De slicer toont "Afdruk geslaagd" — het bestand wordt opgeslagen, niet afgedrukt.',
+      step3: 'In proxymodus stuurt de virtuele printer al het verkeer door naar een echte printer — afdrukken starten direct alsof je rechtstreeks verbonden bent.',
+    },
+    status: {
+      title: 'Statusdetails',
+      printerName: 'Printernaam',
+      model: 'Model',
+      serialNumber: 'Serienummer',
+      mode: 'Modus',
+      pendingFiles: 'Bestanden in behandeling',
+      targetPrinter: 'Doelprinter',
+      ftpPort: 'FTP-poort',
+      mqttPort: 'MQTT-poort',
+      ftpConnections: 'FTP-verbindingen',
+      mqttConnections: 'MQTT-verbindingen',
+    },
+    toast: {
+      updated: 'Instellingen virtuele printer bijgewerkt',
+      failedToUpdate: 'Instellingen bijwerken mislukt',
+      copyFailed: 'Kopiëren mislukt — probeer de tekst handmatig te selecteren',
+      accessCodeRequired: 'Stel eerst een toegangscode in',
+      targetPrinterRequired: 'Selecteer eerst een doelprinter',
+      bindIpRequired: 'Stel eerst een bind-IP in',
+      accessCodeEmpty: 'Toegangscode mag niet leeg zijn',
+      accessCodeLength: 'Toegangscode moet precies 8 tekens zijn',
+      targetCodeChangedRebind: 'De toegangscode komt nu overeen met de nieuwe doelprinter. Voeg dit apparaat opnieuw toe in je slicer om de nieuwe code te gebruiken.',
+      created: 'Virtuele printer aangemaakt',
+      failedToCreate: 'Virtuele printer aanmaken mislukt',
+      deleted: 'Virtuele printer verwijderd',
+      failedToDelete: 'Virtuele printer verwijderen mislukt',
+    },
+    list: {
+      title: 'Virtuele printers',
+      add: 'Toevoegen',
+      addFirst: 'Virtuele printer toevoegen',
+      empty: 'Geen virtuele printers geconfigureerd. Voeg er een toe om te beginnen.',
+    },
+    bindIp: {
+      title: 'Bind-interface',
+      placeholder: 'Selecteer interface...',
+      hint: 'Netwerkinterface waaraan deze virtuele printer wordt gekoppeld. Moet uniek zijn per printer.',
+    },
+    proxy: {
+      accessCodeHint: 'Gebruik in proxymodus de toegangscode van je doelprinter in de slicer. De verbinding wordt transparant doorgestuurd naar de echte printer.',
+    },
+    addDialog: {
+      title: 'Virtuele printer toevoegen',
+      name: 'Naam',
+      hint: 'Je kunt de toegangscode, doelprinter en andere instellingen na het aanmaken configureren.',
+      create: 'Maken',
+    },
+    deleteConfirm: {
+      title: 'Virtuele printer verwijderen',
+      message: 'Weet je zeker dat je "{{name}}" wilt verwijderen? Hiermee worden alle services voor deze printer gestopt.',
+    },
+  },
+
+  // Model Viewer
+  modelViewer: {
+    openInSlicer: 'Openen in slicer',
+    openInSlicerWith: 'Openen in {{slicer}}',
+    moreSlicerOptions: 'Meer sliceropties',
+    openInSlicerFailed: 'Kon niet openen in slicer',
+    tabs: {
+      model: '3D-model',
+    },
+    notAvailable: 'niet beschikbaar',
+    plates: 'Platen',
+    allPlates: 'Alle platen',
+    plateNumber: 'Plaat {{number}}',
+    plateCount: '{{count}} plaat',
+    plateCount_other: '{{count}} platen',
+    objectCount: '{{count}} object',
+    objectCount_other: '{{count}} objecten',
+    filamentCount: '{{count}} filament',
+    filamentCount_other: '{{count}} filamenten',
+    eta: 'ETA {{minutes}} min',
+    noPreview: 'Geen voorbeeld beschikbaar voor dit bestand',
+    pagination: {
+      pageOf: 'Pagina {{current}} van {{total}}',
+      prev: 'Vorige',
+      next: 'Volgende',
+    },
+    errors: {
+      failedToLoad: 'Bestand laden mislukt',
+      noMeshes: 'Geen meshes gevonden in 3MF-bestand',
+      unsupportedFormat: 'Niet-ondersteund bestandsformaat',
+    },
+  },
+
+  // Maintenance type descriptions (built-in)
+  maintenanceDescriptions: {
+    lubricateCarbonRods: 'Breng smeermiddel aan op de carbonstangen voor soepele beweging',
+    lubricateRails: 'Breng smeermiddel aan op de lineaire rails voor soepele beweging',
+    cleanNozzle: 'Reinig hotend en nozzle om verstoppingen te voorkomen',
+    checkBelts: 'Controleer de riemspanning voor nauwkeurige afdrukken',
+    cleanBuildPlate: 'Reinig de bouwplaat voor betere hechting',
+    checkExtruder: 'Controleer de extrudertandwielen op slijtage',
+    checkCooling: 'Controleer of de koelventilatoren goed werken',
+    generalInspection: 'Algemene printerinspectie',
+    cleanCarbonRods: 'Reinig de carbonstangen om wrijving te verminderen',
+    lubricateSteelRods: 'Breng smeermiddel aan op de stalen stangen voor soepele beweging',
+    cleanSteelRods: 'Reinig de stalen stangen om wrijving te verminderen',
+    cleanLinearRails: 'Veeg de lineaire rails schoon om stof en vuil te verwijderen',
+    checkPtfeTube: 'Controleer de PTFE-buis op slijtage of beschadiging',
+    replaceHepaFilter: 'Vervang het HEPA-filter voor een goede luchtkwaliteit',
+    replaceCarbonFilter: 'Vervang het actieve-koolfilter',
+    lubricateLeftNozzleRail: 'Smeer de linker nozzlerail (H2-serie)',
+  },
+
+  // Smart Plugs
+  haSensors: {
+    label: 'Sensoren',
+    unavailable: 'Niet beschikbaar',
+    blocksPrints: '{{entity}} — houdt afdrukken tegen en geeft een melding',
+    states: {
+      open: 'Open',
+      closed: 'Gesloten',
+      unlocked: 'Ontgrendeld',
+      locked: 'Vergrendeld',
+      detected: 'Gedetecteerd',
+      clear: 'Wissen',
+      wet: 'Nat',
+      dry: 'Droog',
+      problem: 'Probleem',
+      ok: 'OK',
+      running: 'Actief',
+      stopped: 'Gestopt',
+      on: 'Aan',
+      off: 'Uit',
+    },
+    sectionTitle: 'Home Assistant-sensoren',
+    add: 'Sensor toevoegen',
+    addTitle: 'Home Assistant-sensor toevoegen',
+    editTitle: 'Home Assistant-sensor bewerken',
+    empty: 'Nog geen sensoren. Koppel een deurcontact of thermometer uit Home Assistant om deze op een printerkaart te tonen.',
+    unknownPrinter: 'Onbekende printer',
+    badgeBlocks: 'Houdt afdrukken tegen',
+    badgeNotifies: 'Meldt',
+    badgeHidden: 'Verborgen op kaart',
+    printer: 'Printer',
+    entity: 'Entiteit',
+    searchPlaceholder: 'Entiteiten zoeken...',
+    noEntities: 'Geen overeenkomende entiteiten',
+    name: 'Weergavenaam',
+    alertWhen: 'Waarschuwen wanneer',
+    alertNever: 'Nooit — alleen weergeven',
+    alertAbove: 'Boven',
+    alertBelow: 'Onder',
+    alertHint: 'De alarmstatus markeert de sensor op de printerkaart en schakelt de onderstaande opties in.',
+    showOnCard: 'Tonen op printerkaart',
+    notifyOnAlert: 'Stuur een melding wanneer een alarm begint',
+    blockPrint: 'Houd afdrukken in de wachtrij tegen zolang er een alarm is',
+    blockPrintHint: 'Taken blijven in de wachtrij en starten vanzelf zodra de sensor weer normaal is. Wordt genegeerd als Home Assistant niet bereikbaar is.',
+    toast: {
+      created: 'Sensor toegevoegd',
+      updated: 'Sensor opgeslagen',
+      deleted: 'Sensor verwijderd',
+    },
+    error: {
+      pickEntity: 'Kies een Home Assistant-entiteit',
+      nameRequired: 'Voer een weergavenaam in',
+      printerRequired: 'Kies een printer',
+      alertRequired: 'Stel eerst een alarmvoorwaarde in',
+    },
+  },
+  locationHaSensors: {
+    sectionTitle: 'Home Assistant-sensoren (opslaglocaties)',
+    sectionDescription:
+      'Koppel temperatuur-, luchtvochtigheids- of batterijsensoren uit Home Assistant aan je opslaglocaties om live waarden te tonen op de filamentkaart en in de tabel. Per locatie is één sensor per categorie toegestaan.',
+    add: 'Sensor toevoegen',
+    addTitle: 'Home Assistant-sensor toevoegen',
+    editTitle: 'Home Assistant-sensor bewerken',
+    empty: 'Nog geen sensoren. Koppel een temperatuur- of luchtvochtigheidssensor uit Home Assistant om deze op de filamentkaart te tonen.',
+    unknownLocation: 'Onbekende locatie',
+    location: 'Opslaglocatie',
+    showOnCard: 'Tonen op filamentkaart',
+    overwriteConfirm: {
+      title: 'Bestaande sensor vervangen?',
+      messageTemperature: '"{{location}}" heeft al een temperatuursensor gekoppeld: {{name}}. Doorgaan vervangt deze.',
+      messageHumidity: '"{{location}}" heeft al een luchtvochtigheidssensor gekoppeld: {{name}}. Doorgaan vervangt deze.',
+      messageBattery: '"{{location}}" heeft al een batterijsensor gekoppeld: {{name}}. Doorgaan vervangt deze.',
+    },
+    autoAdd: {
+      confirmTitle: 'De andere sensoren ook toevoegen?',
+      confirmMessage: 'Bambuddy heeft ook andere sensoren voor deze locatie gevonden. Kies welke je ook wilt toevoegen.',
+      added: 'Ook toegevoegd: {{names}}',
+      noneFound: 'Geen bijpassende temperatuur-, luchtvochtigheids- of batterijsensoren gevonden voor deze locatie.',
+      onlyThisOne: 'Alleen deze',
+    },
+    options: {
+      buttonLabel: 'Sensoropties',
+      title: 'Opties voor locatiesensoren',
+      description: 'Deze waarden worden gebruikt wanneer een temperatuur-, luchtvochtigheids- of batterijsensor automatisch wordt gekoppeld naast de eerste sensor van een locatie.',
+      generalSettings: 'Algemene instellingen',
+      pollInterval: 'Bijwerkinterval (seconden)',
+      pollIntervalHint: 'Hoe vaak Bambuddy Home Assistant raadpleegt en sensorwaarden op het scherm ververst. Minimaal 60 seconden.',
+      colorizeValues: 'Sensorwaarden inkleuren op basis van hun alarmdrempels',
+      aboveColor: 'Kleur boven drempel',
+      belowColor: 'Kleur onder drempel',
+      optimalColor: 'Kleur bij optimale waarde',
+      colors: {
+        red: 'Rood',
+        orange: 'Oranje',
+        yellow: 'Geel',
+        green: 'Groen',
+        blue: 'Blauw',
+        purple: 'Paars',
+        pink: 'Roze',
+      },
+      saved: 'Opties opgeslagen',
+      saveFailed: 'Opslaan van opties mislukt',
+      reset: 'Herstellen',
+      resetConfirm: {
+        title: 'Alle bestaande locatiesensoren herstellen?',
+        message:
+          'Dit overschrijft de alarmdrempels, meldingen en kaartzichtbaarheid van elke bestaande temperatuur-, luchtvochtigheids- en batterijsensor van opslaglocaties met de hierboven ingestelde waarden, en herstelt de naam van elke sensor naar de vriendelijke naam uit Home Assistant — printersensoren blijven ongewijzigd. Dit kan niet ongedaan worden gemaakt. Afhankelijk van het aantal sensoren kan dit even duren.',
+      },
+      resetDone: 'Toegepast op {{count}} locatiesensoren',
+      resetFailed: 'Herstellen van locatiesensoren mislukt',
+    },
+    deleteAllConfirm: {
+      title: 'Alle sensoren verwijderen?',
+      message: 'Hiermee worden alle {{count}} sensoren verwijderd die aan "{{location}}" zijn gekoppeld. Dit kan niet ongedaan worden gemaakt.',
+    },
+    currentlyBound: 'Momenteel gekoppeld: {{entity}}',
+    error: {
+      locationRequired: 'Kies een opslaglocatie',
+    },
+    toast: {
+      created: 'Sensor toegevoegd',
+      updated: 'Sensor opgeslagen',
+      deleted: 'Sensor verwijderd',
+    },
+  },
+  smartPlugs: {
+    offline: 'Offline',
+    admin: 'Beheerder',
+    openPlugAdminPage: 'Beheerpagina van stekker openen',
+    deleteSmartPlug: 'Slimme stekker verwijderen',
+    turnOnSmartPlug: 'Slimme stekker inschakelen',
+    turnOffSmartPlug: 'Slimme stekker uitschakelen',
+    turnOn: 'Inschakelen',
+    turnOff: 'Uitschakelen',
+    addSmartPlug: {
+      scanningNetwork: 'Netwerk scannen...',
+      chooseEntity: 'Kies een entiteit...',
+      connectionFailed: 'Verbinding mislukt',
+      searchEntities: 'Entiteiten zoeken...',
+      searchPowerSensors: 'Vermogenssensoren zoeken...',
+      searchEnergySensors: 'Energiesensoren zoeken...',
+      placeholders: {
+        plugName: 'Woonkamerstekker',
+        mqttStateOnValue: 'AAN, true, 1',
+        mqttSameAsPower: 'Hetzelfde als vermogensonderwerp, of anders',
+      },
+    },
+    // SmartPlugCard
+    linkedTo: 'Gekoppeld aan:',
+    monitorOnly: 'Alleen bewaken',
+    alerts: 'Waarschuwingen',
+    scheduleOn: 'Aan {{time}}',
+    scheduleOff: 'Uit {{time}}',
+    on: 'Aan',
+    off: 'Uit',
+    power: 'Vermogen',
+    kwhToday: 'kWh vandaag',
+    settings: 'Instellingen',
+    automationSettings: 'Automatiseringsinstellingen',
+    showInSwitchbar: 'Tonen in schakelbalk',
+    quickAccessSidebar: 'Snelle toegang vanuit de zijbalk',
+    enabled: 'Ingeschakeld',
+    enableAutomation: 'Automatisering voor deze stekker inschakelen',
+    autoOn: 'Automatisch aan',
+    autoOnDescription: 'Inschakelen wanneer afdruk start',
+    autoOff: 'Automatisch uit',
+    autoOffDescription: 'Uitschakelen wanneer afdruk is voltooid (eenmalig)',
+    autoOffPersistent: 'Ingeschakeld houden',
+    autoOffPersistentDescription: 'Ingeschakeld blijven tussen afdrukken in plaats van eenmalig',
+    controlsPrinterPower: 'Voedt de printer',
+    controlsPrinterPowerDescription: 'Schakel dit uit als deze stekker alleen een accessoire voedt (filterventilator, verlichting). Anders wordt de printer als offline gemarkeerd wanneer je hem uitschakelt.',
+    autoOffAfterDrying: 'Automatisch uitschakelen na drogen',
+    autoOffAfterDryingDescription: 'Uitschakelen wanneer AMS-drogen is voltooid',
+    delayAfterDryingMinutes: 'Droogvertraging (minuten)',
+    turnOffDelayMode: 'Modus uitschakelvertraging',
+    time: 'Tijd',
+    temp: 'Temp',
+    delayMinutes: 'Vertraging (minuten)',
+    tempThreshold: 'Temperatuurdrempel (°C)',
+    tempThresholdDescription: 'Schakelt uit wanneer de nozzle onder deze temperatuur is afgekoeld',
+    edit: 'Bewerken',
+    deleteConfirm: 'Weet je zeker dat je "{{name}}" wilt verwijderen? Dit kan niet ongedaan worden gemaakt.',
+    turnOnConfirm: 'Weet je zeker dat je "{{name}}" wilt inschakelen?',
+    turnOffConfirm: 'Weet je zeker dat je "{{name}}" wilt uitschakelen? Hierdoor wordt de stroom naar het aangesloten apparaat onderbroken.',
+    failedToTurn: '"{{name}}" {{action}} schakelen mislukt',
+    unknown: 'Onbekend',
+    // AddSmartPlugModal
+    addTitle: 'Slimme stekker toevoegen',
+    editTitle: 'Slimme stekker bewerken',
+    stopScanning: 'Scannen stoppen',
+    discoverTasmota: 'Tasmota-apparaten zoeken',
+    foundDevices: '{{count}} apparaat/apparaten gevonden - klik om te selecteren:',
+    noDevicesFound: 'Geen Tasmota-apparaten gevonden op je netwerk',
+    haNotConfigured: 'Home Assistant is niet geconfigureerd. Stel dit in via',
+    haSettingsPath: 'Instellingen → Netwerk → Home Assistant',
+    selectEntity: 'Selecteer entiteit *',
+    ipAddress: 'IP-adres *',
+    nameLabel: 'Naam *',
+    username: 'Gebruikersnaam',
+    password: 'Wachtwoord',
+    authHint: 'Laat leeg als je Tasmota-apparaat geen authenticatie vereist',
+    linkToPrinter: 'Koppelen aan printer',
+    noPrinter: 'Geen printer (alleen handmatige bediening)',
+    linkingDescription: 'Koppelen maakt automatisch in-/uitschakelen mogelijk wanneer afdrukken starten/voltooien',
+    powerAlerts: 'Vermogenswaarschuwingen',
+    alertAbove: 'Waarschuwen indien boven (W)',
+    alertBelow: 'Waarschuwen indien onder (W)',
+    alertDescription: 'Ontvang een melding wanneer het stroomverbruik deze drempels overschrijdt. Laat leeg om die richting uit te schakelen.',
+    dailySchedule: 'Dagelijks schema',
+    turnOnAt: 'Inschakelen om',
+    turnOffAt: 'Uitschakelen om',
+    scheduleDescription: 'Schakel de stekker dagelijks automatisch op deze tijden in/uit. Laat leeg om die actie over te slaan.',
+    showOnPrinterCard: 'Tonen op printerkaart',
+    displayOnPrinterCard: 'Knop op printerkaart weergeven',
+    connectedResult: 'Verbonden!',
+    deviceLabel: 'Apparaat: {{name}} - ',
+    stateLabel: 'Status: {{state}}',
+    test: 'Testen',
+    delete: 'Verwijderen',
+    save: 'Opslaan',
+    add: 'Toevoegen',
+    cancel: 'Annuleren',
+    failedToStartScan: 'Scan starten mislukt',
+    nameRequired: 'Naam is verplicht',
+    entityRequired: 'Entiteit is verplicht voor Home Assistant-stekkers',
+    mqttTopicRequired: 'Ten minste één MQTT-topic moet zijn geconfigureerd voor bewaking van vermogen, energie of status',
+    loadingEntities: 'Entiteiten laden...',
+    loading: 'Laden...',
+    failedToLoadEntities: 'Entiteiten laden mislukt: {{error}}',
+    noEntitiesMatching: 'Geen entiteiten gevonden die overeenkomen met "{{search}}"',
+    noEntitiesAvailable: 'Geen entiteiten beschikbaar',
+    searchingEntities: 'Alle entiteiten doorzoeken ({{count}} gevonden)',
+    showingEntities: 'Toont switch, light, input_boolean ({{count}} beschikbaar)',
+    energyMonitoringOptional: 'Energiemonitoring (optioneel)',
+    energyMonitoringHint: 'Zoek en selecteer sensoren die vermogens-/energiegegevens leveren.',
+    powerSensorW: 'Vermogenssensor (W)',
+    energyTodayKwh: 'Energie vandaag (kWh)',
+    totalEnergyKwh: 'Totale energie (kWh)',
+    noMatchingSensors: 'Geen overeenkomende sensoren',
+    none: 'Geen',
+    mqttNotConfigured: 'MQTT-broker niet geconfigureerd. Stel het brokeradres in via',
+    mqttSettingsPath: 'Instellingen → Netwerk → MQTT-publicatie',
+    mqttNotConfiguredSuffix: '(je hoeft publicatie niet in te schakelen; vul alleen de brokergegevens in).',
+    mqttMonitorOnlyDescription: 'MQTT-stekkers ontvangen vermogens-/energiegegevens via een MQTT-abonnement. In-/uitschakelen is niet beschikbaar - gebruik je MQTT-broker of domoticasysteem.',
+    powerMonitoring: 'Vermogensbewaking',
+    energyMonitoring: 'Energiemonitoring',
+    stateMonitoring: 'Statusbewaking',
+    optional: 'optioneel',
+    topic: 'Topic',
+    jsonPath: 'JSON-pad',
+    multiplier: 'Vermenigvuldigingsfactor',
+    onValue: 'AAN-waarde',
+    mqttPowerHint: 'JSON-pad haalt de waarde uit een JSON-payload (bijv. "power_l1"). Laat leeg als het topic ruwe numerieke waarden publiceert.\nGebruik vermenigvuldigingsfactor 0.001 voor mW→W, 1000 voor kW→W.',
+    mqttEnergyHint: 'JSON-pad haalt de waarde uit een JSON-payload. Laat leeg voor ruwe waarden.\nGebruik vermenigvuldigingsfactor 0.001 voor Wh→kWh, 1000 voor MWh→kWh.',
+    mqttStateHint: 'JSON-pad haalt de waarde uit een JSON-payload. Laat leeg voor ruwe waarden.\nAAN-waarde: de exacte tekenreeks die "AAN" betekent. Laat leeg voor automatische detectie (ON, true, 1).',
+    // REST smart plug
+    restControl: 'Bedienen',
+    restOnUrl: 'URL voor AAN',
+    restOffUrl: 'URL voor UIT',
+    restOnBody: 'Requestbody AAN',
+    restOffBody: 'Requestbody UIT',
+    restMethod: 'HTTP-methode',
+    restHeaders: 'Aangepaste headers (JSON)',
+    restStatusUrl: 'Status-URL',
+    restStatusPath: 'JSON-pad status',
+    restStatusOnValue: 'AAN-waarde',
+    restPowerUrl: 'Vermogens-URL',
+    restPowerPath: 'JSON-pad vermogen',
+    restPowerMultiplier: 'Vermenigvuldigingsfactor vermogen',
+    restEnergyUrl: 'Energie-URL',
+    restEnergyPath: 'JSON-pad energie (vandaag)',
+    restEnergyTotalPath: 'JSON-pad energie (totaal)',
+    restEnergyTotalMultiplier: 'Vermenigvuldigingsfactor totaal',
+    restEnergyTotalPathHint: 'bijv. aenergy.total',
+    restEnergyTotalHint:
+      'Veel stekkers — waaronder alle Shelly\'s — rapporteren alleen een totale teller die nooit wordt gereset. Die hoort hier en niet in het veld hierboven: als je hem als verbruik van vandaag leest, wordt hij om middernacht niet gereset en blijven Gisteren en Totaal leeg. Bambuddy berekent Vandaag en Gisteren hieruit; het duurt één of twee dagen aan metingen voordat die gevuld zijn. Een Shelly rapporteert wattuur, dus gebruik een vermenigvuldigingsfactor van 0.001.',
+    restEnergyMultiplier: 'Vermenigvuldigingsfactor energie',
+    restUrlRequired: 'Ten minste één URL (AAN of UIT) is vereist voor REST-stekkers',
+    restHeadersHint: 'bijv. {"Authorization": "Bearer your-token"}',
+    restBodyHint: 'bijv. ON, {"state": "on"}',
+    restStatusHint: 'URL om de huidige status op te vragen',
+    restPathHint: 'bijv. state of data.power.status',
+    restPowerUrlHint: 'Afzonderlijke URL voor vermogensgegevens (gebruikt Status-URL als deze leeg is)',
+    restEnergyUrlHint: 'Afzonderlijke URL voor energiegegevens (gebruikt Status-URL als deze leeg is)',
+    restEnergyHint: 'Elke waarde kan een eigen URL gebruiken of terugvallen op de Status-URL. Gebruik vermenigvuldigingsfactoren voor eenheidsconversie (bijv. 0.001 om Wh naar kWh om te rekenen).',
+    testConnection: 'Verbinding testen',
+    connectionSuccess: 'Verbinding geslaagd',
+    noSwitchesInSwitchbar: 'Geen schakelaars in de schakelbalk',
+    enableSwitchbarHint: 'Schakel "Tonen in schakelbalk" in via Instellingen > Slimme stekkers',
+  },
+
+  // Notifications
+  notifications: {
+    // Provider types
+    providerTypes: {
+      callmebot: 'CallMeBot/WhatsApp',
+      ntfy: 'ntfy',
+      pushover: 'Pushover',
+      telegram: 'Telegram',
+      email: 'E-mail',
+      discord: 'Discord',
+      webhook: 'Webhook',
+      homeassistant: 'Home Assistant',
+      bark: 'Bark',
+    },
+    // Provider descriptions
+    providerDescriptions: {
+      email: 'SMTP-e-mailmeldingen',
+      telegram: 'Meldingen via Telegram-bot',
+      discord: 'Versturen naar Discord-kanaal via webhook',
+      ntfy: 'Gratis, zelf te hosten pushmeldingen',
+      pushover: 'Eenvoudige, betrouwbare pushmeldingen',
+      callmebot: 'Gratis WhatsApp-meldingen via CallMeBot',
+      webhook: 'Algemene HTTP POST naar elke URL',
+      homeassistant: 'Blijvende meldingen in het Home Assistant-dashboard',
+      bark: 'iOS-pushmeldingen via Bark (zelf te hosten)',
+    },
+    // NotificationProviderCard
+    lastSuccess: 'Laatste: {{date}}',
+    error: 'Fout',
+    printer: 'Printer:',
+    allPrinters: 'Alle printers',
+    sendTestNotification: 'Testmelding verzenden',
+    eventSettings: 'Gebeurtenisinstellingen',
+    enabled: 'Ingeschakeld',
+    sendFromProvider: 'Meldingen via deze provider verzenden',
+    // Event categories
+    printEvents: 'Afdrukgebeurtenissen',
+    printerStatus: 'Printerstatus',
+    amsAlarms: 'AMS-alarmen',
+    amsHtAlarms: 'AMS-HT-alarmen',
+    printQueue: 'Afdrukwachtrij',
+    // Event tags (badges)
+    start: 'Start',
+    plateCheck: 'Plaatcontrole',
+    complete: 'Voltooid',
+    failed: 'Mislukt',
+    stopped: 'Gestopt',
+    progress: 'Voortgang',
+    offline: 'Offline',
+    lowFilament: 'Weinig filament',
+    maintenance: 'Onderhoud',
+    amsHumidity: 'AMS-luchtvochtigheid',
+    amsTemp: 'AMS-temp',
+    amsDryingSuspended: 'Drogen onderbroken',
+    amsHtHumidity: 'AMS-HT-luchtvochtigheid',
+    amsHtTemp: 'AMS-HT-temp',
+    bedCooled: 'Bed afgekoeld',
+    firstLayer: 'Eerste laag',
+    quiet: 'Stil',
+    digest: 'Samenvatting {{time}}',
+    // Event labels (expanded settings)
+    printStarted: 'Afdruk gestart',
+    plateNotEmpty: 'Plaat niet leeg',
+    plateNotEmptyDescription: 'Objecten gedetecteerd vóór afdrukken',
+    plateClearRequired: 'Bevestiging lege plaat vereist',
+    plateClearRequiredDescription: 'Afdruk voltooid, wachtrij wacht op bevestiging van de plaat',
+    plateClear: 'Plaat leeg',
+    printCompleted: 'Afdruk voltooid',
+    bedCooledLabel: 'Bed afgekoeld',
+    bedCooledDescription: 'Bed na afdruk afgekoeld tot onder drempel',
+    firstLayerCompleteLabel: 'Eerste laag voltooid',
+    firstLayerCompleteDescription: 'Melden met momentopname wanneer de eerste laag is voltooid',
+    missingSpoolAssignmentLabel: 'Ontbrekende spoeltoewijzing',
+    billingChargeFailedLabel: 'Kostenregistratie mislukt',
+    billingChargeFailedDescription: 'Melden wanneer afdrukkosten niet konden worden geregistreerd',
+    missingSpoolAssignmentDescription: 'Melden wanneer een afdruk start en vereiste trays geen toegewezen spoel hebben',
+    printFailed: 'Afdruk mislukt',
+    printStopped: 'Afdruk gestopt',
+    progressMilestones: 'Voortgangsmijlpalen',
+    progressMilestonesDescription: 'Melden bij 25%, 50%, 75%',
+    printerOffline: 'Printer offline',
+    printerError: 'Printerfout',
+    aiFailureDetection: 'AI-foutdetectie',
+    aiFailureDetectionDescription: 'Melden wanneer Obico AI een mogelijke afdrukfout detecteert',
+    lowFilamentLabel: 'Weinig filament',
+    maintenanceDue: 'Onderhoud nodig',
+    maintenanceDueDescription: 'Melden wanneer onderhoud nodig is',
+    amsHumidityHigh: 'AMS-luchtvochtigheid hoog',
+    amsHumidityHighDescription: 'Luchtvochtigheid van normale AMS overschrijdt drempel',
+    amsTemperatureHigh: 'AMS-temperatuur hoog',
+    amsTemperatureHighDescription: 'Temperatuur van normale AMS overschrijdt drempel',
+    amsDryingSuspendedTitle: 'Automatisch drogen gestopt',
+    amsDryingSuspendedDescription: 'Automatisch drogen heeft een AMS-eenheid opgegeven omdat de luchtvochtigheid niet onder de drempel kwam',
+    amsHtHumidityHigh: 'AMS-HT-luchtvochtigheid hoog',
+    amsHtHumidityHighDescription: 'AMS-HT-luchtvochtigheid overschrijdt drempel',
+    amsHtTemperatureHigh: 'AMS-HT-temperatuur hoog',
+    amsHtTemperatureHighDescription: 'AMS-HT-temperatuur overschrijdt drempel',
+    // Inventory stock alert events
+    inventoryAlerts: 'Voorraadwaarschuwingen',
+    stockReorderAlert: 'Bestelwaarschuwing',
+    stockReorderAlertDescription: 'SKU heeft het bestelpunt bereikt',
+    stockBreakAlert: 'Voorraadtekortwaarschuwing',
+    stockBreakAlertDescription: 'Voorraad raakt op voordat aanvulling arriveert',
+    // Queue events
+    jobAdded: 'Taak toegevoegd',
+    jobAddedDescription: 'Taak toegevoegd aan wachtrij',
+    jobAssigned: 'Taak toegewezen',
+    jobAssignedDescription: 'Modelgebaseerde taak toegewezen aan printer',
+    jobStarted: 'Taak gestart',
+    jobStartedDescription: 'Wachtrijtaak begonnen met afdrukken',
+    jobWaiting: 'Taak wacht',
+    jobWaitingDescription: 'Taak wacht op filament of printer',
+    jobSkipped: 'Taak overgeslagen',
+    jobSkippedDescription: 'Taak overgeslagen (vorige mislukt)',
+    jobFailed: 'Taak mislukt',
+    jobFailedDescription: 'Taak kon niet starten',
+    queueComplete: 'Wachtrij voltooid',
+    queueCompleteDescription: 'Alle wachtrijtaken voltooid',
+    // Quiet hours
+    quietHours: 'Stille uren',
+    noNotificationsDuring: 'Geen meldingen tijdens deze uren',
+    editProviderToChangeQuietHours: 'Bewerk provider om stille uren te wijzigen',
+    // Daily digest
+    dailyDigest: 'Dagelijkse samenvatting',
+    batchNotifications: 'Bundel meldingen in één dagelijkse samenvatting',
+    sendAt: 'Verzenden om {{time}}',
+    editProviderToChangeDigestTime: 'Bewerk provider om tijdstip van samenvatting te wijzigen',
+    // Actions
+    edit: 'Bewerken',
+    deleteProvider: 'Meldingsprovider verwijderen',
+    deleteConfirm: 'Weet je zeker dat je "{{name}}" wilt verwijderen? Dit kan niet ongedaan worden gemaakt.',
+    delete: 'Verwijderen',
+    // AddNotificationModal
+    addTitle: 'Meldingsprovider toevoegen',
+    editTitle: 'Meldingsprovider bewerken',
+    nameLabel: 'Naam *',
+    namePlaceholder: 'Mijn meldingen',
+    providerTypeLabel: 'Providertype *',
+    configuration: 'Configuratie',
+    testConfiguration: 'Configuratie testen',
+    printerFilter: 'Printerfilter',
+    onlyFromPrinter: 'Alleen meldingen verzenden voor gebeurtenissen van deze printer',
+    quietHoursDnd: 'Stille uren (Niet storen)',
+    quietStart: 'Start',
+    quietEnd: 'Einde',
+    dailyDigestLabel: 'Dagelijkse samenvatting',
+    sendDigestAt: 'Samenvatting verzenden om',
+    digestCollected: 'Gebeurtenissen worden verzameld en op dit tijdstip als één samenvatting verzonden',
+    notificationEvents: 'Meldingsgebeurtenissen',
+    progressPercent: '(25%, 50%, 75%)',
+    bedCooledAfterPrint: '(nadat de afdruk is voltooid)',
+    haSensorAlert: 'Sensorwaarschuwing',
+    haSensorAlertDescription: '(een gekoppelde Home Assistant-sensor vereist aandacht)',
+    locationHaSensorAlert: 'Sensoralarm opslaglocatie',
+    locationHaSensorAlertDescription: '(een Home Assistant-sensor die aan een opslaglocatie is gekoppeld vereist aandacht)',
+    // Per-event ntfy priority (#990)
+    eventPriority: {
+      sectionTitle: 'ntfy-prioriteit',
+      helpNtfy: 'Kies een prioriteit voor elke ingeschakelde gebeurtenis. ntfy gebruikt deze om waarschuwingen op te schalen (geluid, zichtbaarheid, pushgedrag). Niveaus die hier niet zijn ingesteld gebruiken de standaardwaarde van de ntfy-server.',
+      min: 'Min',
+      low: 'Laag',
+      default: 'Standaard',
+      high: 'Hoog',
+      urgent: 'Urgent',
+    },
+    cancel: 'Annuleren',
+    save: 'Opslaan',
+    add: 'Toevoegen',
+    nameRequired: 'Naam is verplicht',
+    fieldRequired: '{{field}} is verplicht',
+    haDataInvalid: 'Het veld Data moet een geldig JSON-object zijn, bijv. {"priority": "high", "ttl": 0}',
+    // Config field labels
+    phoneNumber: 'Telefoonnummer',
+    apiKey: 'API-sleutel',
+    serverUrl: 'Server-URL',
+    topic: 'Topic',
+    authToken: 'Authenticatietoken',
+    userKey: 'Gebruikerssleutel',
+    appToken: 'App-token',
+    priority: 'Prioriteit',
+    pushoverRetry: 'Noodherhaling (s)',
+    pushoverExpire: 'Noodverval (s)',
+    botToken: 'Bottoken',
+    chatId: 'Chat-ID',
+    telegramThreadId: 'Forumtopic-ID',
+    telegramThreadIdHelp: 'Optioneel. Plaatst in één specifiek topic van een forumgroep — het laatste nummer in de topiclink (t.me/c/.../25). Laat leeg voor het algemene topic.',
+    telegramThreadIdInvalid: 'Forumtopic-ID moet een getal zijn.',
+    smtpServer: 'SMTP-server',
+    smtpPort: 'SMTP-poort',
+    security: 'Beveiliging',
+    authentication: 'Authenticatie',
+    username: 'Gebruikersnaam',
+    password: 'Wachtwoord',
+    fromEmail: 'Afzender-e-mail',
+    toEmail: 'Naar e-mailadres',
+    webhookUrl: 'Webhook-URL',
+    payloadFormat: 'Payloadformaat',
+    authorization: 'Autorisatie',
+    titleFieldName: 'Naam titelveld',
+    messageFieldName: 'Naam berichtveld',
+    // NotificationTemplateEditor
+    editTemplate: 'Sjabloon bewerken: {{name}}',
+    titleLabel: 'Titel',
+    bodyLabel: 'Inhoud',
+    titlePlaceholder: 'Meldingstitel...',
+    bodyPlaceholder: 'Meldingsinhoud...',
+    availableVariables: 'Beschikbare variabelen',
+    clickToInsert: 'Klik om op de cursorpositie in de inhoud in te voegen',
+    livePreview: 'Livevoorbeeld',
+    hide: 'Verbergen',
+    show: 'Tonen',
+    loadingPreview: 'Voorbeeld laden...',
+    enterTemplateContent: 'Voer sjablooninhoud in om een voorbeeld te zien',
+    titlePreview: 'Titel:',
+    bodyPreview: 'Inhoud:',
+    resetToDefault: 'Terugzetten naar standaard',
+    titleRequired: 'Titel is verplicht',
+    bodyRequired: 'Inhoud is verplicht',
+    // NotificationLogViewer
+    notificationLog: 'Meldingslogboek',
+    showFailedOnly: 'Alleen mislukt',
+    last24Hours: 'Laatste 24 uur',
+    last7Days: 'Laatste 7 dagen',
+    last30Days: 'Laatste 30 dagen',
+    last90Days: 'Laatste 90 dagen',
+    justNow: 'Zojuist',
+    noFailedNotifications: 'Geen mislukte meldingen',
+    noNotificationsLogged: 'Geen meldingen geregistreerd',
+    unknownProvider: 'Onbekende provider',
+    logTitle: 'Titel',
+    logMessage: 'Bericht',
+    logError: 'Fout',
+    logProvider: 'Provider: {{type}}',
+    logTime: 'Tijd: {{time}}',
+    refresh: 'Vernieuwen',
+    clearOld: 'Oude wissen',
+    statsSummary: 'Laatste {{days}} dagen:',
+    statsNotifications: 'meldingen',
+    statsSent: '{{count}} verzonden',
+    statsFailed: '{{count}} mislukt',
+    // Event type labels (for log viewer)
+    eventTypes: {
+      print_start: 'Afdruk gestart',
+      print_complete: 'Afdruk voltooid',
+      print_failed: 'Afdruk mislukt',
+      print_stopped: 'Afdruk gestopt',
+      print_progress: 'Voortgang',
+      printer_offline: 'Printer offline',
+      printer_error: 'Printerfout',
+      filament_low: 'Weinig filament',
+      maintenance_due: 'Onderhoud nodig',
+      test: 'Testen',
+    },
+    // User email notification preferences
+    userEmail: {
+      title: 'Meldingen',
+      emailNotifications: 'E-mailmeldingen',
+      emailNotificationsDesc: 'Ontvang e-mailmeldingen voor je eigen afdruktaken. E-mails worden verzonden via de SMTP-instellingen van het systeem die zijn geconfigureerd onder Geavanceerde authenticatie.',
+      sendingTo: 'Meldingen worden verzonden naar',
+      noEmailWarning: 'Je account heeft geen e-mailadres. Neem contact op met een beheerder om er een toe te voegen.',
+      printJobNotifications: 'Meldingen voor afdruktaken',
+      printJobNotificationsDesc: 'Kies welke gebeurtenissen e-mailmeldingen activeren voor afdruktaken die je indient.',
+      printJobStarts: 'Afdruktaak start',
+      printJobStartsDesc: 'Ontvang een melding wanneer je afdruktaak begint.',
+      printJobFinishes: 'Afdruktaak voltooid',
+      printJobFinishesDesc: 'Ontvang een melding wanneer je afdruktaak succesvol is voltooid.',
+      printErrors: 'Afdrukfouten',
+      printErrorsDesc: 'Ontvang een melding wanneer je afdruktaak mislukt of een fout tegenkomt.',
+      printJobStops: 'Afdruktaak gestopt',
+      printJobStopsDesc: 'Ontvang een melding wanneer je afdruktaak wordt geannuleerd of gestopt.',
+      saveSuccess: 'Meldingsvoorkeuren opgeslagen.',
+      saveError: 'Meldingsvoorkeuren opslaan mislukt.',
+    },
+  },
+
+  // Rich Text Editor
+  richTextEditor: {
+    bold: 'Vet',
+    italic: 'Cursief',
+    underline: 'Onderstrepen',
+    bulletList: 'Opsomming',
+    numberedList: 'Genummerde lijst',
+    alignLeft: 'Links uitlijnen',
+    alignCenter: 'Centreren',
+    alignRight: 'Rechts uitlijnen',
+    addLink: 'Link toevoegen',
+    removeLink: 'Link verwijderen',
+  },
+
+  // External Links
+  externalLinks: {
+    title: 'Zijbalklinks',
+    sidebarLayout: 'Zijbalk',
+    sidebarLayoutDescription: 'Toon of verberg ingebouwde pagina\'s, voeg externe links toe en sleep items om de navigatie in de zijbalk te herschikken.',
+    systemPages: 'Bambuddy-pagina\'s',
+    externalLinks: 'Externe links',
+    visibleInSidebar: 'Zichtbaar in zijbalk',
+    hiddenFromSidebar: 'Verborgen in zijbalk',
+    requiredInSidebar: 'Verplicht in zijbalk',
+    hidePage: 'Pagina verbergen',
+    showPage: 'Pagina tonen',
+    settingsCannotBeHidden: 'Instellingen kunnen niet worden verborgen',
+    noLinksConfigured: 'Geen externe links geconfigureerd',
+    deleteLink: 'Link verwijderen',
+    removeCustomIcon: 'Aangepast pictogram verwijderen',
+    openInNewTab: 'Openen in nieuw tabblad',
+    placeholders: {
+      linkName: 'Mijn link',
+    },
+  },
+
+  // Keyboard Shortcuts Modal
+  keyboardShortcuts: {
+    title: 'Sneltoetsen',
+    navigation: 'Navigatie',
+    archivesSection: 'Archieven',
+    kProfilesSection: 'K-profielen',
+    generalSection: 'Algemeen',
+    shortcuts: {
+      goToPrinters: 'Ga naar Printers',
+      goToArchives: 'Ga naar Archieven',
+      goToQueue: 'Ga naar Wachtrij',
+      goToStats: 'Ga naar Statistieken',
+      goToProfiles: 'Ga naar Cloudprofielen',
+      goToSettings: 'Ga naar Instellingen',
+      focusSearch: 'Zoeken activeren',
+      openUploadModal: 'Uploadvenster openen',
+      clearSelection: 'Selectie wissen / invoerveld verlaten',
+      contextMenu: 'Contextmenu op kaarten',
+      refreshProfiles: 'Profielen vernieuwen',
+      newProfile: 'Nieuw profiel',
+      exitSelectionMode: 'Selectiemodus afsluiten',
+      showHelp: 'Deze hulp tonen',
+    },
+    footer: 'Druk op Esc of klik buiten het venster om te sluiten',
+  },
+
+  // Notification Log
+  notificationLog: {
+    title: 'Meldingslogboek',
+    events: {
+      printStarted: 'Afdruk gestart',
+      printComplete: 'Afdruk voltooid',
+      printFailed: 'Afdruk mislukt',
+      printStopped: 'Afdruk gestopt',
+      progress: 'Voortgang',
+      printerOffline: 'Printer offline',
+      printerError: 'Printerfout',
+      lowFilament: 'Weinig filament',
+      maintenanceDue: 'Onderhoud nodig',
+      test: 'Testen',
+    },
+    timeAgo: {
+      justNow: 'Zojuist',
+      minutesAgo: '{{minutes}}m geleden',
+      hoursAgo: '{{hours}}u geleden',
+    },
+  },
+
+  // Restore/Backup Modal
+  restoreBackup: {
+    title: 'Back-up herstellen',
+    restoring: 'Herstellen...',
+    restoreComplete: 'Herstel voltooid',
+    restoreFailed: 'Herstel mislukt',
+    importSettings: 'Instellingen importeren uit een back-upbestand',
+    pleaseWait: 'Wacht terwijl je gegevens worden hersteld',
+    clickToSelect: 'Klik om een back-upbestand te selecteren (.json of .zip)',
+    howDuplicateHandling: 'Zo werkt afhandeling van duplicaten:',
+    categories: {
+      printers: 'Printers',
+      smartPlugs: 'Slimme stekkers',
+      notificationProviders: 'Meldingsproviders',
+      filaments: 'Filamenten',
+      archives: 'Archieven',
+      pendingUploads: 'Openstaande uploads',
+      settingsTemplates: 'Instellingen en sjablonen',
+    },
+    matchingInfo: {
+      printers: 'gekoppeld op serienummer',
+      smartPlugs: 'gekoppeld op IP-adres',
+      notificationProviders: 'gekoppeld op naam',
+      filaments: 'gekoppeld op naam + type + merk',
+      archives: 'overeenkomst op inhoudshash',
+      pendingUploads: 'gekoppeld op bestandsnaam',
+      settingsTemplates: 'altijd overschreven',
+    },
+    replaceExisting: 'Bestaande gegevens vervangen',
+    keepExisting: 'Bestaande gegevens behouden',
+    replaceDescription: 'Items die al bestaan overschrijven met gegevens uit de back-up',
+    keepDescription: 'Alleen items herstellen die nog niet bestaan',
+    caution: 'Let op:',
+    cautionText: 'Overschrijven vervangt je huidige configuraties door de back-upgegevens. Toegangscodes van printers worden om veiligheidsredenen nooit overschreven.',
+    itemsRestored: 'Items hersteld',
+    itemsSkipped: 'Items overgeslagen',
+    restored: 'Hersteld',
+    skipped: 'Overgeslagen (bestaan al)',
+    filesLabel: 'Bestanden (3MF, miniaturen, enz.)',
+    newApiKeysGenerated: 'Nieuwe API-sleutels gegenereerd',
+    newApiKeysWarning: 'Deze sleutels worden maar één keer getoond. Kopieer ze nu!',
+    processingBackup: 'Back-upbestand verwerken...',
+    noDataFound: 'Er zijn geen gegevens gevonden om uit het back-upbestand te herstellen.',
+    failedToRestore: 'Back-up herstellen mislukt. Controleer het bestandsformaat.',
+  },
+
+  // Backup Export Modal
+  backupExport: {
+    title: 'Back-up exporteren',
+    selectData: 'Selecteer gegevens om op te nemen',
+    selectAll: 'Alles selecteren',
+    selectNone: 'Niets selecteren',
+    categoryDescriptions: {
+      settings: 'Taal, thema, updatevoorkeuren',
+      notifications: 'ntfy, Pushover, Discord, enz.',
+      templates: 'Aangepaste berichtsjablonen',
+      smartPlugs: 'Tasmota-stekkerconfiguraties',
+      externalLinks: 'Zijbalklinks naar externe services',
+      printers: 'Printerinformatie (toegangscodes uitgesloten)',
+      plateDetection: 'Referentieafbeeldingen van lege plaat',
+      filaments: 'Filamenttypen en kosten',
+      maintenance: 'Aangepaste onderhoudsschema\'s',
+      archives: 'Alle afdrukgegevens + bestanden (3MF, miniaturen, foto\'s)',
+      projects: 'Projecten, BOM-items en bijlagen',
+      pendingUploads: 'Uploads van virtuele printer die op beoordeling wachten',
+      apiKeys: 'Webhook-API-sleutels (nieuwe sleutels worden gegenereerd bij import)',
+    },
+    requiresPrinters: 'Vereist dat Printers is geselecteerd',
+    zipFileWarning: 'Er wordt een ZIP-bestand aangemaakt.',
+    zipFileDescription: 'Bevat alle 3MF-bestanden, miniaturen, timelapses en foto\'s. Dit kan enige tijd duren en een groot bestand opleveren.',
+    includeAccessCodes: 'Toegangscodes opnemen',
+    includeAccessCodesDescription: 'Voor overdracht naar een andere machine',
+    includeAccessCodesWarning: 'Toegangscodes worden als platte tekst opgenomen. Bewaar dit back-upbestand veilig!',
+    categoriesSelected: '{{selectedCount}} categorieën geselecteerd',
+  },
+
+  // Pending Uploads Panel
+  pendingUploads: {
+    placeholders: {
+      notes: 'Voeg notities over deze afdruk toe...',
+    },
+    discardUpload: 'Upload verwijderen',
+    archiveAllUploads: 'Alle uploads archiveren',
+    discardAllUploads: 'Alle uploads verwijderen',
+    archive: 'Archief',
+    timeAgo: {
+      justNow: 'Zojuist',
+      minutesAgo: '{{minutes}}m geleden',
+      hoursAgo: '{{hours}}u geleden',
+      daysAgo: '{{days}}d geleden',
+    },
+  },
+
+  // API Browser
+  apiBrowser: {
+    placeholders: {
+      requestBody: 'JSON-requestbody...',
+      searchEndpoints: 'Endpoints zoeken...',
+    },
+  },
+
+  // Configure AMS Slot Modal
+  configureAmsSlot: {
+    title: 'AMS-slot configureren',
+    slotConfigured: 'Slot geconfigureerd!',
+    configuringSlot: 'Slot configureren:',
+    slotLabel: '{{ams}} Slot {{slot}}',
+    searchPresets: 'Presets zoeken...',
+    colorPlaceholder: 'Kleurnaam of hex (bijv. brown, FF8800)',
+    clearCustomColor: 'Aangepaste kleur wissen',
+    noCloudPresets: 'Geen cloudpresets. Meld je aan bij Bambu Cloud om te synchroniseren.',
+    noPresetsAvailable: 'Geen presets beschikbaar. Meld je aan bij Bambu Cloud of importeer lokale profielen.',
+    noMatchingPresets: 'Geen overeenkomende presets gevonden.',
+    custom: 'Aangepast',
+    builtin: 'Ingebouwd',
+    orcaCloud: 'Orca Cloud',
+    bambuCloud: 'Bambu Cloud',
+    settingsSentToPrinter: 'Instellingen naar printer verzonden',
+    filamentProfile: 'Filamentprofiel',
+    kProfileLabel: 'K-profiel (Pressure Advance)',
+    filteringFor: 'Filteren op: {{material}}',
+    noKProfile: 'Geen K-profiel (gebruik standaard 0.020)',
+    noMatchingKProfiles: 'Geen overeenkomende K-profielen gevonden. Standaard K=0.020 wordt gebruikt.',
+    otherKProfiles: 'Andere K-profielen op deze printer',
+    selectFilamentFirst: 'Selecteer eerst een filamentprofiel',
+    kFromCalibration: 'K={{value}} uit printerkalibratie',
+    customColorLabel: 'Aangepaste kleur (optioneel)',
+    presetColors: '{{name}} kleuren:',
+    showLessColors: 'Minder kleuren tonen',
+    showMoreColors: 'Meer kleuren tonen',
+    clear: 'Wissen',
+    hexLabel: 'Hex: #{{hex}}',
+    resetting: 'Resetten...',
+    resetSlot: 'Slot resetten',
+    cancel: 'Annuleren',
+    configuring: 'Configureren...',
+    configureSlot: 'Sleuf configureren',
+  },
+
+  // Git Backup Settings
+  githubBackup: {
+    title: 'Git-back-up',
+    history: 'Geschiedenis',
+    downloadBackup: 'Back-up downloaden',
+    restoreBackup: 'Back-up herstellen',
+    noBackupsYet: 'Nog geen back-ups',
+  },
+
+  // Email Settings
+  emailSettings: {
+    placeholders: {
+      fromName: 'BamBuddy',
+    },
+  },
+
+  // Tag Management Modal
+  tagManagement: {
+    searchTags: 'Tags zoeken...',
+    renameTag: 'Tag hernoemen',
+    deleteTag: 'Tag verwijderen',
+  },
+
+  // Notification Template Editor
+  notificationTemplates: {
+    placeholders: {
+      title: 'Meldingstitel...',
+      body: 'Meldingsinhoud...',
+    },
+  },
+
+  // Batch Tag Modal
+  batchTag: {
+    placeholders: {
+      newTag: 'Nieuwe tag invoeren...',
+    },
+  },
+
+  // Photo Gallery Modal
+  photoGallery: {
+    deletePhoto: 'Foto verwijderen',
+  },
+
+  // Filament Hover Card
+  filamentHoverCard: {
+    copySpoolUuid: 'Spoel-UUID kopiëren',
+  },
+
+  // K Profiles View
+  kProfilesView: {
+    hasNote: 'Heeft notitie',
+    copyProfile: 'Profiel kopiëren',
+  },
+
+  // Layout/Navigation
+  layout: {
+    openMenu: 'Menu openen',
+    noPermissionSystemInfo: 'Je hebt geen machtiging om systeeminformatie te bekijken',
+  },
+
+  // Dashboard
+  dashboard: {
+    dragToReorder: 'Sleep om te herschikken',
+    hideWidget: 'Widget verbergen',
+  },
+
+  // Notification Provider Card
+  notificationProviderCard: {
+    deleteNotificationProvider: 'Meldingsprovider verwijderen',
+  },
+
+  // File Manager Modal
+  fileManagerModal: {
+    closeFileManager: 'Bestandsbeheer sluiten',
+    sortFiles: 'Bestanden sorteren',
+    goToParentFolder: 'Ga naar bovenliggende map',
+    threeView: '3D-weergave',
+  },
+
+  // Embedded Camera Viewer
+  embeddedCameraViewer: {
+    refreshStream: 'Stream vernieuwen',
+    close: 'Sluiten',
+    zoomOut: 'Uitzoomen',
+    resetZoom: 'Zoom resetten',
+    zoomIn: 'Inzoomen',
+    dragToResize: 'Sleep om formaat te wijzigen',
+  },
+
+  // Timelapse Viewer
+  timelapseViewer: {
+    skipBack5s: '5 sec terug',
+    skipForward5s: '5 sec vooruit',
+  },
+
+  // Notification Providers
+  notificationProviders: {
+    descriptions: {
+      email: 'SMTP-e-mailmeldingen',
+      telegram: 'Meldingen via Telegram-bot',
+      discord: 'Versturen naar Discord-kanaal via webhook',
+      ntfy: 'Gratis, zelf te hosten pushmeldingen',
+      pushover: 'Eenvoudige, betrouwbare pushmeldingen',
+      callmebot: 'Gratis WhatsApp-meldingen via CallMeBot',
+      webhook: 'Algemene HTTP POST naar elke URL',
+    },
+  },
+
+  // Log Viewer
+  logViewer: {
+    searchPlaceholder: 'Zoek bericht of loggernaam...',
+    noLogEntries: 'Geen logboekitems gevonden',
+  },
+
+  // Switchbar Popover
+  switchbarPopover: {
+    noSwitchesInSwitchbar: 'Geen schakelaars in de schakelbalk',
+  },
+
+  // Project Page Modal
+  projectPageModal: {
+    placeholders: {
+      title: 'Titel',
+      designer: 'Ontwerper',
+      license: 'Licentie',
+      description: 'Voer beschrijving in...',
+      profileTitle: 'Profieltitel',
+      profileDescription: 'Profielbeschrijving...',
+    },
+  },
+
+  // Spoolman Settings
+  spoolmanSettings: {},
+
+  // Time
+  time: {
+    unknown: '-',
+    waiting: 'Wachten',
+    justNow: 'Zojuist',
+    now: 'Nu',
+    minsAgo: '{{count}}m geleden',
+    inMins: 'over {{count}}m',
+    hoursAgo: '{{count}}u geleden',
+    inHours: 'over {{count}}u',
+    daysAgo: '{{count}}d geleden',
+    inDays: 'over {{count}}d',
+  },
+
+  // SpoolBuddy Kiosk
+  spoolbuddy: {
+    nav: {
+      dashboard: 'Dashboard',
+      ams: 'AMS',
+      inventory: 'Voorraad',
+      writeTag: 'Schrijven',
+      settings: 'Instellingen',
+    },
+    status: {
+      nfcReady: 'NFC gereed',
+      nfcOff: 'NFC uit',
+      offline: 'Offline',
+      online: 'Online',
+      noPrinters: 'Geen printers',
+      deviceOffline: 'Apparaat offline',
+      waitingConnection: 'Wachten op apparaatverbinding...',
+      systemReady: 'Systeem gereed',
+      status: 'Status',
+    },
+    dashboard: {
+      readyToScan: 'Gereed om te scannen',
+      idleMessage: 'Plaats een spoel op de weegschaal om deze te identificeren',
+      nfcHint: 'NFC-tag wordt automatisch gelezen',
+      device: 'Apparaat',
+      syncWeight: 'Gewicht synchroniseren',
+      weightSynced: 'Gesynchroniseerd!',
+      unknownTag: 'Onbekende tag',
+      newTag: 'Nieuwe tag gedetecteerd',
+      onScale: 'op weegschaal',
+      linkSpool: 'Koppelen aan spoel',
+      linkTagTitle: 'Tag aan spoel koppelen',
+      linkTag: 'Tag koppelen',
+      selectSpool: 'Selecteer een spoel om deze tag aan te koppelen:',
+      noUntagged: 'Geen spoelen zonder tags gevonden',
+      tagDetected: 'Tag gedetecteerd',
+      noTag: 'Geen tag',
+      tagId: 'Tag',
+      grossWeight: 'Brutogewicht',
+      spoolSize: 'Spoelgrootte',
+      close: 'Sluiten',
+      currentSpool: 'Huidige spoel',
+      plateReady: 'Plaat gereed: {{name}}',
+      plateReadyLabel: 'Platen gereed om vrij te geven',
+      plateClearAction: 'Wissen',
+      plateClearedToast: 'Plaat gemarkeerd als vrij',
+      plateClearFailed: 'Plaat kon niet als vrij worden gemarkeerd',
+    },
+    modal: {
+      spoolDetected: 'Spoel gedetecteerd',
+      assignToAms: 'Aan AMS toewijzen',
+      syncWeight: 'Gewicht synchroniseren',
+      weightSynced: 'Gesynchroniseerd!',
+      syncing: 'Synchroniseren...',
+      newTagDetected: 'Nieuwe tag gedetecteerd',
+      addToInventory: 'Aan voorraad toevoegen',
+      assignToAmsTitle: 'Aan AMS toewijzen',
+      selectSlot: 'Selecteer een slot',
+      assign: 'Toewijzen',
+      assigning: 'Toewijzen...',
+      assignSuccess: 'Toegewezen!',
+      assignPendingInsert: 'Toegewezen. Sleuf wordt geconfigureerd zodra je de spoel plaatst.',
+      assignError: 'Spoel toewijzen mislukt. Probeer het opnieuw.',
+      noPrinterSelected: 'Selecteer een printer...',
+      noAmsDetected: 'Geen AMS gedetecteerd op deze printer',
+      slot: 'Sleuf',
+    },
+    weight: {
+      noReading: 'Geen meting',
+      stable: 'Stabiel',
+      measuring: 'Meten...',
+      tare: 'Tarreren',
+      calibrate: 'Kalibreren',
+    },
+    spool: {
+      remaining: 'Resterend',
+      material: 'Materiaal',
+      brand: 'Merk',
+      color: 'Kleur',
+      coreWeight: 'Kern',
+      labelWeight: 'Label',
+      scaleWeight: 'Weegschaal',
+      netWeight: 'Netto',
+      lastUsed: 'Laatst gebruikt',
+    },
+    ams: {
+      noData: 'Geen AMS gedetecteerd',
+      connectAms: 'Sluit een AMS aan om filamentslots te zien',
+      noPrinter: 'Geen printer geselecteerd',
+      selectPrinter: 'Selecteer een printer in de bovenste balk',
+      printerDisconnected: 'Printer niet verbonden',
+      humidity: 'Luchtvochtigheid',
+      level: 'Niveau',
+      active: 'Actief',
+      slot: 'Sleuf',
+      empty: 'Leeg',
+    },
+    inventory: {
+      search: 'Spoelen zoeken...',
+      empty: 'Geen spoelen in voorraad',
+      noResults: 'Geen overeenkomende spoelen',
+      spools: 'spoelen',
+      addSpool: 'Spoel toevoegen',
+    },
+    settings: {
+      // Tabs
+      tabDevice: 'Apparaat',
+      tabDisplay: 'Weergave',
+      tabScale: 'Weegschaal',
+      tabUpdates: 'Updates',
+      // Device tab
+      nfcReader: 'NFC-lezer',
+      type: 'Type',
+      connection: 'Verbinding',
+      notConnected: 'N.v.t.',
+      deviceInfo: 'Apparaatinformatie',
+      hostname: 'Host',
+      uptime: 'Uptime',
+      systemConfig: 'Backend & authenticatie',
+      backendUrl: 'Bambuddy-backend-URL',
+      apiToken: 'API-token',
+      apiTokenPlaceholder: 'Voer API-token in',
+      saveConfig: 'Configuratie opslaan',
+      systemQueued: 'Configuratie in wachtrij geplaatst.',
+      nfcDiagnostic: 'NFC-diagnose',
+      scaleDiagnostic: 'Weegschaaldiagnose',
+      readTagDiagnostic: 'Tagleesdiagnose',
+      testNfc: 'Lezer testen',
+      testScale: 'Nauwkeurigheid testen',
+      testReadTag: 'Tag lezen',
+      systemFieldsRequired: 'Backend-URL is verplicht.',
+      // Display tab
+      brightness: 'Helderheid',
+      saved: 'Opgeslagen',
+      noBacklight: 'Geen DSI-achtergrondverlichting gedetecteerd. Helderheidsregeling vereist een DSI-display.',
+      screenBlank: 'Time-out schermuitschakeling',
+      screenBlankDesc: 'Scherm schakelt uit na inactiviteit. Raak aan om te activeren.',
+      displayNote: 'Helderheid wordt toegepast als softwarefilter.',
+      // Scale tab
+      scaleCalibration: 'Weegschaalkalibratie',
+      currentWeight: 'Huidig gewicht',
+      tareOffset: 'Tarreren',
+      calFactor: 'Factor',
+      knownWeight: 'Bekend gewicht',
+      calStep1: 'Verwijder alle items van de weegschaal en druk op Nulpunt instellen.',
+      calStep2: 'Plaats een bekend gewicht op de weegschaal.',
+      setZero: 'Nulpunt instellen',
+      calibrateNow: 'Kalibreren',
+      calibrated: 'Gekalibreerd',
+      tareSet: 'Tareeropdracht verzonden. Wachten op apparaat...',
+      tareComplete: 'Tarreren voltooid!',
+      tareTimedOut: 'Time-out bij tarreren — draait de SpoolBuddy-daemon?',
+      tareFailed: 'Tareeropdracht verzenden mislukt',
+      zeroSet: 'Nulpunt ingesteld. Plaats een bekend gewicht op de weegschaal.',
+      calibrationDone: 'Kalibratie voltooid!',
+      calibrationFailed: 'Kalibratie mislukt',
+      lastCalibrated: 'Laatst gekalibreerd',
+      stable: 'Stabiel',
+      settling: 'Stabiliseren...',
+      firmware: 'Firmware',
+      scale: 'Weegschaal',
+      noDevice: 'Geen SpoolBuddy-apparaat gevonden',
+      // Updates tab
+      daemonVersion: 'Daemonversie',
+      currentVersion: 'Huidig',
+      versionPending: 'Wachten op daemon...',
+      checking: 'Controleren...',
+      checkUpdates: 'Controleren op updates',
+      updateAvailable: 'Update beschikbaar',
+      updateInstructions: 'Bijwerken via SSH: voer het SpoolBuddy-installatiescript uit om te upgraden.',
+      upToDate: 'Up-to-date',
+      includeBeta: 'Bètaversies opnemen',
+    },
+    writeTag: {
+      tabExisting: 'Bestaande spoel',
+      tabNew: 'Nieuwe spoel',
+      tabReplace: 'Tag vervangen',
+      searchPlaceholder: 'Zoeken op materiaal, kleur, merk...',
+      noUntaggedSpools: 'Geen spoelen zonder tags',
+      noTaggedSpools: 'Geen spoelen met tags',
+      selectSpool: 'Selecteer een spoel en plaats daarna een lege NTAG op de lezer',
+      placeTag: 'Plaats een NTAG op de lezer',
+      tagReady: 'Tag gedetecteerd — gereed om te schrijven',
+      writeTag: 'Tag schrijven',
+      replaceTag: 'Tag vervangen',
+      writing: 'Tag schrijven...',
+      waiting: 'Wachten op SpoolBuddy...',
+      writeSuccess: 'Tag succesvol geschreven!',
+      writeFailed: 'Schrijven mislukt',
+      queueFailed: 'Schrijfopdracht in wachtrij plaatsen mislukt',
+      tryAgain: 'Opnieuw proberen',
+      cancel: 'Annuleren',
+      replaceWarning: 'Oude tag wordt ontkoppeld. Nieuwe tag vervangt deze.',
+      deviceOffline: 'SpoolBuddy is offline',
+      material: 'Materiaal',
+      colorName: 'Kleurnaam',
+      color: 'Kleur',
+      brand: 'Merk',
+      weight: 'Gewicht (g)',
+      createSpool: 'Spoel aanmaken',
+      creating: 'Maken...',
+      spoolCreated: 'Spoel aangemaakt! Gereed om te schrijven.',
+      createFailed: 'Spoel aanmaken mislukt',
+      incompleteDataWarning: 'Tag geschreven met onvolledige Spoolman-gegevens',
+    },
+    quickMenu: {
+      printerPower: 'Printervoeding',
+      systemControls: 'Systeem',
+      restartDaemon: 'Daemon herstarten',
+      restartBrowser: 'Browser herstarten',
+      reboot: 'Opnieuw opstarten',
+      shutdown: 'Afsluiten',
+      swipeToClose: 'Veeg omlaag om te sluiten',
+      confirmTitle: 'Bevestigen',
+      confirmShutdown: 'Weet je zeker dat je SpoolBuddy wilt uitschakelen? Je hebt fysieke toegang nodig om hem weer in te schakelen.',
+      confirmReboot: 'Weet je zeker dat je SpoolBuddy opnieuw wilt opstarten?',
+      confirmRestartDaemon: 'SpoolBuddy-daemon opnieuw starten? NFC en de weegschaal zijn tijdelijk niet beschikbaar.',
+      confirmRestartBrowser: 'Kioskbrowser opnieuw starten? Het scherm wordt kort zwart.',
+      confirm: 'Bevestigen',
+      confirmPlugOn: '{{name}} inschakelen?',
+      confirmPlugOff: '{{name}} uitschakelen?',
+      turnOn: 'Inschakelen',
+      turnOff: 'Uitschakelen',
+    },
+  },
+
+  diagnostic: {
+    modalTitle: 'Verbindingsdiagnose — {{name}}',
+    running: 'Diagnose uitvoeren...',
+    runningElapsed: 'Diagnose uitvoeren... ({{elapsed}}s)',
+    waitingForReportHint: 'Wachten totdat de printer een statusrapport publiceert — dit kan maximaal {{max}} seconden duren.',
+    runFailed: 'Diagnose kon niet worden uitgevoerd: {{error}}',
+    retry: 'Opnieuw uitvoeren',
+    runButton: 'Diagnose uitvoeren',
+    sectionTitle: 'Verbindingsdiagnose',
+    sectionDescription: 'Controleer waarom een printer geen verbinding maakt of niet afdrukt — bereikbaarheid van poorten, LAN Developer Mode, Docker-netwerkmodus en inloggegevens.',
+    noPrinters: 'Geen printers geconfigureerd.',
+    overall: {
+      ok: 'Geen problemen gevonden — de printerverbinding ziet er gezond uit.',
+      warnings: 'De printer zou moeten werken, maar sommige punten vereisen aandacht.',
+      problems: 'Problemen gevonden die verklaren waarom de printer geen verbinding maakt of niet afdrukt.',
+    },
+    check: {
+      port_mqtt: {
+        title: 'Besturingspoort (MQTT 8883)',
+        pass: 'Bereikbaar — de printer accepteert besturingsverbindingen.',
+        fail: 'Poort 8883 is niet bereikbaar. De printer is uitgeschakeld, heeft een ander IP-adres of een firewall blokkeert de poort. Controleer het IP-adres van de printer en of niets poort 8883 blokkeert.',
+      },
+      port_ftps: {
+        title: 'Bestandsoverdrachtpoort (FTPS 990)',
+        pass: 'Bereikbaar — het verzenden van afdrukbestanden werkt.',
+        warn: 'Poort 990 is niet bereikbaar. Monitoring kan nog werken, maar het verzenden van afdrukken naar de printer mislukt. Zorg dat poort 990 niet wordt geblokkeerd.',
+        warn_no_tls: 'Poort 990 is open, maar de bestandsservice van de printer voltooit geen TLS-handshake. Afdrukbestanden, covers en timelapses kunnen niet worden opgehaald, waardoor archieven leeg blijven. Start de printer opnieuw — alleen de poort vrijgeven helpt niet.',
+      },
+      external_storage: {
+        title: 'Verzonden bestanden op externe opslag bewaren (installatiestap 4)',
+        pass: 'De printer meldt dat deze optie aanstaat — verzonden bestanden worden op de SD-kaart opgeslagen en archieven bevatten miniaturen en slicermetadata.',
+        fail: 'De printer meldt dat deze optie uitstaat. Schakel "Verzonden bestanden op externe opslag bewaren" in — op nieuwere firmware (P2S 01.02 / Bambu Studio 2.6+) staat de schakelaar in de Afdrukinstellingen van de printer; op oudere versies in het tabblad Apparaat van Bambu Studio / OrcaSlicer. Zonder deze optie mist elke gearchiveerde afdruk zijn miniatuur en slicermetadata.',
+        skip: 'Niet gecontroleerd — hiervoor is een actieve MQTT-verbinding nodig. Bij oudere slicers, waar deze instelling alleen in de slicer staat, rapporteert de printer hem nooit. Daardoor kan deze controle slagen terwijl de optie uitstaat — controleer installatiestap 4 handmatig.',
+        skip_unsupported_model: 'Dit model heeft een SD-slot maar geen manier om de optie in te schakelen — de huidige P1-seriefirmware toont de schakelaar niet in Bambu Studio en de printer heeft geen scherm. Hier is niets te herstellen; gearchiveerde afdrukken kunnen miniaturen en slicermetadata missen totdat Bambu Lab firmwareondersteuning toevoegt.',
+        fail_no_media: 'De optie staat aan, maar de printer meldt dat er geen kaart of stick in het slot zit, waardoor verzonden bestanden nergens kunnen worden opgeslagen. Plaats er een en druk opnieuw af — tot die tijd mist elke gearchiveerde afdruk zijn miniatuur en slicermetadata.',
+        warn_internal_storage: 'De optie staat aan en opslag is aanwezig, maar de laatste afdruk ging toch naar de interne opslag van de printer, die Bambuddy niet kan lezen. Bij H2-serie en P2S stuurt de knop Afdrukken van Bambu Studio altijd daarheen, ongeacht deze instelling. Afdrukken worden gearchiveerd met naam en timing, maar zonder miniatuur of slicermetadata. Voor volledige archieven start je afdrukken vanuit Bambuddy of slice je in OrcaSlicer — of gebruik in Bambu Studio Verzenden met Extern geselecteerd en start daarna de afdruk.',
+        warn_internal_history: 'De optie staat aan en er is opslag aanwezig, maar de laatste print liep vanaf een bestand dat al op de printer stond — opnieuw afdrukken via het scherm, starten vanuit Handy, of een eerder verstuurd bestand. Die bibliotheek staat op interne opslag die Bambuddy niet kan lezen, en geen enkele instelling verandert dat, omdat er voor die print niets is verstuurd. Hij wordt nog steeds gearchiveerd met naam en tijden, maar zonder thumbnail of slicer-metadata. Start prints vanuit Bambuddy of verstuur ze vanuit de slicer voor volledige archieven.',
+      },
+      port_rtsps: {
+        title: 'Camerapoort ({{protocol}} {{port}})',
+        pass: 'Bereikbaar — de camerastream werkt.',
+        warn: 'Poort {{port}} is niet bereikbaar. De livecamera werkt niet. Dit heeft geen invloed op afdrukken.',
+      },
+      network_mode: {
+        title: 'Docker-netwerkmodus',
+        pass: 'Draait in host-netwerkmodus.',
+        warn: 'Bambuddy draait met Docker bridge-netwerken. Printerdetectie en de Virtuele printer vereisen host-netwerkmodus — maak de container opnieuw aan met "network_mode: host".',
+        skip: 'Niet actief in Docker — niet van toepassing.',
+      },
+      subnet: {
+        title: 'Netwerksubnet',
+        pass: 'De printer en Bambuddy bevinden zich op hetzelfde subnet.',
+        warn: 'De printer ({{printer_ip}}) en Bambuddy ({{host_ip}}) bevinden zich op verschillende subnetten. Ze kunnen elkaar mogelijk niet bereiken tenzij routing tussen de subnetten is geconfigureerd.',
+        skip: 'Subnet kon niet worden bepaald — overgeslagen.',
+      },
+      mqtt_auth: {
+        title: 'Printerinloggegevens',
+        pass: 'De printer heeft de verbinding geaccepteerd.',
+        fail: 'De printer is bereikbaar, maar Bambuddy is er niet mee verbonden. De toegangscode of het serienummer is waarschijnlijk onjuist — de toegangscode verandert telkens wanneer LAN Only of Developer Mode wordt omgeschakeld, dus kopieer hem opnieuw vanaf het printerscherm. Een printer die opnieuw opstart of het maximumaantal gelijktijdige verbindingen heeft bereikt, kan hetzelfde gedrag vertonen.',
+        fail_auth_rejected: 'De printer heeft de inloggegevens van Bambuddy geweigerd. De toegangscode of het serienummer is onjuist — de toegangscode verandert telkens wanneer LAN Only of Developer Mode wordt omgeschakeld, dus kopieer hem opnieuw vanaf het printerscherm en sla hem op in de printerinstellingen.',
+        skip: 'Niet gecontroleerd — de printer kon niet worden bereikt.',
+      },
+      developer_mode: {
+        title: 'LAN Developer Mode',
+        pass: 'Developer Mode is ingeschakeld.',
+        fail: 'Developer Mode staat UIT op de printer. Schakel deze in bij de LAN-instellingen van de printer — en bevestig met OK. Zonder deze modus starten afdrukken niet.',
+        skip: 'Kon niet worden gecontroleerd — vereist een actieve verbinding met de printer.',
+      },
+      printer_publishing: {
+        title: 'Printer publiceert status',
+        pass: 'De printer publiceert statusupdates — AMS, filamenten en K-profielen worden correct naar de slicer gespiegeld.',
+        fail: 'De MQTT-broker heeft de verbinding geaccepteerd, maar de printer heeft geen statusrapporten gepubliceerd. Dit wordt bijna altijd veroorzaakt door een verkeerd of anders geschreven serienummer — het topic device/<serial>/report is hoofdlettergevoelig. Controleer het serienummer in de printerinstellingen opnieuw aan de hand van het printerscherm.',
+        skip: 'Kon niet worden gecontroleerd — vereist een actieve verbinding met de printer.',
+      },
+    },
+  },
+
+  systemHealth: {
+    sectionTitle: 'Systeemgezondheid',
+    sectionDescription: 'Scant recente logs op bekende problemen die je meestal zelf kunt oplossen voordat ze een supportticket worden.',
+    rescan: 'Opnieuw scannen',
+    clean: 'Geen bekende problemen gevonden in de laatste {{times}} logboekitems.',
+    logUnavailable: 'Bestandslogging is uitgeschakeld, waardoor logs niet kunnen worden gescand. Schakel bestandslogging in om deze controle te gebruiken.',
+    learnMore: 'Hoe op te lossen',
+    fixLabel: 'Oplossing:',
+    occurrences: '{{times}}× gezien — laatst om {{lastSeen}}',
+    category: {
+      layer8: 'Dit kun je zelf oplossen',
+      environment: 'Omgeving',
+      bug: 'Meld dit probleem',
+    },
+    signature: {
+      'ftp-auth-rejected': {
+        name: 'Printer heeft toegangscode geweigerd',
+        cause: 'De printer heeft de login voor bestandsoverdracht geweigerd. De toegangscode is onjuist of gewijzigd nadat Developer Mode werd omgeschakeld.',
+        fix: 'Kopieer de toegangscode opnieuw vanaf het printerscherm (LAN-instellingen) en werk deze bij in de printerinstellingen van Bambuddy.',
+      },
+      'ftp-connection-timeout': {
+        name: 'Time-out bij bestandsoverdrachtverbinding',
+        cause: 'Bambuddy kon de bestandsoverdrachtpoort van de printer (FTPS 990) niet bereiken. De poort is geblokkeerd, de printer staat uit of bevindt zich op een ander subnet.',
+        fix: 'Zorg dat niets poort 990 tussen Bambuddy en de printer blokkeert en dat beide zich op hetzelfde netwerk bevinden.',
+      },
+      'ftp-ssl-error': {
+        name: 'Beveiligde bestandsoverdracht-handshake mislukt',
+        cause: 'De bestandsservice van de printer reageerde op poort 990 zonder TLS. De bestandsserver is vastgelopen — een printerprobleem, geen firewall- of firmwareprobleem.',
+        fix: 'Start de printer opnieuw. Tot die tijd kunnen afdrukbestanden, covers en timelapses niet worden opgehaald; afdrukken zelf wordt niet beïnvloed.',
+      },
+      'bambu-cloud-captcha': {
+        name: 'Bambu Cloud vraagt om een CAPTCHA',
+        cause: 'De anti-misbruiklaag van Bambu geeft dit netwerk een challenge, waardoor aanmelden bij Bambu Cloud niet kan worden voltooid. Dit is gekoppeld aan het openbare IP-adres, niet aan je account of deze installatie.',
+        fix: 'Wacht — dit verdwijnt normaal binnen enkele uren en herhaald aanmelden verlengt het probleem. Om ondertussen verbinding te maken kun je aanmelden met een toegangstoken uit een browsersessie.',
+      },
+      'mqtt-connection-flapping': {
+        name: 'Printerverbinding blijft wegvallen',
+        cause: 'De besturingsverbinding (MQTT 8883) verbreekt en herstelt herhaaldelijk — meestal door een zwakke netwerkverbinding of een gedeeltelijk geblokkeerde poort.',
+        fix: 'Controleer het wifi-signaal bij de printer, gebruik bij voorkeur een bekabelde verbinding en zorg dat poort 8883 betrouwbaar bereikbaar is.',
+      },
+      'camera-connection-refused': {
+        name: 'Camerastream niet bereikbaar',
+        cause: 'De livecamera kon niet worden bereikt op RTSPS-poort 322. De poort is geblokkeerd of de camera/LAN-liveweergave staat uit op de printer.',
+        fix: 'Schakel de camera en LAN-liveweergave op de printer in en zorg dat poort 322 niet wordt geblokkeerd. Dit heeft geen invloed op afdrukken.',
+      },
+      'database-locked': {
+        name: 'Database-schrijfconflict',
+        cause: 'De SQLite-database krijgt onder belasting fouten met "database is locked" — dit komt vaak voor wanneer meerdere printers tegelijk worden gebruikt.',
+        fix: 'Schakel Bambuddy over naar een externe PostgreSQL-database. Zie de PostgreSQL-handleiding in de documentatie.',
+      },
+    },
+  },
+
+  vpDiagnostic: {
+    title: 'Installatiecontrole — {{name}}',
+    runButton: 'Installatiecontrole uitvoeren',
+    running: 'Installatiecontrole uitvoeren...',
+    runFailed: 'Installatiecontrole kon niet worden uitgevoerd: {{error}}',
+    retry: 'Opnieuw uitvoeren',
+    overall: {
+      ok: 'Alle controles geslaagd — deze virtuele printer is correct ingesteld.',
+      warnings: 'De virtuele printer zou moeten werken, maar sommige punten vereisen aandacht.',
+      problems: 'Problemen gevonden die verklaren waarom de slicer deze virtuele printer niet kan zien of gebruiken.',
+    },
+    check: {
+      enabled: {
+        title: 'Virtuele printer ingeschakeld',
+        fail: 'Deze virtuele printer is uitgeschakeld. Zet hem aan om hem detecteerbaar te maken.',
+      },
+      running: {
+        title: 'Services actief',
+        fail: 'De virtuele printer is ingeschakeld, maar de services draaien niet. Controleer het Bambuddy-logboek — een bind-IP-conflict of machtigingsfout voorkomt meestal dat ze starten.',
+      },
+      bind_interface: {
+        title: 'Netwerkinterface koppelen',
+        fail: 'De bind-interface is niet ingesteld of bestaat niet meer op deze host. Kies een huidige interface in de keuzelijst Bind-interface.',
+      },
+      access_code: {
+        title: 'Toegangscode ingesteld',
+        fail: 'Er is geen toegangscode ingesteld. De slicer moet dezelfde toegangscode van 8 tekens krijgen die je hier instelt.',
+      },
+      target_printer: {
+        title: 'Doelprinter',
+        fail: 'Er is geen doelprinter geselecteerd. Proxymodus heeft een echte printer nodig om naar door te sturen.',
+        warn: 'De doelprinter is momenteel offline — proxyverkeer wordt hervat zodra hij opnieuw verbinding maakt.',
+      },
+      port_ftps: {
+        title: 'Bestandsuploadservice (poort {{port}})',
+        fail: 'Niets luistert op poort {{port}} van het bind-IP, waardoor de slicer geen bestanden kan uploaden. Een poortconflict op deze interface is de gebruikelijke oorzaak.',
+      },
+      port_mqtt: {
+        title: 'Besturingsservice (poort {{port}})',
+        fail: 'Niets luistert op poort {{port}} van het bind-IP, waardoor de slicer geen verbinding kan maken of status kan tonen.',
+      },
+      port_bind: {
+        title: 'Detectieservice (poort {{port}})',
+        fail: 'Niets luistert op poort {{port}} van het bind-IP, waardoor de detectiehandshake van de slicer mislukt.',
+      },
+      privileged_ports: {
+        title: 'Binden aan bevoorrechte poort',
+        fail: 'Poort {{port}} is lager dan 1024 en deze service mag deze niet binden — daarom luistert hierboven niets. Voeg AmbientCapabilities=CAP_NET_BIND_SERVICE toe aan /etc/systemd/system/bambuddy.service en herstart, of voer "sudo setcap cap_net_bind_service=+ep $(readlink -f $(which python3))" uit. Voeg bij Docker cap_add: [NET_BIND_SERVICE] toe.',
+      },
+      certificate: {
+        title: 'TLS-certificaat',
+        pass: 'Certificaat gereed. Zorg dat het Bambuddy-CA-certificaat (hierboven) is geïmporteerd in de trust store van je slicer.',
+        fail: 'Het TLS-certificaat voor deze virtuele printer ontbreekt. Controleer of de Bambuddy-datamap beschrijfbaar is.',
+      },
+    },
+  },
+
+  bugReport: {
+    title: 'Bug melden',
+    description: 'Beschrijving',
+    descriptionPlaceholder: 'Wat ging er mis? Beschrijf het probleem...',
+    email: 'E-mail (optioneel)',
+    emailPlaceholder: 'your@email.com',
+    emailPrivacy: 'Als je dit invult, wordt je e-mailadres opgenomen in een ingeklapte sectie van het GitHub-issue zodat de beheerder contact kan opnemen.',
+    screenshot: 'Schermafbeelding',
+    uploadOrPaste: 'Upload, plak of sleep een afbeelding',
+    dataCollectedSummary: 'Welke gegevens worden in het rapport opgenomen?',
+    dataIncluded: 'Opgenomen:',
+    dataIncludedList: 'Appversie, besturingssysteem, architectuur, Python-versie, databasestatistieken (alleen aantallen), printermodellen, aantallen nozzles, firmwareversies, verbindingsstatus, integratiestatus (Spoolman, MQTT, HA), niet-gevoelige instellingen, aantal netwerkinterfaces, Docker-details, dependencyversies.',
+    dataNeverIncluded: 'Nooit opgenomen:',
+    dataNeverIncludedList: 'Printernamen, serienummers, toegangscodes, wachtwoorden, IP-adressen, e-mailadressen, API-sleutels, tokens, webhook-URL\'s, hostnamen of gebruikersnamen.',
+    submit: 'Verzenden',
+    startLogging: 'Debuglogging starten',
+    stepEnableLogging: 'Debuglogging ingeschakeld',
+    stepReproduce: 'Reproduceer het probleem nu',
+    stepStopLogging: 'Stoppen en rapport verzenden',
+    stopAndSubmit: 'Stoppen en verzenden',
+    maxDuration: 'Stopt automatisch na {{minutes}} min',
+    stoppingLogs: 'Logs verzamelen en verzenden...',
+    submitting: 'Bugrapport verzenden...',
+    submittingStepConnection: 'Printerverbindingscontroles uitvoeren',
+    submittingStepVirtualPrinters: 'Installatiecontroles virtuele printer uitvoeren',
+    submittingStepLogScan: 'Recente logboeken scannen op bekende problemen',
+    submittingStepSubmit: 'Rapport naar GitHub verzenden',
+    submitSuccess: 'Bugrapport succesvol verzonden!',
+    submitFailed: 'Bugrapport verzenden mislukt',
+    diagnosticChecking: 'Printerverbindingen controleren...',
+    diagnosticHealthy: 'Verbindingscontrole geslaagd — geen problemen gevonden met je printers.',
+    diagnosticSummary: '{{problems}} van {{total}} printers hebben verbindingsproblemen',
+    diagnosticIntro: 'Een of meer printers hebben een verbindingsprobleem dat je probleem kan veroorzaken. Klap hieronder een printer open om de oplossing te zien — dit oplossen kan het probleem verhelpen zonder bugrapport. Je kunt hieronder nog steeds een rapport verzenden.',
+    logHealthSummary: 'Bekende problemen gevonden in je logs',
+    logHealthIntro: 'Recente logs komen overeen met bekende problemen. Bekijk de oplossingen hieronder — dit oplossen kan het probleem verhelpen zonder bugrapport. Je kunt hieronder nog steeds een rapport verzenden.',
+    thankYou: 'Bedankt!',
+    submitted: 'Je bugrapport is verzonden.',
+    viewIssue: 'Issue bekijken',
+    closeKeepsRecording: 'Je kunt dit paneel sluiten terwijl je het probleem reproduceert — de opname blijft actief en bij opnieuw openen kom je hier terug.',
+    resumeRecording: 'Bugrapportopname — {{elapsed}}. Klik om af te ronden.',
+    resumeReport: 'Rapport hervatten',
+    unexpectedError: 'Er is een onverwachte fout opgetreden',
+  },
+  failureDetection: {
+    title: 'AI-foutdetectie',
+    description: 'Bewaak afdrukken met een zelf-gehoste Obico ML API en voer automatisch acties uit bij gedetecteerde fouten.',
+    mlUrl: 'Obico ML API-URL',
+    mlUrlHint: 'Basis-URL van je zelf-gehoste Obico ml_api-container (bijv. http://192.168.1.10:3333).',
+    mlToken: 'ML API-token (optioneel)',
+    mlTokenPlaceholder: 'Laat leeg als de server zonder ML_API_TOKEN draait',
+    mlTokenHint: 'Moet overeenkomen met de omgevingsvariabele ML_API_TOKEN van je Obico ml_api-container. Laat leeg wanneer de container zonder token draait.',
+    test: 'Testen',
+    testSuccess: 'ML API bereikbaar en gezond.',
+    testSuccessTokenUnknown: 'ML API bereikbaar en gezond. Het token kon niet worden gecontroleerd.',
+    testFailed: 'ML API kon niet worden bereikt.',
+    sensitivity: 'Gevoeligheid',
+    sensitivityLow: 'Laag (minder fout-positieven)',
+    sensitivityMedium: 'Gemiddeld (gebalanceerd)',
+    sensitivityHigh: 'Hoog (vroeg detecteren, meer fout-positieven)',
+    sensitivityHint: 'Past de betrouwbaarheidsdrempels aan die waarschuwingen en fouten activeren.',
+    action: 'Actie bij gedetecteerde fout',
+    actionNotify: 'Alleen melden',
+    actionPause: 'Afdruk pauzeren',
+    actionPauseOff: 'Pauzeren en stroom uitschakelen',
+    pollInterval: 'Controle-interval (seconden)',
+    pollIntervalHint: 'Hoe vaak elke printer tijdens het afdrukken wordt gecontroleerd. Minimaal 5s, maximaal 120s.',
+    externalUrlMissing: 'Externe URL is niet ingesteld.',
+    externalUrlHint: 'De ML API haalt de cameramomentopname via een URL op. Stel de Externe URL in bij Algemene instellingen zodat de ML API-container Bambuddy kan bereiken.',
+    perPrinterTitle: 'Bewaakte printers',
+    perPrinterHint: 'Kies welke printers door de detectieservice worden bewaakt.',
+    monitorAll: 'Alle verbonden printers bewaken',
+    statusTitle: 'Status',
+    serviceRunning: 'Service actief',
+    thresholds: 'Lage / hoge drempels',
+    activePrinters: 'Actieve afdrukken',
+    noActivePrints: 'Er worden momenteel geen afdrukken uitgevoerd.',
+    historyTitle: 'Recente detecties',
+    noHistory: 'Nog geen detecties.',
+  },
+
+  makerworld: {
+    title: 'MakerWorld',
+    description: 'Plak een MakerWorld-model-URL om het rechtstreeks vanuit Bambuddy te importeren en af te drukken — zonder naar de Bambu Handy-app te gaan.',
+    pasteUrlHeader: 'Importeren vanuit MakerWorld',
+    pasteUrlPlaceholder: 'https://makerworld.com/en/models/… of plak een MakerWorld-link',
+    resolveButton: 'Ophalen',
+    signInRequiredTitle: 'Aanmelden bij Bambu Cloud vereist om te downloaden',
+    signInRequiredBody: 'Je kunt modeldetails anoniem bekijken, maar MakerWorld vereist een Bambu Cloud-account om 3MF-bestanden te downloaden.',
+    signInExpiredTitle: 'Bambu Cloud-login verlopen',
+    signInExpiredBody: 'Je bent nog aangemeld bij Bambuddy, maar Bambu Lab accepteert het opgeslagen token niet meer, waardoor downloads mislukken. Meld je opnieuw aan bij Bambu Cloud.',
+    openCloudSettings: 'Cloudinstellingen openen',
+    untitledModel: 'Naamloos model',
+    byCreator: 'door {{name}}',
+    downloadsCount: '{{count}} downloads',
+    licensePrefix: 'Licentie',
+    alreadyImported: 'Al in bibliotheek',
+    openOnMakerworld: 'Openen op MakerWorld',
+    alreadyInLibrary: 'Dit model staat al in je bibliotheek — je vindt het in Bestandsbeheer → MakerWorld',
+    importSuccess: '{{filename}} geïmporteerd — opgeslagen in Bestandsbeheer → MakerWorld',
+    platesHeader: 'Platen ({{count}})',
+    plateDefaultName: 'Plaat {{n}}',
+    materialCount: '{{count}} filamenten',
+    amsRequired: 'AMS vereist',
+    slicedFor: 'Gesliced voor {{printer}}',
+    alsoCompatible: 'Ook als compatibel gemarkeerd: {{printers}}',
+    importToLibrary: 'Opslaan',
+    sliceIn: 'Opslaan en slicen in {{slicer}}',
+    disclaimer: 'De MakerWorld-integratie gebruikt door de community gedocumenteerde API-endpoints. Bambuddy is niet verbonden met of goedgekeurd door MakerWorld of Bambu Lab.',
+    lastImportSuccess: 'Geïmporteerd in je bibliotheek',
+    lastImportAlreadyInLibrary: 'Al in je bibliotheek',
+    viewInLibrary: 'Bekijken in Bestandsbeheer',
+    openInBambuStudio: 'Openen in Bambu Studio',
+    openInOrcaSlicer: 'Openen in OrcaSlicer',
+    importTo: 'Importeren in bestandsbeheer',
+    recentImportsHeader: 'Recente imports',
+    phaseResolving: 'Ophalen',
+    phaseDownloading: 'Downloaden',
+    folderAuto: 'MakerWorld (standaard)',
+    importAll: 'Alles importeren',
+    importAllProgress: 'Importeren {{current}}/{{total}}',
+    openGallery: 'Afbeeldingengalerij openen',
+    galleryPrev: 'Vorige afbeelding',
+    galleryNext: 'Volgende afbeelding',
+    deleteImport: 'Uit bibliotheek verwijderen',
+    importDeleting: 'Verwijderen…',
+    importDeleted: 'Uit bibliotheek verwijderd',
+    confirmDelete: '{{filename}} uit de bibliotheek verwijderen? Hiermee wordt het lokale bestand verwijderd, maar de plaat kan opnieuw vanuit MakerWorld worden geïmporteerd.',
+    errors: {
+      resolveFailed: 'Die MakerWorld-URL kon niet worden opgehaald.',
+      downloadFailed: 'Download mislukt. Probeer het opnieuw.',
+      deleteFailed: 'Bestand kon niet uit de bibliotheek worden verwijderd.',
+    },
+  },
+  gcodeViewer: {
+    filamentSlot: 'Filament {{n}}',
+    loading: 'Toolpath lezen…',
+    loadFailed: 'G-code voor dit bestand kon niet worden geladen.',
+    showTravel: 'Verplaatsingen',
+    topLayer: 'Bovenste laag',
+    bottomLayer: 'Onderste laag',
+    noSource: 'Er is geen bestand opgegeven om te bekijken.',
+    view: {
+      filament: 'Filament',
+      feature: 'Kenmerk',
+      height: 'Hoogte',
+      width: 'Breedte',
+    },
+    feature: {
+      wall: 'Wanden',
+      sparseInfill: 'Schaars infill',
+      solidInfill: 'Volledig infill',
+      bridge: 'Brug / overhang',
+      support: 'Support',
+      skirt: 'Skirt / brim',
+      gapFill: 'Gap fill',
+      ironing: 'Ironing',
+      primeTower: 'Prime tower',
+    },
+    back: 'Terug',
+    backToArchives: 'Terug naar Afdrukarchieven',
+    backToFiles: 'Terug naar Bestandsbeheer',
+    plates: 'Platen',
+    plateN: 'Plaat {{n}}',
+  },
+  libraryTrash: {
+    title: 'Prullenbak',
+    headerButton: 'Prullenbak',
+    headerTooltip: 'Bekijk bestanden die naar de prullenbak zijn verplaatst',
+    backToFiles: 'Terug naar Bestandsbeheer',
+    subtitleAdmin: 'Verwijderde bestanden blijven hier {{days}} dagen en worden daarna automatisch verwijderd. Deze weergave toont verwijderde bestanden van alle gebruikers.',
+    subtitleUser: 'Verwijderde bestanden blijven hier {{days}} dagen en worden daarna automatisch verwijderd.',
+    loading: 'Prullenbak laden…',
+    loadError: 'Prullenbak kon niet worden geladen.',
+    empty: 'De prullenbak is leeg.',
+    summary: '{{count}} bestanden · {{size}}',
+    emptyTrash: 'Prullenbak legen',
+    restore: 'Herstellen',
+    purgeNow: 'Nu verwijderen',
+    autoPurgeIn: 'Automatische verwijdering over {{when}}',
+    days: 'dagen',
+    retentionLabel: 'Automatisch verwijderen na',
+    selectAll: 'Alles selecteren',
+    selectOne: '{{filename}} selecteren',
+    selectionCount: '{{count}} geselecteerd',
+    bulkRestore: 'Geselecteerde herstellen',
+    bulkPurge: 'Geselecteerde verwijderen',
+    col: {
+      filename: 'Bestand',
+      folder: 'Map',
+      size: 'Grootte',
+      deleted: 'Naar prullenbak verplaatst',
+      autoPurge: 'Automatisch verwijderen',
+      owner: 'Eigenaar',
+      actions: 'Acties',
+    },
+    confirm: {
+      purgeTitle: 'Definitief verwijderen?',
+      purgeBody: '{{filename}} wordt van de schijf verwijderd en kan niet worden hersteld.',
+      emptyTitle: 'Prullenbak legen?',
+      emptyBody: 'Alle {{count}} bestanden worden van de schijf verwijderd. Dit kan niet ongedaan worden gemaakt.',
+      bulkPurgeTitle: 'Geselecteerde bestanden definitief verwijderen?',
+      bulkPurgeBody: 'De {{count}} geselecteerde bestanden worden van de schijf verwijderd en kunnen niet worden hersteld.',
+      cta: 'Definitief verwijderen',
+    },
+    toast: {
+      restored: 'Bestand hersteld.',
+      restoreFailed: 'Bestand kon niet worden hersteld.',
+      purged: 'Bestand definitief verwijderd.',
+      purgeFailed: 'Bestand kon niet worden verwijderd.',
+      emptied: '{{count}} bestand(en) uit prullenbak verwijderd.',
+      emptyFailed: 'Prullenbak kon niet worden geleegd.',
+      retentionSaved: 'Automatisch verwijderen ingesteld op {{days}} dagen.',
+      retentionFailed: 'Bewaarinstelling kon niet worden opgeslagen.',
+      bulkRestored: '{{count}} bestand(en) hersteld.',
+      bulkPurged: '{{count}} bestand(en) verwijderd.',
+    },
+  },
+  libraryPurge: {
+    title: 'Oude bestanden opschonen',
+    headerButton: 'Oude opschonen',
+    headerTooltip: 'Oude bestanden in bulk naar prullenbak verplaatsen',
+    description: 'Ruim oude bestanden in je bibliotheek in één keer op. Bestanden met afdrukgeschiedenis worden beoordeeld op de datum waarop ze voor het laatst zijn afgedrukt; nooit afgedrukte bestanden op hun uploaddatum.',
+    ageLabel: 'Bestanden ouder dan verplaatsen',
+    days: 'dagen',
+    includeNeverPrinted: 'Bestanden opnemen die nooit zijn afgedrukt',
+    effectsTitle: 'Wat gebeurt er wanneer je op Opschonen klikt',
+    effect1: 'Overeenkomende bestanden worden naar de Prullenbak verplaatst — ze worden nog niet van de schijf verwijderd.',
+    effect2: 'Je kunt ze vanuit de Prullenbak herstellen totdat de bewaartermijn is verstreken.',
+    effect3: 'Na de bewaartermijn verwijdert de prullenbakopruimer ze definitief van de schijf.',
+    effect4: 'Bestanden in externe (gekoppelde) mappen worden overgeslagen — Bambuddy verwijdert nooit bytes waarvan het geen eigenaar is.',
+    previewLoading: 'Controleren hoeveel bestanden overeenkomen…',
+    previewFailed: 'Voorbeeld van opschoning kon niet worden gemaakt.',
+    previewSummary: '{{count}} bestanden · {{size}} zouden naar de prullenbak worden verplaatst',
+    andMore: '…en nog {{count}}',
+    warning: 'Bestanden in de prullenbak tellen nog mee voor opslagruimte totdat de bewaartermijn verloopt. Leeg daarna de Prullenbak om direct schijfruimte vrij te maken.',
+    confirmCta: '{{count}} naar prullenbak verplaatsen',
+    purging: 'Naar prullenbak verplaatsen…',
+    toast: {
+      success: '{{count}} bestand(en) naar prullenbak verplaatst.',
+      failed: 'Bestanden opschonen mislukt.',
+    },
+  },
+  libraryAutoPurge: {
+    enableLabel: 'Oude bestanden automatisch opschonen',
+    enableDescription: 'Voert de beheeropschoning eenmaal per dag uit. Bestanden gaan eerst naar de Prullenbak — ze worden niet direct verwijderd.',
+    ageLabel: 'Bestanden automatisch opschonen ouder dan',
+    ageDescription: 'Minimaal 7 dagen, maximaal 10 jaar. Gebruikt dezelfde leeftijdsregel als de handmatige knop Opschonen.',
+    days: 'dagen',
+    includeNeverPrinted: 'Bestanden opnemen die nooit zijn afgedrukt',
+    saveFailed: 'Instellingen voor automatisch opschonen konden niet worden opgeslagen.',
+  },
+  archivePurge: {
+    headerButton: 'Oude opschonen',
+    headerTooltip: 'Oude archieven in bulk verwijderen',
+    title: 'Oude archieven opschonen',
+    description: 'Ruim oude afdrukgeschiedenis op. Elk archief wordt beoordeeld op de meest recente voltooiingsdatum van een afdruk — opnieuw afdrukken vernieuwt de leeftijd, zodat actief werk nooit wordt opgeschoond.',
+    ageLabel: 'Archieven verwijderen die niet zijn afgedrukt in de laatste',
+    days: 'dagen',
+    effectsTitle: 'Wat gebeurt er wanneer je op Opschonen klikt',
+    effect1: 'Elk overeenkomend archief wordt verborgen uit de lijsten en de bestanden worden van de schijf verwijderd (3MF, miniatuur, timelapse, bron-3MF, F3D-ontwerpbestand, foto\'s).',
+    effect2: 'De archiefrij blijft in de database zodat Snelle statistieken de bijdragen voor filament, tijd, kosten en energie behoudt — hetzelfde als de standaard bij het verwijderen van één archief.',
+    effect3: 'Vink hieronder "Ook uit statistieken verwijderen" aan om ook de bijdrage aan Snelle statistieken te verwijderen (zelfde als de optie bij het verwijderen van één archief). Dit is onomkeerbaar.',
+    effect4: 'Opnieuw afdrukken vernieuwt de leeftijdsklok van een archief, zodat archieven die je nog gebruikt veilig blijven.',
+    purgeStatsLabel: 'Ook uit statistieken verwijderen',
+    purgeStatsHint: 'Verwijdert de overeenkomende archieven uit Snelle statistieken (filament, tijd, kosten, energie). Zonder dit behoudt Snelle statistieken elke bijdrage en verdwijnen alleen de bestanden van de schijf.',
+    previewLoading: 'Controleren hoeveel archieven overeenkomen…',
+    previewFailed: 'Voorbeeld van opschoning kon niet worden gemaakt.',
+    previewSummary: '{{count}} archieven · {{size}} zouden worden verwijderd',
+    andMore: '…en nog {{count}}',
+    warning: 'Bestanden worden van de schijf verwijderd en kunnen niet worden hersteld. Download of markeer als favoriet wat je wilt bewaren voordat je doorgaat.',
+    confirmCta: '{{count}} archief/archieven verwijderen',
+    purging: 'Verwijderen…',
+    toast: {
+      success: '{{count}} archief/archieven verwijderd.',
+      failed: 'Archieven opschonen mislukt.',
+    },
+  },
+  archiveAutoPurge: {
+    enableLabel: 'Oude archieven automatisch opschonen',
+    enableDescription: 'Verbergt eenmaal per dag archieven uit de lijsten en verwijdert hun bestanden van de schijf als ze niet binnen de ingestelde termijn zijn afgedrukt. Opnieuw afdrukken reset de klok.',
+    ageLabel: 'Archieven automatisch verwijderen die niet zijn afgedrukt in de laatste',
+    ageDescription: 'Minimaal 7 dagen, maximaal 10 jaar. Gebaseerd op de meest recente voltooiing van een afdruk — opnieuw afdrukken vernieuwt de leeftijd. Verwijdert de 3MF, miniatuur, timelapse, bron-3MF, F3D en foto\'s.',
+    days: 'dagen',
+    purgeStatsLabel: 'Ook uit statistieken verwijderen',
+    purgeStatsDescription: 'Wanneer ingeschakeld verwijdert de dagelijkse opruimer elk opgeschoond archief ook uit Snelle statistieken (filament, tijd, kosten, energie). Standaard uit — Snelle statistieken behoudt de bijdrage; alleen de bestanden verdwijnen van de schijf.',
+    runNow: 'Archieven nu opschonen',
+    saveFailed: 'Instellingen voor automatisch opschonen konden niet worden opgeslagen.',
+  },
+  cameraTokens: {
+    scope: {
+      camera_stream: 'Camerastream',
+      camwall: 'Cameramuur',
+      overlay: 'Streamingoverlay',
+    },
+    title: 'Camera-API-tokens',
+    navTitle: 'Camera-API-tokens',
+    description:
+      'Langlevende tokens om de camerastream in Home Assistant, Frigate, kiosks of andere tools met een stabiele URL in te sluiten. Kies het bereik wanneer je er een maakt; een token kan op elk moment worden ingetrokken.',
+    loading: 'Laden…',
+    confirmRevoke: {
+      title: 'Dit token intrekken?',
+      body: 'Elk apparaat dat "{{name}}" gebruikt verliest direct toegang. Dit kan niet ongedaan worden gemaakt.',
+      cancel: 'Annuleren',
+      confirm: 'Intrekken',
+    },
+    create: {
+      scopeLabel: 'Bereik',
+      hintCameraStream:
+        'Een camerastreamtoken kan alleen camerastreams en momentopnamen ophalen. Gebruik dit voor Home Assistant, Frigate of iets dat één camera insluit.',
+      hintCamWall:
+        'Een Cameramuur-token opent /camwall op een scherm zonder login. Het kan de naam en status van elke printer en hun camerastreams zien. Het kan geen bestandsnamen, adressen of toegangscodes zien.',
+      hintOverlay:
+        'Een Streaming Overlay-token opent /overlay/{printerId} op een scherm zonder login — voor OBS of een livestream. Het kan de camerastream van één printer plus de live afdrukstatus zien, inclusief de bestandsnaam op het scherm. Het kan geen adressen of toegangscodes zien.',
+      title: 'Nieuw token aanmaken',
+      nameLabel: 'Tokennaam',
+      namePlaceholder: 'bijv. Home Assistant',
+      daysLabel: 'Dagen tot vervaldatum',
+      submit: 'Maken',
+      hint:
+        'Maximale geldigheidsduur is 365 dagen. De tokenwaarde wordt slechts één keer getoond bij het aanmaken — kopieer deze nu.',
+    },
+    created: {
+      camWallUrlTitle: 'Cameramuur-URL voor dit scherm',
+      camWallUrlHint:
+        'Open dit op het scherm. Iedereen die de URL kan lezen kan de muur bekijken, dus behandel hem als een sleutel — trek het token in om de toegang van het scherm te stoppen.',
+      overlayUrlTitle: 'Overlay-URL voor OBS',
+      overlayUrlHint:
+        'Voeg dit toe als Browser Source in OBS. Wijzig het nummer /overlay/1 in het nummer van je printer (uit de URL op de pagina Printers). Iedereen die de URL kan lezen kan de stream bekijken, dus behandel hem als een sleutel — trek het token in om de toegang te stoppen.',
+      title: 'Token aangemaakt — kopieer het nu',
+      warning:
+        'Dit is de enige keer dat dit token zichtbaar is. Nadat je dit venster sluit kun je het nooit meer bekijken.',
+      copy: 'Kopiëren',
+      dismiss: 'Ik heb het opgeslagen',
+    },
+    list: {
+      scope: 'Bereik',
+      myTitle: 'Mijn tokens',
+      allTitle: 'Alle gebruikers (beheerweergave)',
+      empty: 'Nog geen tokens.',
+      name: 'Naam',
+      owner: 'Eigenaar',
+      prefix: 'Voorvoegsel',
+      created: 'Aangemaakt',
+      expires: 'Verloopt',
+      lastUsed: 'Laatst gebruikt',
+      revoke: 'Intrekken',
+      expired: 'Verlopen',
+    },
+    toast: {
+      created: 'Token aangemaakt',
+      createFailed: 'Token aanmaken mislukt',
+      revoked: 'Token ingetrokken',
+      revokeFailed: 'Token intrekken mislukt',
+      loadFailed: 'Tokens laden mislukt',
+      copied: 'Naar klembord gekopieerd',
+      copyFailed: 'Kopiëren mislukt — selecteer en kopieer handmatig',
+    },
+  },
+  // Forecast & Inventory Intelligence
+  forecast: {
+    title: 'Prognose',
+    noSpools: 'Geen actieve spoelen gevonden. Voeg spoelen toe aan je voorraad om prognosegegevens te zien.',
+    noUsageData: 'Geen verbruiksgegevens beschikbaar — voorraadverloop kan niet worden voorspeld.',
+    // Table headers
+    sku: 'SKU',
+    material: 'Materiaal',
+    stock: 'Voorraad',
+    dailyRate: 'Tempo',
+    daysLeft: 'Dagen resterend',
+    emptyBy: 'Leeg op',
+    reorderBy: 'Bestellen vóór',
+    actions: 'Acties',
+    // Rate tier badges
+    trend: 'Trend',
+    estimated: 'Geschat',
+    noData: 'Geen gegevens',
+    // Timeframe
+    timeframe: 'Periode',
+    // Chart
+    chartTitle: 'Verwachte voorraad — top 5 materialen',
+    dashedLinesROP: 'Stippellijnen = bestelpunten',
+    stockLevel: 'Voorraadniveau',
+    reorderPoint: 'Bestelpunt',
+    safetyMargin: 'Veiligheidsmarge',
+    // Legend labels
+    trendLegend: 'Trend (op historie gebaseerd, serviceniveau 95%)',
+    estimatedLegend: 'Geschat (gewichtsverschil)',
+    noDataLegend: 'Geen gegevens',
+    ropLabel: 'ROP',
+    ssLabel: 'SS',
+    safetyStockLegend: 'Veiligheidsvoorraad',
+    stockArrivalLegend: 'Voorraadontvangst',
+    stockoutLegend: 'Voorraad op',
+    // Alerts toolbar
+    alertCount_one: '{{count}} waarschuwing',
+    alertCount_other: '{{count}} waarschuwingen',
+    order: 'Bestellen',
+    // Settings
+    save: 'Opslaan',
+    cancel: 'Annuleren',
+    settingsSaved: 'Instellingen opgeslagen',
+    failedSaveSettings: 'Instellingen opslaan mislukt',
+    globalLeadTimeSaved: 'Globale levertijd opgeslagen',
+    globalLeadTime: 'Globale levertijd',
+    globalLeadTimeHint: 'Ondergrens voor globale levertijd — gebruikt bij de berekening van het bestelpunt voor alle SKU\'s',
+    skuLeadTimeOverride: 'Overschrijving levertijd',
+    skuLeadTimeHint: '0 = globale levertijd gebruiken. Stel >0 in om voor deze SKU te overschrijven.',
+    safetyMarginLabel: 'Veiligheidsmarge',
+    effectiveLeadTime: 'Effectieve levertijd',
+    effectiveLeadTimeHint: 'max(globaal {{global}}d, SKU {{sku}}d)',
+    reorderPointHint: 'd̄ × LT + veiligheidsmarge — bestellen wanneer voorraad dit niveau bereikt',
+    safetyMarginHint: 'Statistische veiligheidsvoorraad (z=1.65 × σ × √LT) + door gebruiker ingestelde buffer',
+    safetyMarginHintDays: 'Buffer bovenop de statistische veiligheidsvoorraad.{{approx}}',
+    safetyMarginHintDaysApprox: ' ≈ {{g}}g bij huidig verbruik.',
+    safetyMarginHintG: 'Vaste gewichtsbuffer bovenop de statistische veiligheidsvoorraad.{{approx}}',
+    safetyMarginHintGApprox: ' ≈ {{days}}d bij huidig verbruik.',
+    individualSpools: 'Afzonderlijke spoelen',
+    labelWeight: 'Label',
+    spoolCount_one: '{{count}} spoel',
+    spoolCount_other: '{{count}} spoelen',
+    // Alerts
+    stockBreakRisk: 'Risico op voorraadtekort',
+    stockBreakDetail: 'Nog {{days}}d, levertijd {{lt}}d.',
+    stockBreakBefore: 'Voorraadtekort vóór aanvulling',
+    reorderNow: 'Nu bestellen',
+    reorderTriggerPassed: 'Activeringsdatum {{date}} is verstreken.',
+    // Shopping list
+    shoppingList: 'Boodschappenlijst',
+    shoppingListItems_one: '({{count}} item)',
+    shoppingListItems_other: '({{count}} items)',
+    shoppingListEmpty: 'Boodschappenlijst is leeg. Klik op het winkelwagenpictogram van een rij om items toe te voegen.',
+    addToCart: 'Aan boodschappenlijst toevoegen',
+    alertsSnoozed: 'Waarschuwingen voor deze SKU dempen',
+    alertsEnabled: 'Waarschuwingen voor deze SKU weer inschakelen',
+    addedToCart: 'Toegevoegd aan boodschappenlijst',
+    failedAddItem: 'Item toevoegen mislukt',
+    listView: 'Lijst',
+    logisticsView: 'Logistiek',
+    qty: 'Aantal',
+    weight: 'Gewicht',
+    leadTime: 'Levertijd',
+    expectedRestock: 'Verwachte aanvulling',
+    status: 'Status',
+    note: 'Notitie',
+    pending: 'In afwachting',
+    purchased: 'Gekocht',
+    received: 'Ontvangen',
+    markPurchased: 'Markeren als gekocht',
+    markReceived: 'Markeren als ontvangen — voegt spoelen toe aan Voorraad',
+    resetToPending: 'Terugzetten naar in behandeling',
+    remove: 'Verwijderen',
+    clearAll: 'Alles wissen',
+    downloadCsv: 'CSV',
+    // Add to cart modal
+    addToCartTitle: 'Aan boodschappenlijst toevoegen',
+    byQuantity: 'Op aantal',
+    byDuration: 'Op duur',
+    numberOfSpools: 'Aantal spoelen',
+    lastHowManyDays: 'Hoeveel dagen moet dit meegaan?',
+    noUsageQty: 'Geen verbruiksgegevens — aantal ingesteld op 1.',
+    noteOptional: 'Notitie (optioneel)',
+    notePlaceholder: 'bijv. voor project X, dringend…',
+    addNSpools_one: '{{count}} spoel toevoegen',
+    addNSpools_other: '{{count}} spoelen toevoegen',
+    // Cart logistics
+    onArrival: 'Bij aankomst',
+    stockBreakIn: 'Voorraadtekort over {{days}}d.',
+    stockRunsOutBefore: 'Voorraad raakt op voordat de levertijd van {{lt}}d is verstreken.',
+    atRate: 'Bij {{rate}}g/dag heb je nodig',
+    moreSpools_one: 'nog {{count}} spoel',
+    moreSpools_other: 'nog {{count}} spoelen',
+    bridgeGap: 'om het tekort te overbruggen.',
+    // Permissions
+    noReadAccess: 'Je hebt geen machtiging om voorraadprognoses te bekijken.',
+    noWriteAccess: 'Je hebt geen machtiging om prognose-instellingen te wijzigen.',
+  },
+};

File diff suppressed because it is too large
+ 0 - 0
static/assets/index-DFcvtgAy.js


+ 1 - 1
static/index.html

@@ -26,7 +26,7 @@
 
     <!-- Splash screens for iOS -->
     <link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
-    <script type="module" crossorigin src="/assets/index-DL1B9Y3L.js"></script>
+    <script type="module" crossorigin src="/assets/index-DFcvtgAy.js"></script>
     <link rel="stylesheet" crossorigin href="/assets/index-BzJRM4M1.css">
   </head>
   <body>

Some files were not shown because too many files changed in this diff