Index: src/views/pages/admin-audio.ejs
===================================================================
--- src/views/pages/admin-audio.ejs	(revision 929d98d2002f662695a6e55138a1e9094f722bad)
+++ src/views/pages/admin-audio.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
@@ -4,6 +4,6 @@
   <header class="ax-header">
     <div>
-      <h1>Audio tracks</h1>
-      <p class="ax-tagline">Site-level MP3s. Gebruik <code>[[track:&lt;id&gt;]]</code> in een post om een play-knop in te voegen.</p>
+      <h1><%= t('aaud.title') %></h1>
+      <p class="ax-tagline"><%= t('aaud.tagline_pre') %> <code>[[track:&lt;id&gt;]]</code> <%= t('aaud.tagline_post') %></p>
     </div>
   </header>
@@ -14,5 +14,5 @@
   <%# ── UPLOAD ──────────────────────────────────────────────── %>
   <section class="ax-card">
-    <div class="ax-card-title">Upload</div>
+    <div class="ax-card-title"><%= t('aaud.upload') %></div>
 
     <%# Bulk drag-drop zone. Multiple files at once, transcoded sequentially.
@@ -25,17 +25,17 @@
       <div class="ax-row ax-row-2">
         <label class="ax-field">
-          <span>Artiest <small>(toegepast op alle bestanden)</small></span>
-          <input type="text" id="batch-artist" placeholder="Optioneel">
+          <span><%= t('aaud.artist') %> <small><%= t('aaud.applied_all') %></small></span>
+          <input type="text" id="batch-artist" placeholder="<%= t('aaud.optional') %>">
         </label>
         <label class="ax-field">
-          <span>Album <small>(toegepast op alle bestanden)</small></span>
-          <input type="text" id="batch-album" placeholder="Optioneel">
+          <span><%= t('aaud.album') %> <small><%= t('aaud.applied_all') %></small></span>
+          <input type="text" id="batch-album" placeholder="<%= t('aaud.optional') %>">
         </label>
       </div>
       <label class="ax-field ax-file">
-        <span>Cover <small>(optioneel, toegepast op alle bestanden — jpg/png/webp/gif, max 5 MB)</small></span>
+        <span><%= t('aaud.cover') %> <small><%= t('aaud.cover_hint') %></small></span>
         <span class="ax-file-control">
-          <span class="ax-btn ax-btn-secondary ax-file-btn">🖼 Kies cover</span>
-          <span class="ax-file-name" data-empty>Geen bestand gekozen</span>
+          <span class="ax-btn ax-btn-secondary ax-file-btn">🖼 <%= t('aaud.choose_cover') %></span>
+          <span class="ax-file-name" data-empty><%= t('aaud.no_file') %></span>
           <input type="file" id="batch-cover" accept="image/jpeg,image/png,image/webp,image/gif">
         </span>
@@ -45,6 +45,6 @@
       <label class="ax-dropzone" id="audio-dropzone" tabindex="0">
         <span class="ax-dropzone-icon">🎵</span>
-        <strong class="ax-dropzone-title">Sleep audio hierheen</strong>
-        <span class="ax-dropzone-sub">of klik om bestanden te kiezen<br><small>mp3, m4a, ogg, opus, flac · max <%= maxBytesMb %> MB · wav max <%= maxWavMb %> MB</small></span>
+        <strong class="ax-dropzone-title"><%= t('aaud.drag_here') %></strong>
+        <span class="ax-dropzone-sub"><%= t('aaud.or_click') %><br><small>mp3, m4a, ogg, opus, flac · max <%= maxBytesMb %> MB · wav max <%= maxWavMb %> MB</small></span>
         <input type="file" id="audio-files" accept="audio/*" multiple>
       </label>
@@ -54,6 +54,6 @@
 
       <div class="ax-form-actions" id="upload-actions" hidden>
-        <button type="button" class="ax-btn ax-btn-primary" id="start-upload-btn">⬆ Start upload</button>
-        <button type="button" class="ax-btn" id="clear-queue-btn">Wis lijst</button>
+        <button type="button" class="ax-btn ax-btn-primary" id="start-upload-btn">⬆ <%= t('aaud.start_upload') %></button>
+        <button type="button" class="ax-btn" id="clear-queue-btn"><%= t('aaud.clear_list') %></button>
       </div>
     </div>
@@ -63,8 +63,8 @@
   <section class="ax-card">
     <div class="ax-card-title" style="display:flex;align-items:center;gap:.5rem">
-      Tracks <span class="ax-count"><%= tracks.length %></span>
+      <%= t('aaud.tracks') %> <span class="ax-count"><%= tracks.length %></span>
       <button type="button" class="ax-btn ax-btn-secondary" id="add-link-track-btn"
-              style="margin-left:auto;font-size:.85rem" title="Een track zonder audiobestand — alleen titel + open-in links">
-        + Track zonder audio
+              style="margin-left:auto;font-size:.85rem" title="<%= t('aaud.add_link_track_title') %>">
+        + <%= t('aaud.add_link_track') %>
       </button>
     </div>
@@ -73,7 +73,18 @@
       <div class="ax-empty">
         <div class="ax-empty-icon">♫</div>
-        <p>Nog geen tracks. Upload er een hierboven.</p>
+        <p><%= t('aaud.no_tracks') %></p>
       </div>
     <% } else { %>
+      <%
+        // Pre-fetch loop labels — `t` is shadowed by the loop var below.
+        var _aaudUntitled = t('aaud.untitled');
+        var _aaudPlay     = t('aaud.play');
+        var _aaudEdit     = t('aaud.edit');
+        var _aaudDlOn     = t('aaud.dl_on');
+        var _aaudDlOff    = t('aaud.dl_off');
+        var _aaudDelete   = t('aaud.delete');
+        var _aaudCopy     = t('aaud.copy_click');
+        var _aaudDelConfirm = t('aaud.delete_confirm');
+      %>
       <ul class="ax-list">
         <% tracks.forEach(function(t) { %>
@@ -86,5 +97,5 @@
 
             <div class="ax-track-meta">
-              <div class="ax-track-title" data-cell="title"><%= t.title || '(zonder titel)' %></div>
+              <div class="ax-track-title" data-cell="title"><%= t.title || _aaudUntitled %></div>
               <div class="ax-track-sub">
                 <span data-cell="artist"><%= t.artist || '—' %></span>
@@ -92,5 +103,5 @@
                 <span class="ax-track-sep">·</span><span><%= t.size ? Math.round(t.size/1024) + ' KB' : '—' %></span>
               </div>
-              <code class="ax-embed" data-copy="[[track:<%= t.id %>]]" title="Klik om te kopiëren">[[track:<%= t.id %>]]</code>
+              <code class="ax-embed" data-copy="[[track:<%= t.id %>]]" title="<%= _aaudCopy %>">[[track:<%= t.id %>]]</code>
             </div>
 
@@ -100,16 +111,16 @@
                         data-stream-url="<%= t.stream_url %>"
                         data-track-id="<%= t.id %>"
-                        aria-label="Afspelen" title="Afspelen">
+                        aria-label="<%= _aaudPlay %>" title="<%= _aaudPlay %>">
                   <span class="ax-track-play-icon" aria-hidden="true">▶</span>
                 </button>
               <% } %>
-              <button type="button" class="ax-icon-btn" data-track-edit data-id="<%= t.id %>" aria-label="Bewerken" title="Bewerken">✎</button>
+              <button type="button" class="ax-icon-btn" data-track-edit data-id="<%= t.id %>" aria-label="<%= _aaudEdit %>" title="<%= _aaudEdit %>">✎</button>
               <% if (typeof premiumUnlocked === 'undefined' || premiumUnlocked) { %>
                 <button type="button" class="ax-icon-btn" data-track-dl data-id="<%= t.id %>" data-on="<%= t.downloadable ? '1' : '0' %>"
-                        aria-label="Download-voor-email" title="<%= t.downloadable ? 'Download-voor-email staat AAN — klik om uit te zetten' : 'Download-voor-email staat uit — klik om aan te zetten' %>"
+                        aria-label="<%= t.downloadable ? _aaudDlOn : _aaudDlOff %>" title="<%= t.downloadable ? _aaudDlOn : _aaudDlOff %>"
                         style="<%= t.downloadable ? 'color:var(--accent,#6b8f71)' : 'opacity:.5' %>">⬇</button>
               <% } %>
-              <form action="/admin/audio/<%= t.id %>/delete" method="post" onsubmit="return confirm('Track verwijderen?')" class="ax-track-delete">
-                <button type="submit" class="ax-icon-btn ax-icon-btn-danger" aria-label="Verwijderen" title="Verwijderen">🗑</button>
+              <form action="/admin/audio/<%= t.id %>/delete" method="post" onsubmit="return confirm('<%= _aaudDelConfirm %>')" class="ax-track-delete">
+                <button type="submit" class="ax-icon-btn ax-icon-btn-danger" aria-label="<%= _aaudDelete %>" title="<%= _aaudDelete %>">🗑</button>
               </form>
             </div>
@@ -122,8 +133,8 @@
   <% if ((typeof premiumUnlocked === 'undefined' || premiumUnlocked) && typeof embedUrl !== 'undefined') { %>
     <section class="ax-section" style="margin-top:1.5rem">
-      <h2 style="margin:0 0 .4rem">🔊 Embedbare speler</h2>
-      <p style="opacity:.75;font-size:.9rem;margin:0 0 .6rem">Plak deze code op je eigen website/blog om je muziek met deze speler in te sluiten:</p>
+      <h2 style="margin:0 0 .4rem">🔊 <%= t('aaud.embed_player') %></h2>
+      <p style="opacity:.75;font-size:.9rem;margin:0 0 .6rem"><%= t('aaud.embed_hint') %></p>
       <textarea readonly rows="3" onclick="this.select()" style="width:100%;font-family:monospace;font-size:12px;padding:10px;border-radius:8px;border:1px solid var(--rule);background:var(--paper-2);color:inherit;">&lt;iframe src="<%= embedUrl %>" width="100%" height="420" frameborder="0" loading="lazy" style="border-radius:12px"&gt;&lt;/iframe&gt;</textarea>
-      <p style="margin:.6rem 0 0"><a class="btn" href="<%= embedUrl %>" target="_blank">▶ Speler-voorbeeld openen</a></p>
+      <p style="margin:.6rem 0 0"><a class="btn" href="<%= embedUrl %>" target="_blank">▶ <%= t('aaud.preview_player') %></a></p>
     </section>
   <% } %>
@@ -573,5 +584,5 @@
         nameEl.removeAttribute('data-empty');
       } else {
-        nameEl.textContent = 'Geen bestand gekozen';
+        nameEl.textContent = '<%= t('aaud.no_file') %>';
         nameEl.setAttribute('data-empty', '');
       }
@@ -592,5 +603,5 @@
       if (icon) icon.textContent = playing ? '⏸' : '▶';
       btn.classList.toggle('is-playing', playing);
-      btn.setAttribute('aria-label', playing ? 'Pauzeren' : 'Afspelen');
+      btn.setAttribute('aria-label', playing ? '<%= t('aaud.pause') %>' : '<%= t('aaud.play') %>');
     }
 
@@ -695,9 +706,9 @@
   function setItemStatus(item, status, label) {
     const labels = {
-      queued:      'Wachten',
-      uploading:   'Uploaden…',
-      transcoding: 'Converteren…',
-      done:        '✓ Klaar',
-      error:       '✗ Fout',
+      queued:      '<%= t('aaud.st_queued') %>',
+      uploading:   '<%= t('aaud.st_uploading') %>',
+      transcoding: '<%= t('aaud.st_transcoding') %>',
+      done:        '✓ <%= t('aaud.st_done') %>',
+      error:       '✗ <%= t('aaud.st_error') %>',
     };
     item.status = status;
@@ -726,5 +737,5 @@
       li.innerHTML =
         '<div class="ax-queue-name"></div>' +
-        '<span class="ax-queue-status ax-queue-status--queued">Wachten</span>';
+        '<span class="ax-queue-status ax-queue-status--queued"><%= t('aaud.st_queued') %></span>';
       // Use textContent to avoid HTML-injection if a filename contains markup.
       li.querySelector('.ax-queue-name').textContent = file.name;
@@ -813,5 +824,5 @@
       } catch (err) {
         console.error('upload failed for', item.file.name, err);
-        setItemStatus(item, 'error', '✗ ' + (err.message || 'Mislukt'));
+        setItemStatus(item, 'error', '✗ ' + (err.message || '<%= t('aaud.failed') %>'));
       }
     }
@@ -864,5 +875,5 @@
       let data;
       try { data = await res.json(); }
-      catch (_) { throw new Error('Onverwacht serverantwoord (' + res.status + ')'); }
+      catch (_) { throw new Error('<%= t('aaud.err_unexpected') %> (' + res.status + ')'); }
 
       if (!res.ok || !data.ok) {
@@ -870,5 +881,5 @@
       }
 
-      setItemStatus(item, 'done', '✓ ' + (data.title || 'Klaar'));
+      setItemStatus(item, 'done', '✓ ' + (data.title || '<%= t('aaud.st_done') %>'));
     } catch (err) {
       clearTimeout(transcodeHint);
@@ -885,5 +896,5 @@
         el.classList.add('is-copied');
         const original = el.textContent;
-        el.textContent = '✓ gekopieerd';
+        el.textContent = '✓ <%= t('aaud.copied') %>';
         setTimeout(() => {
           el.classList.remove('is-copied');
@@ -903,12 +914,12 @@
           method: 'POST', credentials: 'same-origin',
           headers: { 'Content-Type': 'application/json' },
-          body: JSON.stringify({ title: 'Nieuwe track' }),
+          body: JSON.stringify({ title: '<%= t('aaud.new_track') %>' }),
         });
         const j = await r.json();
-        if (!r.ok || !j.ok) throw new Error(j.error || 'Aanmaken mislukt');
+        if (!r.ok || !j.ok) throw new Error(j.error || '<%= t('aaud.create_failed') %>');
         if (typeof window.openTrackEditor !== 'function') { location.reload(); return; }
         window.openTrackEditor({ id: j.id, onSaved: () => location.reload() });
       } catch (err) {
-        alert('Track aanmaken mislukt: ' + err.message);
+        alert('<%= t('aaud.create_failed') %>: ' + err.message);
       } finally {
         addLinkBtn.disabled = false;
@@ -923,5 +934,5 @@
     btn.addEventListener('click', () => {
       if (typeof window.openTrackEditor !== 'function') {
-        alert('Track editor niet geladen');
+        alert('<%= t('aaud.editor_not_loaded') %>');
         return;
       }
@@ -936,5 +947,5 @@
           const artistEl = row.querySelector('[data-cell="artist"]');
           const albumEl  = row.querySelector('[data-cell="album"]');
-          if (titleEl)  titleEl.textContent  = track.title  || '(zonder titel)';
+          if (titleEl)  titleEl.textContent  = track.title  || '<%= t('aaud.untitled') %>';
           if (artistEl) artistEl.textContent = track.artist || '—';
           if (albumEl)  {
@@ -984,8 +995,8 @@
         btn.style.opacity = want ? '1' : '.5';
         btn.title = want
-          ? 'Download-voor-email staat AAN — klik om uit te zetten'
-          : 'Download-voor-email staat uit — klik om aan te zetten';
+          ? '<%= t('aaud.dl_on') %>'
+          : '<%= t('aaud.dl_off') %>';
       } catch (e) {
-        alert('Kon niet wijzigen: ' + (e.message || e));
+        alert('<%= t('aaud.change_failed') %>: ' + (e.message || e));
       } finally {
         btn.disabled = false;
Index: src/views/pages/admin-circle.ejs
===================================================================
--- src/views/pages/admin-circle.ejs	(revision 929d98d2002f662695a6e55138a1e9094f722bad)
+++ src/views/pages/admin-circle.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
@@ -1,5 +1,5 @@
 <div class="container admin-page">
-  <h1>Cirkel</h1>
-  <p><a href="/admin/settings" class="btn">&larr; Instellingen</a></p>
+  <h1><%= t('acir.title') %></h1>
+  <p><a href="/admin/settings" class="btn">&larr; <%= t('acir.back_settings') %></a></p>
 
   <% if (success) { %><div class="alert alert-success"><%= success %></div><% } %>
@@ -8,51 +8,59 @@
   <% if (tenancy !== 'circle') { %>
     <section class="set-card">
-      <p class="set-help">De modus staat niet op <strong>Cirkels</strong>. Zet 'm aan bij
-        <a href="/admin/settings">Instellingen</a> om je cirkel-feed te tonen op <code>/cirkel</code>.
-        Je kunt hieronder al wel bronnen klaarzetten.</p>
+      <p class="set-help"><%= t('acir.mode_off_1') %> <strong><%= t('acir.circles') %></strong>. <%= t('acir.mode_off_2') %>
+        <a href="/admin/settings"><%= t('acir.settings') %></a> <%= t('acir.mode_off_3') %> <code>/cirkel</code>.
+        <%= t('acir.mode_off_4') %></p>
     </section>
   <% } %>
 
   <section class="set-card" style="margin-top:1rem">
-    <h2>Mijn zichtbaarheid</h2>
-    <p class="set-help">Doe je mee aan cirkels? Dit maakt je <strong>al-publieke</strong> posts
-      ophaalbaar voor andere Klonkt-sites via een ondertekende feed
-      (<code>/.klonkt/outbox.json</code>). Het is een deelname-keuze, geen privacy-slot:
-      je posts blijven sowieso openbaar op je site, ook als dit uit staat. Wil je iets
-      afschermen, maak die post dan niet-publiek.</p>
+    <h2><%= t('acir.visibility_title') %></h2>
+    <p class="set-help"><%= t('acir.visibility_help_1') %> <strong><%= t('acir.all_public') %></strong> <%= t('acir.visibility_help_2') %>
+      (<code>/.klonkt/outbox.json</code>). <%= t('acir.visibility_help_3') %></p>
     <form method="post" action="/admin/circle/allow" class="set-form">
       <label class="set-opt" style="align-items:center">
         <input type="checkbox" name="allow_circle" value="on" <%= allowCircle ? 'checked' : '' %>>
-        <span><strong>Toon mijn site in cirkels van anderen</strong></span>
+        <span><strong><%= t('acir.show_in_circles') %></strong></span>
       </label>
-      <button type="submit" class="btn btn-primary">Opslaan</button>
+      <button type="submit" class="btn btn-primary"><%= t('acir.save') %></button>
     </form>
   </section>
 
   <section class="set-card" style="margin-top:1rem">
-    <h2>Klonkt-site toevoegen</h2>
-    <p class="set-help">Plak de basis-URL van een andere Klonkt-site. Asymmetrisch: jij toont
-      hen, los van of zij jou tonen.</p>
+    <h2><%= t('acir.add_title') %></h2>
+    <p class="set-help"><%= t('acir.add_help') %></p>
     <form method="post" action="/admin/circle/add" class="set-form">
       <label class="set-field">
-        <span>URL</span>
+        <span><%= t('acir.url') %></span>
         <input type="url" name="remote_url" placeholder="https://artiest.klonkt.com" required>
       </label>
       <label class="set-field">
-        <span>Label <small style="font-weight:400">— optioneel</small></span>
-        <input type="text" name="label" maxlength="80" placeholder="bijv. Joost Klein">
+        <span><%= t('acir.label') %> <small style="font-weight:400">— <%= t('acir.optional') %></small></span>
+        <input type="text" name="label" maxlength="80" placeholder="<%= t('acir.label_ph') %>">
       </label>
-      <button type="submit" class="btn btn-primary">Toevoegen</button>
+      <button type="submit" class="btn btn-primary"><%= t('acir.add') %></button>
     </form>
   </section>
 
   <section class="set-card" style="margin-top:1rem">
-    <h2>In mijn cirkel (<%= links.length %>)</h2>
+    <h2><%= t('acir.in_circle', { n: links.length }) %></h2>
     <% if (!links.length) { %>
-      <p class="set-help">Nog geen bronnen. Voeg er hierboven een toe.</p>
+      <p class="set-help"><%= t('acir.no_sources') %></p>
     <% } else { %>
       <form method="post" action="/admin/circle/sync-all" style="margin:0 0 .9rem">
-        <button type="submit" class="btn">&#8635; Alles nu synchroniseren</button>
+        <button type="submit" class="btn">&#8635; <%= t('acir.sync_all') %></button>
       </form>
+      <%
+        var _cActive = t('acir.st_active');
+        var _cPosts = t('acir.posts');
+        var _cLast = t('acir.last');
+        var _cMismatch = t('acir.st_mismatch');
+        var _cMismatchReason = t('acir.mismatch_reason');
+        var _cError = t('acir.st_error');
+        var _cPaused = t('acir.st_paused');
+        var _cRefresh = t('acir.refresh');
+        var _cRemove = t('acir.remove');
+        var _cRemoveConfirm = t('acir.remove_confirm');
+      %>
       <ul class="circ-list">
         <% links.forEach(function(l){ %>
@@ -63,14 +71,14 @@
               <div class="circ-status">
                 <% if (l.status === 'active') { %>
-                  <span class="circ-badge ok">&#10003; actief</span>
-                  <span class="circ-meta"><%= counts[l.id] || 0 %> posts<% if (l.last_synced) { %> &middot; laatst: <%= l.last_synced %><% } %></span>
+                  <span class="circ-badge ok">&#10003; <%= _cActive %></span>
+                  <span class="circ-meta"><%= counts[l.id] || 0 %> <%= _cPosts %><% if (l.last_synced) { %> &middot; <%= _cLast %>: <%= l.last_synced %><% } %></span>
                 <% } else if (l.status === 'outdated') { %>
-                  <span class="circ-badge err">&#8635; versie-mismatch</span>
-                  <div class="circ-reason"><%= l.last_error || 'Deze site draait een andere Klonkt-protocolversie — bijwerken nodig om te federeren.' %></div>
+                  <span class="circ-badge err">&#8635; <%= _cMismatch %></span>
+                  <div class="circ-reason"><%= l.last_error || _cMismatchReason %></div>
                 <% } else if (l.status === 'error') { %>
-                  <span class="circ-badge err">&#9888; fout</span>
+                  <span class="circ-badge err">&#9888; <%= _cError %></span>
                   <% if (l.last_error) { %><div class="circ-reason"><%= l.last_error %></div><% } %>
                 <% } else if (l.status === 'paused') { %>
-                  <span class="circ-badge">&#9208; gepauzeerd</span>
+                  <span class="circ-badge">&#9208; <%= _cPaused %></span>
                 <% } else { %>
                   <%# Onbekende status: toon 'm rauw + eventuele reden i.p.v. stil "gepauzeerd". %>
@@ -82,9 +90,9 @@
             <div class="circ-actions">
               <form method="post" action="/admin/circle/<%= l.id %>/sync" style="display:inline">
-                <button type="submit" class="btn">Verversen</button>
+                <button type="submit" class="btn"><%= _cRefresh %></button>
               </form>
               <form method="post" action="/admin/circle/<%= l.id %>/remove" style="display:inline"
-                    onsubmit="return confirm('Verwijderen uit je cirkel?')">
-                <button type="submit" class="btn btn-danger">Verwijder</button>
+                    onsubmit="return confirm('<%= _cRemoveConfirm %>')">
+                <button type="submit" class="btn btn-danger"><%= _cRemove %></button>
               </form>
             </div>
Index: src/views/pages/admin-comments.ejs
===================================================================
--- src/views/pages/admin-comments.ejs	(revision 929d98d2002f662695a6e55138a1e9094f722bad)
+++ src/views/pages/admin-comments.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
@@ -1,13 +1,13 @@
 <div class="container admin-comments-page">
-  <p><a href="/admin" class="btn">&larr; Beheer</a></p>
-  <h1>Comment moderation</h1>
+  <p><a href="/admin" class="btn">&larr; <%= t('acom.back') %></a></p>
+  <h1><%= t('acom.title') %></h1>
 
   <p class="muted">
-    Mode for this site: <strong><%= moderationMode %></strong>
+    <%= t('acom.mode_for_site') %> <strong><%= moderationMode %></strong>
     <% if (moderationMode === 'trust') { %>
-      &mdash; comments are auto-approved. Switch to <em>moderate</em> in
-      <a href="/admin/sites/<%= site.slug %>/edit">site settings</a> to queue them.
+      &mdash; <%= t('acom.mode_trust_a') %> <em><%= t('acom.mode_moderate_word') %></em>
+      <a href="/admin/sites/<%= site.slug %>/edit"><%= t('acom.site_settings') %></a> <%= t('acom.mode_trust_b') %>
     <% } else { %>
-      &mdash; new comments require approval before showing up on posts.
+      &mdash; <%= t('acom.mode_moderate_hint') %>
     <% } %>
   </p>
@@ -16,8 +16,14 @@
   <% if (error)   { %><div class="audio-flash audio-flash--err"><%= error %></div><% } %>
 
-  <h2>Pending (<%= pending.length %>)</h2>
+  <h2><%= t('acom.pending', { n: pending.length }) %></h2>
   <% if (!pending.length) { %>
-    <p class="muted">Nothing waiting.</p>
+    <p class="muted"><%= t('acom.nothing_waiting') %></p>
   <% } else { %>
+    <%
+      var _c_reply    = t('acom.reply');
+      var _c_on       = t('acom.on');
+      var _c_approve  = t('acom.approve');
+      var _c_reject   = t('acom.reject');
+    %>
     <ol class="moderation-list">
       <% pending.forEach(function(c) { %>
@@ -25,6 +31,6 @@
           <div class="moderation-meta">
             <strong><%= c.author_username %></strong>
-            <% if (c.parent_comment_id) { %><span class="muted">(reply)</span><% } %>
-            on <a href="<%= siteUrlBase %>/<%= c.post_slug %>"><%= c.post_title || c.post_slug %></a>
+            <% if (c.parent_comment_id) { %><span class="muted">(<%= _c_reply %>)</span><% } %>
+            <%= _c_on %> <a href="<%= siteUrlBase %>/<%= c.post_slug %>"><%= c.post_title || c.post_slug %></a>
             <span class="muted">&middot; <%= formatDateTime(c.created_at) %></span>
           </div>
@@ -32,8 +38,8 @@
           <div class="moderation-actions">
             <form method="post" action="<%= siteUrlBase %>/admin/comments/<%= c.id %>/approve" style="display:inline">
-              <button type="submit" class="btn btn-primary">Approve</button>
+              <button type="submit" class="btn btn-primary"><%= _c_approve %></button>
             </form>
             <form method="post" action="<%= siteUrlBase %>/admin/comments/<%= c.id %>/reject" style="display:inline">
-              <button type="submit" class="btn btn-danger">Reject</button>
+              <button type="submit" class="btn btn-danger"><%= _c_reject %></button>
             </form>
           </div>
@@ -43,7 +49,7 @@
   <% } %>
 
-  <h2>Recent decisions</h2>
+  <h2><%= t('acom.recent') %></h2>
   <% if (!recent.length) { %>
-    <p class="muted">Nothing yet.</p>
+    <p class="muted"><%= t('acom.nothing_yet') %></p>
   <% } else { %>
     <ul class="decision-list">
Index: src/views/pages/admin-epk.ejs
===================================================================
--- src/views/pages/admin-epk.ejs	(revision 929d98d2002f662695a6e55138a1e9094f722bad)
+++ src/views/pages/admin-epk.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
@@ -1,35 +1,33 @@
 <div class="container admin-page">
-  <h1>Perskit bewerken</h1>
-  <p><a href="/admin" class="btn">&larr; Beheer</a> <a href="/pers" class="btn" target="_blank" rel="noopener">📰 Bekijk perskit</a></p>
+  <h1><%= t('aepk.title') %></h1>
+  <p><a href="/admin" class="btn">&larr; <%= t('aepk.back_admin') %></a> <a href="/pers" class="btn" target="_blank" rel="noopener">📰 <%= t('aepk.view_epk') %></a></p>
 
   <% if (success) { %><div class="alert alert-success"><%= success %></div><% } %>
 
   <section class="set-card">
-    <h2>Tekst</h2>
+    <h2><%= t('aepk.text_heading') %></h2>
     <p class="set-help">
-      De perskit (<code>/pers</code>) toont je sitenaam + foto, deze bio en contact,
-      plus automatisch je meest beluisterde nummers en recente posts. Laat de bio
-      leeg om de site-tagline te gebruiken; laat contact leeg om niets te tonen
-      (je login-mail wordt nooit automatisch getoond).
+      <%= t('aepk.text_help') %>
     </p>
 
     <form method="post" action="/admin/epk" class="set-form">
       <label class="set-field">
-        <span>Pers-bio</span>
+        <span><%= t('aepk.bio_label') %></span>
         <textarea name="epk_bio" rows="5" maxlength="1000"
-                  placeholder="Korte beschrijving van jou/het project voor pers &amp; boekers."><%= epkBio %></textarea>
+                  placeholder="<%= t('aepk.bio_ph') %>"><%= epkBio %></textarea>
       </label>
       <label class="set-field">
-        <span>Pers-contact</span>
+        <span><%= t('aepk.contact_label') %></span>
         <input type="text" name="epk_contact" maxlength="300"
-               placeholder="bv. pers@jouwdomein.nl of een boekingslink"
+               placeholder="<%= t('aepk.contact_ph') %>"
                value="<%= epkContact %>">
       </label>
 
       <div class="set-field">
-        <span>Nummers op de perskit <small>(kies er max <%= maxEpkTracks %>; laat leeg voor automatisch de top <%= maxEpkTracks %> meest beluisterd)</small></span>
+        <span><%= t('aepk.tracks_label') %> <small><%= t('aepk.tracks_hint', { n: maxEpkTracks }) %></small></span>
         <% if (!allTracks.length) { %>
-          <p class="set-help" style="margin:0">Nog geen nummers — voeg eerst audio toe in Beheer → Audio.</p>
+          <p class="set-help" style="margin:0"><%= t('aepk.no_tracks') %></p>
         <% } else { %>
+          <% var _untitled = t('aepk.untitled'); %>
           <div class="epk-track-pick" data-max="<%= maxEpkTracks %>">
             <% allTracks.forEach(function(t){ %>
@@ -37,5 +35,5 @@
                 <input type="checkbox" name="epk_tracks" value="<%= t.id %>" <%= chosenTracks.indexOf(t.id) >= 0 ? 'checked' : '' %>>
                 <span class="epk-box" aria-hidden="true">✓</span>
-                <span class="epk-pick-title"><%= t.title || '(zonder titel)' %></span>
+                <span class="epk-pick-title"><%= t.title || _untitled %></span>
                 <% if (t.artist) { %><span class="epk-pick-artist"><%= t.artist %></span><% } %>
               </label>
@@ -45,5 +43,5 @@
       </div>
 
-      <button type="submit" class="btn btn-primary">Opslaan</button>
+      <button type="submit" class="btn btn-primary"><%= t('aepk.save') %></button>
     </form>
   </section>
Index: src/views/pages/admin-google.ejs
===================================================================
--- src/views/pages/admin-google.ejs	(revision 929d98d2002f662695a6e55138a1e9094f722bad)
+++ src/views/pages/admin-google.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
@@ -1,5 +1,5 @@
 <div class="container admin-page">
-  <h1>Google-login</h1>
-  <p><a href="/admin/settings" class="btn">&larr; Instellingen</a></p>
+  <h1><%= t('agoog.title') %></h1>
+  <p><a href="/admin/settings" class="btn">&larr; <%= t('agoog.back') %></a></p>
 
   <% if (success) { %><div class="alert alert-success"><%= success %></div><% } %>
@@ -7,57 +7,57 @@
 
   <section class="set-card">
-    <h2>Google-login (bezoekers)</h2>
-    <p class="set-help">Laat bezoekers met Google inloggen om te reageren — met je <strong>eigen</strong>
-      Google Cloud-client. Maak er een aan op
+    <h2><%= t('agoog.card_title') %></h2>
+    <p class="set-help"><%= t('agoog.card_help_pre') %> <strong><%= t('agoog.card_help_own') %></strong>
+      <%= t('agoog.card_help_mid') %>
       <a href="https://console.cloud.google.com/apis/credentials" target="_blank" rel="noopener">console.cloud.google.com</a>
-      (OAuth client-ID, type "Web application").
-      <% if (google && google.configured) { %><strong>Status: ingesteld&nbsp;✓</strong><% } else { %>Nog niet ingesteld.<% } %></p>
+      <%= t('agoog.card_help_post') %>
+      <% if (google && google.configured) { %><strong><%= t('agoog.status_set') %></strong><% } else { %><%= t('agoog.status_unset') %><% } %></p>
 
     <details class="set-google-guide">
-      <summary>Hoe stel ik dit in? (stap voor stap)</summary>
+      <summary><%= t('agoog.guide_summary') %></summary>
       <ol class="set-google-steps">
-        <li>Ga naar de <a href="https://console.cloud.google.com/" target="_blank" rel="noopener">Google Cloud Console</a> en maak (of kies) een project.</li>
-        <li><strong>APIs &amp; Services → OAuth consent screen</strong>: User type <strong>External</strong>. Voeg de scopes <code>email</code>, <code>profile</code> en <code>openid</code> toe. (In testmodus: voeg jezelf toe als testgebruiker.)</li>
-        <li><strong>APIs &amp; Services → Credentials → Create credentials → OAuth client ID</strong>, type <strong>Web application</strong>.</li>
-        <li>Bij <strong>Authorized redirect URIs</strong>: plak de redirect-URI hieronder (moet <strong>exact</strong> matchen).</li>
-        <li>Kopieer de <strong>Client ID</strong> + <strong>Client Secret</strong> die je krijgt naar de velden hieronder en klik Opslaan.</li>
+        <li><%= t('agoog.step1_pre') %> <a href="https://console.cloud.google.com/" target="_blank" rel="noopener">Google Cloud Console</a> <%= t('agoog.step1_post') %></li>
+        <li><strong>APIs &amp; Services → OAuth consent screen</strong>: <%= t('agoog.step2_usertype') %> <strong>External</strong>. <%= t('agoog.step2_scopes_pre') %> <code>email</code>, <code>profile</code> <%= t('agoog.step2_scopes_and') %> <code>openid</code> <%= t('agoog.step2_scopes_post') %> <%= t('agoog.step2_testmode') %></li>
+        <li><strong>APIs &amp; Services → Credentials → Create credentials → OAuth client ID</strong>, <%= t('agoog.step3_type') %> <strong>Web application</strong>.</li>
+        <li><%= t('agoog.step4_pre') %> <strong>Authorized redirect URIs</strong><%= t('agoog.step4_post') %></li>
+        <li><%= t('agoog.step5_pre') %> <strong>Client ID</strong> + <strong>Client Secret</strong> <%= t('agoog.step5_post') %></li>
       </ol>
-      <p class="set-help" style="margin:.5rem 0 0"><strong>Let op:</strong> je hoeft <strong>geen API's aan te zetten</strong> — "Google Cloud APIs", People API e.d. zijn <em>niet</em> nodig. Inloggen werkt via OpenID Connect (alleen het toestemmingsscherm + de OAuth-client).</p>
+      <p class="set-help" style="margin:.5rem 0 0"><strong><%= t('agoog.note_label') %></strong> <%= t('agoog.note_pre') %> <strong><%= t('agoog.note_noapi') %></strong> <%= t('agoog.note_mid') %> <em><%= t('agoog.note_not') %></em> <%= t('agoog.note_post') %></p>
     </details>
 
     <% if (google && google.redirectUri) { %>
       <div class="set-redirect">
-        <div class="set-redirect-label">Redirect-URI — kopieer deze en plak 'm in Google</div>
+        <div class="set-redirect-label"><%= t('agoog.redirect_label') %></div>
         <code class="set-redirect-url" style="user-select:all"><%= google.redirectUri %></code>
         <ol class="set-redirect-steps">
-          <li>Ga naar <a href="https://console.cloud.google.com/apis/credentials" target="_blank" rel="noopener">Google Cloud Console → APIs &amp; Services → Credentials</a>.</li>
-          <li>Open jouw <strong>OAuth client-ID</strong> (type "Web application").</li>
-          <li>Onder <strong>Authorized redirect URIs</strong> → klik <strong>+ ADD URI</strong> en plak de regel hierboven.</li>
-          <li>Klik <strong>Save</strong> en wacht ~1 minuut.</li>
+          <li><%= t('agoog.rstep1_pre') %> <a href="https://console.cloud.google.com/apis/credentials" target="_blank" rel="noopener">Google Cloud Console → APIs &amp; Services → Credentials</a>.</li>
+          <li><%= t('agoog.rstep2') %></li>
+          <li><%= t('agoog.rstep3_pre') %> <strong>Authorized redirect URIs</strong> → <%= t('agoog.rstep3_mid') %> <strong>+ ADD URI</strong> <%= t('agoog.rstep3_post') %></li>
+          <li><%= t('agoog.rstep4_pre') %> <strong>Save</strong> <%= t('agoog.rstep4_post') %></li>
         </ol>
-        <p class="set-redirect-warn">Moet <strong>exact</strong> kloppen: <code>https://</code> (geen <code>http</code>), juiste domein, en <strong>geen</strong> schuine streep aan het eind. Anders krijg je <em>"Error 400: redirect_uri_mismatch"</em>.</p>
+        <p class="set-redirect-warn"><%= t('agoog.warn_pre') %> <strong><%= t('agoog.warn_exact') %></strong> <%= t('agoog.warn_mid1') %> <code>https://</code> (<%= t('agoog.warn_nohttp') %> <code>http</code>)<%= t('agoog.warn_mid2') %> <strong><%= t('agoog.warn_noslash') %></strong> <%= t('agoog.warn_mid3') %> <em><%= t('agoog.warn_error') %></em>.</p>
       </div>
     <% } else { %>
-      <p class="set-help" style="color:var(--accent)">Stel eerst <code>PUBLIC_BASE_URL</code> in — daar wordt de redirect-URI van afgeleid.</p>
+      <p class="set-help" style="color:var(--accent)"><%= t('agoog.set_baseurl_pre') %> <code>PUBLIC_BASE_URL</code> <%= t('agoog.set_baseurl_post') %></p>
     <% } %>
 
     <form method="post" action="/admin/settings/google" class="set-form">
       <label class="set-field">
-        <span>Client ID</span>
+        <span><%= t('agoog.client_id') %></span>
         <input type="text" name="google_client_id" value="<%= google ? google.clientId : '' %>" placeholder="123-abc.apps.googleusercontent.com" autocomplete="off">
       </label>
       <label class="set-field">
-        <span>Client Secret <% if (google && google.secretSet) { %><small style="font-weight:400">— ingesteld; leeg laten = ongewijzigd</small><% } %></span>
-        <input type="password" name="google_client_secret" placeholder="<%= (google && google.secretSet) ? '•••••••• (ingesteld)' : 'GOCSPX-…' %>" autocomplete="off">
+        <span><%= t('agoog.client_secret') %> <% if (google && google.secretSet) { %><small style="font-weight:400">— <%= t('agoog.client_secret_set') %></small><% } %></span>
+        <input type="password" name="google_client_secret" placeholder="<%= (google && google.secretSet) ? t('agoog.secret_ph_set') : 'GOCSPX-…' %>" autocomplete="off">
       </label>
       <div class="set-actions">
-        <button type="submit" class="btn btn-primary">Opslaan</button>
+        <button type="submit" class="btn btn-primary"><%= t('agoog.save') %></button>
         <% if (google && (google.clientId || google.secretSet)) { %>
-          <button type="submit" name="clear" value="1" class="btn">Loskoppelen</button>
+          <button type="submit" name="clear" value="1" class="btn"><%= t('agoog.disconnect') %></button>
         <% } %>
       </div>
     </form>
     <% if (typeof premiumEnabled !== 'undefined' && premiumEnabled && !isPremium) { %>
-      <p class="set-help" style="margin-top:.7rem; color:var(--accent)">Premium-laag staat aan zonder Patreon — de Google-knop blijft verborgen tot premium ontgrendeld is.</p>
+      <p class="set-help" style="margin-top:.7rem; color:var(--accent)"><%= t('agoog.premium_locked') %></p>
     <% } %>
   </section>
Index: src/views/pages/admin-help.ejs
===================================================================
--- src/views/pages/admin-help.ejs	(revision 929d98d2002f662695a6e55138a1e9094f722bad)
+++ src/views/pages/admin-help.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
@@ -1,9 +1,9 @@
 <div class="container admin-page hb">
-  <p><a href="/admin" class="btn">&larr; Beheer</a></p>
-  <h1>Handleiding</h1>
-  <p class="hb-intro">Uitleg van alle functies. Typ hieronder om te zoeken op een onderwerp of instructie.</p>
+  <p><a href="/admin" class="btn">&larr; <%= t('ahelp.back') %></a></p>
+  <h1><%= t('ahelp.title') %></h1>
+  <p class="hb-intro"><%= t('ahelp.intro') %></p>
 
   <div class="hb-search">
-    <input type="search" id="hb-q" placeholder="Zoek… (bv. 'agenda', 'foto', 'cirkel', 'downloads')" autocomplete="off" aria-label="Zoek in de handleiding">
+    <input type="search" id="hb-q" placeholder="<%= t('ahelp.search_placeholder') %>" autocomplete="off" aria-label="<%= t('ahelp.search_aria') %>">
   </div>
   <p class="hb-count" id="hb-count" aria-live="polite"></p>
@@ -12,111 +12,111 @@
 
     <section class="hb-item">
-      <h2>✍️ Nieuwe post schrijven</h2>
-      <p>Beheer → <strong>Nieuwe post</strong>. Geef een titel en schrijf je inhoud. Onderaan kies je de status: <em>concept</em> (niet zichtbaar) of <em>gepubliceerd</em>. Concepten staan bovenaan in je Beheer-overzicht zodat je ze terugvindt.</p>
+      <h2>✍️ <%= t('ahelp.s_newpost_h') %></h2>
+      <p><%- t('ahelp.s_newpost_b') %></p>
     </section>
 
     <section class="hb-item">
-      <h2>📝 Samenvatting &amp; Cirkel Preview</h2>
-      <p>Het veld <strong>Samenvatting &amp; Cirkel Preview</strong> (de excerpt) is de korte previewtekst onder een post in lijsten, én de samenvatting die andere sites tonen als ze je post via een <strong>Cirkel</strong> overnemen. Laat je 'm leeg, dan wordt automatisch het begin van de post gebruikt.</p>
+      <h2>📝 <%= t('ahelp.s_excerpt_h') %></h2>
+      <p><%- t('ahelp.s_excerpt_b') %></p>
     </section>
 
     <section class="hb-item">
-      <h2>📌 Post pinnen / volgorde</h2>
-      <p>In de post-editor kun je een post <strong>pinnen</strong> met een rang (1 = bovenaan). Gepinde posts staan vooraan in de tijdlijn/grid, op volgorde van hun rang. Rang leeg of 0 = niet gepind.</p>
+      <h2>📌 <%= t('ahelp.s_pin_h') %></h2>
+      <p><%- t('ahelp.s_pin_b') %></p>
     </section>
 
     <section class="hb-item">
-      <h2>🗓️ Publiceren plannen &amp; alleen voor fans <small>(premium)</small></h2>
-      <p>In de editor kun je een <strong>publicatiedatum</strong> in de toekomst zetten — de post verschijnt dan automatisch op dat moment. Met <strong>Alleen voor fans</strong> zien niet-ingelogde bezoekers alleen een teaser + login-uitnodiging; ingelogde fans zien alles.</p>
+      <h2>🗓️ <%= t('ahelp.s_schedule_h') %> <small>(<%= t('ahelp.premium') %>)</small></h2>
+      <p><%- t('ahelp.s_schedule_b') %></p>
     </section>
 
     <section class="hb-item">
-      <h2>🖼️ Afbeeldingen in posts</h2>
-      <p>Afbeeldingen in de tekst en de cover-afbeelding worden altijd <strong>volledig</strong> getoond op de volledige breedte (niet bijgesneden), met hun natuurlijke hoogte.</p>
+      <h2>🖼️ <%= t('ahelp.s_images_h') %></h2>
+      <p><%- t('ahelp.s_images_b') %></p>
     </section>
 
     <section class="hb-item">
-      <h2>🎵 Audio &amp; nummers toevoegen</h2>
-      <p>Beheer → <strong>Audio</strong>. Upload een bestand of voeg een <em>link-only</em> nummer toe (zonder upload, alleen "open in"-links). Per nummer vul je titel, artiest, cover, en optioneel album/positie in. In een post toon je een nummer met de shortcode <code>[[track:id]]</code>, een album met <code>[[album:Naam]]</code>, een playlist met <code>[[playlist:id]]</code>.</p>
+      <h2>🎵 <%= t('ahelp.s_audio_h') %></h2>
+      <p><%- t('ahelp.s_audio_b') %></p>
     </section>
 
     <section class="hb-item">
-      <h2>© Credit, licentie &amp; "open in"</h2>
-      <p>Per nummer kun je een <strong>eigenaar/credit</strong> (met © -knop) en een <strong>licentie</strong> instellen — die worden ook in de mp3-metadata geschreven. Met de <strong>open-in</strong>-velden (Spotify / YouTube / SoundCloud) verschijnen knoppen om het nummer op die platforms te openen.</p>
+      <h2>© <%= t('ahelp.s_credit_h') %></h2>
+      <p><%- t('ahelp.s_credit_b') %></p>
     </section>
 
     <section class="hb-item">
-      <h2>⬇ Downloads <small>(premium)</small></h2>
-      <p>Markeer een nummer als <strong>downloadbaar</strong> in Beheer → Audio (⬇-knop). Bezoekers vinden ze op de <strong>/downloads</strong>-pagina en laten hun e-mail achter om het bestand te krijgen (komt op je mailinglijst). Wil je downloads prominent in de feed? Maak een gewone post met slug <code>downloads</code> en pin 'm.</p>
+      <h2>⬇ <%= t('ahelp.s_downloads_h') %> <small>(<%= t('ahelp.premium') %>)</small></h2>
+      <p><%- t('ahelp.s_downloads_b') %></p>
     </section>
 
     <section class="hb-item">
-      <h2>📃 Albums &amp; playlists</h2>
-      <p>Geef nummers hetzelfde <strong>album</strong> + een <strong>positie</strong> om een album te vormen. Playlists maak je in Beheer → <strong>Playlists</strong>. Beide toon je in een post met <code>[[album:Naam]]</code> of <code>[[playlist:id]]</code>.</p>
+      <h2>📃 <%= t('ahelp.s_albums_h') %></h2>
+      <p><%- t('ahelp.s_albums_b') %></p>
     </section>
 
     <section class="hb-item">
-      <h2>📅 Agenda / evenementen <small>(premium)</small></h2>
-      <p>Beheer → <strong>Agenda</strong>. Zet bovenaan <strong>"Agenda tonen op de site"</strong> aan — dan verschijnt de Agenda-knop in de balk en is de agendapagina bereikbaar. Voeg evenementen toe (datum, plaats, locatie, tickets). Bezoekers kunnen zich (los van de nieuwsbrief) aanmelden voor een seintje bij een nieuw evenement.</p>
+      <h2>📅 <%= t('ahelp.s_agenda_h') %> <small>(<%= t('ahelp.premium') %>)</small></h2>
+      <p><%- t('ahelp.s_agenda_b') %></p>
     </section>
 
     <section class="hb-item">
-      <h2>📰 Perskit <small>(premium)</small></h2>
-      <p>Een deelbare perspagina op <strong>/pers</strong>. Bewerk 'm via de <strong>✎ Bewerken</strong>-knop op die pagina zelf (alleen jij ziet die). Stel een korte pers-bio + contact in, en kies <strong>tot 5 nummers</strong> die getoond worden (of laat leeg = automatisch de top-5 meest beluisterd).</p>
+      <h2>📰 <%= t('ahelp.s_presskit_h') %> <small>(<%= t('ahelp.premium') %>)</small></h2>
+      <p><%- t('ahelp.s_presskit_b') %></p>
     </section>
 
     <section class="hb-item">
-      <h2>🔗 Cirkels (federatie)</h2>
-      <p>In Beheer → Instellingen zet je je site op <strong>Cirkel</strong>-modus. Daarna voeg je in Beheer → <strong>Cirkel</strong> andere Klonkt-sites toe; hun publieke posts verschijnen dan in je <strong>/cirkel</strong>-feed. Wat anderen van jóu zien is de titel + de "Samenvatting &amp; Cirkel Preview". Hubs doen niet mee aan cirkels (alleen solo-sites).</p>
+      <h2>🔗 <%= t('ahelp.s_circles_h') %></h2>
+      <p><%- t('ahelp.s_circles_b') %></p>
     </section>
 
     <section class="hb-item">
-      <h2>📊 Statistieken <small>(premium)</small></h2>
-      <p>Beheer → <strong>Statistieken</strong>. Cookievrij gemeten. <strong>Bezoeker-dagen</strong> = unieke bezoekers per dag, opgeteld (géén aantal personen). <strong>Weergaven</strong> = home/feed- en post-loads. Beheerder-bezoeken en bots tellen niet mee. Goed voor trends; absolute aantallen met een korrel zout.</p>
+      <h2>📊 <%= t('ahelp.s_stats_h') %> <small>(<%= t('ahelp.premium') %>)</small></h2>
+      <p><%- t('ahelp.s_stats_b') %></p>
     </section>
 
     <section class="hb-item">
-      <h2>✉️ Nieuwsbrief <small>(premium)</small></h2>
-      <p>Beheer → <strong>Nieuwsbrief</strong>: stel een bericht op en stuur het naar je bevestigde abonnees. Bezoekers melden zich aan via de footer of <strong>/nieuwsbrief</strong>. Versturen vereist dat e-mail (SMTP) is ingesteld.</p>
+      <h2>✉️ <%= t('ahelp.s_newsletter_h') %> <small>(<%= t('ahelp.premium') %>)</small></h2>
+      <p><%- t('ahelp.s_newsletter_b') %></p>
     </section>
 
     <section class="hb-item">
-      <h2>🔗 Link-in-bio <small>(premium)</small></h2>
-      <p>Een Linktree-achtige pagina op <strong>/links</strong> met je profiel-links. De kliks per link zie je terug in Statistieken.</p>
+      <h2>🔗 <%= t('ahelp.s_linkbio_h') %> <small>(<%= t('ahelp.premium') %>)</small></h2>
+      <p><%- t('ahelp.s_linkbio_b') %></p>
     </section>
 
     <section class="hb-item">
-      <h2>▶️ Embedbare speler <small>(premium)</small></h2>
-      <p>Beheer → Audio toont een kopieerbare <code>&lt;iframe&gt;</code>-code (<strong>/embed</strong>) waarmee je je speler op een andere website kunt insluiten.</p>
+      <h2>▶️ <%= t('ahelp.s_embed_h') %> <small>(<%= t('ahelp.premium') %>)</small></h2>
+      <p><%- t('ahelp.s_embed_b') %></p>
     </section>
 
     <section class="hb-item">
-      <h2>🎨 Uiterlijk (thema, foto, accent)</h2>
-      <p>Beheer → <strong>Uiterlijk</strong>: stel je sitenaam, tagline, profielfoto, accentkleur en kleurpalet in, en de standaard feed-weergave (Tijdlijn of Grid).</p>
+      <h2>🎨 <%= t('ahelp.s_appearance_h') %></h2>
+      <p><%- t('ahelp.s_appearance_b') %></p>
     </section>
 
     <section class="hb-item">
-      <h2>👥 Solo- / Hub-modus</h2>
-      <p>Beheer → <strong>Instellingen</strong>: <em>Solo</em> = één site (jij). <em>Hub</em> = een bedrijfs-/labelsite met meerdere makers, elk een eigen Klonkt onder <code>/user/&lt;naam&gt;</code>. <em>Cirkel</em> = solo + federatie met andere sites.</p>
+      <h2>👥 <%= t('ahelp.s_tenancy_h') %></h2>
+      <p><%- t('ahelp.s_tenancy_b') %></p>
     </section>
 
     <section class="hb-item">
-      <h2>⭐ Premium / Patreon</h2>
-      <p>Premium-functies (Hub, Statistieken, Agenda, Downloads, Perskit, Nieuwsbrief, Link-in-bio, Embed, fan-login) ontgrendel je in Beheer → Instellingen door Patreon te koppelen ($10 lifetime). Updates en de kernapp blijven altijd gratis.</p>
+      <h2>⭐ <%= t('ahelp.s_premium_h') %></h2>
+      <p><%- t('ahelp.s_premium_b') %></p>
     </section>
 
     <section class="hb-item">
-      <h2>🔑 Google-login (bezoekers)</h2>
-      <p>Beheer → Instellingen → <strong>Google</strong>. Optioneel: laat bezoekers/fans met Google inloggen om te reageren. Jij beheert met je wachtwoord — Google geeft nooit beheerrechten. Vul je eigen Google-client-gegevens in (per site).</p>
+      <h2>🔑 <%= t('ahelp.s_google_h') %></h2>
+      <p><%- t('ahelp.s_google_b') %></p>
     </section>
 
     <section class="hb-item">
-      <h2>🔄 Updates</h2>
-      <p>Beheer → <strong>Updates</strong> (alleen god) toont de huidige versie en of er een nieuwere is. Met "Nu bijwerken" haal je de laatste versie binnen.</p>
+      <h2>🔄 <%= t('ahelp.s_updates_h') %></h2>
+      <p><%- t('ahelp.s_updates_b') %></p>
     </section>
 
   </div>
 
-  <p class="hb-empty" id="hb-empty" hidden>Geen onderwerpen gevonden voor je zoekopdracht.</p>
+  <p class="hb-empty" id="hb-empty" hidden><%= t('ahelp.empty') %></p>
 </div>
 
Index: src/views/pages/admin-newsletter.ejs
===================================================================
--- src/views/pages/admin-newsletter.ejs	(revision 929d98d2002f662695a6e55138a1e9094f722bad)
+++ src/views/pages/admin-newsletter.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
@@ -7,35 +7,37 @@
 %>
 <section class="anl">
-  <h1>Nieuwsbrief</h1>
+  <h1><%= t('anews.title') %></h1>
 
   <% if (msg) { %><div class="anl-msg anl-<%= msgKind %>"><%= msg %></div><% } %>
 
   <div class="anl-stats">
-    <div class="anl-stat"><span class="anl-n"><%= c.confirmed %></span><span class="anl-l">bevestigd</span></div>
-    <div class="anl-stat"><span class="anl-n"><%= c.pending %></span><span class="anl-l">in afwachting</span></div>
-    <div class="anl-stat"><span class="anl-n"><%= c.unsub %></span><span class="anl-l">uitgeschreven</span></div>
+    <div class="anl-stat"><span class="anl-n"><%= c.confirmed %></span><span class="anl-l"><%= t('anews.confirmed') %></span></div>
+    <div class="anl-stat"><span class="anl-n"><%= c.pending %></span><span class="anl-l"><%= t('anews.pending') %></span></div>
+    <div class="anl-stat"><span class="anl-n"><%= c.unsub %></span><span class="anl-l"><%= t('anews.unsub') %></span></div>
   </div>
 
   <% if (!smtp) { %>
-    <div class="anl-msg anl-warn">⚠ SMTP is nog niet ingesteld. Aanmeldingen worden wél verzameld, maar versturen kan pas als je SMTP-gegevens invult (<code>SMTP_HOST / SMTP_PORT / SMTP_USER / SMTP_PASS / SMTP_FROM</code> in <code>.env</code>).</div>
+    <div class="anl-msg anl-warn"><%= t('anews.smtp_warn_1') %> (<code>SMTP_HOST / SMTP_PORT / SMTP_USER / SMTP_PASS / SMTP_FROM</code> <%= t('anews.smtp_warn_2') %> <code>.env</code>).</div>
   <% } %>
 
-  <p class="anl-share">Aanmeldlink om te delen: <a href="<%= nlSubscribeUrl %>"><%= nlSubscribeUrl %></a></p>
+  <p class="anl-share"><%= t('anews.share') %> <a href="<%= nlSubscribeUrl %>"><%= nlSubscribeUrl %></a></p>
 
+  <% var _sendConfirm = t('anews.send_confirm', { n: c.confirmed }); %>
   <form method="POST" action="<%= siteUrlBase %>/admin/newsletter/send" class="anl-form">
-    <label>Onderwerp
-      <input type="text" name="subject" required maxlength="200" value="<%= (typeof nlSubject!=='undefined')?nlSubject:'' %>" placeholder="Nieuwe single uit!">
+    <label><%= t('anews.subject') %>
+      <input type="text" name="subject" required maxlength="200" value="<%= (typeof nlSubject!=='undefined')?nlSubject:'' %>" placeholder="<%= t('anews.subject_ph') %>">
     </label>
-    <label>Bericht
-      <textarea name="body" rows="10" required placeholder="Schrijf je update…"><%= (typeof nlBody!=='undefined')?nlBody:'' %></textarea>
+    <label><%= t('anews.body') %>
+      <textarea name="body" rows="10" required placeholder="<%= t('anews.body_ph') %>"><%= (typeof nlBody!=='undefined')?nlBody:'' %></textarea>
     </label>
-    <button type="submit" class="anl-send" <%= smtp ? '' : 'disabled' %> onclick="return confirm('Nieuwsbrief versturen naar <%= c.confirmed %> bevestigde abonnee(s)?')">Versturen naar <%= c.confirmed %> abonnee(s)</button>
+    <button type="submit" class="anl-send" <%= smtp ? '' : 'disabled' %> onclick="return confirm('<%= _sendConfirm %>')"><%= t('anews.send_btn', { n: c.confirmed }) %></button>
   </form>
 
   <% if (hist.length) { %>
-    <h2 class="anl-h2">Verstuurd</h2>
+    <h2 class="anl-h2"><%= t('anews.sent_heading') %></h2>
+    <% var _recipients = t('anews.recipients'); %>
     <ul class="anl-hist">
       <% hist.forEach(function(h){ %>
-        <li><span class="anl-hist-subj"><%= h.subject %></span><span class="anl-hist-meta"><%= h.recipient_count %> ontvanger(s) · <%= formatDateTime ? formatDateTime(h.sent_at) : h.sent_at %></span></li>
+        <li><span class="anl-hist-subj"><%= h.subject %></span><span class="anl-hist-meta"><%= h.recipient_count %> <%= _recipients %> · <%= formatDateTime ? formatDateTime(h.sent_at) : h.sent_at %></span></li>
       <% }); %>
     </ul>
Index: src/views/pages/admin-playlists.ejs
===================================================================
--- src/views/pages/admin-playlists.ejs	(revision 929d98d2002f662695a6e55138a1e9094f722bad)
+++ src/views/pages/admin-playlists.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
@@ -10,8 +10,7 @@
   <header class="ax-header">
     <div>
-      <h1>Playlists</h1>
+      <h1><%= t('apl.title') %></h1>
       <p class="ax-tagline">
-        Canonieke playlists. Bewerk een playlist hier en de wijzigingen werken
-        door in álle posts die hem gebruiken via <code>[[playlist:&lt;id&gt;]]</code>.
+        <%= t('apl.tagline_pre') %> <code>[[playlist:&lt;id&gt;]]</code><%= t('apl.tagline_post') %>
       </p>
     </div>
@@ -21,5 +20,5 @@
   <div class="ax-actions-bar">
     <button type="button" class="ax-btn ax-btn-primary" id="pl-new-btn">
-      ＋ Nieuwe playlist
+      ＋ <%= t('apl.new_playlist') %>
     </button>
   </div>
@@ -29,9 +28,19 @@
       <div class="ax-empty">
         <div class="ax-empty-icon">📃</div>
-        <p>Nog geen playlists.</p>
-        <p class="ax-empty-sub">Maak er een via de knop hierboven, of via de 📃 knop in de post-editor.</p>
+        <p><%= t('apl.none') %></p>
+        <p class="ax-empty-sub"><%= t('apl.none_sub') %></p>
       </div>
     </div>
   <% } else { %>
+    <%
+      // Pre-fetch loop labels (defensive — keep `t` calls out of the loop body).
+      var _aplPlaylist = t('apl.pill_playlist');
+      var _aplAlbum    = t('apl.pill_album');
+      var _aplTrack    = t('apl.track');
+      var _aplTracks   = t('apl.tracks');
+      var _aplCopy     = t('apl.copy_click');
+      var _aplEdit     = t('apl.edit');
+      var _aplDelete   = t('apl.delete');
+    %>
     <ul class="ax-list">
       <% playlists.forEach(function(p) { %>
@@ -44,17 +53,17 @@
             <div class="ax-track-title">
               <%= p.title %>
-              <span class="ax-pill"><%= p.kind === 'playlist' ? '📃 playlist' : '💿 album' %></span>
+              <span class="ax-pill"><%= p.kind === 'playlist' ? '📃 ' + _aplPlaylist : '💿 ' + _aplAlbum %></span>
             </div>
             <div class="ax-track-sub">
-              <span><%= p.track_count %> <%= p.track_count === 1 ? 'track' : 'tracks' %></span>
+              <span><%= p.track_count %> <%= p.track_count === 1 ? _aplTrack : _aplTracks %></span>
               <% if (p.artist) { %><span class="ax-track-sep">·</span><span><%= p.artist %></span><% } %>
               <% if (p.year) { %><span class="ax-track-sep">·</span><span><%= p.year %></span><% } %>
             </div>
-            <code class="ax-embed" data-copy="[[playlist:<%= p.id %>]]" title="Klik om te kopiëren">[[playlist:<%= p.id %>]]</code>
+            <code class="ax-embed" data-copy="[[playlist:<%= p.id %>]]" title="<%= _aplCopy %>">[[playlist:<%= p.id %>]]</code>
           </div>
 
           <div class="ax-track-actions">
-            <button type="button" class="ax-icon-btn" data-pl-edit data-id="<%= p.id %>" aria-label="Bewerken" title="Bewerken">✎</button>
-            <button type="button" class="ax-icon-btn ax-icon-btn-danger" data-pl-delete data-id="<%= p.id %>" data-title="<%= p.title %>" aria-label="Verwijderen" title="Verwijderen">🗑</button>
+            <button type="button" class="ax-icon-btn" data-pl-edit data-id="<%= p.id %>" aria-label="<%= _aplEdit %>" title="<%= _aplEdit %>">✎</button>
+            <button type="button" class="ax-icon-btn ax-icon-btn-danger" data-pl-delete data-id="<%= p.id %>" data-title="<%= p.title %>" aria-label="<%= _aplDelete %>" title="<%= _aplDelete %>">🗑</button>
           </div>
         </li>
@@ -229,5 +238,5 @@
         el.classList.add('is-copied');
         const original = el.textContent;
-        el.textContent = '✓ gekopieerd';
+        el.textContent = '✓ <%= t('apl.copied') %>';
         setTimeout(() => { el.classList.remove('is-copied'); el.textContent = original; }, 1200);
       } catch (_) { /* fall back to selection */ }
@@ -247,5 +256,5 @@
       const id = btn.dataset.id;
       const title = btn.dataset.title || id;
-      if (!confirm(`Playlist "${title}" verwijderen? Posts die deze playlist embedden tonen vanaf nu een placeholder.`)) return;
+      if (!confirm('<%= t('apl.delete_confirm') %>'.replace('{title}', title))) return;
       try {
         const r = await fetch(`/admin/playlists/api/${encodeURIComponent(id)}/delete`, {
@@ -256,7 +265,7 @@
         const j = await r.json();
         if (j.ok) location.reload();
-        else alert('Verwijderen mislukt: ' + (j.error || ''));
+        else alert('<%= t('apl.delete_failed') %>: ' + (j.error || ''));
       } catch (err) {
-        alert('Verwijderen mislukt: ' + err.message);
+        alert('<%= t('apl.delete_failed') %>: ' + err.message);
       }
     });
Index: src/views/pages/admin-seo.ejs
===================================================================
--- src/views/pages/admin-seo.ejs	(revision 929d98d2002f662695a6e55138a1e9094f722bad)
+++ src/views/pages/admin-seo.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
@@ -1,6 +1,6 @@
 <div class="container admin-site-edit-page">
-  <p><a href="/admin" class="btn">&larr; Beheer</a></p>
-  <h1>SEO &amp; vindbaarheid</h1>
-  <p class="admin-tagline">Geavanceerde SEO van <strong><%= site.title %></strong> — hoe je site in zoekmachines en bij het delen op social media verschijnt.</p>
+  <p><a href="/admin" class="btn">&larr; <%= t('aseo.back') %></a></p>
+  <h1><%= t('aseo.title') %></h1>
+  <p class="admin-tagline"><%= t('aseo.tagline_pre') %> <strong><%= site.title %></strong> <%= t('aseo.tagline_post') %></p>
 
   <% if (typeof success !== 'undefined' && success) { %><div class="audio-flash audio-flash--ok"><%= success %></div><% } %>
@@ -10,47 +10,47 @@
 
     <fieldset>
-      <legend>Indexeren</legend>
+      <legend><%= t('aseo.index_legend') %></legend>
       <label class="cb">
         <input type="checkbox" name="robots_index" value="1" <%= site.robots_index ? 'checked' : '' %>>
-        <span>Zoekmachines mogen deze site indexeren <small class="form-hint-inline">— uit = <code>noindex</code> + sitemap.xml verborgen</small></span>
+        <span><%= t('aseo.index_label') %> <small class="form-hint-inline">— <%= t('aseo.index_hint_pre') %> <code>noindex</code> <%= t('aseo.index_hint_post') %></small></span>
       </label>
     </fieldset>
 
     <fieldset>
-      <legend>Titel &amp; omschrijving</legend>
+      <legend><%= t('aseo.title_legend') %></legend>
       <label>
-        <span>Titel-sjabloon <small class="form-hint-inline">— gebruik <code>{title}</code> en <code>{site}</code></small></span>
+        <span><%= t('aseo.title_template') %> <small class="form-hint-inline">— <%= t('aseo.title_template_hint_pre') %> <code>{title}</code> <%= t('aseo.title_template_hint_and') %> <code>{site}</code></small></span>
         <input type="text" name="title_template" value="<%= site.title_template || '{title} — {site}' %>" maxlength="200" placeholder="{title} — {site}">
       </label>
       <label>
-        <span>Standaard-omschrijving <small class="form-hint-inline">— meta description / og:description als een pagina er geen heeft</small></span>
-        <textarea name="default_description" rows="2" maxlength="500" placeholder="Korte omschrijving van je site (max ~160 tekens werkt het best)"><%= site.default_description || '' %></textarea>
+        <span><%= t('aseo.default_desc') %> <small class="form-hint-inline">— <%= t('aseo.default_desc_hint') %></small></span>
+        <textarea name="default_description" rows="2" maxlength="500" placeholder="<%= t('aseo.default_desc_ph') %>"><%= site.default_description || '' %></textarea>
       </label>
       <label>
-        <span>Canonieke basis-URL <small class="form-hint-inline">— de productie-HTTPS-URL, voorkomt dubbele-content-straf</small></span>
+        <span><%= t('aseo.canonical') %> <small class="form-hint-inline">— <%= t('aseo.canonical_hint') %></small></span>
         <input type="text" name="canonical" value="<%= site.canonical || '' %>" placeholder="https://jouw-domein.com" maxlength="200">
       </label>
       <label>
-        <span>Auteur <small class="form-hint-inline">— meta author-tag</small></span>
-        <input type="text" name="author" value="<%= site.author || '' %>" maxlength="120" placeholder="Jouw naam">
+        <span><%= t('aseo.author') %> <small class="form-hint-inline">— <%= t('aseo.author_hint') %></small></span>
+        <input type="text" name="author" value="<%= site.author || '' %>" maxlength="120" placeholder="<%= t('aseo.author_ph') %>">
       </label>
     </fieldset>
 
     <fieldset>
-      <legend>Delen op social media</legend>
+      <legend><%= t('aseo.social_legend') %></legend>
       <label>
-        <span>Standaard deel-afbeelding (URL) <small class="form-hint-inline">— og:image / Twitter-card; ~1200×630px</small></span>
+        <span><%= t('aseo.og_image') %> <small class="form-hint-inline">— <%= t('aseo.og_image_hint') %></small></span>
         <input type="text" name="og_image_default" value="<%= site.og_image_default || '' %>" placeholder="https://jouw-domein.com/share.jpg" maxlength="500">
       </label>
       <label>
-        <span>Taal-locale <small class="form-hint-inline">— og:locale, bv. <code>nl_NL</code> of <code>en_US</code></small></span>
+        <span><%= t('aseo.og_locale') %> <small class="form-hint-inline">— <%= t('aseo.og_locale_hint_pre') %> <code>nl_NL</code> <%= t('aseo.og_locale_hint_or') %> <code>en_US</code></small></span>
         <input type="text" name="og_locale" value="<%= site.og_locale || '' %>" placeholder="nl_NL" maxlength="32">
       </label>
       <label>
-        <span>Twitter / X-handle <small class="form-hint-inline">— met @</small></span>
+        <span><%= t('aseo.twitter') %> <small class="form-hint-inline">— <%= t('aseo.twitter_hint') %></small></span>
         <input type="text" name="twitter" value="<%= site.twitter || '' %>" placeholder="@jouwnaam" maxlength="64">
       </label>
       <label>
-        <span>Facebook App-ID <small class="form-hint-inline">— fb:app_id (optioneel)</small></span>
+        <span><%= t('aseo.fb_app') %> <small class="form-hint-inline">— <%= t('aseo.fb_app_hint') %></small></span>
         <input type="text" name="facebook_app_id" value="<%= site.facebook_app_id || '' %>" maxlength="64">
       </label>
@@ -58,22 +58,22 @@
 
     <fieldset>
-      <legend>Uitgever (JSON-LD / rich results)</legend>
+      <legend><%= t('aseo.publisher_legend') %></legend>
       <label>
-        <span>Type</span>
+        <span><%= t('aseo.type') %></span>
         <select name="schema_type">
-          <option value="Person" <%= site.schema_type === 'Organization' ? '' : 'selected' %>>Persoon</option>
-          <option value="Organization" <%= site.schema_type === 'Organization' ? 'selected' : '' %>>Organisatie / bedrijf</option>
+          <option value="Person" <%= site.schema_type === 'Organization' ? '' : 'selected' %>><%= t('aseo.type_person') %></option>
+          <option value="Organization" <%= site.schema_type === 'Organization' ? 'selected' : '' %>><%= t('aseo.type_org') %></option>
         </select>
       </label>
       <label>
-        <span>Naam <small class="form-hint-inline">— valt terug op de site-titel</small></span>
+        <span><%= t('aseo.publisher_name') %> <small class="form-hint-inline">— <%= t('aseo.publisher_name_hint') %></small></span>
         <input type="text" name="publisher_name" value="<%= site.publisher_name || '' %>" maxlength="200">
       </label>
       <label>
-        <span>URL</span>
+        <span><%= t('aseo.publisher_url') %></span>
         <input type="text" name="publisher_url" value="<%= site.publisher_url || '' %>" placeholder="https://jouw-domein.com" maxlength="200">
       </label>
       <label>
-        <span>Logo (URL)</span>
+        <span><%= t('aseo.publisher_logo') %></span>
         <input type="text" name="publisher_logo" value="<%= site.publisher_logo || '' %>" placeholder="https://jouw-domein.com/logo.png" maxlength="500">
       </label>
@@ -81,24 +81,24 @@
 
     <fieldset>
-      <legend>Zoekmachine-verificatie</legend>
+      <legend><%= t('aseo.verify_legend') %></legend>
       <label>
-        <span>Google site-verificatie</span>
+        <span><%= t('aseo.verify_google') %></span>
         <input type="text" name="google_verification" value="<%= site.google_verification || '' %>" maxlength="200">
       </label>
       <label>
-        <span>Bing <small class="form-hint-inline">— msvalidate.01</small></span>
+        <span><%= t('aseo.verify_bing') %> <small class="form-hint-inline">— <%= t('aseo.verify_bing_hint') %></small></span>
         <input type="text" name="bing_verification" value="<%= site.bing_verification || '' %>" maxlength="200">
       </label>
       <label>
-        <span>Pinterest <small class="form-hint-inline">— p:domain_verify</small></span>
+        <span><%= t('aseo.verify_pinterest') %> <small class="form-hint-inline">— <%= t('aseo.verify_pinterest_hint') %></small></span>
         <input type="text" name="pinterest_verification" value="<%= site.pinterest_verification || '' %>" maxlength="200">
       </label>
       <label>
-        <span>Yandex</span>
+        <span><%= t('aseo.verify_yandex') %></span>
         <input type="text" name="yandex_verification" value="<%= site.yandex_verification || '' %>" maxlength="200">
       </label>
     </fieldset>
 
-    <button type="submit" class="btn btn-primary">SEO opslaan</button>
+    <button type="submit" class="btn btn-primary"><%= t('aseo.save') %></button>
   </form>
 </div>
Index: src/views/pages/admin-settings.ejs
===================================================================
--- src/views/pages/admin-settings.ejs	(revision 929d98d2002f662695a6e55138a1e9094f722bad)
+++ src/views/pages/admin-settings.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
@@ -1,5 +1,5 @@
 <div class="container admin-page">
-  <h1>Instellingen</h1>
-  <p><a href="/admin" class="btn">&larr; Beheer</a></p>
+  <h1><%= t('aset.title') %></h1>
+  <p><a href="/admin" class="btn">&larr; <%= t('aset.back_admin') %></a></p>
 
   <% if (success) { %><div class="alert alert-success"><%= success %></div><% } %>
@@ -7,8 +7,7 @@
 
   <section class="set-card">
-    <h2>Modus</h2>
+    <h2><%= t('aset.mode') %></h2>
     <p class="set-help">
-      Bepaalt hoe deze installatie werkt. Wisselen is veilig: er wordt niets verwijderd —
-      Solo verbergt alleen de multi-onderdelen en toont je primaire site.
+      <%= t('aset.mode_help') %>
     </p>
 
@@ -17,6 +16,6 @@
         <input type="radio" name="tenancy" value="solo" <%= tenancy === 'solo' ? 'checked' : '' %>>
         <span>
-          <strong>Solo</strong> — één site (de jouwe).
-          <small>Geen gebruikers-directory, geen site-wissel. Bezoekers kunnen wel met Google inloggen om te reageren.</small>
+          <strong><%= t('aset.solo') %></strong> — <%= t('aset.solo_title') %>
+          <small><%= t('aset.solo_desc') %></small>
         </span>
       </label>
@@ -25,7 +24,7 @@
         <input type="radio" name="tenancy" value="hub" <%= tenancy === 'hub' ? 'checked' : '' %> <%= _hubLocked ? 'disabled' : '' %>>
         <span>
-          <strong>Hub</strong> — bedrijfssite met <code>/user/</code>.
-          <% if (_hubLocked) { %><span style="display:inline-block;font-size:.7rem;font-weight:700;color:var(--accent);border:1px solid var(--accent);border-radius:999px;padding:.05rem .45rem;margin-left:.3rem;vertical-align:1px;">🔒 premium</span><% } %>
-          <small>Jij (admin) wijst gebruikers een eigen Klonkt Hub toe. Gesloten cirkel: geen open registratie.<% if (_hubLocked) { %> <em>Vereist premium — koppel Patreon in deze pagina.</em><% } %></small>
+          <strong><%= t('aset.hub') %></strong> — <%= t('aset.hub_title') %> <code>/user/</code>.
+          <% if (_hubLocked) { %><span style="display:inline-block;font-size:.7rem;font-weight:700;color:var(--accent);border:1px solid var(--accent);border-radius:999px;padding:.05rem .45rem;margin-left:.3rem;vertical-align:1px;">🔒 <%= t('aset.premium_badge') %></span><% } %>
+          <small><%= t('aset.hub_desc') %><% if (_hubLocked) { %> <em><%= t('aset.hub_locked_note') %></em><% } %></small>
         </span>
       </label>
@@ -33,9 +32,9 @@
         <input type="radio" name="tenancy" value="circle" <%= tenancy === 'circle' ? 'checked' : '' %>>
         <span>
-          <strong>Cirkels</strong> &mdash; solo + federatie.
-          <small>E&eacute;n eigen site die de publieke posts van andere Klonkt-sites toont. Asymmetrisch: jij bepaalt wie in jouw cirkel zit.</small>
-        </span>
-      </label>
-      <button type="submit" class="btn btn-primary">Opslaan</button>
+          <strong><%= t('aset.circle') %></strong> &mdash; <%= t('aset.circle_title') %>
+          <small><%= t('aset.circle_desc') %></small>
+        </span>
+      </label>
+      <button type="submit" class="btn btn-primary"><%= t('aset.save') %></button>
     </form>
   </section>
@@ -45,32 +44,31 @@
   <% if (tenancy === 'hub') { %>
   <section class="set-card set-card--mode" style="margin-top:1rem">
-    <h2>Hub-hoofdpagina</h2>
+    <h2><%= t('aset.hub_home') %></h2>
     <p class="set-help">
-      De openbare hub-pagina (<code>/</code>) is generiek — van geen enkele gebruiker.
-      Hier stel je de naam en intro in. Jouw eigen Klonkt Hub blijft los bestaan onder <code>/user/</code>.
+      <%= t('aset.hub_home_help_1') %> (<code>/</code>) <%= t('aset.hub_home_help_2') %> <code>/user/</code>.
     </p>
     <form method="post" action="/admin/settings" class="set-form" enctype="multipart/form-data">
       <label class="set-field">
-        <span>Naam</span>
-        <input type="text" name="hub_title" maxlength="80" value="<%= hubTitle %>" placeholder="bijv. Studio Noord">
-      </label>
-      <label class="set-field">
-        <span>Tagline</span>
-        <input type="text" name="hub_tagline" maxlength="120" value="<%= hubTagline %>" placeholder="bijv. Onafhankelijk muzieklabel">
-      </label>
-      <label class="set-field">
-        <span>Intro</span>
-        <textarea name="hub_intro" maxlength="400" rows="3" placeholder="Korte introtekst onder de titel."><%= hubIntro %></textarea>
-      </label>
-      <label class="set-field">
-        <span>Hero-afbeelding (URL) <small style="font-weight:400;color:var(--ink-muted)">— optioneel; achtergrond van de hero</small></span>
+        <span><%= t('aset.name') %></span>
+        <input type="text" name="hub_title" maxlength="80" value="<%= hubTitle %>" placeholder="<%= t('aset.name_ph') %>">
+      </label>
+      <label class="set-field">
+        <span><%= t('aset.tagline') %></span>
+        <input type="text" name="hub_tagline" maxlength="120" value="<%= hubTagline %>" placeholder="<%= t('aset.tagline_ph') %>">
+      </label>
+      <label class="set-field">
+        <span><%= t('aset.intro') %></span>
+        <textarea name="hub_intro" maxlength="400" rows="3" placeholder="<%= t('aset.intro_ph') %>"><%= hubIntro %></textarea>
+      </label>
+      <label class="set-field">
+        <span><%= t('aset.hero_image') %> <small style="font-weight:400;color:var(--ink-muted)">— <%= t('aset.hero_image_hint') %></small></span>
         <input type="text" name="hub_hero_image" maxlength="300" value="<%= hubHeroImage %>" placeholder="/media/... of https://...">
       </label>
       <label class="set-field">
-        <span>…of upload een afbeelding <small style="font-weight:400;color:var(--ink-muted)">— jpg/png/webp/gif, max 5 MB; vervangt de URL hierboven</small></span>
+        <span><%= t('aset.hero_upload') %> <small style="font-weight:400;color:var(--ink-muted)">— <%= t('aset.hero_upload_hint') %></small></span>
         <input type="file" name="hub_hero_file" accept="image/png,image/jpeg,image/webp,image/gif">
       </label>
       <label class="set-field">
-        <span>Donkere overlay <small style="font-weight:400;color:var(--ink-muted)">— maakt de hero donkerder zodat de tekst leesbaar blijft</small></span>
+        <span><%= t('aset.hero_overlay') %> <small style="font-weight:400;color:var(--ink-muted)">— <%= t('aset.hero_overlay_hint') %></small></span>
         <span style="display:flex;align-items:center;gap:0.75rem">
           <input type="range" name="hub_hero_overlay" min="0" max="100" step="5" value="<%= hubHeroOverlay %>" style="flex:1"
@@ -81,12 +79,12 @@
       <% if (hubHeroImage) { %>
       <div class="set-field">
-        <span style="font-weight:400;color:var(--ink-muted);font-size:0.9rem">Voorbeeld (met overlay):</span>
+        <span style="font-weight:400;color:var(--ink-muted);font-size:0.9rem"><%= t('aset.preview_overlay') %></span>
         <div style="position:relative;width:100%;aspect-ratio:16/7;margin-top:0.4rem;border-radius:8px;overflow:hidden;border:1px solid var(--rule);background:var(--paper-2)">
-          <img src="<%= hubHeroImage %>" alt="Hero-voorbeeld" style="display:block;width:100%;height:100%;object-fit:cover">
+          <img src="<%= hubHeroImage %>" alt="<%= t('aset.hero_preview_alt') %>" style="display:block;width:100%;height:100%;object-fit:cover">
           <div id="hero-ov-preview" style="position:absolute;inset:0;background:#000;opacity:<%= (hubHeroOverlay/100).toFixed(2) %>;pointer-events:none"></div>
         </div>
       </div>
       <% } %>
-      <button type="submit" class="btn btn-primary">Opslaan</button>
+      <button type="submit" class="btn btn-primary"><%= t('aset.save') %></button>
     </form>
   </section>
@@ -95,11 +93,10 @@
   <% if (tenancy === 'circle') { %>
   <section class="set-card set-card--mode" style="margin-top:1rem">
-    <h2>🔗 Je cirkel</h2>
+    <h2>🔗 <%= t('aset.your_circle') %></h2>
     <p class="set-help">
-      Beheer welke andere Klonkt-sites je in je cirkel toont, en of jouw site in
-      cirkels van anderen mag verschijnen. Asymmetrisch: jij bepaalt wie je volgt.
+      <%= t('aset.your_circle_help') %>
     </p>
     <div class="set-actions">
-      <a href="/admin/circle" class="btn btn-primary">Beheer je cirkel &rarr;</a>
+      <a href="/admin/circle" class="btn btn-primary"><%= t('aset.manage_circle') %> &rarr;</a>
     </div>
   </section>
@@ -110,19 +107,17 @@
   <% if (premium && premium.enabled) { %>
   <section class="set-card" style="margin-top:1rem">
-    <h2>Premium (Patreon)</h2>
-    <p class="set-help">Ontgrendel de premium-modules (Hub-modus, Statistieken, Fan-login,
-      Prutter-DMs) met je <strong>$10-lifetime</strong> Patreon-steun. De app en alle updates
-      blijven gratis.</p>
+    <h2><%= t('aset.premium') %></h2>
+    <p class="set-help"><%= t('aset.premium_help_1') %> <strong><%= t('aset.premium_lifetime') %></strong> <%= t('aset.premium_help_2') %></p>
     <% if (premium.premium) { %>
-      <p class="set-help"><strong>✓ Premium actief.</strong>
-        <% if (premium.supportCents) { %>Lifetime-steun: $<%= (premium.supportCents/100).toFixed(2) %>.<% } %></p>
-      <a href="/admin/patreon/disconnect" class="btn">Patreon ontkoppelen</a>
+      <p class="set-help"><strong>✓ <%= t('aset.premium_active') %></strong>
+        <% if (premium.supportCents) { %><%= t('aset.lifetime_support') %> $<%= (premium.supportCents/100).toFixed(2) %>.<% } %></p>
+      <a href="/admin/patreon/disconnect" class="btn"><%= t('aset.patreon_disconnect') %></a>
     <% } else if (premium.connected) { %>
-      <p class="set-help">Patreon gekoppeld, maar nog geen $10 lifetime<% if (premium.supportCents) { %>
-        (nu $<%= (premium.supportCents/100).toFixed(2) %>)<% } %>. Steun de campagne en koppel opnieuw.</p>
-      <a href="/admin/patreon/connect" class="btn btn-primary">Opnieuw koppelen</a>
+      <p class="set-help"><%= t('aset.patreon_no_lifetime') %><% if (premium.supportCents) { %>
+        (<%= t('aset.now') %> $<%= (premium.supportCents/100).toFixed(2) %>)<% } %>. <%= t('aset.patreon_support_again') %></p>
+      <a href="/admin/patreon/connect" class="btn btn-primary"><%= t('aset.patreon_reconnect') %></a>
     <% } else { %>
-      <p class="set-help">Nog niet gekoppeld.</p>
-      <a href="/admin/patreon/connect" class="btn btn-primary">Koppel Patreon</a>
+      <p class="set-help"><%= t('aset.patreon_not_connected') %></p>
+      <a href="/admin/patreon/connect" class="btn btn-primary"><%= t('aset.patreon_connect') %></a>
     <% } %>
   </section>
@@ -131,8 +126,8 @@
   <%# Google-login staat op een eigen Beheer-pagina (compacte link hier). %>
   <section class="set-card" style="margin-top:1rem">
-    <h2>Google-login (bezoekers)</h2>
-    <p class="set-help">Laat bezoekers met Google inloggen om te reageren — met je eigen Google Cloud-client.
-      <% if (google && google.configured) { %><strong>Status: ingesteld&nbsp;✓</strong><% } else { %>Nog niet ingesteld.<% } %></p>
-    <div class="set-actions"><a href="/admin/settings/google" class="btn btn-primary">Google-login instellen &rarr;</a></div>
+    <h2><%= t('aset.google_login') %></h2>
+    <p class="set-help"><%= t('aset.google_login_help') %>
+      <% if (google && google.configured) { %><strong><%= t('aset.status_set') %>&nbsp;✓</strong><% } else { %><%= t('aset.not_set_yet') %><% } %></p>
+    <div class="set-actions"><a href="/admin/settings/google" class="btn btn-primary"><%= t('aset.google_login_setup') %> &rarr;</a></div>
   </section>
 
@@ -140,12 +135,12 @@
   <% if (typeof premiumUnlocked === 'undefined' || premiumUnlocked) { %>
   <section class="set-card" style="margin-top:1rem">
-    <h2>Nieuwsbrief</h2>
-    <p class="set-help">Toon een <strong>aanmeldveld in de footer</strong> van je site zodat bezoekers zich op elke pagina kunnen inschrijven. (De volledige aanmeldpagina blijft op <code><%= (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : '' %>/nieuwsbrief</code>.)</p>
+    <h2><%= t('aset.newsletter') %></h2>
+    <p class="set-help"><%= t('aset.newsletter_help_1') %> <strong><%= t('aset.newsletter_footer_field') %></strong> <%= t('aset.newsletter_help_2') %> <code><%= (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : '' %>/nieuwsbrief</code>.)</p>
     <form method="post" action="/admin/settings/footer" class="set-form">
       <label class="set-toggle" style="display:flex;align-items:center;gap:.6rem;cursor:pointer">
         <input type="checkbox" name="footer_newsletter" value="1" <%= footerNewsletter ? 'checked' : '' %>>
-        <span>Aanmeldveld in de footer tonen</span>
-      </label>
-      <div class="set-actions"><button type="submit" class="btn btn-primary">Opslaan</button></div>
+        <span><%= t('aset.newsletter_show_footer') %></span>
+      </label>
+      <div class="set-actions"><button type="submit" class="btn btn-primary"><%= t('aset.save') %></button></div>
     </form>
   </section>
@@ -154,28 +149,28 @@
   <%# E-mail / SMTP — nodig voor nieuwsbrief versturen, notify-mails en wachtwoord-reset. %>
   <section class="set-card" style="margin-top:1rem">
-    <h2>E-mail (SMTP)</h2>
-    <p class="set-help">Nodig om de <strong>nieuwsbrief te versturen</strong>, <strong>show-notify</strong>-mails te sturen en wachtwoord-reset per mail te laten werken. Vul de gegevens van je mailprovider in (bv. je hostingmail, Gmail-app-wachtwoord, Brevo, Mailgun…).
-      <% if (smtp && smtp.configured) { %><br><strong>Status: ingesteld&nbsp;✓</strong><% if (smtp.fromEnv) { %> <small>(via .env)</small><% } %><% } else { %><br>Nog niet ingesteld — versturen werkt nog niet.<% } %></p>
+    <h2><%= t('aset.email_smtp') %></h2>
+    <p class="set-help"><%= t('aset.smtp_help_1') %> <strong><%= t('aset.smtp_help_newsletter') %></strong><%= t('aset.smtp_help_2') %> <strong><%= t('aset.smtp_help_notify') %></strong><%= t('aset.smtp_help_3') %>
+      <% if (smtp && smtp.configured) { %><br><strong><%= t('aset.status_set') %>&nbsp;✓</strong><% if (smtp.fromEnv) { %> <small>(<%= t('aset.via_env') %>)</small><% } %><% } else { %><br><%= t('aset.smtp_not_set') %><% } %></p>
 
     <form method="post" action="/admin/settings/smtp" class="set-form">
-      <label><span>SMTP-host</span>
+      <label><span><%= t('aset.smtp_host') %></span>
         <input type="text" name="smtp_host" value="<%= smtp ? smtp.host : '' %>" placeholder="smtp.jouwprovider.nl" autocomplete="off">
       </label>
-      <label><span>Poort <small style="font-weight:400">— 587 (STARTTLS) of 465 (SSL)</small></span>
+      <label><span><%= t('aset.smtp_port') %> <small style="font-weight:400">— <%= t('aset.smtp_port_hint') %></small></span>
         <input type="text" name="smtp_port" value="<%= (smtp && smtp.port) ? smtp.port : '' %>" placeholder="587" autocomplete="off" inputmode="numeric">
       </label>
-      <label><span>Gebruikersnaam</span>
+      <label><span><%= t('aset.smtp_user') %></span>
         <input type="text" name="smtp_user" value="<%= smtp ? smtp.user : '' %>" placeholder="jij@jouwprovider.nl" autocomplete="off">
       </label>
-      <label><span>Wachtwoord <% if (smtp && smtp.passSet) { %><small style="font-weight:400">— ingesteld; leeg laten = ongewijzigd</small><% } %></span>
-        <input type="password" name="smtp_pass" placeholder="<%= (smtp && smtp.passSet) ? '•••••••• (ingesteld)' : 'app-wachtwoord' %>" autocomplete="off">
-      </label>
-      <label><span>Afzender <small style="font-weight:400">— optioneel; standaard = gebruikersnaam</small></span>
-        <input type="text" name="smtp_from" value="<%= smtp ? smtp.from : '' %>" placeholder="Jouw Naam &lt;jij@jouwprovider.nl&gt;" autocomplete="off">
+      <label><span><%= t('aset.smtp_pass') %> <% if (smtp && smtp.passSet) { %><small style="font-weight:400">— <%= t('aset.smtp_pass_set_hint') %></small><% } %></span>
+        <input type="password" name="smtp_pass" placeholder="<%= (smtp && smtp.passSet) ? t('aset.smtp_pass_ph_set') : t('aset.smtp_pass_ph') %>" autocomplete="off">
+      </label>
+      <label><span><%= t('aset.smtp_from') %> <small style="font-weight:400">— <%= t('aset.smtp_from_hint') %></small></span>
+        <input type="text" name="smtp_from" value="<%= smtp ? smtp.from : '' %>" placeholder="<%= t('aset.smtp_from_ph') %>" autocomplete="off">
       </label>
       <div class="set-actions">
-        <button type="submit" class="btn btn-primary">SMTP opslaan</button>
+        <button type="submit" class="btn btn-primary"><%= t('aset.smtp_save') %></button>
         <% if (smtp && (smtp.host || smtp.passSet)) { %>
-          <button type="submit" name="clear" value="1" class="btn" formnovalidate>Wissen</button>
+          <button type="submit" name="clear" value="1" class="btn" formnovalidate><%= t('aset.clear') %></button>
         <% } %>
       </div>
@@ -184,8 +179,8 @@
     <% if (smtp && smtp.configured) { %>
       <form method="post" action="/admin/settings/smtp/test" class="set-form" style="margin-top:.8rem">
-        <label><span>Testmail sturen naar</span>
+        <label><span><%= t('aset.test_mail_to') %></span>
           <input type="email" name="to" placeholder="jij@email.nl" autocomplete="off">
         </label>
-        <div class="set-actions"><button type="submit" class="btn">✉ Testmail sturen</button></div>
+        <div class="set-actions"><button type="submit" class="btn">✉ <%= t('aset.send_test_mail') %></button></div>
       </form>
     <% } %>
Index: src/views/pages/admin-shows.ejs
===================================================================
--- src/views/pages/admin-shows.ejs	(revision 929d98d2002f662695a6e55138a1e9094f722bad)
+++ src/views/pages/admin-shows.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
@@ -6,6 +6,6 @@
 %>
 <section class="ash">
-  <p><a href="/admin" class="btn">&larr; Beheer</a></p>
-  <h1>Agenda</h1>
+  <p><a href="/admin" class="btn">&larr; <%= t('ashow.back_admin') %></a></p>
+  <h1><%= t('ashow.title') %></h1>
 
   <% if (msg) { %><div class="ash-msg ash-<%= (typeof msgKind!=='undefined')?msgKind:'' %>"><%= msg %></div><% } %>
@@ -15,28 +15,29 @@
     <label style="display:flex;align-items:center;gap:10px;cursor:pointer;font-weight:600">
       <input type="checkbox" name="enabled" value="1" <%= agOn ? 'checked' : '' %> onchange="this.form.submit()" style="width:18px;height:18px;accent-color:var(--accent)">
-      <span>Agenda tonen op de site <small style="font-weight:400;opacity:.7">(Agenda-knop in de balk + de agendapagina)</small></span>
+      <span><%= t('ashow.show_toggle') %> <small style="font-weight:400;opacity:.7"><%= t('ashow.show_toggle_hint') %></small></span>
     </label>
-    <noscript><button type="submit" class="btn">Opslaan</button></noscript>
+    <noscript><button type="submit" class="btn"><%= t('ashow.save') %></button></noscript>
   </form>
-  <% if (!agOn) { %><div class="ash-msg">De agenda staat nu <strong>uit</strong> — bezoekers zien geen Agenda-knop en de agendapagina is niet bereikbaar. Zet 'm aan om je evenementen te tonen.</div><% } %>
+  <% if (!agOn) { %><div class="ash-msg"><%= t('ashow.off_notice_1') %> <strong><%= t('ashow.off') %></strong> <%= t('ashow.off_notice_2') %></div><% } %>
 
-  <p class="ash-note"><strong><%= nc %></strong> abonnee(s) voor evenement-aankondigingen.
-    <% if (!smtp) { %><br>⚠ SMTP niet ingesteld — evenementen worden opgeslagen, maar notify-mails kunnen pas verstuurd worden zodra je SMTP invult.<% } %></p>
+  <p class="ash-note"><strong><%= nc %></strong> <%= t('ashow.subscribers') %>
+    <% if (!smtp) { %><br><%= t('ashow.smtp_warn') %><% } %></p>
 
   <form method="POST" action="<%= siteUrlBase %>/admin/shows" class="ash-form">
     <div class="ash-grid">
-      <label>Datum<input type="date" name="date" required></label>
-      <label>Tijd (optioneel)<input type="time" name="time"></label>
-      <label>Plaats<input type="text" name="city" required placeholder="Amsterdam"></label>
-      <label>Land (optioneel)<input type="text" name="country" placeholder="NL"></label>
-      <label class="ash-wide">Locatie/zaal (optioneel)<input type="text" name="venue" placeholder="Paradiso"></label>
-      <label class="ash-wide">Ticket-URL (optioneel)<input type="url" name="ticket_url" placeholder="https://..."></label>
-      <label class="ash-wide">Notitie (optioneel)<input type="text" name="notes" placeholder="Support: ..."></label>
+      <label><%= t('ashow.f_date') %><input type="date" name="date" required></label>
+      <label><%= t('ashow.f_time') %><input type="time" name="time"></label>
+      <label><%= t('ashow.f_city') %><input type="text" name="city" required placeholder="Amsterdam"></label>
+      <label><%= t('ashow.f_country') %><input type="text" name="country" placeholder="NL"></label>
+      <label class="ash-wide"><%= t('ashow.f_venue') %><input type="text" name="venue" placeholder="Paradiso"></label>
+      <label class="ash-wide"><%= t('ashow.f_ticket') %><input type="url" name="ticket_url" placeholder="https://..."></label>
+      <label class="ash-wide"><%= t('ashow.f_notes') %><input type="text" name="notes" placeholder="<%= t('ashow.f_notes_ph') %>"></label>
     </div>
-    <label class="ash-check"><input type="checkbox" name="notify" value="1" <%= smtp ? '' : 'disabled' %>> Abonnees per e-mail op de hoogte brengen<%= smtp ? '' : ' (SMTP vereist)' %></label>
-    <button type="submit" class="ash-add">+ Evenement toevoegen</button>
+    <label class="ash-check"><input type="checkbox" name="notify" value="1" <%= smtp ? '' : 'disabled' %>> <%= t('ashow.notify_label') %><%= smtp ? '' : ' ' + t('ashow.smtp_required') %></label>
+    <button type="submit" class="ash-add"><%= t('ashow.add_event') %></button>
   </form>
 
   <% if (ss.length) { %>
+    <% var _delConfirm = t('ashow.del_confirm'); %>
     <ul class="ash-list">
       <% ss.forEach(function(s){ %>
@@ -44,5 +45,5 @@
           <span class="ash-when"><%= s.date %><% if (s.time) { %> <%= s.time %><% } %></span>
           <span class="ash-where"><%= s.city %><% if (s.venue) { %> — <%= s.venue %><% } %></span>
-          <form method="POST" action="<%= siteUrlBase %>/admin/shows/<%= s.id %>/delete" onsubmit="return confirm('Evenement verwijderen??')" style="margin:0">
+          <form method="POST" action="<%= siteUrlBase %>/admin/shows/<%= s.id %>/delete" onsubmit="return confirm('<%= _delConfirm %>')" style="margin:0">
             <button type="submit" class="ash-del">🗑</button>
           </form>
@@ -51,5 +52,5 @@
     </ul>
   <% } else { %>
-    <p class="ash-empty">Nog geen evenementen.</p>
+    <p class="ash-empty"><%= t('ashow.empty') %></p>
   <% } %>
 </section>
Index: src/views/pages/admin-site-edit.ejs
===================================================================
--- src/views/pages/admin-site-edit.ejs	(revision 929d98d2002f662695a6e55138a1e9094f722bad)
+++ src/views/pages/admin-site-edit.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
@@ -1,5 +1,5 @@
 <div class="container admin-site-edit-page">
-  <p><a href="/admin" class="btn">&larr; Beheer</a></p>
-  <h1><%= isNew ? 'Nieuwe site' : 'Uiterlijk: ' + site.title %></h1>
+  <p><a href="/admin" class="btn">&larr; <%= t('asite.back_admin') %></a></p>
+  <h1><%= isNew ? t('asite.title_new') : t('asite.title_edit') + ': ' + site.title %></h1>
 
   <% if (typeof success !== 'undefined' && success) { %><div class="audio-flash audio-flash--ok"><%= success %></div><% } %>
@@ -9,17 +9,17 @@
 
     <fieldset>
-      <legend>Identiteit</legend>
-      <label>
-        <span>Slug (URL) <% if (!isNew) { %><em>(vast)</em><% } %></span>
+      <legend><%= t('asite.identity') %></legend>
+      <label>
+        <span><%= t('asite.slug') %> <% if (!isNew) { %><em><%= t('asite.slug_fixed') %></em><% } %></span>
         <span class="slug-url">
           <span class="slug-url-host" id="slug-host"
                 data-prefix="<%= (typeof tenancy !== 'undefined' && tenancy === 'hub') ? '/user/' : '/' %>">website.com/</span><input
                 type="text" name="slug" id="slug-input" class="slug-url-input" value="<%= site.slug %>" required <% if (!isNew) { %>readonly<% } %>
-                pattern="[a-z0-9_-]{2,40}" placeholder="jouwslug"
+                pattern="[a-z0-9_-]{2,40}" placeholder="<%= t('asite.slug_placeholder') %>"
                 autocapitalize="none" autocorrect="off" spellcheck="false">
         </span>
       </label>
       <label>
-        <span>Titel <small class="form-hint-inline">— getoond in de kop en als weergavenaam</small></span>
+        <span><%= t('asite.field_title') %> <small class="form-hint-inline"><%= t('asite.field_title_hint') %></small></span>
         <input type="text" name="title" value="<%= site.title || '' %>" required maxlength="200">
       </label>
@@ -28,8 +28,9 @@
       <% if (user && user.role === 'god') { %>
       <label>
-        <span>Eigenaar <small class="form-hint-inline">— wie deze Klonkt zelf mag beheren</small></span>
+        <span><%= t('asite.owner') %> <small class="form-hint-inline"><%= t('asite.owner_hint') %></small></span>
+        <% var _godSuffix = t('asite.owner_god_suffix'); %>
         <select name="owner_id">
           <% (typeof users !== 'undefined' ? users : []).forEach(function(u){ %>
-            <option value="<%= u.id %>" <%= (site.owner_id === u.id) ? 'selected' : '' %>><%= u.username %><%= u.role === 'god' ? ' (god)' : '' %></option>
+            <option value="<%= u.id %>" <%= (site.owner_id === u.id) ? 'selected' : '' %>><%= u.username %><%= u.role === 'god' ? _godSuffix : '' %></option>
           <% }); %>
         </select>
@@ -37,13 +38,13 @@
       <% } %>
       <label>
-        <span>Tagline <small class="form-hint-inline">— korte oneliner</small></span>
+        <span><%= t('asite.tagline') %> <small class="form-hint-inline"><%= t('asite.tagline_hint') %></small></span>
         <input type="text" name="tagline" value="<%= site.tagline || '' %>" maxlength="200">
       </label>
       <label>
-        <span>Bio / omschrijving <small class="form-hint-inline">— getoond in de profielkop en gebruikt voor SEO</small></span>
+        <span><%= t('asite.bio') %> <small class="form-hint-inline"><%= t('asite.bio_hint') %></small></span>
         <textarea name="description" rows="3" maxlength="500"><%= site.description || '' %></textarea>
       </label>
       <label>
-        <span>Profielfoto</span>
+        <span><%= t('asite.profile_photo') %></span>
         <div class="photo-picker" id="photo-picker">
           <div class="photo-thumb" id="photo-thumb" <%= site.profile_photo ? '' : 'data-empty' %>>
@@ -54,8 +55,8 @@
           </div>
           <div class="photo-controls">
-            <input type="text" name="profile_photo" id="photo-url" value="<%= site.profile_photo || '' %>" placeholder="/media/avatars/foo.jpg of https://…">
+            <input type="text" name="profile_photo" id="photo-url" value="<%= site.profile_photo || '' %>" placeholder="<%= t('asite.photo_url_placeholder') %>">
             <div class="photo-actions">
-              <button type="button" class="btn" id="photo-upload-trigger">📷 Uploaden</button>
-              <button type="button" class="btn" id="photo-clear" <%= site.profile_photo ? '' : 'hidden' %>>Verwijder</button>
+              <button type="button" class="btn" id="photo-upload-trigger"><%= t('asite.photo_upload') %></button>
+              <button type="button" class="btn" id="photo-clear" <%= site.profile_photo ? '' : 'hidden' %>><%= t('asite.photo_clear') %></button>
               <small id="photo-status" class="form-hint photo-status"></small>
             </div>
@@ -65,15 +66,15 @@
       </label>
       <label>
-        <span>Taal (ISO-code)</span>
+        <span><%= t('asite.language') %></span>
         <input type="text" name="language" value="<%= site.language || 'nl' %>" maxlength="8">
       </label>
       <label class="cb">
         <input type="checkbox" name="profile_enabled" value="1" <%= site.profile_enabled ? 'checked' : '' %>>
-        <span>Profielkop tonen onder de navigatie</span>
+        <span><%= t('asite.profile_enabled') %></span>
       </label>
     </fieldset>
 
     <fieldset>
-      <legend>Vormgeving</legend>
+      <legend><%= t('asite.appearance') %></legend>
 
       <%# ── Curated accent picker ───────────────────────────────────────
@@ -82,5 +83,5 @@
           still what's stored in the DB — we just constrain WHICH hex. %>
       <fieldset class="accent-grid">
-        <legend>Accentkleur</legend>
+        <legend><%= t('asite.accent_color') %></legend>
         <% const _curAccent = (site.accent || '#c2410c').toLowerCase(); %>
         <% accents.forEach(function(a) { %>
@@ -99,22 +100,22 @@
           visitors with a localStorage override, still win over this default. %>
       <fieldset class="theme-default">
-        <legend>Standaardthema voor nieuwe bezoekers</legend>
+        <legend><%= t('asite.theme_default') %></legend>
         <% const td = site.theme_override || ''; %>
         <label class="cb">
           <input type="radio" name="theme_override" value=""      <%= td === ''      ? 'checked' : '' %>>
-          <span>Auto (volg device-voorkeur)</span>
+          <span><%= t('asite.theme_auto') %></span>
         </label>
         <label class="cb">
           <input type="radio" name="theme_override" value="light" <%= td === 'light' ? 'checked' : '' %>>
-          <span>Licht</span>
+          <span><%= t('asite.theme_light') %></span>
         </label>
         <label class="cb">
           <input type="radio" name="theme_override" value="dark"  <%= td === 'dark'  ? 'checked' : '' %>>
-          <span>Donker</span>
+          <span><%= t('asite.theme_dark') %></span>
         </label>
       </fieldset>
 
       <fieldset class="palette-grid">
-        <legend>Palet</legend>
+        <legend><%= t('asite.palette') %></legend>
         <% palettes.forEach(function(p) { %>
           <label class="palette-swatch">
@@ -137,27 +138,27 @@
 
     <fieldset>
-      <legend>Gedrag</legend>
+      <legend><%= t('asite.behavior') %></legend>
       <label class="cb">
         <input type="checkbox" name="is_public" value="1" <%= site.is_public ? 'checked' : '' %>>
-        <span>Publieke site (uitvinken voor een besloten kring)</span>
+        <span><%= t('asite.is_public') %></span>
       </label>
       <label class="cb">
         <input type="checkbox" name="robots_index" value="1" <%= site.robots_index ? 'checked' : '' %>>
-        <span>Zoekmachines mogen indexeren (sitemap.xml is verborgen indien uit)</span>
+        <span><%= t('asite.robots_index') %></span>
       </label>
       <label class="cb">
         <input type="checkbox" name="require_login_to_comment" value="1" <%= site.require_login_to_comment ? 'checked' : '' %>>
-        <span>Inloggen vereist om te reageren</span>
-      </label>
-      <label>
-        <span>Reactie-moderatie</span>
+        <span><%= t('asite.require_login_comment') %></span>
+      </label>
+      <label>
+        <span><%= t('asite.comment_moderation') %></span>
         <select name="comments_moderation_mode">
-          <option value="trust"    <%= (site.comments_moderation_mode || 'trust') === 'trust'    ? 'selected' : '' %>>Vertrouwen (automatisch goedkeuren)</option>
-          <option value="moderate" <%= site.comments_moderation_mode === 'moderate' ? 'selected' : '' %>>Modereren (eerst in de wachtrij)</option>
+          <option value="trust"    <%= (site.comments_moderation_mode || 'trust') === 'trust'    ? 'selected' : '' %>><%= t('asite.moderation_trust') %></option>
+          <option value="moderate" <%= site.comments_moderation_mode === 'moderate' ? 'selected' : '' %>><%= t('asite.moderation_moderate') %></option>
         </select>
       </label>
       <label class="cb">
         <input type="checkbox" name="enable_audio_player" value="1" <%= site.enable_audio_player ? 'checked' : '' %>>
-        <span>Audiospeler + embeds inschakelen</span>
+        <span><%= t('asite.enable_audio') %></span>
       </label>
       <%# Prutter is een Hub-only premium-functie — toggle alleen tonen in Hub-modus. %>
@@ -165,5 +166,5 @@
       <label class="cb">
         <input type="checkbox" name="enable_prutter" value="1" <%= site.enable_prutter ? 'checked' : '' %>>
-        <span>Prutter (DM&rsquo;s tussen leden) inschakelen <small class="form-hint-inline">— Hub-functie, premium</small></span>
+        <span><%= t('asite.enable_prutter') %> <small class="form-hint-inline"><%= t('asite.enable_prutter_hint') %></small></span>
       </label>
       <% } %>
@@ -171,6 +172,6 @@
 
     <fieldset class="profile-links-fieldset">
-      <legend>Social / streaming-links</legend>
-      <p class="form-hint">Getoond als merk-iconen op de profielkop. Voeg er zoveel toe als je wilt.</p>
+      <legend><%= t('asite.links') %></legend>
+      <p class="form-hint"><%= t('asite.links_hint') %></p>
 
       <div id="profile-links-rows">
@@ -200,53 +201,52 @@
       </template>
 
-      <button type="button" id="profile-link-add" class="btn">+ Link toevoegen</button>
+      <button type="button" id="profile-link-add" class="btn"><%= t('asite.link_add') %></button>
     </fieldset>
 
     <fieldset>
-      <legend>Feed-weergave</legend>
-      <label>
-        <span>Standaardweergave voor de homepage</span>
+      <legend><%= t('asite.feed_view') %></legend>
+      <label>
+        <span><%= t('asite.feed_default') %></span>
         <select name="feed_view_default">
-          <option value="timeline" <%= (site.feed_view_default || 'timeline') === 'timeline' ? 'selected' : '' %>>Tijdlijn (chronologische lijst)</option>
-          <option value="grid"     <%= site.feed_view_default === 'grid' ? 'selected' : '' %>>Grid (kaarten)</option>
+          <option value="timeline" <%= (site.feed_view_default || 'timeline') === 'timeline' ? 'selected' : '' %>><%= t('asite.feed_timeline') %></option>
+          <option value="grid"     <%= site.feed_view_default === 'grid' ? 'selected' : '' %>><%= t('asite.feed_grid') %></option>
         </select>
       </label>
       <label class="cb">
         <input type="checkbox" name="feed_view_switch" value="1" <%= site.feed_view_switch ? 'checked' : '' %>>
-        <span>Tijdlijn ↔ grid-schakelaar boven de feed tonen</span>
+        <span><%= t('asite.feed_switch') %></span>
       </label>
       <label class="cb">
         <input type="checkbox" name="show_search" value="1" <%= site.show_search ? 'checked' : '' %>>
-        <span>Zoekknop in de navigatie tonen</span>
+        <span><%= t('asite.show_search') %></span>
       </label>
       <label class="cb">
         <input type="checkbox" name="show_archive_link" value="1" <%= site.show_archive_link ? 'checked' : '' %>>
-        <span>Archief-link in de navigatie tonen</span>
+        <span><%= t('asite.show_archive') %></span>
       </label>
     </fieldset>
 
     <fieldset class="seo-pointer">
-      <legend>SEO &amp; social</legend>
-      <p>Titel-sjabloon, canonical, deel-afbeelding, verificatie-metas en meer staan
-        nu op een eigen pagina: <a href="/admin/seo">🔎 SEO &amp; vindbaarheid</a>.</p>
+      <legend><%= t('asite.seo') %></legend>
+      <p><%= t('asite.seo_pointer') %> <a href="/admin/seo"><%= t('asite.seo_link') %></a>.</p>
     </fieldset>
 
     <fieldset>
-      <legend>Eigen CSS &amp; HTML <em>(optioneel)</em></legend>
-      <label>
-        <span>Eigen CSS (geïnjecteerd als &lt;style&gt; in &lt;head&gt;)</span>
+      <legend><%= t('asite.custom_legend') %> <em><%= t('asite.optional') %></em></legend>
+      <label>
+        <span><%= t('asite.custom_css') %></span>
         <textarea name="custom_css" rows="3" placeholder=".foo { color: red; }"><%= site.custom_css || '' %></textarea>
       </label>
       <label>
-        <span>Eigen &lt;head&gt;-HTML (analytics, extra meta&rsquo;s)</span>
+        <span><%= t('asite.custom_head') %></span>
         <textarea name="custom_head_html" rows="3" placeholder="<meta name=&quot;...&quot;>"><%= site.custom_head_html || '' %></textarea>
       </label>
       <label>
-        <span>Eigen footer-HTML</span>
+        <span><%= t('asite.custom_foot') %></span>
         <textarea name="custom_foot_html" rows="3"><%= site.custom_foot_html || '' %></textarea>
       </label>
     </fieldset>
 
-    <button type="submit" class="btn btn-primary"><%= isNew ? 'Site aanmaken' : 'Wijzigingen opslaan' %></button>
+    <button type="submit" class="btn btn-primary"><%= isNew ? t('asite.submit_create') : t('asite.submit_save') %></button>
   </form>
 </div>
Index: src/views/pages/admin-sites.ejs
===================================================================
--- src/views/pages/admin-sites.ejs	(revision 929d98d2002f662695a6e55138a1e9094f722bad)
+++ src/views/pages/admin-sites.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
@@ -1,9 +1,9 @@
 <div class="container ax-page">
 
-  <p><a href="/admin" class="btn">&larr; Beheer</a></p>
+  <p><a href="/admin" class="btn">&larr; <%= t('asit2.back') %></a></p>
   <header class="ax-header">
     <div>
-      <h1>Sites</h1>
-      <p class="ax-tagline">Beheer alle sites op deze installatie.</p>
+      <h1><%= t('asit2.title') %></h1>
+      <p class="ax-tagline"><%= t('asit2.tagline') %></p>
     </div>
   </header>
@@ -13,5 +13,5 @@
 
   <div class="ax-actions-bar">
-    <a href="/admin/sites/new" class="ax-btn ax-btn-primary">＋ Nieuwe site</a>
+    <a href="/admin/sites/new" class="ax-btn ax-btn-primary">＋ <%= t('asit2.new_site') %></a>
   </div>
 
@@ -20,9 +20,30 @@
       <div class="ax-empty">
         <div class="ax-empty-icon">🌐</div>
-        <p>Nog geen sites.</p>
-        <p class="ax-empty-sub">Maak er een via de knop hierboven.</p>
+        <p><%= t('asit2.empty') %></p>
+        <p class="ax-empty-sub"><%= t('asit2.empty_sub') %></p>
       </div>
     </div>
   <% } else { %>
+    <%
+      var _s_primary    = t('asit2.pill_primary');
+      var _s_primary_t  = t('asit2.pill_primary_title');
+      var _s_public     = t('asit2.pill_public');
+      var _s_public_t   = t('asit2.pill_public_title');
+      var _s_private    = t('asit2.pill_private');
+      var _s_private_t  = t('asit2.pill_private_title');
+      var _s_noindex    = t('asit2.pill_noindex');
+      var _s_noindex_t  = t('asit2.pill_noindex_title');
+      var _s_by         = t('asit2.by');
+      var _s_t_posts    = t('asit2.t_posts');
+      var _s_l_posts    = t('asit2.l_posts');
+      var _s_t_created  = t('asit2.t_created');
+      var _s_l_created  = t('asit2.l_created');
+      var _s_makeprim   = t('asit2.make_primary');
+      var _s_makeprim_t = t('asit2.make_primary_title');
+      var _s_makeprim_c = t('asit2.make_primary_confirm');
+      var _s_edit       = t('asit2.edit');
+      var _s_del        = t('asit2.delete');
+      var _s_del_c      = t('asit2.delete_confirm');
+    %>
     <ul class="ax-list">
       <% sites.forEach(function(s) { %>
@@ -37,13 +58,13 @@
                 <a href="/user/<%= s.slug %>"><%= s.title %></a>
                 <% if (s.is_primary) { %>
-                  <span class="ax-pill ax-pill-primary" title="Hoofd-/labelsite van deze installatie">★ primair</span>
+                  <span class="ax-pill ax-pill-primary" title="<%= _s_primary_t %>">★ <%= _s_primary %></span>
                 <% } %>
                 <% if (s.is_public) { %>
-                  <span class="ax-pill ax-pill-ok" title="Publiek zichtbaar">✓ public</span>
+                  <span class="ax-pill ax-pill-ok" title="<%= _s_public_t %>">✓ <%= _s_public %></span>
                 <% } else { %>
-                  <span class="ax-pill" title="Niet publiek">🔒 private</span>
+                  <span class="ax-pill" title="<%= _s_private_t %>">🔒 <%= _s_private %></span>
                 <% } %>
                 <% if (!s.robots_index) { %>
-                  <span class="ax-pill" title="Niet geïndexeerd door zoekmachines">🚫🤖 noindex</span>
+                  <span class="ax-pill" title="<%= _s_noindex_t %>">🚫🤖 <%= _s_noindex %></span>
                 <% } %>
               </div>
@@ -52,5 +73,5 @@
                 <% if (s.owner_username) { %>
                   <span class="ax-track-sep">·</span>
-                  <span>door <%= s.owner_username %></span>
+                  <span><%= _s_by %> <%= s.owner_username %></span>
                 <% } %>
               </div>
@@ -59,11 +80,11 @@
 
           <div class="ax-site-stats">
-            <span class="ax-stat" title="Aantal posts">
+            <span class="ax-stat" title="<%= _s_t_posts %>">
               <span class="ax-stat-num"><%= s.post_count %></span>
-              <span class="ax-stat-label">posts</span>
+              <span class="ax-stat-label"><%= _s_l_posts %></span>
             </span>
-            <span class="ax-stat" title="Aangemaakt op">
+            <span class="ax-stat" title="<%= _s_t_created %>">
               <span class="ax-stat-num ax-stat-date"><%= formatDate(s.created_at) %></span>
-              <span class="ax-stat-label">created</span>
+              <span class="ax-stat-label"><%= _s_l_created %></span>
             </span>
           </div>
@@ -72,12 +93,12 @@
             <% if (!s.is_primary && (typeof canMutate === 'undefined' || canMutate)) { %>
               <form method="post" action="/admin/sites/<%= s.slug %>/make-primary" style="margin:0;display:inline"
-                    onsubmit="return confirm('Deze site instellen als de primaire/hoofd-site?')">
-                <button type="submit" class="ax-icon-btn" aria-label="Maak primair" title="Maak primaire/hoofd-site">★</button>
+                    onsubmit="return confirm('<%= _s_makeprim_c %>')">
+                <button type="submit" class="ax-icon-btn" aria-label="<%= _s_makeprim %>" title="<%= _s_makeprim_t %>">★</button>
               </form>
             <% } %>
-            <a href="/admin/sites/<%= s.slug %>/edit" class="ax-icon-btn" aria-label="Bewerken" title="Bewerken">✎</a>
+            <a href="/admin/sites/<%= s.slug %>/edit" class="ax-icon-btn" aria-label="<%= _s_edit %>" title="<%= _s_edit %>">✎</a>
             <form method="post" action="/admin/sites/<%= s.slug %>/delete" class="ax-delete-form"
-                  onsubmit="return confirm('Site verwijderen? Lukt alleen als er geen posts zijn.')">
-              <button type="submit" class="ax-icon-btn ax-icon-btn-danger" aria-label="Verwijderen" title="Verwijderen">🗑</button>
+                  onsubmit="return confirm('<%= _s_del_c %>')">
+              <button type="submit" class="ax-icon-btn ax-icon-btn-danger" aria-label="<%= _s_del %>" title="<%= _s_del %>">🗑</button>
             </form>
           </div>
Index: src/views/pages/admin-stats.ejs
===================================================================
--- src/views/pages/admin-stats.ejs	(revision 929d98d2002f662695a6e55138a1e9094f722bad)
+++ src/views/pages/admin-stats.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
@@ -1,35 +1,33 @@
 <div class="container stats-page">
   <p><a href="/admin" class="btn">&larr; Beheer</a></p>
-  <h1>Statistieken</h1>
-  <p class="stats-note">Cookievrij gemeten — geen tracking-cookies, geen toestemmingsbanner.
-    Bezoekers worden per dag geteld via een dagelijks roterende, anonieme hash (IP/browser worden
-    niet bewaard). Je eigen beheerder-bezoeken en bekende bots/crawlers tellen niet mee.</p>
+  <h1><%= t('astat.title') %></h1>
+  <p class="stats-note"><%= t('astat.intro') %></p>
 
   <% var s = stats; var _max = Math.max(1, ...s.series.map(function(d){ return d.pageviews; })); %>
 
   <div class="stats-cards">
-    <div class="stat-card"><div class="stat-num"><%= s.totals.visitors %></div><div class="stat-label">Bezoeker-dagen (<%= s.days %>d)</div></div>
-    <div class="stat-card"><div class="stat-num"><%= s.totals.pageviews %></div><div class="stat-label">Weergaven (<%= s.days %>d)</div></div>
-    <div class="stat-card"><div class="stat-num"><%= s.totals.plays %></div><div class="stat-label">Plays (totaal)</div></div>
-    <div class="stat-card"><div class="stat-num"><%= s.totals.postViews %></div><div class="stat-label">Post-weergaven (totaal)</div></div>
+    <div class="stat-card"><div class="stat-num"><%= s.totals.visitors %></div><div class="stat-label"><%= t('astat.visitor_days', { n: s.days }) %></div></div>
+    <div class="stat-card"><div class="stat-num"><%= s.totals.pageviews %></div><div class="stat-label"><%= t('astat.pageviews_days', { n: s.days }) %></div></div>
+    <div class="stat-card"><div class="stat-num"><%= s.totals.plays %></div><div class="stat-label"><%= t('astat.plays_total') %></div></div>
+    <div class="stat-card"><div class="stat-num"><%= s.totals.postViews %></div><div class="stat-label"><%= t('astat.postviews_total') %></div></div>
   </div>
 
   <% if (typeof s.allTime !== 'undefined') { %>
-    <p class="stats-note">All-time: <strong><%= s.allTime.pageviews %></strong> weergaven · <strong><%= s.allTime.visitorDays %></strong> bezoeker-dagen.</p>
+    <p class="stats-note"><%= t('astat.alltime_pre') %> <strong><%= s.allTime.pageviews %></strong> <%= t('astat.alltime_mid') %> · <strong><%= s.allTime.visitorDays %></strong> <%= t('astat.alltime_post') %></p>
   <% } %>
 
   <details class="stats-help">
-    <summary>Wat betekenen deze cijfers?</summary>
+    <summary><%= t('astat.help_summary') %></summary>
     <ul>
-      <li><strong>Bezoeker-dagen</strong> — het aantal unieke bezoekers <em>per dag, bij elkaar opgeteld</em>. Eén persoon die 5 dagen langskomt = 5 bezoeker-dagen. Cookieloos kan er niet over dagen heen geteld worden, dus dit is géén aantal unieke personen — het echte aantal mensen ligt (vaak fors) lager.</li>
-      <li><strong>Weergaven</strong> — hoe vaak de home/feed of een post geladen is (ook bij klikken binnen de site). Andere pagina's (agenda, downloads, links) tellen hier niet in mee.</li>
-      <li><strong>Plays</strong> — totaal aantal keren dat een track is gestart.</li>
-      <li><strong>Post-weergaven</strong> — totaal over alle posts samen.</li>
+      <li><strong><%= t('astat.help_vd_term') %></strong> — <%= t('astat.help_vd_a') %> <em><%= t('astat.help_vd_em') %></em><%= t('astat.help_vd_b') %></li>
+      <li><strong><%= t('astat.help_pv_term') %></strong> — <%= t('astat.help_pv') %></li>
+      <li><strong><%= t('astat.help_plays_term') %></strong> — <%= t('astat.help_plays') %></li>
+      <li><strong><%= t('astat.help_postviews_term') %></strong> — <%= t('astat.help_postviews') %></li>
     </ul>
-    <p>Beheerder-bezoeken en bekende bots/crawlers worden overgeslagen. Het ruwe IP wordt nooit bewaard. Goed voor trends; neem absolute aantallen met een korrel zout.</p>
+    <p><%= t('astat.help_footer') %></p>
   </details>
 
   <div class="stats-period">
-    Periode:
+    <%= t('astat.period') %>
     <% [7,14,30,90].forEach(function(n){ %>
       <a href="/admin/stats?days=<%= n %>" class="<%= s.days === n ? 'is-on' : '' %>"><%= n %>d</a>
@@ -39,8 +37,8 @@
   <section class="stats-chart-wrap">
     <div class="stats-chart-head">
-      <h2>Laatste <%= s.days %> dagen</h2>
+      <h2><%= t('astat.last_n_days', { n: s.days }) %></h2>
       <div class="stats-legend">
-        <span class="lg lg-vis">Bezoeker-dagen</span>
-        <span class="lg lg-pv">Weergaven</span>
+        <span class="lg lg-vis"><%= t('astat.lg_visitor_days') %></span>
+        <span class="lg lg-pv"><%= t('astat.lg_pageviews') %></span>
       </div>
     </div>
@@ -51,5 +49,5 @@
            var visPct = Math.round(d.visitors / _max * 100);
       %>
-        <div class="sc-col" title="<%= d.day %> — <%= d.pageviews %> weergaven, <%= d.visitors %> bezoeker-dagen">
+        <div class="sc-col" title="<%= t('astat.bar_title', { day: d.day, pv: d.pageviews, vd: d.visitors }) %>">
           <div class="sc-bar">
             <span class="sc-pv" style="height: <%= pvPct %>%"></span>
@@ -64,7 +62,7 @@
   <div class="stats-tops">
     <section class="stats-top">
-      <h2>Populairste posts</h2>
+      <h2><%= t('astat.top_posts') %></h2>
       <% if (!s.topPosts.length || s.topPosts[0].views === 0) { %>
-        <p class="stats-empty">Nog geen weergaven.</p>
+        <p class="stats-empty"><%= t('astat.no_views') %></p>
       <% } else { %>
         <ol>
@@ -76,7 +74,7 @@
     </section>
     <section class="stats-top">
-      <h2>Meest geluisterd</h2>
+      <h2><%= t('astat.most_played') %></h2>
       <% if (!s.topTracks.length || s.topTracks[0].plays === 0) { %>
-        <p class="stats-empty">Nog geen plays.</p>
+        <p class="stats-empty"><%= t('astat.no_plays') %></p>
       <% } else { %>
         <ol>
@@ -91,5 +89,5 @@
   <% if (typeof s.referrers !== 'undefined' && s.referrers.length) { %>
     <section class="stats-top" style="margin-top:1.2rem">
-      <h2>🌐 Bronnen (waar bezoekers vandaan komen)</h2>
+      <h2>🌐 <%= t('astat.sources') %></h2>
       <ol>
         <% s.referrers.forEach(function(r){ %>
@@ -102,5 +100,5 @@
   <% if (typeof linkClicks !== 'undefined' && linkClicks.length) { %>
     <section class="stats-top" style="margin-top:1.2rem">
-      <h2>🔗 Link-in-bio klikken</h2>
+      <h2>🔗 <%= t('astat.linkbio_clicks') %></h2>
       <ol>
         <% linkClicks.forEach(function(l){ %>
Index: src/views/pages/admin-updates.ejs
===================================================================
--- src/views/pages/admin-updates.ejs	(revision 929d98d2002f662695a6e55138a1e9094f722bad)
+++ src/views/pages/admin-updates.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
@@ -1,5 +1,5 @@
 <div class="container admin-page">
-  <h1>Updates</h1>
-  <p><a href="/admin" class="btn">&larr; Beheer</a></p>
+  <h1><%= t('aupd.title') %></h1>
+  <p><a href="/admin" class="btn">&larr; <%= t('aupd.back_admin') %></a></p>
 
   <% if (success) { %><div class="alert alert-success"><%= success %></div><% } %>
@@ -7,22 +7,22 @@
 
   <section class="set-card">
-    <h2>Versie van deze Klonkt</h2>
+    <h2><%= t('aupd.version_heading') %></h2>
 
     <div class="upd-row">
-      <span class="upd-label">App-versie</span>
+      <span class="upd-label"><%= t('aupd.app_version') %></span>
       <span class="upd-val"><%= appVersion || '—' %></span>
     </div>
     <div class="upd-row">
-      <span class="upd-label">Huidig</span>
+      <span class="upd-label"><%= t('aupd.current') %></span>
       <span class="upd-val">
         <% if (currentSha) { %><code><%= currentSha %></code> <span class="upd-desc"><%= currentDesc %></span>
-        <% } else { %><em>onbekend (nog niet via de update-knop bijgewerkt)</em><% } %>
+        <% } else { %><em><%= t('aupd.current_unknown') %></em><% } %>
       </span>
     </div>
     <div class="upd-row">
-      <span class="upd-label">Nieuwste</span>
+      <span class="upd-label"><%= t('aupd.latest') %></span>
       <span class="upd-val">
         <% if (latestSha) { %><code><%= latestSha %></code> <span class="upd-desc"><%= latestDesc %></span>
-        <% } else { %><em>kon de nieuwste versie niet ophalen</em><% } %>
+        <% } else { %><em><%= t('aupd.latest_failed') %></em><% } %>
       </span>
     </div>
@@ -30,9 +30,9 @@
     <div class="upd-status">
       <% if (!canCheck) { %>
-        <span class="upd-badge warn">&#9888; Geen update-bron bereikbaar</span>
+        <span class="upd-badge warn">&#9888; <%= t('aupd.no_source') %></span>
       <% } else if (upToDate) { %>
-        <span class="upd-badge ok">&#10003; Up-to-date</span>
+        <span class="upd-badge ok">&#10003; <%= t('aupd.uptodate') %></span>
       <% } else { %>
-        <span class="upd-badge new">&#8635; Update beschikbaar<% if (behind) { %> (<%= behind %> commit<%= behind === '1' ? '' : 's' %> achter)<% } %></span>
+        <span class="upd-badge new">&#8635; <%= t('aupd.update_available') %><% if (behind) { %> (<%= behind === '1' ? t('aupd.behind_one', { n: behind }) : t('aupd.behind_many', { n: behind }) %>)<% } %></span>
       <% } %>
     </div>
@@ -40,7 +40,7 @@
     <% if (canCheck) { %>
     <form method="post" action="/admin/updates/run" class="set-form"
-          onsubmit="return confirm('De site wordt naar de nieuwste versie gebracht en herstart kort. Doorgaan?');">
+          onsubmit="return confirm('<%= t('aupd.run_confirm') %>');">
       <button type="submit" class="btn btn-primary">
-        <%= upToDate ? '↻ Opnieuw uitrollen' : '⬆ Nu bijwerken' %>
+        <%= upToDate ? '↻ ' + t('aupd.redeploy') : '⬆ ' + t('aupd.update_now') %>
       </button>
     </form>
@@ -48,6 +48,5 @@
 
     <p class="set-help" style="margin-top:1rem">
-      Bijwerken haalt de nieuwste code op en herstart deze site kort (~10s). Doe dit
-      rustig — er gaat niets verloren (je posts, instellingen en cirkel blijven staan).
+      <%= t('aupd.help') %>
     </p>
   </section>
Index: src/views/pages/admin-users.ejs
===================================================================
--- src/views/pages/admin-users.ejs	(revision 929d98d2002f662695a6e55138a1e9094f722bad)
+++ src/views/pages/admin-users.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
@@ -1,9 +1,9 @@
 <div class="container ax-page">
 
-  <p><a href="/admin" class="btn">&larr; Beheer</a></p>
+  <p><a href="/admin" class="btn">&larr; <%= t('ausr.back') %></a></p>
   <header class="ax-header">
     <div>
-      <h1>Users</h1>
-      <p class="ax-tagline">Beheer gebruikers, rollen, en verwijderingen. Rol <strong>kijker</strong> = alles bekijken (incl. Beheer), niets wijzigen — handig voor demo's.</p>
+      <h1><%= t('ausr.title') %></h1>
+      <p class="ax-tagline"><%= t('ausr.tagline_a') %> <strong><%= t('ausr.role_kijker') %></strong> <%= t('ausr.tagline_b') %></p>
     </div>
   </header>
@@ -16,8 +16,24 @@
       <div class="ax-empty">
         <div class="ax-empty-icon">👤</div>
-        <p>Geen gebruikers.</p>
+        <p><%= t('ausr.empty') %></p>
       </div>
     </div>
   <% } else { %>
+    <%
+      var _u_self      = t('ausr.you');
+      var _u_t_sites   = t('ausr.t_sites');
+      var _u_t_posts   = t('ausr.t_posts');
+      var _u_t_joined  = t('ausr.t_joined');
+      var _u_l_sites   = t('ausr.l_sites');
+      var _u_l_posts   = t('ausr.l_posts');
+      var _u_l_joined  = t('ausr.l_joined');
+      var _u_newklonkt = t('ausr.new_klonkt');
+      var _u_rol       = t('ausr.role_label');
+      var _u_r_kijker  = t('ausr.role_kijker');
+      var _u_r_member  = t('ausr.role_member');
+      var _u_r_admin   = t('ausr.role_admin');
+      var _u_r_god     = t('ausr.role_god');
+      var _u_del       = t('ausr.delete');
+    %>
     <ul class="ax-list">
       <% users.forEach(function(u) { %>
@@ -33,5 +49,5 @@
               <div class="ax-user-name">
                 <a href="/users/<%= encodeURIComponent(u.username) %>"><%= u.username %></a>
-                <% if (u.id === user.id) { %><span class="ax-pill">jij</span><% } %>
+                <% if (u.id === user.id) { %><span class="ax-pill"><%= _u_self %></span><% } %>
               </div>
               <div class="ax-user-email"><%= u.email %></div>
@@ -40,15 +56,15 @@
 
           <div class="ax-user-stats">
-            <span class="ax-stat" title="Sites">
+            <span class="ax-stat" title="<%= _u_t_sites %>">
               <span class="ax-stat-num"><%= u.site_count %></span>
-              <span class="ax-stat-label">sites</span>
+              <span class="ax-stat-label"><%= _u_l_sites %></span>
             </span>
-            <span class="ax-stat" title="Posts">
+            <span class="ax-stat" title="<%= _u_t_posts %>">
               <span class="ax-stat-num"><%= u.post_count %></span>
-              <span class="ax-stat-label">posts</span>
+              <span class="ax-stat-label"><%= _u_l_posts %></span>
             </span>
-            <span class="ax-stat" title="Geregistreerd op">
+            <span class="ax-stat" title="<%= _u_t_joined %>">
               <span class="ax-stat-num ax-stat-date"><%= formatDate(u.created_at) %></span>
-              <span class="ax-stat-label">joined</span>
+              <span class="ax-stat-label"><%= _u_l_joined %></span>
             </span>
           </div>
@@ -59,14 +75,14 @@
             <% if (typeof tenancy !== 'undefined' && tenancy === 'hub' && canMutate) { %>
               <a href="/admin/sites/new?owner=<%= u.id %>" class="ax-icon-btn"
-                 aria-label="Nieuwe Klonkt voor <%= u.username %>" title="Nieuwe Klonkt voor deze gebruiker">＋🌐</a>
+                 aria-label="<%= t('ausr.new_klonkt_for', { name: u.username }) %>" title="<%= _u_newklonkt %>">＋🌐</a>
             <% } %>
             <form method="post" action="/admin/users/<%= u.id %>/role" class="ax-role-form">
               <label class="ax-role">
-                <span>Rol</span>
+                <span><%= _u_rol %></span>
                 <select name="role" onchange="this.form.submit()" <% if (u.id === user.id || !canMutate) { %>disabled<% } %>>
-                  <option value="kijker" <%= u.role === 'kijker' ? 'selected' : '' %>>kijker</option>
-                  <option value="member" <%= u.role === 'member' ? 'selected' : '' %>>member</option>
-                  <option value="admin"  <%= u.role === 'admin'  ? 'selected' : '' %>>admin</option>
-                  <option value="god"    <%= u.role === 'god'    ? 'selected' : '' %>>god</option>
+                  <option value="kijker" <%= u.role === 'kijker' ? 'selected' : '' %>><%= _u_r_kijker %></option>
+                  <option value="member" <%= u.role === 'member' ? 'selected' : '' %>><%= _u_r_member %></option>
+                  <option value="admin"  <%= u.role === 'admin'  ? 'selected' : '' %>><%= _u_r_admin %></option>
+                  <option value="god"    <%= u.role === 'god'    ? 'selected' : '' %>><%= _u_r_god %></option>
                 </select>
               </label>
@@ -75,9 +91,9 @@
             <% if (u.id !== user.id && canMutate) { %>
               <% var _warn = (u.post_count + u.site_count > 0)
-                   ? ('Dit verwijdert ook hun site + ' + u.post_count + ' post(s). ')
+                   ? (t('ausr.del_warn', { n: u.post_count }) + ' ')
                    : ''; %>
               <form method="post" action="/admin/users/<%= u.id %>/delete" class="ax-delete-form"
-                    onsubmit="return confirm('Gebruiker <%= u.username %> verwijderen? <%= _warn %>Dit kan niet ongedaan worden.')">
-                <button type="submit" class="ax-icon-btn ax-icon-btn-danger" aria-label="Verwijderen" title="Verwijderen">🗑</button>
+                    onsubmit="return confirm('<%= t('ausr.del_confirm', { name: u.username }) %> <%= _warn %><%= t('ausr.del_undo') %>')">
+                <button type="submit" class="ax-icon-btn ax-icon-btn-danger" aria-label="<%= _u_del %>" title="<%= _u_del %>">🗑</button>
               </form>
             <% } else { %>
