Index: src/views/pages/404.ejs
===================================================================
--- src/views/pages/404.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
+++ src/views/pages/404.ejs	(revision 4885eabc577791921a46ec8cb5d38e2b4ae1c649)
@@ -1,10 +1,10 @@
 <section class="nf">
   <div class="nf-code">404</div>
-  <h1 class="nf-title">Pagina niet gevonden</h1>
-  <p class="nf-sub">Deze pagina bestaat niet (meer). Misschien is de link verouderd of verkeerd getypt.</p>
+  <h1 class="nf-title"><%= t('e404.title') %></h1>
+  <p class="nf-sub"><%= t('e404.sub') %></p>
   <div class="nf-actions">
-    <a class="nf-btn nf-btn-primary" href="<%= (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : '' %>/">← Naar de homepagina</a>
+    <a class="nf-btn nf-btn-primary" href="<%= (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : '' %>/">← <%= t('e404.home') %></a>
     <% if (typeof site !== 'undefined' && site && site.show_archive_link !== 0) { %>
-      <a class="nf-btn" href="<%= (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : '' %>/archive">Archief</a>
+      <a class="nf-btn" href="<%= (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : '' %>/archive"><%= t('e404.archive') %></a>
     <% } %>
   </div>
Index: src/views/pages/account.ejs
===================================================================
--- src/views/pages/account.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
+++ src/views/pages/account.ejs	(revision 4885eabc577791921a46ec8cb5d38e2b4ae1c649)
@@ -2,8 +2,8 @@
 
   <header class="ax-header">
-    <a href="/" class="ax-back" aria-label="Terug naar home">←</a>
+    <a href="/" class="ax-back" aria-label="<%= t('acct.back_home') %>">←</a>
     <div>
-      <h1>Account</h1>
-      <p class="ax-tagline">Profiel en avatar.</p>
+      <h1><%= t('acct.title') %></h1>
+      <p class="ax-tagline"><%= t('acct.subtitle') %></p>
     </div>
   </header>
@@ -16,6 +16,6 @@
       <span class="ax-viewer-ico" aria-hidden="true">👁️</span>
       <div>
-        <strong>Kijker-modus</strong>
-        <span>Dit is een demo-account. Je kunt alles bekijken, maar niets wijzigen — ook geen foto of bio.</span>
+        <strong><%= t('acct.viewer_mode') %></strong>
+        <span><%= t('acct.viewer_note') %></span>
       </div>
     </div>
@@ -24,10 +24,10 @@
   <%# ── PROFILE ────────────────────────────────────────────── %>
   <section class="ax-card">
-    <div class="ax-card-title">Profiel</div>
+    <div class="ax-card-title"><%= t('acct.profile') %></div>
 
     <div class="ax-profile-id">
       <% if (canMutate) { %>
         <form action="/account/avatar" method="post" enctype="multipart/form-data" class="ax-avatar-form">
-          <label class="ax-avatar-wrap" title="Klik om je foto te wijzigen">
+          <label class="ax-avatar-wrap" title="<%= t('acct.avatar_change') %>">
             <span class="ax-profile-avatar">
               <% if (account.avatar_url) { %>
@@ -56,9 +56,9 @@
         <div class="ax-profile-role"><%= account.role %> · <%= account.email %></div>
         <% if (account.created_at) { %>
-          <div class="ax-profile-joined">Lid sinds <%= formatDate(account.created_at) %></div>
+          <div class="ax-profile-joined"><%= t('acct.member_since') %> <%= formatDate(account.created_at) %></div>
         <% } %>
         <% if (account.avatar_url && canMutate) { %>
           <form action="/account/avatar/remove" method="post" class="ax-avatar-remove">
-            <button type="submit" class="ax-linkbtn">Foto verwijderen</button>
+            <button type="submit" class="ax-linkbtn"><%= t('acct.avatar_remove') %></button>
           </form>
         <% } %>
@@ -69,13 +69,13 @@
       <form action="/account/profile" method="post" class="ax-form ax-form-bio">
         <label class="ax-field">
-          <span>E-mailadres</span>
-          <input type="email" name="email" value="<%= account.email || '' %>" maxlength="254" autocomplete="email" placeholder="jij@email.nl">
-        </label>
-        <label class="ax-field">
-          <span>Bio</span>
-          <textarea name="bio" rows="3" maxlength="500" placeholder="Een korte regel over jezelf"><%= account.bio || '' %></textarea>
+          <span><%= t('acct.email') %></span>
+          <input type="email" name="email" value="<%= account.email || '' %>" maxlength="254" autocomplete="email" placeholder="<%= t('acct.email_ph') %>">
+        </label>
+        <label class="ax-field">
+          <span><%= t('acct.bio') %></span>
+          <textarea name="bio" rows="3" maxlength="500" placeholder="<%= t('acct.bio_ph') %>"><%= account.bio || '' %></textarea>
         </label>
         <div class="ax-form-actions">
-          <button type="submit" class="ax-btn ax-btn-primary">Opslaan</button>
+          <button type="submit" class="ax-btn ax-btn-primary"><%= t('acct.save') %></button>
         </div>
       </form>
@@ -83,6 +83,6 @@
       <div class="ax-form ax-form-bio">
         <label class="ax-field">
-          <span>Bio</span>
-          <textarea rows="3" disabled placeholder="Geen bio"><%= account.bio || '' %></textarea>
+          <span><%= t('acct.bio') %></span>
+          <textarea rows="3" disabled placeholder="<%= t('acct.bio_empty') %>"><%= account.bio || '' %></textarea>
         </label>
       </div>
@@ -93,16 +93,16 @@
   <% if (typeof editableSite !== 'undefined' && editableSite && canMutate) { %>
   <section class="ax-card">
-    <div class="ax-card-title">Site</div>
+    <div class="ax-card-title"><%= t('acct.site') %></div>
     <form action="/account/site" method="post" class="ax-form">
       <label class="ax-field">
-        <span>Site-naam <small>— getoond in de kop van je site</small></span>
+        <span><%= t('acct.site_name') %> <small><%= t('acct.site_name_hint') %></small></span>
         <input type="text" name="site_title" value="<%= editableSite.title || '' %>" maxlength="200" required>
       </label>
       <label class="ax-field">
-        <span>Tagline <small>— korte oneliner (optioneel)</small></span>
+        <span><%= t('acct.tagline') %> <small><%= t('acct.tagline_hint') %></small></span>
         <input type="text" name="site_tagline" value="<%= editableSite.tagline || '' %>" maxlength="200">
       </label>
       <div class="ax-form-actions">
-        <button type="submit" class="ax-btn ax-btn-primary">Site opslaan</button>
+        <button type="submit" class="ax-btn ax-btn-primary"><%= t('acct.site_save') %></button>
       </div>
     </form>
@@ -115,22 +115,22 @@
   <% if (hasPassword) { %>
   <section class="ax-card">
-    <div class="ax-card-title">Wachtwoord wijzigen</div>
+    <div class="ax-card-title"><%= t('acct.password_change') %></div>
     <form action="/account/password" method="post" class="ax-form">
       <label class="ax-field">
-        <span>Huidig wachtwoord</span>
+        <span><%= t('acct.password_current') %></span>
         <input type="password" name="current" required autocomplete="current-password">
       </label>
       <div class="ax-row ax-row-2">
         <label class="ax-field">
-          <span>Nieuw wachtwoord <small>(min 8 tekens)</small></span>
+          <span><%= t('acct.password_new') %> <small><%= t('acct.password_min') %></small></span>
           <input type="password" name="new_password" required minlength="8" autocomplete="new-password">
         </label>
         <label class="ax-field">
-          <span>Bevestig nieuw wachtwoord</span>
+          <span><%= t('acct.password_confirm') %></span>
           <input type="password" name="confirm" required minlength="8" autocomplete="new-password">
         </label>
       </div>
       <div class="ax-form-actions">
-        <button type="submit" class="ax-btn ax-btn-primary">Wachtwoord wijzigen</button>
+        <button type="submit" class="ax-btn ax-btn-primary"><%= t('acct.password_change') %></button>
       </div>
     </form>
@@ -139,13 +139,13 @@
   <% if (typeof googleAvailable !== 'undefined' && googleAvailable) { %>
   <section class="ax-card">
-    <div class="ax-card-title">Inloggen met Google</div>
+    <div class="ax-card-title"><%= t('acct.google_login') %></div>
     <% if (typeof googleLinked !== 'undefined' && googleLinked) { %>
-      <p class="ax-tagline" style="margin:0 0 1rem">✓ Je Google-account is gekoppeld — je kunt ook met Google inloggen.</p>
+      <p class="ax-tagline" style="margin:0 0 1rem"><%= t('acct.google_linked') %></p>
       <form action="/account/google/unlink" method="post">
-        <button type="submit" class="ax-btn">Google ontkoppelen</button>
+        <button type="submit" class="ax-btn"><%= t('acct.google_unlink') %></button>
       </form>
     <% } else { %>
-      <p class="ax-tagline" style="margin:0 0 1rem">Koppel je Google-account zodat je voortaan ook met één klik via Google kunt inloggen (naast je wachtwoord).</p>
-      <a href="/auth/google/link" class="ax-btn ax-btn-primary" data-full-load>Koppel Google-account</a>
+      <p class="ax-tagline" style="margin:0 0 1rem"><%= t('acct.google_link_hint') %></p>
+      <a href="/auth/google/link" class="ax-btn ax-btn-primary" data-full-load><%= t('acct.google_link') %></a>
     <% } %>
   </section>
@@ -153,6 +153,6 @@
   <% } else { %>
   <section class="ax-card">
-    <div class="ax-card-title">Inloggen</div>
-    <p class="ax-tagline" style="margin:0">Je bent ingelogd via Google (<%= account.email %>). Er is geen apart wachtwoord.</p>
+    <div class="ax-card-title"><%= t('acct.login') %></div>
+    <p class="ax-tagline" style="margin:0"><%= t('acct.login_google_only', { email: account.email }) %></p>
   </section>
   <% } %>
Index: src/views/pages/archive.ejs
===================================================================
--- src/views/pages/archive.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
+++ src/views/pages/archive.ejs	(revision 4885eabc577791921a46ec8cb5d38e2b4ae1c649)
@@ -9,11 +9,11 @@
     <a href="<%= _archBase %>/" class="btn archive-back"
        hx-get="<%= _archBase %>/?partial=1" hx-target="#pcms-main"
-       hx-push-url="<%= _archBase %>/" hx-indicator="#pcms-loading">&larr; Terug</a>
+       hx-push-url="<%= _archBase %>/" hx-indicator="#pcms-loading">&larr; <%= t('parch.back') %></a>
   </p>
-  <h1>Archief</h1>
-  <p class="archive-meta"><%= totalPosts %> bericht<%= totalPosts !== 1 ? 'en' : '' %></p>
+  <h1><%= t('parch.title') %></h1>
+  <p class="archive-meta"><%= totalPosts === 1 ? t('parch.count_one', { n: totalPosts }) : t('parch.count_many', { n: totalPosts }) %></p>
 
   <% if (totalPosts === 0) { %>
-    <p class="empty-state">Nog geen berichten.</p>
+    <p class="empty-state"><%= t('parch.empty') %></p>
   <% } %>
 
Index: src/views/pages/artists-directory.ejs
===================================================================
--- src/views/pages/artists-directory.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
+++ src/views/pages/artists-directory.ejs	(revision 4885eabc577791921a46ec8cb5d38e2b4ae1c649)
@@ -15,11 +15,11 @@
   <nav class="adir-nav">
     <div class="adir-nav-inner container">
-      <a class="adir-nav-home" href="/"><span aria-hidden="true">←</span> <%= (typeof hubTitle !== 'undefined' && hubTitle) ? hubTitle : 'Home' %></a>
+      <a class="adir-nav-home" href="/"><span aria-hidden="true">←</span> <%= (typeof hubTitle !== 'undefined' && hubTitle) ? hubTitle : t('adir.home') %></a>
       <span class="adir-nav-spacer"></span>
       <% if (user) { %>
         <a class="adir-nav-link" href="/account"><%= user.username %></a>
-        <a class="adir-nav-link" href="/auth/logout">Uitloggen</a>
+        <a class="adir-nav-link" href="/auth/logout"><%= t('adir.logout') %></a>
       <% } else { %>
-        <a class="adir-nav-link" href="/auth/login">Inloggen</a>
+        <a class="adir-nav-link" href="/auth/login"><%= t('adir.login') %></a>
       <% } %>
     </div>
@@ -27,14 +27,14 @@
 
   <header class="adir-head container">
-    <h1 class="adir-title">Leden</h1>
-    <p class="adir-sub"><%= total %> Klonkt<%= total === 1 ? '' : "'s" %></p>
+    <h1 class="adir-title"><%= t('adir.title') %></h1>
+    <p class="adir-sub"><%= t(total === 1 ? 'adir.count_one' : 'adir.count_many', { n: total }) %></p>
 
     <form class="adir-search" method="get" action="/leden" role="search">
-      <input type="search" name="q" value="<%= q %>" placeholder="Zoek op naam…"
-             autocomplete="off" aria-label="Zoek leden">
-      <button type="submit" class="adir-search-btn" aria-label="Zoeken">
+      <input type="search" name="q" value="<%= q %>" placeholder="<%= t('adir.search_ph') %>"
+             autocomplete="off" aria-label="<%= t('adir.search_aria') %>">
+      <button type="submit" class="adir-search-btn" aria-label="<%= t('adir.search_btn') %>">
         <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
       </button>
-      <% if (q) { %><a class="adir-search-clear" href="/leden">Wissen</a><% } %>
+      <% if (q) { %><a class="adir-search-clear" href="/leden"><%= t('adir.clear') %></a><% } %>
     </form>
   </header>
@@ -43,5 +43,5 @@
     <% if (!artists.length) { %>
       <p class="adir-empty">
-        <% if (q) { %>Geen leden gevonden voor "<strong><%= q %></strong>".<% } else { %>Nog geen leden.<% } %>
+        <% if (q) { %><%- t('adir.empty_q', { q: '<strong>' + q + '</strong>' }) %><% } else { %><%= t('adir.empty') %><% } %>
       </p>
     <% } else { %>
@@ -55,5 +55,5 @@
             <span class="roster-name"><%= a.title %></span>
             <% if (a.tagline) { %><span class="roster-tag"><%= a.tagline %></span><% } %>
-            <span class="roster-count"><%= a.post_count %> post<%= a.post_count === 1 ? '' : 's' %></span>
+            <span class="roster-count"><%= t(a.post_count === 1 ? 'adir.posts_one' : 'adir.posts_many', { n: a.post_count }) %></span>
           </a>
         <% }); %>
@@ -61,15 +61,15 @@
 
       <% if (pages > 1) { %>
-        <nav class="adir-pager" aria-label="Paginering">
+        <nav class="adir-pager" aria-label="<%= t('adir.pager_aria') %>">
           <% if (page > 1) { %>
-            <a class="adir-page" href="<%= _qs({ page: page - 1 }) %>" rel="prev">← Vorige</a>
+            <a class="adir-page" href="<%= _qs({ page: page - 1 }) %>" rel="prev"><%= t('adir.prev') %></a>
           <% } else { %>
-            <span class="adir-page is-disabled">← Vorige</span>
+            <span class="adir-page is-disabled"><%= t('adir.prev') %></span>
           <% } %>
-          <span class="adir-page-info">Pagina <%= page %> van <%= pages %></span>
+          <span class="adir-page-info"><%= t('adir.page_info', { page: page, pages: pages }) %></span>
           <% if (page < pages) { %>
-            <a class="adir-page" href="<%= _qs({ page: page + 1 }) %>" rel="next">Volgende →</a>
+            <a class="adir-page" href="<%= _qs({ page: page + 1 }) %>" rel="next"><%= t('adir.next') %></a>
           <% } else { %>
-            <span class="adir-page is-disabled">Volgende →</span>
+            <span class="adir-page is-disabled"><%= t('adir.next') %></span>
           <% } %>
         </nav>
Index: src/views/pages/auth-login.ejs
===================================================================
--- src/views/pages/auth-login.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
+++ src/views/pages/auth-login.ejs	(revision 4885eabc577791921a46ec8cb5d38e2b4ae1c649)
@@ -11,12 +11,12 @@
   <%
     var GMAP = {
-      admin: { icon: '🔐', title: 'Dit is een beheerders-account',
-        body: 'Je logde in met Google, maar dit e-mailadres hoort bij de beheerder van deze site. Inloggen met Google is alleen voor luisteraars/fans — beheerders gebruiken altijd hun wachtwoord (zo kan een Google-login nooit per ongeluk beheerrechten geven).',
-        list: ['<strong>Ben je de beheerder?</strong> <a href="/auth/admin">Log in met je gebruikersnaam + wachtwoord</a>.', '<strong>Wil je voortaan met Google inloggen?</strong> Log eerst in met wachtwoord en <strong>koppel je Google-account</strong> onder Account → daarna werkt Google-login ook voor jou.', '<strong>Wil je als fan inloggen?</strong> Gebruik een <strong>ander Google-account</strong> (niet je beheerdersadres).'] },
-      session: { icon: '⏱️', title: 'Login afgebroken', body: 'De Google-login is afgebroken of de sessie was verlopen. Probeer het gewoon opnieuw.' },
-      email: { icon: '✉️', title: 'Geen geverifieerd e-mailadres', body: 'Je Google-account heeft geen geverifieerd e-mailadres. Verifieer je adres bij Google.' },
-      linked: { icon: '🔗', title: 'Adres al gekoppeld', body: 'Dit e-mailadres is al aan een ander Google-account gekoppeld. Gebruik dat account, of neem contact op met de beheerder.' },
-      unavailable: { icon: '🚫', title: 'Google-login niet beschikbaar', body: 'Inloggen met Google is op deze site (nog) niet ingesteld.' },
-      failed: { icon: '⚠️', title: 'Google-login mislukt', body: 'Er ging iets mis bij het inloggen met Google. Probeer het opnieuw.' },
+      admin: { icon: '🔐', title: t('glog.admin_title'),
+        body: t('glog.admin_body'),
+        list: [t('glog.admin_li1'), t('glog.admin_li2'), t('glog.admin_li3')] },
+      session: { icon: '⏱️', title: t('glog.session_title'), body: t('glog.session_body') },
+      email: { icon: '✉️', title: t('glog.email_title'), body: t('glog.email_body') },
+      linked: { icon: '🔗', title: t('glog.linked_title'), body: t('glog.linked_body') },
+      unavailable: { icon: '🚫', title: t('glog.unavailable_title'), body: t('glog.unavailable_body') },
+      failed: { icon: '⚠️', title: t('glog.failed_title'), body: t('glog.failed_body') },
     };
     var _g = (typeof gerr !== 'undefined' && gerr && GMAP[gerr]) ? GMAP[gerr] : null;
Index: src/views/pages/auth-reset-request.ejs
===================================================================
--- src/views/pages/auth-reset-request.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
+++ src/views/pages/auth-reset-request.ejs	(revision 4885eabc577791921a46ec8cb5d38e2b4ae1c649)
@@ -1,12 +1,12 @@
 <div class="container auth-page">
-  <h1>Wachtwoord resetten</h1>
+  <h1><%= t('arrq.title') %></h1>
 
   <% if (sent) { %>
     <div class="alert alert-success">
-      Als er een account bestaat voor dat e-mailadres, is er een reset-link verstuurd.
+      <%= t('arrq.sent') %>
     </div>
     <% if (devResetUrl) { %>
       <div class="alert alert-info">
-        <strong>Geen e-mailserver ingesteld</strong> — reset-link hieronder.
+        <%- t('arrq.no_mailserver') %>
         <p style="margin: 0.5rem 0 0; word-break: break-all;">
           <a href="<%= devResetUrl %>"><%= devResetUrl %></a>
@@ -15,9 +15,9 @@
     <% } %>
     <% if (typeof mailer !== 'undefined' && !mailer) { %>
-      <p class="auth-link-muted">Geen e-mail ingesteld? De beheerder kan ook <code>npm run reset-admin</code> draaien op de server.</p>
+      <p class="auth-link-muted"><%- t('arrq.no_mail_cli') %></p>
     <% } %>
-    <p class="auth-link"><a href="/auth/login">&larr; Terug naar inloggen</a></p>
+    <p class="auth-link"><a href="/auth/login"><%= t('arrq.back_login') %></a></p>
   <% } else { %>
-    <p class="auth-tagline">Vul je e-mail in; we sturen een reset-link.</p>
+    <p class="auth-tagline"><%= t('arrq.tagline') %></p>
 
     <% if (error) { %><div class="alert alert-error"><%= error %></div><% } %>
@@ -25,9 +25,9 @@
     <form method="post" action="/auth/reset-request" class="auth-form">
       <label>
-        <span>E-mail</span>
+        <span><%= t('arrq.email') %></span>
         <input type="email" name="email" required autofocus autocomplete="email">
       </label>
-      <button type="submit" class="btn btn-primary">Stuur reset-link</button>
-      <p class="auth-link"><a href="/auth/login">&larr; Terug naar inloggen</a></p>
+      <button type="submit" class="btn btn-primary"><%= t('arrq.submit') %></button>
+      <p class="auth-link"><a href="/auth/login"><%= t('arrq.back_login') %></a></p>
     </form>
   <% } %>
Index: src/views/pages/auth-reset.ejs
===================================================================
--- src/views/pages/auth-reset.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
+++ src/views/pages/auth-reset.ejs	(revision 4885eabc577791921a46ec8cb5d38e2b4ae1c649)
@@ -1,14 +1,15 @@
 <div class="container auth-page">
-  <h1>Wachtwoord resetten</h1>
+  <h1><%= t('arst.title') %></h1>
 
   <% if (!token) { %>
     <% if (error) { %><div class="alert alert-error"><%= error %></div><% } %>
     <p class="auth-link">
-      <a href="/auth/reset-request">Vraag een nieuwe reset-link aan</a>
+      <a href="/auth/reset-request"><%= t('arst.request_new') %></a>
       &middot;
-      <a href="/auth/login">Terug naar inloggen</a>
+      <a href="/auth/login"><%= t('arst.back_login') %></a>
     </p>
   <% } else { %>
-    <p class="auth-tagline">Stel een nieuw wachtwoord in voor <strong><%= username %></strong>.</p>
+    <p class="auth-tagline"><%- t('arst.set_for', { username: '<strong>' + username + '</strong>' }) %></p>
+    <%# arst.set_for bevat {username} → rendered met <%- omdat username in <strong> staat %>
 
     <% if (error) { %><div class="alert alert-error"><%= error %></div><% } %>
@@ -16,12 +17,12 @@
     <form method="post" action="/auth/reset/<%= token %>" class="auth-form">
       <label>
-        <span>Nieuw wachtwoord (min 8 tekens)</span>
+        <span><%= t('arst.new_pw') %></span>
         <input type="password" name="new_password" required minlength="8" autofocus autocomplete="new-password">
       </label>
       <label>
-        <span>Bevestig nieuw wachtwoord</span>
+        <span><%= t('arst.confirm_pw') %></span>
         <input type="password" name="confirm" required minlength="8" autocomplete="new-password">
       </label>
-      <button type="submit" class="btn btn-primary">Wachtwoord instellen</button>
+      <button type="submit" class="btn btn-primary"><%= t('arst.submit') %></button>
     </form>
   <% } %>
Index: src/views/pages/changelog.ejs
===================================================================
--- src/views/pages/changelog.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
+++ src/views/pages/changelog.ejs	(revision 4885eabc577791921a46ec8cb5d38e2b4ae1c649)
@@ -1,11 +1,11 @@
 <div class="container changelog-page">
-  <p><a href="<%= (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : '' %>/" class="btn">&larr; Terug</a></p>
-  <h1>Wijzigingen</h1>
+  <p><a href="<%= (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : '' %>/" class="btn">&larr; <%= t('chlog.back') %></a></p>
+  <h1><%= t('chlog.title') %></h1>
   <p class="cl-meta">
-    App-versie <strong>v<%= appVersion %></strong>
+    <%= t('chlog.app_version') %> <strong>v<%= appVersion %></strong>
     <span class="cl-sep">·</span>
-    Federatie-proto <strong><%= proto %></strong>
+    <%= t('chlog.fed_proto') %> <strong><%= proto %></strong>
     <% if (typeof user !== 'undefined' && user && user.role === 'god') { %>
-      <span class="cl-sep">·</span> <a href="/admin/updates">Updates beheren</a>
+      <span class="cl-sep">·</span> <a href="/admin/updates"><%= t('chlog.manage_updates') %></a>
     <% } %>
   </p>
Index: src/views/pages/circle-feed.ejs
===================================================================
--- src/views/pages/circle-feed.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
+++ src/views/pages/circle-feed.ejs	(revision 4885eabc577791921a46ec8cb5d38e2b4ae1c649)
@@ -10,6 +10,6 @@
     </svg>
   </span>
-  <h1>Cirkel</h1>
-  <p class="cirkel-sub">Wat er speelt bij de andere sites in mijn cirkel.</p>
+  <h1><%= t('cfeed.title') %></h1>
+  <p class="cirkel-sub"><%= t('cfeed.sub') %></p>
   <% if (typeof sites !== 'undefined' && sites && sites.length) { %>
     <% if (sites.length <= 5) { %>
@@ -24,5 +24,5 @@
     <% } else { %>
       <%# Meer dan 5: geen chips (zou een muur worden) — alleen het aantal. %>
-      <span class="cirkel-count"><%= sites.length %> sites in mijn cirkel</span>
+      <span class="cirkel-count"><%= t('cfeed.count', { n: sites.length }) %></span>
     <% } %>
   <% } %>
@@ -31,5 +31,5 @@
 <% if (!posts.length) { %>
   <div class="container">
-    <p class="cirkel-empty">Nog niets in mijn cirkel.</p>
+    <p class="cirkel-empty"><%= t('cfeed.empty') %></p>
   </div>
 <% } else { %>
@@ -50,5 +50,5 @@
 
   <!-- ===== GRID (body[data-feed-view="grid"]) ===== -->
-  <section class="feed-grid container" aria-label="Grid-weergave">
+  <section class="feed-grid container" aria-label="<%= t('cfeed.grid_view') %>">
     <div class="grid-tiles">
       <% posts.forEach(function(post){ %>
Index: src/views/pages/circle-post.ejs
===================================================================
--- src/views/pages/circle-post.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
+++ src/views/pages/circle-post.ejs	(revision 4885eabc577791921a46ec8cb5d38e2b4ae1c649)
@@ -2,5 +2,5 @@
   <p class="cirkel-post-back">
     <a href="/cirkel" hx-get="/cirkel?partial=1" hx-target="#pcms-main" hx-swap="innerHTML"
-       hx-push-url="/cirkel" hx-indicator="#pcms-loading">&larr; Cirkel</a>
+       hx-push-url="/cirkel" hx-indicator="#pcms-loading">&larr; <%= t('cpost.back') %></a>
   </p>
 
@@ -8,5 +8,5 @@
     <div class="cirkel-post-src">
       <% if (post.sourceAvatar) { %><img class="cirkel-post-avatar" src="<%= post.sourceAvatar %>" alt=""><% } %>
-      <span>via
+      <span><%= t('cpost.via') %>
         <% if (post.sourceUrl) { %><a href="<%= post.sourceUrl %>" target="_blank" rel="noopener"><%= post.sourceName %></a>
         <% } else { %><%= post.sourceName %><% } %>
@@ -37,5 +37,5 @@
   <% if (post.originalUrl) { %>
     <p class="cirkel-post-readmore">
-      <a href="<%= post.originalUrl %>" target="_blank" rel="noopener">Lees verder bij <%= post.sourceName %> &rarr;</a>
+      <a href="<%= post.originalUrl %>" target="_blank" rel="noopener"><%= t('cpost.read_more', { source: post.sourceName }) %> &rarr;</a>
     </p>
   <% } %>
Index: src/views/pages/download.ejs
===================================================================
--- src/views/pages/download.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
+++ src/views/pages/download.ejs	(revision 4885eabc577791921a46ec8cb5d38e2b4ae1c649)
@@ -1,2 +1,10 @@
+<%
+  var _ready_h1     = t('dl.ready_title');
+  var _ready_sub    = t('dl.ready_sub');
+  var _manual       = t('dl.manual');
+  var _capture_sub  = t('dl.capture_sub');
+  var _email_ph     = t('dl.email_ph');
+  var _download_btn = t('dl.download_btn');
+%>
 <%
   var t = (typeof dlTrack !== 'undefined') ? dlTrack : {};
@@ -15,7 +23,7 @@
 
     <% if (st === 'ready') { %>
-      <h1 class="dl-h1">Bedankt! ⬇</h1>
-      <p class="dl-sub">Je download zou nu moeten starten. Gebeurt er niets?</p>
-      <p><a class="dl-go" href="<%= fileUrl %>">Download handmatig starten</a></p>
+      <h1 class="dl-h1"><%= _ready_h1 %></h1>
+      <p class="dl-sub"><%= _ready_sub %></p>
+      <p><a class="dl-go" href="<%= fileUrl %>"><%= _manual %></a></p>
       <script>
         // Auto-start de download (zelfde-origin attachment-link).
@@ -23,10 +31,10 @@
       </script>
     <% } else { %>
-      <h1 class="dl-h1">Download <%= t.title %></h1>
-      <p class="dl-sub">Laat je e-mailadres achter en je krijgt het bestand. Je komt dan ook op de nieuwsbrieflijst — uitschrijven kan altijd.</p>
+      <h1 class="dl-h1"><%= _download_btn %> <%= t.title %></h1>
+      <p class="dl-sub"><%= _capture_sub %></p>
       <% if (typeof dlError !== 'undefined' && dlError) { %><p class="dl-err"><%= dlError %></p><% } %>
       <form method="POST" action="<%= siteUrlBase %>/download/<%= t.id %>" class="dl-form">
-        <input type="email" name="email" required placeholder="jouw@email.nl" value="<%= (typeof dlPrefill!=='undefined')?dlPrefill:'' %>" autocomplete="email">
-        <button type="submit" class="dl-go">⬇ Download</button>
+        <input type="email" name="email" required placeholder="<%= _email_ph %>" value="<%= (typeof dlPrefill!=='undefined')?dlPrefill:'' %>" autocomplete="email">
+        <button type="submit" class="dl-go">⬇ <%= _download_btn %></button>
       </form>
     <% } %>
Index: src/views/pages/epk.ejs
===================================================================
--- src/views/pages/epk.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
+++ src/views/pages/epk.ejs	(revision 4885eabc577791921a46ec8cb5d38e2b4ae1c649)
@@ -11,15 +11,15 @@
     <% if (_photo) { %><div class="epk-photo"><img src="<%= _photo %>" alt="<%= site.title %>"></div><% } %>
     <div class="epk-hero-text">
-      <div class="epk-kicker">Perskit<% if (typeof user !== 'undefined' && user && user.role === 'god') { %> <a class="epk-edit-link" href="/admin/epk">✎ Bewerken</a><% } %></div>
+      <div class="epk-kicker"><%= t('epk.kicker') %><% if (typeof user !== 'undefined' && user && user.role === 'god') { %> <a class="epk-edit-link" href="/admin/epk">✎ <%= t('epk.edit') %></a><% } %></div>
       <h1 class="epk-title"><%= site.title %></h1>
       <% if (epkBio) { %><p class="epk-bio"><%= epkBio %></p><% } %>
       <div class="epk-actions">
         <% if (_isMail) { %>
-          <a class="epk-btn epk-btn-primary" href="mailto:<%= epkContact %>">✉ Contact / boeking</a>
+          <a class="epk-btn epk-btn-primary" href="mailto:<%= epkContact %>">✉ <%= t('epk.contact_booking') %></a>
         <% } else if (_isUrl) { %>
-          <a class="epk-btn epk-btn-primary" href="<%= epkContact %>" target="_blank" rel="noopener">Contact / boeking</a>
+          <a class="epk-btn epk-btn-primary" href="<%= epkContact %>" target="_blank" rel="noopener"><%= t('epk.contact_booking') %></a>
         <% } %>
-        <a class="epk-btn" href="<%= siteUrlBase %>/">Bekijk de site →</a>
-        <% if (_photo) { %><a class="epk-btn" href="<%= _photo %>" download>⬇ Persfoto</a><% } %>
+        <a class="epk-btn" href="<%= siteUrlBase %>/"><%= t('epk.view_site') %></a>
+        <% if (_photo) { %><a class="epk-btn" href="<%= _photo %>" download>⬇ <%= t('epk.press_photo') %></a><% } %>
       </div>
     </div>
@@ -28,5 +28,5 @@
   <% if (epkTracks && epkTracks.length) { %>
     <div class="epk-block">
-      <h2 class="epk-h2">Meest beluisterd</h2>
+      <h2 class="epk-h2"><%= t('epk.most_played') %></h2>
       <ul class="epk-tracks">
         <% epkTracks.forEach(function(t, i){ %>
@@ -47,5 +47,5 @@
   <% if (epkPosts && epkPosts.length) { %>
     <div class="epk-block">
-      <h2 class="epk-h2">Recent</h2>
+      <h2 class="epk-h2"><%= t('epk.recent') %></h2>
       <ul class="epk-posts">
         <% epkPosts.forEach(function(p){ %>
Index: src/views/pages/fan-gate.ejs
===================================================================
--- src/views/pages/fan-gate.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
+++ src/views/pages/fan-gate.ejs	(revision 4885eabc577791921a46ec8cb5d38e2b4ae1c649)
@@ -9,8 +9,8 @@
   <div class="fg-card">
     <div class="fg-lock">🔒</div>
-    <h1 class="fg-h1">Alleen voor fans</h1>
+    <h1 class="fg-h1"><%= t('fgate.title') %></h1>
     <% if (typeof fgTitle !== 'undefined' && fgTitle) { %><p class="fg-which">"<%= fgTitle %>"</p><% } %>
-    <p class="fg-sub">Dit is exclusief voor ingelogde fans. Log in om het te bekijken.</p>
-    <p><a class="fg-btn" href="/auth/login?next=<%= encodeURIComponent(fgNext || '/') %>">Inloggen / aanmelden</a></p>
+    <p class="fg-sub"><%= t('fgate.sub') %></p>
+    <p><a class="fg-btn" href="/auth/login?next=<%= encodeURIComponent(fgNext || '/') %>"><%= t('fgate.login') %></a></p>
   </div>
 </section>
Index: src/views/pages/favorites.ejs
===================================================================
--- src/views/pages/favorites.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
+++ src/views/pages/favorites.ejs	(revision 4885eabc577791921a46ec8cb5d38e2b4ae1c649)
@@ -1,8 +1,8 @@
 <div class="container favorites-page">
-  <h1>Favorieten</h1>
-  <p class="fav-sub">Posts die je hebt geliket. Tik op het ♥ bij een post om 'm hier te bewaren.</p>
+  <h1><%= t('fav.title') %></h1>
+  <p class="fav-sub"><%= t('fav.sub') %></p>
 
   <% if (!posts || !posts.length) { %>
-    <p class="fav-empty">Je hebt nog geen favorieten. Open een post en tik op het hartje ♡.</p>
+    <p class="fav-empty"><%= t('fav.empty') %></p>
   <% } else { %>
     <ul class="post-list">
Index: src/views/pages/home.ejs
===================================================================
--- src/views/pages/home.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
+++ src/views/pages/home.ejs	(revision 4885eabc577791921a46ec8cb5d38e2b4ae1c649)
@@ -23,6 +23,6 @@
   <% if (allPosts.length === 0) { %>
     <div class="empty">
-      <h1>Hier is het nog stil.</h1>
-      <p>Nog geen posts. Spannend.</p>
+      <h1><%= t('phome.empty_title') %></h1>
+      <p><%= t('phome.empty_sub') %></p>
       <% if (user && canMutate && permissions.canCreatePost(user, site)) { %>
         <p style="margin-top:2rem">
@@ -30,5 +30,5 @@
              hx-get="<%= _base %>/posts/new?partial=1" hx-target="#pcms-main" hx-swap="innerHTML"
              hx-push-url="<%= _base %>/posts/new" hx-indicator="#pcms-loading">
-            Schrijf je eerste post →
+            <%= t('phome.write_first') %> →
           </a>
         </p>
@@ -47,5 +47,5 @@
 <!-- ========== GRID (only visible when body[data-feed-view="grid"]) ========== -->
 <% if (allPosts.length > 0) { %>
-<section class="feed-grid container" aria-label="Grid-weergave">
+<section class="feed-grid container" aria-label="<%= t('phome.grid_view') %>">
   <div class="grid-tiles">
     <% allPosts.forEach(function(post) { %>
Index: src/views/pages/hub-home.ejs
===================================================================
--- src/views/pages/hub-home.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
+++ src/views/pages/hub-home.ejs	(revision 4885eabc577791921a46ec8cb5d38e2b4ae1c649)
@@ -28,9 +28,9 @@
         </span>
         <span class="hub-main-info">
-          <span class="hub-main-badge">Hoofdpagina</span>
+          <span class="hub-main-badge"><%= t('phub.main_badge') %></span>
           <span class="hub-main-name"><%= mainSite.title %></span>
           <% if (mainSite.tagline) { %><span class="hub-main-tag"><%= mainSite.tagline %></span><% } %>
         </span>
-        <span class="hub-main-cta">Bekijk de pagina <span aria-hidden="true">→</span></span>
+        <span class="hub-main-cta"><%= t('phub.view_page') %> <span aria-hidden="true">→</span></span>
       </a>
     </section>
@@ -43,5 +43,5 @@
     <section class="hub-sec">
       <h2 class="hub-sec-title">
-        Leden
+        <%= t('phub.members') %>
         <% if (_hasMore) { %><span class="hub-sec-count"><%= _total %></span><% } %>
       </h2>
@@ -57,5 +57,5 @@
             <span class="roster-name"><%= a.title %></span>
             <% if (a.tagline) { %><span class="roster-tag"><%= a.tagline %></span><% } %>
-            <span class="roster-count"><%= a.post_count %> post<%= a.post_count === 1 ? '' : 's' %></span>
+            <span class="roster-count"><%= a.post_count %> <%= a.post_count === 1 ? t('phub.post_one') : t('phub.post_many') %></span>
           </a>
         <% }); %>
@@ -63,5 +63,5 @@
       <% if (_hasMore) { %>
         <div class="hub-more">
-          <a class="hub-more-link" href="/leden">Alle <%= _total %> leden <span aria-hidden="true">→</span></a>
+          <a class="hub-more-link" href="/leden"><%= t('phub.all_members', { n: _total }) %> <span aria-hidden="true">→</span></a>
         </div>
       <% } %>
@@ -72,5 +72,5 @@
     <% if (posts && posts.length) { %>
     <section class="hub-sec">
-      <h2 class="hub-sec-title">Laatste posts van onze leden.</h2>
+      <h2 class="hub-sec-title"><%= t('phub.latest_posts') %></h2>
       <div class="hub-feed">
         <% posts.forEach(function(p){ %>
Index: src/views/pages/linkbio.ejs
===================================================================
--- src/views/pages/linkbio.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
+++ src/views/pages/linkbio.ejs	(revision 4885eabc577791921a46ec8cb5d38e2b4ae1c649)
@@ -13,5 +13,5 @@
 
   <% if (!links.length) { %>
-    <p class="lb-empty">Nog geen links ingesteld.</p>
+    <p class="lb-empty"><%= t('lbio.empty') %></p>
   <% } else { %>
     <div class="lb-links">
@@ -25,5 +25,5 @@
   <% } %>
 
-  <p class="lb-foot"><a href="<%= siteUrlBase %>/">← naar de site</a></p>
+  <p class="lb-foot"><a href="<%= siteUrlBase %>/">← <%= t('lbio.back_to_site') %></a></p>
 </section>
 
Index: src/views/pages/my-site.ejs
===================================================================
--- src/views/pages/my-site.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
+++ src/views/pages/my-site.ejs	(revision 4885eabc577791921a46ec8cb5d38e2b4ae1c649)
@@ -1,33 +1,39 @@
 <div class="container admin-page">
-  <p><a href="/" class="btn">&larr; Overzicht</a></p>
-  <h1>Mijn Klonkt Hub</h1>
+  <p><a href="/" class="btn">&larr; <%= t('myst.overview') %></a></p>
+  <h1><%= t('myst.title') %></h1>
   <p class="admin-tagline"><%= mySite.title %></p>
 
-  <nav class="admin-quick-links" aria-label="Beheer-snelkoppelingen">
-    <a href="/user/<%= mySite.slug %>/posts/new" class="btn">✍️ Nieuwe post</a>
-    <a href="/admin/sites/<%= mySite.slug %>/edit" class="btn">🎨 Uiterlijk</a>
-    <a href="/user/<%= mySite.slug %>/admin/comments" class="btn">💬 Reacties</a>
-    <a href="/user/<%= mySite.slug %>" class="btn">👁️ Bekijk mijn site</a>
-    <a href="/account" class="btn">👤 Account</a>
+  <nav class="admin-quick-links" aria-label="<%= t('myst.quick_links_aria') %>">
+    <a href="/user/<%= mySite.slug %>/posts/new" class="btn">✍️ <%= t('myst.new_post') %></a>
+    <a href="/admin/sites/<%= mySite.slug %>/edit" class="btn">🎨 <%= t('myst.appearance') %></a>
+    <a href="/user/<%= mySite.slug %>/admin/comments" class="btn">💬 <%= t('myst.comments') %></a>
+    <a href="/user/<%= mySite.slug %>" class="btn">👁️ <%= t('myst.view_site') %></a>
+    <a href="/account" class="btn">👤 <%= t('myst.account') %></a>
   </nav>
 
   <div class="admin-stats">
-    <div class="stat-card"><div class="stat-num"><%= mine.posts %></div><div class="stat-label">Posts</div></div>
-    <div class="stat-card"><div class="stat-num"><%= mine.published %></div><div class="stat-label">Gepubliceerd</div></div>
+    <div class="stat-card"><div class="stat-num"><%= mine.posts %></div><div class="stat-label"><%= t('myst.posts') %></div></div>
+    <div class="stat-card"><div class="stat-num"><%= mine.published %></div><div class="stat-label"><%= t('myst.published') %></div></div>
   </div>
 
   <% if (typeof posts !== 'undefined' && posts && posts.length) { %>
     <% var _drafts = posts.filter(function(p){ return p.isDraft; }).length; %>
-    <h2 class="ms-posts-title">Posts<% if (_drafts) { %> <span class="pl-draftcount"><%= _drafts %> concept<%= _drafts === 1 ? '' : 'en' %></span><% } %></h2>
+    <%
+      var _lblBadge = t('myst.draft_badge');
+      var _lblUntitled = t('myst.untitled');
+      var _lblEdit = t('myst.edit');
+      var _lblView = t('myst.view');
+    %>
+    <h2 class="ms-posts-title"><%= t('myst.posts') %><% if (_drafts) { %> <span class="pl-draftcount"><%= _drafts === 1 ? t('myst.draft_count_one', { n: _drafts }) : t('myst.draft_count_many', { n: _drafts }) %></span><% } %></h2>
     <ul class="post-admin-list">
       <% posts.forEach(function(p) { %>
         <li class="pal-row<%= p.isDraft ? ' is-draft' : '' %>">
           <a class="pal-title" href="<%= p.editUrl %>">
-            <% if (p.isDraft) { %><span class="pal-badge">Concept</span><% } %>
-            <span class="pal-name"><%= p.title || '(zonder titel)' %></span>
+            <% if (p.isDraft) { %><span class="pal-badge"><%= _lblBadge %></span><% } %>
+            <span class="pal-name"><%= p.title || _lblUntitled %></span>
           </a>
           <span class="pal-actions">
-            <a class="pal-link" href="<%= p.editUrl %>">Bewerken</a>
-            <% if (!p.isDraft) { %><a class="pal-link" href="<%= p.viewUrl %>">Bekijk</a><% } %>
+            <a class="pal-link" href="<%= p.editUrl %>"><%= _lblEdit %></a>
+            <% if (!p.isDraft) { %><a class="pal-link" href="<%= p.viewUrl %>"><%= _lblView %></a><% } %>
           </span>
         </li>
Index: src/views/pages/newsletter.ejs
===================================================================
--- src/views/pages/newsletter.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
+++ src/views/pages/newsletter.ejs	(revision 4885eabc577791921a46ec8cb5d38e2b4ae1c649)
@@ -1,5 +1,5 @@
 <%
   var _site = site || {};
-  var _title = _site.title || 'deze artiest';
+  var _title = _site.title || t('news.this_artist');
   var st = (typeof nlState !== 'undefined') ? nlState : 'form';
 %>
@@ -7,36 +7,36 @@
   <div class="nl-card">
     <% if (st === 'form') { %>
-      <h1 class="nl-h1">Blijf op de hoogte</h1>
-      <p class="nl-sub">Schrijf je in voor de nieuwsbrief van <strong><%= _title %></strong> — nieuwe muziek, shows en updates, rechtstreeks in je inbox. Uitschrijven kan altijd met één klik.</p>
+      <h1 class="nl-h1"><%= t('news.form_title') %></h1>
+      <p class="nl-sub"><%= t('news.form_sub_before') %><strong><%= _title %></strong><%= t('news.form_sub_after') %></p>
       <form method="POST" action="<%= siteUrlBase %>/nieuwsbrief" class="nl-form">
-        <input type="email" name="email" required placeholder="jouw@email.nl" value="<%= (typeof nlPrefill !== 'undefined') ? nlPrefill : '' %>" autocomplete="email">
-        <button type="submit" class="nl-btn">Inschrijven</button>
+        <input type="email" name="email" required placeholder="<%= t('news.email_ph') %>" value="<%= (typeof nlPrefill !== 'undefined') ? nlPrefill : '' %>" autocomplete="email">
+        <button type="submit" class="nl-btn"><%= t('news.subscribe') %></button>
       </form>
     <% } else if (st === 'check') { %>
-      <h1 class="nl-h1">Bijna klaar ✉</h1>
-      <p class="nl-sub">We hebben een bevestigingsmail gestuurd naar <strong><%= (typeof nlEmail!=='undefined')?nlEmail:'je adres' %></strong>. Klik op de link in die mail om je inschrijving te bevestigen.</p>
+      <h1 class="nl-h1"><%= t('news.check_title') %></h1>
+      <p class="nl-sub"><%= t('news.check_sub_before') %><strong><%= (typeof nlEmail!=='undefined')?nlEmail:t('news.your_address') %></strong><%= t('news.check_sub_after') %></p>
     <% } else if (st === 'done') { %>
-      <h1 class="nl-h1">Je bent ingeschreven ✓</h1>
-      <p class="nl-sub">Bedankt — je staat op de lijst van <strong><%= _title %></strong>.</p>
+      <h1 class="nl-h1"><%= t('news.done_title') %></h1>
+      <p class="nl-sub"><%= t('news.done_sub_before') %><strong><%= _title %></strong><%= t('news.done_sub_after') %></p>
     <% } else if (st === 'confirmed') { %>
-      <h1 class="nl-h1">Inschrijving bevestigd ✓</h1>
-      <p class="nl-sub">Top! Je ontvangt voortaan de nieuwsbrief van <strong><%= _title %></strong>.</p>
+      <h1 class="nl-h1"><%= t('news.confirmed_title') %></h1>
+      <p class="nl-sub"><%= t('news.confirmed_sub_before') %><strong><%= _title %></strong><%= t('news.confirmed_sub_after') %></p>
     <% } else if (st === 'unsubbed') { %>
-      <h1 class="nl-h1">Uitgeschreven</h1>
-      <p class="nl-sub">Je bent afgemeld. Je ontvangt geen nieuwsbrieven meer. Van gedachten veranderd? Je kunt je altijd opnieuw inschrijven.</p>
+      <h1 class="nl-h1"><%= t('news.unsubbed_title') %></h1>
+      <p class="nl-sub"><%= t('news.unsubbed_sub') %></p>
     <% } else if (st === 'invalid') { %>
-      <h1 class="nl-h1">Ongeldig e-mailadres</h1>
-      <p class="nl-sub">Controleer je adres en probeer 't opnieuw.</p>
-      <p><a class="nl-btn nl-btn-ghost" href="<%= siteUrlBase %>/nieuwsbrief">← Terug</a></p>
+      <h1 class="nl-h1"><%= t('news.invalid_title') %></h1>
+      <p class="nl-sub"><%= t('news.invalid_sub') %></p>
+      <p><a class="nl-btn nl-btn-ghost" href="<%= siteUrlBase %>/nieuwsbrief"><%= t('news.back') %></a></p>
     <% } else if (st === 'smtperror') { %>
-      <h1 class="nl-h1">Even niet gelukt</h1>
-      <p class="nl-sub">De bevestigingsmail kon niet verstuurd worden. Probeer 't later nog eens.</p>
+      <h1 class="nl-h1"><%= t('news.smtperror_title') %></h1>
+      <p class="nl-sub"><%= t('news.smtperror_sub') %></p>
     <% } else if (st === 'badtoken') { %>
-      <h1 class="nl-h1">Link ongeldig of verlopen</h1>
-      <p class="nl-sub">Deze link werkt niet meer. Schrijf je eventueel opnieuw in.</p>
-      <p><a class="nl-btn nl-btn-ghost" href="<%= siteUrlBase %>/nieuwsbrief">Naar inschrijven</a></p>
+      <h1 class="nl-h1"><%= t('news.badtoken_title') %></h1>
+      <p class="nl-sub"><%= t('news.badtoken_sub') %></p>
+      <p><a class="nl-btn nl-btn-ghost" href="<%= siteUrlBase %>/nieuwsbrief"><%= t('news.to_subscribe') %></a></p>
     <% } else { %>
-      <h1 class="nl-h1">Er ging iets mis</h1>
-      <p class="nl-sub">Probeer 't later nog eens.</p>
+      <h1 class="nl-h1"><%= t('news.error_title') %></h1>
+      <p class="nl-sub"><%= t('news.error_sub') %></p>
     <% } %>
   </div>
Index: src/views/pages/post-edit.ejs
===================================================================
--- src/views/pages/post-edit.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
+++ src/views/pages/post-edit.ejs	(revision 4885eabc577791921a46ec8cb5d38e2b4ae1c649)
@@ -15,5 +15,5 @@
 %>
 <div class="container post-edit-page">
-  <h1><%= isNew ? 'Nieuwe post' : 'Post bewerken' %></h1>
+  <h1><%= isNew ? t('pedit.title_new') : t('pedit.title_edit') %></h1>
 
   <form method="post" action="<%= _base %><%= isNew ? '/posts/create' : '/posts/' + post.slug + '/save' %>" class="post-edit-form">
@@ -22,21 +22,21 @@
     <section class="pe-card">
       <label class="pe-field">
-        <span>Titel</span>
+        <span><%= t('pedit.f_title') %></span>
         <input type="text" name="title" value="<%= post.title %>" required>
       </label>
       <div class="pe-row pe-row-2">
         <label class="pe-field">
-          <span>Slug (URL)</span>
-          <input type="text" name="slug" value="<%= post.slug %>" placeholder="auto van titel als leeg">
+          <span><%= t('pedit.f_slug') %></span>
+          <input type="text" name="slug" value="<%= post.slug %>" placeholder="<%= t('pedit.slug_placeholder') %>">
         </label>
         <label class="pe-field">
-          <span>Tags <small>(komma-gescheiden)</small></span>
+          <span><%= t('pedit.f_tags') %> <small><%= t('pedit.tags_hint') %></small></span>
           <input type="text" name="tags" value="<%= Array.isArray(post.tags) ? post.tags.join(', ') : '' %>">
         </label>
       </div>
       <label class="pe-field">
-        <span>Samenvatting &amp; Cirkel Preview</span>
+        <span><%= t('pedit.f_excerpt') %></span>
         <textarea name="excerpt" rows="2"><%= post.excerpt %></textarea>
-        <small class="pe-hint" style="opacity:.7">Wordt ook gebruikt als samenvatting in <strong>Cirkels</strong> (andere sites die je post tonen). Leeg laten = begin van de post.</small>
+        <small class="pe-hint" style="opacity:.7"><%- t('pedit.excerpt_hint') %></small>
       </label>
     </section>
@@ -44,5 +44,5 @@
     <%# ── COVER ────────────────────────────────────────────────── %>
     <section class="pe-card">
-      <div class="pe-section-title">Cover</div>
+      <div class="pe-section-title"><%= t('pedit.s_cover') %></div>
       <div class="pe-cover-row">
         <div class="pe-cover-thumb" id="cover-preview-wrap" <%= _hasCover ? '' : 'data-empty' %>>
@@ -52,13 +52,13 @@
         <div class="pe-cover-actions">
           <label class="pe-field">
-            <span>Cover URL</span>
+            <span><%= t('pedit.f_cover_url') %></span>
             <input type="text" name="cover_image_url" id="cover-url-field"
                    value="<%= post.cover_image_url || '' %>"
                    inputmode="url" autocapitalize="none" spellcheck="false"
-                   placeholder="/media/…  of https://…  (of upload met de knop)">
+                   placeholder="<%= t('pedit.cover_url_placeholder') %>">
           </label>
           <div class="pe-cover-upload">
             <button type="button" class="pe-btn pe-btn-secondary" id="cover-upload-trigger">
-              📷 Upload nieuwe cover
+              📷 <%= t('pedit.cover_upload_btn') %>
             </button>
             <input type="file" id="cover-upload-field" accept="image/jpeg,image/png,image/webp,image/gif" hidden>
@@ -72,57 +72,57 @@
     <section class="pe-card pe-card-content">
       <div class="pe-section-title">
-        Content
-        <small class="pe-content-hint">Sleep een afbeelding om in te voegen · selecteer tekst om op te maken</small>
+        <%= t('pedit.s_content') %>
+        <small class="pe-content-hint"><%= t('pedit.content_hint') %></small>
       </div>
       <div class="pe-editor-frame">
         <div class="pe-toolbar" id="pe-toolbar" role="toolbar" aria-label="Format">
-          <button type="button" id="pe-fs-done" class="pe-fs-done" title="Klaar met bewerken">✓ Klaar</button>
-          <button type="button" data-cmd="bold" title="Vet (Ctrl+B)" aria-label="Vet">
+          <button type="button" id="pe-fs-done" class="pe-fs-done" title="<%= t('pedit.tb_done_title') %>">✓ <%= t('pedit.tb_done') %></button>
+          <button type="button" data-cmd="bold" title="<%= t('pedit.tb_bold_title') %>" aria-label="<%= t('pedit.tb_bold') %>">
             <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4h8a4 4 0 0 1 0 8H6z"/><path d="M6 12h9a4 4 0 0 1 0 8H6z"/></svg>
           </button>
-          <button type="button" data-cmd="italic" title="Cursief (Ctrl+I)" aria-label="Cursief">
+          <button type="button" data-cmd="italic" title="<%= t('pedit.tb_italic_title') %>" aria-label="<%= t('pedit.tb_italic') %>">
             <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="19" y1="4" x2="10" y2="4"/><line x1="14" y1="20" x2="5" y2="20"/><line x1="15" y1="4" x2="9" y2="20"/></svg>
           </button>
-          <button type="button" data-cmd="underline" title="Onderstreept" aria-label="Onderstreept">
+          <button type="button" data-cmd="underline" title="<%= t('pedit.tb_underline') %>" aria-label="<%= t('pedit.tb_underline') %>">
             <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4v6a6 6 0 0 0 12 0V4"/><line x1="4" y1="20" x2="20" y2="20"/></svg>
           </button>
           <span class="pe-toolbar-sep" aria-hidden="true"></span>
-          <button type="button" class="pe-tb-text" data-cmd="formatBlock" data-arg="h2" title="Kop">H2</button>
-          <button type="button" class="pe-tb-text" data-cmd="formatBlock" data-arg="h3" title="Subkop">H3</button>
-          <button type="button" class="pe-tb-text" data-cmd="formatBlock" data-arg="p"  title="Paragraaf">¶</button>
+          <button type="button" class="pe-tb-text" data-cmd="formatBlock" data-arg="h2" title="<%= t('pedit.tb_h2') %>">H2</button>
+          <button type="button" class="pe-tb-text" data-cmd="formatBlock" data-arg="h3" title="<%= t('pedit.tb_h3') %>">H3</button>
+          <button type="button" class="pe-tb-text" data-cmd="formatBlock" data-arg="p"  title="<%= t('pedit.tb_p') %>">¶</button>
           <span class="pe-toolbar-sep" aria-hidden="true"></span>
-          <button type="button" data-cmd="insertUnorderedList" title="Lijst" aria-label="Lijst">
+          <button type="button" data-cmd="insertUnorderedList" title="<%= t('pedit.tb_ul') %>" aria-label="<%= t('pedit.tb_ul') %>">
             <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="9" y1="6" x2="20" y2="6"/><line x1="9" y1="12" x2="20" y2="12"/><line x1="9" y1="18" x2="20" y2="18"/><circle cx="4.5" cy="6" r="1.2"/><circle cx="4.5" cy="12" r="1.2"/><circle cx="4.5" cy="18" r="1.2"/></svg>
           </button>
-          <button type="button" data-cmd="insertOrderedList" title="Genummerde lijst" aria-label="Genummerde lijst">
+          <button type="button" data-cmd="insertOrderedList" title="<%= t('pedit.tb_ol') %>" aria-label="<%= t('pedit.tb_ol') %>">
             <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="10" y1="6" x2="21" y2="6"/><line x1="10" y1="12" x2="21" y2="12"/><line x1="10" y1="18" x2="21" y2="18"/><path d="M4 6h1v4"/><path d="M4 10h2"/><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"/></svg>
           </button>
-          <button type="button" data-cmd="formatBlock" data-arg="blockquote" title="Quote" aria-label="Quote">
+          <button type="button" data-cmd="formatBlock" data-arg="blockquote" title="<%= t('pedit.tb_quote') %>" aria-label="<%= t('pedit.tb_quote') %>">
             <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 21c3 0 7-1 7-8V5c0-1.25-.75-2-2-2H4c-1.25 0-2 .75-2 2v6c0 1.25.75 2 2 2h2.5C6 17.5 4 19 3 19v2z"/><path d="M14 21c3 0 7-1 7-8V5c0-1.25-.75-2-2-2h-4c-1.25 0-2 .75-2 2v6c0 1.25.75 2 2 2h2.5c-.5 4.5-2.5 6-3.5 6v2z"/></svg>
           </button>
-          <button type="button" data-cmd="link-prompt" title="Link (Ctrl+K)" aria-label="Link">
+          <button type="button" data-cmd="link-prompt" title="<%= t('pedit.tb_link_title') %>" aria-label="<%= t('pedit.tb_link') %>">
             <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>
           </button>
-          <button type="button" data-cmd="code-wrap" title="Code (inline)" aria-label="Code">
+          <button type="button" data-cmd="code-wrap" title="<%= t('pedit.tb_code_title') %>" aria-label="<%= t('pedit.tb_code') %>">
             <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
           </button>
           <span class="pe-toolbar-sep" aria-hidden="true"></span>
-          <button type="button" id="insert-image-btn" title="Afbeelding invoegen" aria-label="Afbeelding">
+          <button type="button" id="insert-image-btn" title="<%= t('pedit.tb_image_title') %>" aria-label="<%= t('pedit.tb_image') %>">
             <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><path d="M21 15l-5-5L5 21"/></svg>
           </button>
-          <button type="button" id="insert-track-btn" title="Track invoegen" aria-label="Track">
+          <button type="button" id="insert-track-btn" title="<%= t('pedit.tb_track_title') %>" aria-label="<%= t('pedit.tb_track') %>">
             <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 18V5l12-2v13"/><circle cx="6" cy="18" r="3"/><circle cx="18" cy="16" r="3"/></svg>
           </button>
-          <button type="button" id="insert-playlist-btn" title="Playlist invoegen" aria-label="Playlist">
+          <button type="button" id="insert-playlist-btn" title="<%= t('pedit.tb_playlist_title') %>" aria-label="<%= t('pedit.tb_playlist') %>">
             <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="15" y2="18"/><polygon points="3 5 3 13 9 9"/></svg>
           </button>
-          <button type="button" id="insert-embed-btn" title="Embed (YouTube, Spotify, SoundCloud, Vimeo…)" aria-label="Media embedden">
+          <button type="button" id="insert-embed-btn" title="<%= t('pedit.tb_embed_title') %>" aria-label="<%= t('pedit.tb_embed') %>">
             <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="4" width="20" height="16" rx="2"/><polygon points="10 9 15.5 12 10 15"/></svg>
           </button>
           <span class="pe-toolbar-spacer"></span>
-          <button type="button" data-cmd="removeFormat" title="Wis opmaak" aria-label="Wis opmaak">
+          <button type="button" data-cmd="removeFormat" title="<%= t('pedit.tb_clear') %>" aria-label="<%= t('pedit.tb_clear') %>">
             <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 7V4h16v3"/><path d="M9 20h6"/><path d="M5 5l14 14" stroke-linecap="round"/></svg>
           </button>
-          <button type="button" id="pe-fullscreen-btn" title="Volledig scherm" aria-label="Volledig scherm" aria-pressed="false">
+          <button type="button" id="pe-fullscreen-btn" title="<%= t('pedit.tb_fullscreen') %>" aria-label="<%= t('pedit.tb_fullscreen') %>" aria-pressed="false">
             <svg class="fs-icon-expand" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="15 3 21 3 21 9"/><polyline points="9 21 3 21 3 15"/><line x1="21" y1="3" x2="14" y2="10"/><line x1="3" y1="21" x2="10" y2="14"/></svg>
             <svg class="fs-icon-compress" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="4 14 10 14 10 20"/><polyline points="20 10 14 10 14 4"/><line x1="14" y1="10" x2="21" y2="3"/><line x1="3" y1="21" x2="10" y2="14"/></svg>
@@ -135,10 +135,10 @@
              role="textbox"
              aria-multiline="true"
-             aria-label="Content"
-             data-placeholder="Begin met schrijven…"></div>
+             aria-label="<%= t('pedit.editor_aria') %>"
+             data-placeholder="<%= t('pedit.editor_placeholder') %>"></div>
 
         <%# Sticky "tik om te bewerken"-hint (alleen zichtbaar op touch + niet-fullscreen
             via CSS). Puur visueel (pointer-events:none); de tik op het veld opent fullscreen. %>
-        <div class="pe-edit-hint" aria-hidden="true">✎ Tik om te bewerken</div>
+        <div class="pe-edit-hint" aria-hidden="true">✎ <%= t('pedit.tap_to_edit') %></div>
 
         <%# Hidden field is what actually submits to the server. The visible
@@ -153,5 +153,5 @@
         <div class="pe-editor-status">
           <small id="content-upload-status" class="pe-status"></small>
-          <span class="pe-char-count"><span id="char-count">0</span> tekens</span>
+          <span class="pe-char-count"><span id="char-count">0</span> <%= t('pedit.chars') %></span>
         </div>
 
@@ -162,23 +162,23 @@
     <%# ── META ─────────────────────────────────────────────────── %>
     <section class="pe-card">
-      <div class="pe-section-title">Publicatie</div>
+      <div class="pe-section-title"><%= t('pedit.s_publication') %></div>
       <div class="pe-row pe-row-2">
         <label class="pe-field">
-          <span>Status</span>
+          <span><%= t('pedit.f_status') %></span>
           <% var _st = isNew ? 'published' : (post.status || 'draft'); %>
           <select name="status">
-            <option value="published" <%= _st === 'published' ? 'selected' : '' %>>Gepubliceerd</option>
-            <option value="draft"     <%= _st === 'draft'     ? 'selected' : '' %>>Concept</option>
-            <option value="archived"  <%= _st === 'archived'  ? 'selected' : '' %>>Gearchiveerd</option>
+            <option value="published" <%= _st === 'published' ? 'selected' : '' %>><%= t('pedit.status_published') %></option>
+            <option value="draft"     <%= _st === 'draft'     ? 'selected' : '' %>><%= t('pedit.status_draft') %></option>
+            <option value="archived"  <%= _st === 'archived'  ? 'selected' : '' %>><%= t('pedit.status_archived') %></option>
           </select>
         </label>
         <label class="pe-field">
-          <span>Type</span>
+          <span><%= t('pedit.f_type') %></span>
           <select name="type">
             <% var ptype = post.type || 'post'; %>
-            <option value="post"  <%= ptype === 'post'  ? 'selected' : '' %>>Post</option>
-            <option value="foto"  <%= ptype === 'foto'  ? 'selected' : '' %>>Foto</option>
-            <option value="video" <%= ptype === 'video' ? 'selected' : '' %>>Video</option>
-            <option value="audio" <%= ptype === 'audio' ? 'selected' : '' %>>Audio</option>
+            <option value="post"  <%= ptype === 'post'  ? 'selected' : '' %>><%= t('pedit.type_post') %></option>
+            <option value="foto"  <%= ptype === 'foto'  ? 'selected' : '' %>><%= t('pedit.type_foto') %></option>
+            <option value="video" <%= ptype === 'video' ? 'selected' : '' %>><%= t('pedit.type_video') %></option>
+            <option value="audio" <%= ptype === 'audio' ? 'selected' : '' %>><%= t('pedit.type_audio') %></option>
           </select>
         </label>
@@ -188,11 +188,11 @@
           <label class="pe-checkbox">
             <input type="checkbox" id="pin-toggle" <%= (Number(post.pinned) > 0) ? 'checked' : '' %>>
-            <span>📌 Vastpinnen bovenaan</span>
+            <span>📌 <%= t('pedit.pin_label') %></span>
           </label>
           <input type="hidden" name="pinned" id="pin-rank" value="<%= post.pinned || 0 %>">
           <div class="pe-pin-pos" id="pin-pos" <%= (Number(post.pinned) > 0) ? '' : 'hidden' %>>
             <span class="pe-pin-steps">
-              <button type="button" class="pe-pin-btn" id="pin-up" aria-label="Hoger zetten">▲</button>
-              <button type="button" class="pe-pin-btn" id="pin-down" aria-label="Lager zetten">▼</button>
+              <button type="button" class="pe-pin-btn" id="pin-up" aria-label="<%= t('pedit.pin_up') %>">▲</button>
+              <button type="button" class="pe-pin-btn" id="pin-down" aria-label="<%= t('pedit.pin_down') %>">▼</button>
             </span>
             <span class="pe-pin-label" id="pin-label"></span>
@@ -201,15 +201,15 @@
         <label class="pe-checkbox">
           <input type="checkbox" name="noindex" value="1" <%= post.noindex ? 'checked' : '' %>>
-          <span>🚫 noindex (verberg voor zoekmachines)</span>
+          <span>🚫 <%= t('pedit.noindex_label') %></span>
         </label>
         <% if (typeof premiumUnlocked === 'undefined' || premiumUnlocked) { %>
           <label class="pe-checkbox">
             <input type="checkbox" name="fan_only" value="1" <%= post.fan_only ? 'checked' : '' %>>
-            <span>🔒 Alleen voor ingelogde fans</span>
+            <span>🔒 <%= t('pedit.fan_only_label') %></span>
           </label>
           <div style="margin-top:8px">
-            <label style="display:block;font-size:12.5px;opacity:.8;margin-bottom:4px">📅 Publiceren op (laat leeg = direct)</label>
+            <label style="display:block;font-size:12.5px;opacity:.8;margin-bottom:4px">📅 <%= t('pedit.publish_at_label') %></label>
             <input type="datetime-local" name="publish_at" value="<%= post.publish_at ? String(post.publish_at).replace(' ','T').slice(0,16) : '' %>" style="padding:8px 10px;border-radius:8px;border:1px solid var(--rule,rgba(128,128,128,.4));background:transparent;color:inherit">
-            <% if (post.status === 'scheduled' && post.publish_at) { %><div style="font-size:12px;opacity:.7;margin-top:4px">⏳ Ingepland voor <%= post.publish_at %></div><% } %>
+            <% if (post.status === 'scheduled' && post.publish_at) { %><div style="font-size:12px;opacity:.7;margin-top:4px">⏳ <%= t('pedit.scheduled_for', { d: post.publish_at }) %></div><% } %>
           </div>
         <% } %>
@@ -219,10 +219,10 @@
     <%# ── ACTIONS (sticky at bottom) ──────────────────────────── %>
     <div class="pe-actions">
-      <a href="<%= _base %><%= isNew ? '/' : '/' + post.slug %>" class="pe-btn">Annuleren</a>
+      <a href="<%= _base %><%= isNew ? '/' : '/' + post.slug %>" class="pe-btn"><%= t('pedit.cancel') %></a>
       <div class="pe-actions-spacer"></div>
       <% if (!isNew && post.status !== 'published') { %>
-        <button type="submit" name="action" value="publish" class="pe-btn pe-btn-success">📤 Publish</button>
+        <button type="submit" name="action" value="publish" class="pe-btn pe-btn-success">📤 <%= t('pedit.publish') %></button>
       <% } %>
-      <button type="submit" class="pe-btn pe-btn-primary">💾 Opslaan</button>
+      <button type="submit" class="pe-btn pe-btn-primary">💾 <%= t('pedit.save') %></button>
     </div>
   </form>
@@ -983,14 +983,14 @@
       if (!coverField.files[0]) return;
       coverStatus.classList.remove('is-error');
-      coverStatus.textContent = 'Uploaden…';
+      coverStatus.textContent = '<%= t('pedit.js_uploading') %>';
       try {
         const j = await uploadImage(coverField.files[0]);
         coverUrl.value = j.url;
         showCoverPreview(j.url);
-        coverStatus.textContent = 'Geüpload ✓';
+        coverStatus.textContent = '<%= t('pedit.js_uploaded') %> ✓';
         setTimeout(() => { coverStatus.textContent = ''; }, 2000);
       } catch (e) {
         coverStatus.classList.add('is-error');
-        coverStatus.textContent = 'Mislukt: ' + e.message;
+        coverStatus.textContent = '<%= t('pedit.js_failed') %>: ' + e.message;
       }
     });
@@ -1053,8 +1053,8 @@
       // UUIDs are noisy — show a 6-char prefix for visual hint
       const v = String(value || '');
-      return 'Track ' + (v.length > 8 ? v.slice(0, 6) + '…' : v);
-    }
-    if (kind === 'album')    return 'Album: ' + value;
-    if (kind === 'playlist') return 'Playlist: ' + value;
+      return '<%= t('pedit.chip_track') %> ' + (v.length > 8 ? v.slice(0, 6) + '…' : v);
+    }
+    if (kind === 'album')    return '<%= t('pedit.chip_album') %> ' + value;
+    if (kind === 'playlist') return '<%= t('pedit.chip_playlist') %> ' + value;
     if (kind === 'embed') {
       const clean = String(value || '').replace(/^https?:\/\/(www\.)?/, '');
@@ -1187,5 +1187,5 @@
   }
   function linkPrompt() {
-    const url = window.prompt('Link URL (https://… of /pad)');
+    const url = window.prompt('<%= t('pedit.js_link_prompt') %>');
     if (!url) return;
     execCmd('createLink', url);
@@ -1288,5 +1288,5 @@
     if (fsBtn) {
       fsBtn.setAttribute('aria-pressed', on ? 'true' : 'false');
-      fsBtn.title = on ? 'Klaar' : 'Volledig scherm';
+      fsBtn.title = on ? '<%= t('pedit.tb_done') %>' : '<%= t('pedit.tb_fullscreen') %>';
     }
     if (window.visualViewport) {
@@ -1342,5 +1342,5 @@
     editor.setAttribute('contenteditable', 'false');
     editor.classList.add('pe-tap-to-edit');
-    editor.setAttribute('data-placeholder', 'Tik om te schrijven…');
+    editor.setAttribute('data-placeholder', '<%= t('pedit.tap_to_write') %>');
     editor.addEventListener('click', function () {
       if (!isFs()) openFs();
@@ -1390,5 +1390,5 @@
   async function uploadAndInsertImage(file) {
     contentStatus.classList.remove('is-error');
-    contentStatus.textContent = 'Uploaden…';
+    contentStatus.textContent = '<%= t('pedit.js_uploading') %>';
     try {
       const j = await uploadImage(file);
@@ -1396,10 +1396,10 @@
       editor.focus({ preventScroll: true });
       document.execCommand('insertHTML', false, img);
-      contentStatus.textContent = 'Ingevoegd ✓';
+      contentStatus.textContent = '<%= t('pedit.js_inserted') %> ✓';
       setTimeout(() => { contentStatus.textContent = ''; }, 2000);
       updateCharCount();
     } catch (e) {
       contentStatus.classList.add('is-error');
-      contentStatus.textContent = 'Mislukt: ' + e.message;
+      contentStatus.textContent = '<%= t('pedit.js_failed') %>: ' + e.message;
     }
   }
@@ -1459,8 +1459,8 @@
   if (embedBtn) {
     embedBtn.addEventListener('click', () => {
-      const raw = window.prompt('Plak een media-URL om in te sluiten\n(YouTube, Spotify, SoundCloud, Vimeo, Apple Music, Bandcamp):');
+      const raw = window.prompt('<%= t('pedit.js_embed_prompt') %>');
       if (!raw) return;
       const url = raw.trim();
-      if (!/^https?:\/\//i.test(url)) { alert('Geef een volledige URL (https://…).'); return; }
+      if (!/^https?:\/\//i.test(url)) { alert('<%= t('pedit.js_embed_invalid') %>'); return; }
       insertChip('embed', url);
     });
@@ -1501,5 +1501,5 @@
       if (!filtered.length) {
         tpList.innerHTML = '';
-        tpEmpty.textContent = q ? 'Geen tracks gevonden voor "' + q + '"' : 'Nog geen tracks. Upload via Beheer → Audio.';
+        tpEmpty.textContent = q ? '<%= t('pedit.js_no_tracks_found') %> ' + q : '<%= t('pedit.js_no_tracks_yet') %>';
         tpList.appendChild(tpEmpty);
         return;
@@ -1527,5 +1527,5 @@
     async function loadTracks() {
       if (Array.isArray(tpCache)) return tpCache;
-      tpEmpty.textContent = 'Tracks laden…';
+      tpEmpty.textContent = '<%= t('pedit.js_tracks_loading') %>';
       try {
         const r = await fetch('/admin/playlists/api/tracks', { credentials: 'same-origin' });
@@ -1534,5 +1534,5 @@
       } catch (e) {
         tpCache = [];
-        tpEmpty.textContent = 'Kon tracks niet laden: ' + e.message;
+        tpEmpty.textContent = '<%= t('pedit.js_tracks_load_fail') %>: ' + e.message;
       }
       return tpCache;
@@ -1596,5 +1596,5 @@
     playlistBtn.addEventListener('click', async () => {
       if (typeof window.openPlaylistEditor !== 'function') {
-        alert('Playlist editor niet geladen');
+        alert('<%= t('pedit.js_playlist_editor_missing') %>');
         return;
       }
@@ -1604,7 +1604,7 @@
         if (j.ok && Array.isArray(j.playlists) && j.playlists.length > 0) {
           const choice = prompt(
-            'Bestaande playlists:\n\n' +
+            '<%= t('pedit.js_playlist_existing') %>\n\n' +
             j.playlists.map((p, i) => `${i + 1}. ${p.title} (${p.track_count} tracks)`).join('\n') +
-            '\n\nKies een nummer om in te voegen, leeg = nieuwe maken:'
+            '\n\n<%= t('pedit.js_playlist_choose') %>'
           );
           if (choice && /^\d+$/.test(choice.trim())) {
@@ -1644,6 +1644,6 @@
   <div class="tp-sheet" role="dialog" aria-modal="true" aria-labelledby="tp-title">
     <header class="tp-header">
-      <h2 id="tp-title" class="tp-h2">Track invoegen</h2>
-      <button type="button" class="tp-close" data-tp-close aria-label="Sluiten">
+      <h2 id="tp-title" class="tp-h2"><%= t('pedit.tp_title') %></h2>
+      <button type="button" class="tp-close" data-tp-close aria-label="<%= t('pedit.tp_close') %>">
         <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
       </button>
@@ -1653,8 +1653,8 @@
         <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
       </span>
-      <input type="search" class="tp-search" id="tp-search" placeholder="Zoek op titel of artiest…" autocomplete="off" inputmode="search">
+      <input type="search" class="tp-search" id="tp-search" placeholder="<%= t('pedit.tp_search_placeholder') %>" autocomplete="off" inputmode="search">
     </div>
-    <div class="tp-list" id="tp-list" role="listbox" aria-label="Tracks">
-      <div class="tp-empty" id="tp-empty">Tracks laden…</div>
+    <div class="tp-list" id="tp-list" role="listbox" aria-label="<%= t('pedit.tp_list_aria') %>">
+      <div class="tp-empty" id="tp-empty"><%= t('pedit.js_tracks_loading') %></div>
     </div>
   </div>
@@ -1682,6 +1682,6 @@
   function descr(n) {
     n = Number(n) || 0;
-    if (n <= 1) return 'bovenaan';
-    return n + 'e van boven';
+    if (n <= 1) return '<%= t('pedit.pin_top') %>';
+    return n + '<%= t('pedit.pin_nth_suffix') %>';
   }
   function render() {
Index: src/views/pages/prutter-conversation.ejs
===================================================================
--- src/views/pages/prutter-conversation.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
+++ src/views/pages/prutter-conversation.ejs	(revision 4885eabc577791921a46ec8cb5d38e2b4ae1c649)
@@ -1,5 +1,5 @@
 <div class="prutter-conv-page" data-conversation-id="<%= conversation.id %>" data-me="<%= user.id %>">
   <header class="prutter-conv-header">
-    <a class="prutter-back" href="<%= siteUrlBase %>/prutter" aria-label="Terug naar inbox" title="Inbox">
+    <a class="prutter-back" href="<%= siteUrlBase %>/prutter" aria-label="<%= t('prcv.back_aria') %>" title="<%= t('prcv.inbox') %>">
       <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg>
     </a>
@@ -12,7 +12,7 @@
     </div>
     <div class="prutter-conv-id">
-      <span class="prutter-conv-name"><%= other ? other.username : 'Onbekend' %></span>
+      <span class="prutter-conv-name"><%= other ? other.username : t('prcv.unknown') %></span>
       <% if (other) { %>
-        <a class="prutter-conv-sub" href="/users/<%= encodeURIComponent(other.username) %>">Bekijk profiel</a>
+        <a class="prutter-conv-sub" href="/users/<%= encodeURIComponent(other.username) %>"><%= t('prcv.view_profile') %></a>
       <% } %>
     </div>
@@ -34,6 +34,6 @@
         hx-target="#prutter-thread"
         hx-swap="beforeend">
-    <textarea name="content" rows="1" maxlength="2000" placeholder="Bericht…" required></textarea>
-    <button type="submit" class="btn btn-primary">Stuur</button>
+    <textarea name="content" rows="1" maxlength="2000" placeholder="<%= t('prcv.placeholder') %>" required></textarea>
+    <button type="submit" class="btn btn-primary"><%= t('prcv.send') %></button>
   </form>
 </div>
Index: src/views/pages/prutter-inbox.ejs
===================================================================
--- src/views/pages/prutter-inbox.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
+++ src/views/pages/prutter-inbox.ejs	(revision 4885eabc577791921a46ec8cb5d38e2b4ae1c649)
@@ -2,10 +2,10 @@
   <header class="prutter-header">
     <h1>Prutter</h1>
-    <p class="prutter-tagline">Your direct messages on this site.</p>
+    <p class="prutter-tagline"><%= t('prin.tagline') %></p>
   </header>
 
   <% if (!conversations.length) { %>
     <p class="prutter-empty">
-      No conversations yet. Open someone's profile and click <em>Send DM</em> to start one.
+      <%= t('prin.empty') %>
     </p>
   <% } else { %>
@@ -31,5 +31,5 @@
                 <div class="prutter-conv-preview"><%= c.last_message_preview %></div>
               <% } else { %>
-                <div class="prutter-conv-preview muted">Empty conversation</div>
+                <div class="prutter-conv-preview muted"><%= t('prin.empty_conv') %></div>
               <% } %>
             </div>
Index: src/views/pages/tag.ejs
===================================================================
--- src/views/pages/tag.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
+++ src/views/pages/tag.ejs	(revision 4885eabc577791921a46ec8cb5d38e2b4ae1c649)
@@ -1,13 +1,13 @@
 <div class="container tag-page">
   <header class="tag-page-header">
-    <p class="tag-eyebrow">Tag</p>
+    <p class="tag-eyebrow"><%= t('ptag.eyebrow') %></p>
     <h1>#<%= tag %></h1>
     <p class="tag-meta">
-      <%= posts.length %> post<%= posts.length === 1 ? '' : 's' %>
+      <%= posts.length === 1 ? t('ptag.count_one', { n: posts.length }) : t('ptag.count_many', { n: posts.length }) %>
     </p>
   </header>
 
   <% if (!posts.length) { %>
-    <p class="tag-empty">No posts with this tag yet.</p>
+    <p class="tag-empty"><%= t('ptag.empty') %></p>
   <% } else { %>
     <ol class="tag-results">
@@ -26,5 +26,5 @@
                  hx-get="/<%= p.slug %>?partial=1" hx-target="#pcms-main" hx-swap="innerHTML"
                  hx-push-url="/<%= p.slug %>" hx-indicator="#pcms-loading">
-                <%= p.title || '(untitled)' %>
+                <%= p.title || t('ptag.untitled') %>
               </a>
             </h2>
Index: src/views/pages/type.ejs
===================================================================
--- src/views/pages/type.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
+++ src/views/pages/type.ejs	(revision 4885eabc577791921a46ec8cb5d38e2b4ae1c649)
@@ -1,11 +1,11 @@
 <div class="container type-page">
   <header class="type-page-header">
-    <p class="type-eyebrow">Type</p>
+    <p class="type-eyebrow"><%= t('ptype.eyebrow') %></p>
     <h1><%= type.charAt(0).toUpperCase() + type.slice(1) %></h1>
-    <p class="type-meta"><%= posts.length %> post<%= posts.length === 1 ? '' : 's' %></p>
+    <p class="type-meta"><%= posts.length === 1 ? t('ptype.count_one', { n: posts.length }) : t('ptype.count_many', { n: posts.length }) %></p>
   </header>
 
   <% if (!posts.length) { %>
-    <p class="type-empty">No posts of this type yet.</p>
+    <p class="type-empty"><%= t('ptype.empty') %></p>
   <% } else { %>
     <ol class="type-results">
@@ -24,5 +24,5 @@
                  hx-get="/<%= p.slug %>?partial=1" hx-target="#pcms-main" hx-swap="innerHTML"
                  hx-push-url="/<%= p.slug %>" hx-indicator="#pcms-loading">
-                <%= p.title || '(untitled)' %>
+                <%= p.title || t('ptype.untitled') %>
               </a>
             </h2>
Index: src/views/pages/user.ejs
===================================================================
--- src/views/pages/user.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
+++ src/views/pages/user.ejs	(revision 4885eabc577791921a46ec8cb5d38e2b4ae1c649)
@@ -19,15 +19,15 @@
       <% } %>
       <p class="user-stats">
-        <%= posts.length %> post<%= posts.length === 1 ? '' : 's' %> on this site
+        <%= posts.length %> <%= posts.length === 1 ? t('pusr.post_one') : t('pusr.post_many') %> <%= t('pusr.on_this_site') %>
         <% if (totalPosts > posts.length) { %>
-          &middot; <%= totalPosts %> total
+          &middot; <%= t('pusr.total', { n: totalPosts }) %>
         <% } %>
         <% if (author.created_at) { %>
-          &middot; joined <%= formatDate(author.created_at) %>
+          &middot; <%= t('pusr.joined', { date: formatDate(author.created_at) }) %>
         <% } %>
       </p>
       <% if (user && user.id !== author.id && site && site.enable_prutter && (typeof tenancy !== 'undefined' && tenancy === 'hub') && (typeof premiumUnlocked === 'undefined' || premiumUnlocked)) { %>
         <p class="user-actions">
-          <a href="<%= siteUrlBase %>/prutter/new?to=<%= encodeURIComponent(author.username) %>" class="btn btn-primary">💬 Send DM</a>
+          <a href="<%= siteUrlBase %>/prutter/new?to=<%= encodeURIComponent(author.username) %>" class="btn btn-primary">💬 <%= t('pusr.send_dm') %></a>
         </p>
       <% } %>
@@ -36,7 +36,7 @@
 
   <% if (!posts.length) { %>
-    <p class="user-empty">No posts on this site yet.</p>
+    <p class="user-empty"><%= t('pusr.empty') %></p>
   <% } else { %>
-    <h2 class="user-posts-heading">Posts</h2>
+    <h2 class="user-posts-heading"><%= t('pusr.posts_heading') %></h2>
     <ol class="user-posts">
       <% posts.forEach(function(p) { %>
@@ -54,5 +54,5 @@
                  hx-get="/<%= p.slug %>?partial=1" hx-target="#pcms-main" hx-swap="innerHTML"
                  hx-push-url="/<%= p.slug %>" hx-indicator="#pcms-loading">
-                <%= p.title || '(untitled)' %>
+                <%= p.title || t('pusr.untitled') %>
               </a>
             </h3>
Index: src/views/pages/viewer-blocked.ejs
===================================================================
--- src/views/pages/viewer-blocked.ejs	(revision bbf9d1a5d21d0049f96961fccd5666511634974a)
+++ src/views/pages/viewer-blocked.ejs	(revision 4885eabc577791921a46ec8cb5d38e2b4ae1c649)
@@ -2,13 +2,12 @@
   <div class="vb-card">
     <div class="vb-ico" aria-hidden="true">👁️</div>
-    <h1 class="vb-title">Kijker-modus</h1>
+    <h1 class="vb-title"><%= t('vblk.title') %></h1>
     <p class="vb-text">
-      Dit is een alleen-lezen account. Je kunt alles bekijken, maar
-      <strong>niets wijzigen</strong> — opslaan, uploaden, verwijderen en
-      reageren zijn uitgeschakeld.
+      <%= t('vblk.text_before') %>
+      <strong><%= t('vblk.text_strong') %></strong> <%= t('vblk.text_after') %>
     </p>
     <div class="vb-actions">
-      <button type="button" class="vb-btn vb-btn-primary" onclick="history.back()">← Terug</button>
-      <a class="vb-btn" href="/">Naar de hoofdpagina</a>
+      <button type="button" class="vb-btn vb-btn-primary" onclick="history.back()">← <%= t('vblk.back') %></button>
+      <a class="vb-btn" href="/"><%= t('vblk.to_home') %></a>
     </div>
   </div>
