Changeset 4885eab in Klonkt for src/views/pages/post-edit.ejs


Ignore:
Timestamp:
06/21/2026 03:05:14 PM (3 months ago)
Author:
roboburr <roboburr@…>
Branches:
main
Children:
5e61b17
Parents:
bbf9d1a
Message:

feat(i18n phase 6): remaining public + other pages translated (NL/EN/DE)

post-edit, account, newsletter, download, press kit /pers, fan-gate, link-in-bio,
my-site, password-reset (2), artists directory, auth-login Google error map,
user/hub-home/circle-feed+post/viewer-blocked/home/favourites/changelog/404/
type/tag/archive/prutter (inbox+conversation). embed-player skipped (no t
in that standalone route). 1026 view-keys cross-checked → 0 missing.

Co-Authored-By: Claude <noreply@…>

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/views/pages/post-edit.ejs

    rbbf9d1a r4885eab  
    1515%>
    1616<div class="container post-edit-page">
    17   <h1><%= isNew ? 'Nieuwe post' : 'Post bewerken' %></h1>
     17  <h1><%= isNew ? t('pedit.title_new') : t('pedit.title_edit') %></h1>
    1818
    1919  <form method="post" action="<%= _base %><%= isNew ? '/posts/create' : '/posts/' + post.slug + '/save' %>" class="post-edit-form">
     
    2222    <section class="pe-card">
    2323      <label class="pe-field">
    24         <span>Titel</span>
     24        <span><%= t('pedit.f_title') %></span>
    2525        <input type="text" name="title" value="<%= post.title %>" required>
    2626      </label>
    2727      <div class="pe-row pe-row-2">
    2828        <label class="pe-field">
    29           <span>Slug (URL)</span>
    30           <input type="text" name="slug" value="<%= post.slug %>" placeholder="auto van titel als leeg">
     29          <span><%= t('pedit.f_slug') %></span>
     30          <input type="text" name="slug" value="<%= post.slug %>" placeholder="<%= t('pedit.slug_placeholder') %>">
    3131        </label>
    3232        <label class="pe-field">
    33           <span>Tags <small>(komma-gescheiden)</small></span>
     33          <span><%= t('pedit.f_tags') %> <small><%= t('pedit.tags_hint') %></small></span>
    3434          <input type="text" name="tags" value="<%= Array.isArray(post.tags) ? post.tags.join(', ') : '' %>">
    3535        </label>
    3636      </div>
    3737      <label class="pe-field">
    38         <span>Samenvatting &amp; Cirkel Preview</span>
     38        <span><%= t('pedit.f_excerpt') %></span>
    3939        <textarea name="excerpt" rows="2"><%= post.excerpt %></textarea>
    40         <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>
     40        <small class="pe-hint" style="opacity:.7"><%- t('pedit.excerpt_hint') %></small>
    4141      </label>
    4242    </section>
     
    4444    <%# ── COVER ────────────────────────────────────────────────── %>
    4545    <section class="pe-card">
    46       <div class="pe-section-title">Cover</div>
     46      <div class="pe-section-title"><%= t('pedit.s_cover') %></div>
    4747      <div class="pe-cover-row">
    4848        <div class="pe-cover-thumb" id="cover-preview-wrap" <%= _hasCover ? '' : 'data-empty' %>>
     
    5252        <div class="pe-cover-actions">
    5353          <label class="pe-field">
    54             <span>Cover URL</span>
     54            <span><%= t('pedit.f_cover_url') %></span>
    5555            <input type="text" name="cover_image_url" id="cover-url-field"
    5656                   value="<%= post.cover_image_url || '' %>"
    5757                   inputmode="url" autocapitalize="none" spellcheck="false"
    58                    placeholder="/media/…  of https://…  (of upload met de knop)">
     58                   placeholder="<%= t('pedit.cover_url_placeholder') %>">
    5959          </label>
    6060          <div class="pe-cover-upload">
    6161            <button type="button" class="pe-btn pe-btn-secondary" id="cover-upload-trigger">
    62               📷 Upload nieuwe cover
     62              📷 <%= t('pedit.cover_upload_btn') %>
    6363            </button>
    6464            <input type="file" id="cover-upload-field" accept="image/jpeg,image/png,image/webp,image/gif" hidden>
     
    7272    <section class="pe-card pe-card-content">
    7373      <div class="pe-section-title">
    74         Content
    75         <small class="pe-content-hint">Sleep een afbeelding om in te voegen · selecteer tekst om op te maken</small>
     74        <%= t('pedit.s_content') %>
     75        <small class="pe-content-hint"><%= t('pedit.content_hint') %></small>
    7676      </div>
    7777      <div class="pe-editor-frame">
    7878        <div class="pe-toolbar" id="pe-toolbar" role="toolbar" aria-label="Format">
    79           <button type="button" id="pe-fs-done" class="pe-fs-done" title="Klaar met bewerken">✓ Klaar</button>
    80           <button type="button" data-cmd="bold" title="Vet (Ctrl+B)" aria-label="Vet">
     79          <button type="button" id="pe-fs-done" class="pe-fs-done" title="<%= t('pedit.tb_done_title') %>">✓ <%= t('pedit.tb_done') %></button>
     80          <button type="button" data-cmd="bold" title="<%= t('pedit.tb_bold_title') %>" aria-label="<%= t('pedit.tb_bold') %>">
    8181            <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>
    8282          </button>
    83           <button type="button" data-cmd="italic" title="Cursief (Ctrl+I)" aria-label="Cursief">
     83          <button type="button" data-cmd="italic" title="<%= t('pedit.tb_italic_title') %>" aria-label="<%= t('pedit.tb_italic') %>">
    8484            <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>
    8585          </button>
    86           <button type="button" data-cmd="underline" title="Onderstreept" aria-label="Onderstreept">
     86          <button type="button" data-cmd="underline" title="<%= t('pedit.tb_underline') %>" aria-label="<%= t('pedit.tb_underline') %>">
    8787            <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>
    8888          </button>
    8989          <span class="pe-toolbar-sep" aria-hidden="true"></span>
    90           <button type="button" class="pe-tb-text" data-cmd="formatBlock" data-arg="h2" title="Kop">H2</button>
    91           <button type="button" class="pe-tb-text" data-cmd="formatBlock" data-arg="h3" title="Subkop">H3</button>
    92           <button type="button" class="pe-tb-text" data-cmd="formatBlock" data-arg="p"  title="Paragraaf">¶</button>
     90          <button type="button" class="pe-tb-text" data-cmd="formatBlock" data-arg="h2" title="<%= t('pedit.tb_h2') %>">H2</button>
     91          <button type="button" class="pe-tb-text" data-cmd="formatBlock" data-arg="h3" title="<%= t('pedit.tb_h3') %>">H3</button>
     92          <button type="button" class="pe-tb-text" data-cmd="formatBlock" data-arg="p"  title="<%= t('pedit.tb_p') %>">¶</button>
    9393          <span class="pe-toolbar-sep" aria-hidden="true"></span>
    94           <button type="button" data-cmd="insertUnorderedList" title="Lijst" aria-label="Lijst">
     94          <button type="button" data-cmd="insertUnorderedList" title="<%= t('pedit.tb_ul') %>" aria-label="<%= t('pedit.tb_ul') %>">
    9595            <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>
    9696          </button>
    97           <button type="button" data-cmd="insertOrderedList" title="Genummerde lijst" aria-label="Genummerde lijst">
     97          <button type="button" data-cmd="insertOrderedList" title="<%= t('pedit.tb_ol') %>" aria-label="<%= t('pedit.tb_ol') %>">
    9898            <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>
    9999          </button>
    100           <button type="button" data-cmd="formatBlock" data-arg="blockquote" title="Quote" aria-label="Quote">
     100          <button type="button" data-cmd="formatBlock" data-arg="blockquote" title="<%= t('pedit.tb_quote') %>" aria-label="<%= t('pedit.tb_quote') %>">
    101101            <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>
    102102          </button>
    103           <button type="button" data-cmd="link-prompt" title="Link (Ctrl+K)" aria-label="Link">
     103          <button type="button" data-cmd="link-prompt" title="<%= t('pedit.tb_link_title') %>" aria-label="<%= t('pedit.tb_link') %>">
    104104            <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>
    105105          </button>
    106           <button type="button" data-cmd="code-wrap" title="Code (inline)" aria-label="Code">
     106          <button type="button" data-cmd="code-wrap" title="<%= t('pedit.tb_code_title') %>" aria-label="<%= t('pedit.tb_code') %>">
    107107            <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>
    108108          </button>
    109109          <span class="pe-toolbar-sep" aria-hidden="true"></span>
    110           <button type="button" id="insert-image-btn" title="Afbeelding invoegen" aria-label="Afbeelding">
     110          <button type="button" id="insert-image-btn" title="<%= t('pedit.tb_image_title') %>" aria-label="<%= t('pedit.tb_image') %>">
    111111            <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>
    112112          </button>
    113           <button type="button" id="insert-track-btn" title="Track invoegen" aria-label="Track">
     113          <button type="button" id="insert-track-btn" title="<%= t('pedit.tb_track_title') %>" aria-label="<%= t('pedit.tb_track') %>">
    114114            <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>
    115115          </button>
    116           <button type="button" id="insert-playlist-btn" title="Playlist invoegen" aria-label="Playlist">
     116          <button type="button" id="insert-playlist-btn" title="<%= t('pedit.tb_playlist_title') %>" aria-label="<%= t('pedit.tb_playlist') %>">
    117117            <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>
    118118          </button>
    119           <button type="button" id="insert-embed-btn" title="Embed (YouTube, Spotify, SoundCloud, Vimeo…)" aria-label="Media embedden">
     119          <button type="button" id="insert-embed-btn" title="<%= t('pedit.tb_embed_title') %>" aria-label="<%= t('pedit.tb_embed') %>">
    120120            <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>
    121121          </button>
    122122          <span class="pe-toolbar-spacer"></span>
    123           <button type="button" data-cmd="removeFormat" title="Wis opmaak" aria-label="Wis opmaak">
     123          <button type="button" data-cmd="removeFormat" title="<%= t('pedit.tb_clear') %>" aria-label="<%= t('pedit.tb_clear') %>">
    124124            <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>
    125125          </button>
    126           <button type="button" id="pe-fullscreen-btn" title="Volledig scherm" aria-label="Volledig scherm" aria-pressed="false">
     126          <button type="button" id="pe-fullscreen-btn" title="<%= t('pedit.tb_fullscreen') %>" aria-label="<%= t('pedit.tb_fullscreen') %>" aria-pressed="false">
    127127            <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>
    128128            <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>
     
    135135             role="textbox"
    136136             aria-multiline="true"
    137              aria-label="Content"
    138              data-placeholder="Begin met schrijven…"></div>
     137             aria-label="<%= t('pedit.editor_aria') %>"
     138             data-placeholder="<%= t('pedit.editor_placeholder') %>"></div>
    139139
    140140        <%# Sticky "tik om te bewerken"-hint (alleen zichtbaar op touch + niet-fullscreen
    141141            via CSS). Puur visueel (pointer-events:none); de tik op het veld opent fullscreen. %>
    142         <div class="pe-edit-hint" aria-hidden="true">✎ Tik om te bewerken</div>
     142        <div class="pe-edit-hint" aria-hidden="true">✎ <%= t('pedit.tap_to_edit') %></div>
    143143
    144144        <%# Hidden field is what actually submits to the server. The visible
     
    153153        <div class="pe-editor-status">
    154154          <small id="content-upload-status" class="pe-status"></small>
    155           <span class="pe-char-count"><span id="char-count">0</span> tekens</span>
     155          <span class="pe-char-count"><span id="char-count">0</span> <%= t('pedit.chars') %></span>
    156156        </div>
    157157
     
    162162    <%# ── META ─────────────────────────────────────────────────── %>
    163163    <section class="pe-card">
    164       <div class="pe-section-title">Publicatie</div>
     164      <div class="pe-section-title"><%= t('pedit.s_publication') %></div>
    165165      <div class="pe-row pe-row-2">
    166166        <label class="pe-field">
    167           <span>Status</span>
     167          <span><%= t('pedit.f_status') %></span>
    168168          <% var _st = isNew ? 'published' : (post.status || 'draft'); %>
    169169          <select name="status">
    170             <option value="published" <%= _st === 'published' ? 'selected' : '' %>>Gepubliceerd</option>
    171             <option value="draft"     <%= _st === 'draft'     ? 'selected' : '' %>>Concept</option>
    172             <option value="archived"  <%= _st === 'archived'  ? 'selected' : '' %>>Gearchiveerd</option>
     170            <option value="published" <%= _st === 'published' ? 'selected' : '' %>><%= t('pedit.status_published') %></option>
     171            <option value="draft"     <%= _st === 'draft'     ? 'selected' : '' %>><%= t('pedit.status_draft') %></option>
     172            <option value="archived"  <%= _st === 'archived'  ? 'selected' : '' %>><%= t('pedit.status_archived') %></option>
    173173          </select>
    174174        </label>
    175175        <label class="pe-field">
    176           <span>Type</span>
     176          <span><%= t('pedit.f_type') %></span>
    177177          <select name="type">
    178178            <% var ptype = post.type || 'post'; %>
    179             <option value="post"  <%= ptype === 'post'  ? 'selected' : '' %>>Post</option>
    180             <option value="foto"  <%= ptype === 'foto'  ? 'selected' : '' %>>Foto</option>
    181             <option value="video" <%= ptype === 'video' ? 'selected' : '' %>>Video</option>
    182             <option value="audio" <%= ptype === 'audio' ? 'selected' : '' %>>Audio</option>
     179            <option value="post"  <%= ptype === 'post'  ? 'selected' : '' %>><%= t('pedit.type_post') %></option>
     180            <option value="foto"  <%= ptype === 'foto'  ? 'selected' : '' %>><%= t('pedit.type_foto') %></option>
     181            <option value="video" <%= ptype === 'video' ? 'selected' : '' %>><%= t('pedit.type_video') %></option>
     182            <option value="audio" <%= ptype === 'audio' ? 'selected' : '' %>><%= t('pedit.type_audio') %></option>
    183183          </select>
    184184        </label>
     
    188188          <label class="pe-checkbox">
    189189            <input type="checkbox" id="pin-toggle" <%= (Number(post.pinned) > 0) ? 'checked' : '' %>>
    190             <span>📌 Vastpinnen bovenaan</span>
     190            <span>📌 <%= t('pedit.pin_label') %></span>
    191191          </label>
    192192          <input type="hidden" name="pinned" id="pin-rank" value="<%= post.pinned || 0 %>">
    193193          <div class="pe-pin-pos" id="pin-pos" <%= (Number(post.pinned) > 0) ? '' : 'hidden' %>>
    194194            <span class="pe-pin-steps">
    195               <button type="button" class="pe-pin-btn" id="pin-up" aria-label="Hoger zetten">▲</button>
    196               <button type="button" class="pe-pin-btn" id="pin-down" aria-label="Lager zetten">▼</button>
     195              <button type="button" class="pe-pin-btn" id="pin-up" aria-label="<%= t('pedit.pin_up') %>">▲</button>
     196              <button type="button" class="pe-pin-btn" id="pin-down" aria-label="<%= t('pedit.pin_down') %>">▼</button>
    197197            </span>
    198198            <span class="pe-pin-label" id="pin-label"></span>
     
    201201        <label class="pe-checkbox">
    202202          <input type="checkbox" name="noindex" value="1" <%= post.noindex ? 'checked' : '' %>>
    203           <span>🚫 noindex (verberg voor zoekmachines)</span>
     203          <span>🚫 <%= t('pedit.noindex_label') %></span>
    204204        </label>
    205205        <% if (typeof premiumUnlocked === 'undefined' || premiumUnlocked) { %>
    206206          <label class="pe-checkbox">
    207207            <input type="checkbox" name="fan_only" value="1" <%= post.fan_only ? 'checked' : '' %>>
    208             <span>🔒 Alleen voor ingelogde fans</span>
     208            <span>🔒 <%= t('pedit.fan_only_label') %></span>
    209209          </label>
    210210          <div style="margin-top:8px">
    211             <label style="display:block;font-size:12.5px;opacity:.8;margin-bottom:4px">📅 Publiceren op (laat leeg = direct)</label>
     211            <label style="display:block;font-size:12.5px;opacity:.8;margin-bottom:4px">📅 <%= t('pedit.publish_at_label') %></label>
    212212            <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">
    213             <% if (post.status === 'scheduled' && post.publish_at) { %><div style="font-size:12px;opacity:.7;margin-top:4px">⏳ Ingepland voor <%= post.publish_at %></div><% } %>
     213            <% 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><% } %>
    214214          </div>
    215215        <% } %>
     
    219219    <%# ── ACTIONS (sticky at bottom) ──────────────────────────── %>
    220220    <div class="pe-actions">
    221       <a href="<%= _base %><%= isNew ? '/' : '/' + post.slug %>" class="pe-btn">Annuleren</a>
     221      <a href="<%= _base %><%= isNew ? '/' : '/' + post.slug %>" class="pe-btn"><%= t('pedit.cancel') %></a>
    222222      <div class="pe-actions-spacer"></div>
    223223      <% if (!isNew && post.status !== 'published') { %>
    224         <button type="submit" name="action" value="publish" class="pe-btn pe-btn-success">📤 Publish</button>
     224        <button type="submit" name="action" value="publish" class="pe-btn pe-btn-success">📤 <%= t('pedit.publish') %></button>
    225225      <% } %>
    226       <button type="submit" class="pe-btn pe-btn-primary">💾 Opslaan</button>
     226      <button type="submit" class="pe-btn pe-btn-primary">💾 <%= t('pedit.save') %></button>
    227227    </div>
    228228  </form>
     
    983983      if (!coverField.files[0]) return;
    984984      coverStatus.classList.remove('is-error');
    985       coverStatus.textContent = 'Uploaden…';
     985      coverStatus.textContent = '<%= t('pedit.js_uploading') %>';
    986986      try {
    987987        const j = await uploadImage(coverField.files[0]);
    988988        coverUrl.value = j.url;
    989989        showCoverPreview(j.url);
    990         coverStatus.textContent = 'Geüpload ✓';
     990        coverStatus.textContent = '<%= t('pedit.js_uploaded') %> ✓';
    991991        setTimeout(() => { coverStatus.textContent = ''; }, 2000);
    992992      } catch (e) {
    993993        coverStatus.classList.add('is-error');
    994         coverStatus.textContent = 'Mislukt: ' + e.message;
     994        coverStatus.textContent = '<%= t('pedit.js_failed') %>: ' + e.message;
    995995      }
    996996    });
     
    10531053      // UUIDs are noisy — show a 6-char prefix for visual hint
    10541054      const v = String(value || '');
    1055       return 'Track ' + (v.length > 8 ? v.slice(0, 6) + '…' : v);
    1056     }
    1057     if (kind === 'album')    return 'Album: ' + value;
    1058     if (kind === 'playlist') return 'Playlist: ' + value;
     1055      return '<%= t('pedit.chip_track') %> ' + (v.length > 8 ? v.slice(0, 6) + '…' : v);
     1056    }
     1057    if (kind === 'album')    return '<%= t('pedit.chip_album') %> ' + value;
     1058    if (kind === 'playlist') return '<%= t('pedit.chip_playlist') %> ' + value;
    10591059    if (kind === 'embed') {
    10601060      const clean = String(value || '').replace(/^https?:\/\/(www\.)?/, '');
     
    11871187  }
    11881188  function linkPrompt() {
    1189     const url = window.prompt('Link URL (https://… of /pad)');
     1189    const url = window.prompt('<%= t('pedit.js_link_prompt') %>');
    11901190    if (!url) return;
    11911191    execCmd('createLink', url);
     
    12881288    if (fsBtn) {
    12891289      fsBtn.setAttribute('aria-pressed', on ? 'true' : 'false');
    1290       fsBtn.title = on ? 'Klaar' : 'Volledig scherm';
     1290      fsBtn.title = on ? '<%= t('pedit.tb_done') %>' : '<%= t('pedit.tb_fullscreen') %>';
    12911291    }
    12921292    if (window.visualViewport) {
     
    13421342    editor.setAttribute('contenteditable', 'false');
    13431343    editor.classList.add('pe-tap-to-edit');
    1344     editor.setAttribute('data-placeholder', 'Tik om te schrijven…');
     1344    editor.setAttribute('data-placeholder', '<%= t('pedit.tap_to_write') %>');
    13451345    editor.addEventListener('click', function () {
    13461346      if (!isFs()) openFs();
     
    13901390  async function uploadAndInsertImage(file) {
    13911391    contentStatus.classList.remove('is-error');
    1392     contentStatus.textContent = 'Uploaden…';
     1392    contentStatus.textContent = '<%= t('pedit.js_uploading') %>';
    13931393    try {
    13941394      const j = await uploadImage(file);
     
    13961396      editor.focus({ preventScroll: true });
    13971397      document.execCommand('insertHTML', false, img);
    1398       contentStatus.textContent = 'Ingevoegd ✓';
     1398      contentStatus.textContent = '<%= t('pedit.js_inserted') %> ✓';
    13991399      setTimeout(() => { contentStatus.textContent = ''; }, 2000);
    14001400      updateCharCount();
    14011401    } catch (e) {
    14021402      contentStatus.classList.add('is-error');
    1403       contentStatus.textContent = 'Mislukt: ' + e.message;
     1403      contentStatus.textContent = '<%= t('pedit.js_failed') %>: ' + e.message;
    14041404    }
    14051405  }
     
    14591459  if (embedBtn) {
    14601460    embedBtn.addEventListener('click', () => {
    1461       const raw = window.prompt('Plak een media-URL om in te sluiten\n(YouTube, Spotify, SoundCloud, Vimeo, Apple Music, Bandcamp):');
     1461      const raw = window.prompt('<%= t('pedit.js_embed_prompt') %>');
    14621462      if (!raw) return;
    14631463      const url = raw.trim();
    1464       if (!/^https?:\/\//i.test(url)) { alert('Geef een volledige URL (https://…).'); return; }
     1464      if (!/^https?:\/\//i.test(url)) { alert('<%= t('pedit.js_embed_invalid') %>'); return; }
    14651465      insertChip('embed', url);
    14661466    });
     
    15011501      if (!filtered.length) {
    15021502        tpList.innerHTML = '';
    1503         tpEmpty.textContent = q ? 'Geen tracks gevonden voor "' + q + '"' : 'Nog geen tracks. Upload via Beheer → Audio.';
     1503        tpEmpty.textContent = q ? '<%= t('pedit.js_no_tracks_found') %> ' + q : '<%= t('pedit.js_no_tracks_yet') %>';
    15041504        tpList.appendChild(tpEmpty);
    15051505        return;
     
    15271527    async function loadTracks() {
    15281528      if (Array.isArray(tpCache)) return tpCache;
    1529       tpEmpty.textContent = 'Tracks laden…';
     1529      tpEmpty.textContent = '<%= t('pedit.js_tracks_loading') %>';
    15301530      try {
    15311531        const r = await fetch('/admin/playlists/api/tracks', { credentials: 'same-origin' });
     
    15341534      } catch (e) {
    15351535        tpCache = [];
    1536         tpEmpty.textContent = 'Kon tracks niet laden: ' + e.message;
     1536        tpEmpty.textContent = '<%= t('pedit.js_tracks_load_fail') %>: ' + e.message;
    15371537      }
    15381538      return tpCache;
     
    15961596    playlistBtn.addEventListener('click', async () => {
    15971597      if (typeof window.openPlaylistEditor !== 'function') {
    1598         alert('Playlist editor niet geladen');
     1598        alert('<%= t('pedit.js_playlist_editor_missing') %>');
    15991599        return;
    16001600      }
     
    16041604        if (j.ok && Array.isArray(j.playlists) && j.playlists.length > 0) {
    16051605          const choice = prompt(
    1606             'Bestaande playlists:\n\n' +
     1606            '<%= t('pedit.js_playlist_existing') %>\n\n' +
    16071607            j.playlists.map((p, i) => `${i + 1}. ${p.title} (${p.track_count} tracks)`).join('\n') +
    1608             '\n\nKies een nummer om in te voegen, leeg = nieuwe maken:'
     1608            '\n\n<%= t('pedit.js_playlist_choose') %>'
    16091609          );
    16101610          if (choice && /^\d+$/.test(choice.trim())) {
     
    16441644  <div class="tp-sheet" role="dialog" aria-modal="true" aria-labelledby="tp-title">
    16451645    <header class="tp-header">
    1646       <h2 id="tp-title" class="tp-h2">Track invoegen</h2>
    1647       <button type="button" class="tp-close" data-tp-close aria-label="Sluiten">
     1646      <h2 id="tp-title" class="tp-h2"><%= t('pedit.tp_title') %></h2>
     1647      <button type="button" class="tp-close" data-tp-close aria-label="<%= t('pedit.tp_close') %>">
    16481648        <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>
    16491649      </button>
     
    16531653        <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>
    16541654      </span>
    1655       <input type="search" class="tp-search" id="tp-search" placeholder="Zoek op titel of artiest…" autocomplete="off" inputmode="search">
     1655      <input type="search" class="tp-search" id="tp-search" placeholder="<%= t('pedit.tp_search_placeholder') %>" autocomplete="off" inputmode="search">
    16561656    </div>
    1657     <div class="tp-list" id="tp-list" role="listbox" aria-label="Tracks">
    1658       <div class="tp-empty" id="tp-empty">Tracks laden…</div>
     1657    <div class="tp-list" id="tp-list" role="listbox" aria-label="<%= t('pedit.tp_list_aria') %>">
     1658      <div class="tp-empty" id="tp-empty"><%= t('pedit.js_tracks_loading') %></div>
    16591659    </div>
    16601660  </div>
     
    16821682  function descr(n) {
    16831683    n = Number(n) || 0;
    1684     if (n <= 1) return 'bovenaan';
    1685     return n + 'e van boven';
     1684    if (n <= 1) return '<%= t('pedit.pin_top') %>';
     1685    return n + '<%= t('pedit.pin_nth_suffix') %>';
    16861686  }
    16871687  function render() {
Note: See TracChangeset for help on using the changeset viewer.