Changeset bbf9d1a in Klonkt for src/views/pages/admin-audio.ejs


Ignore:
Timestamp:
06/21/2026 02:32:39 PM (3 months ago)
Author:
roboburr <roboburr@…>
Branches:
main
Children:
4885eab
Parents:
929d98d
Message:

feat(i18n phase 5): all admin sub-pages translated (NL/EN/DE)

settings, site-edit, seo, google, audio, stats, playlists, users, sites,
comments, circle, shows, newsletter, updates, epk, help. ~520 new keys
per language. Loop-shadowing of t() resolved by fetching before the loop. 709
view-keys cross-checked against the dictionary (0 missing).

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/views/pages/admin-audio.ejs

    r929d98d rbbf9d1a  
    44  <header class="ax-header">
    55    <div>
    6       <h1>Audio tracks</h1>
    7       <p class="ax-tagline">Site-level MP3s. Gebruik <code>[[track:&lt;id&gt;]]</code> in een post om een play-knop in te voegen.</p>
     6      <h1><%= t('aaud.title') %></h1>
     7      <p class="ax-tagline"><%= t('aaud.tagline_pre') %> <code>[[track:&lt;id&gt;]]</code> <%= t('aaud.tagline_post') %></p>
    88    </div>
    99  </header>
     
    1414  <%# ── UPLOAD ──────────────────────────────────────────────── %>
    1515  <section class="ax-card">
    16     <div class="ax-card-title">Upload</div>
     16    <div class="ax-card-title"><%= t('aaud.upload') %></div>
    1717
    1818    <%# Bulk drag-drop zone. Multiple files at once, transcoded sequentially.
     
    2525      <div class="ax-row ax-row-2">
    2626        <label class="ax-field">
    27           <span>Artiest <small>(toegepast op alle bestanden)</small></span>
    28           <input type="text" id="batch-artist" placeholder="Optioneel">
     27          <span><%= t('aaud.artist') %> <small><%= t('aaud.applied_all') %></small></span>
     28          <input type="text" id="batch-artist" placeholder="<%= t('aaud.optional') %>">
    2929        </label>
    3030        <label class="ax-field">
    31           <span>Album <small>(toegepast op alle bestanden)</small></span>
    32           <input type="text" id="batch-album" placeholder="Optioneel">
     31          <span><%= t('aaud.album') %> <small><%= t('aaud.applied_all') %></small></span>
     32          <input type="text" id="batch-album" placeholder="<%= t('aaud.optional') %>">
    3333        </label>
    3434      </div>
    3535      <label class="ax-field ax-file">
    36         <span>Cover <small>(optioneel, toegepast op alle bestanden — jpg/png/webp/gif, max 5 MB)</small></span>
     36        <span><%= t('aaud.cover') %> <small><%= t('aaud.cover_hint') %></small></span>
    3737        <span class="ax-file-control">
    38           <span class="ax-btn ax-btn-secondary ax-file-btn">🖼 Kies cover</span>
    39           <span class="ax-file-name" data-empty>Geen bestand gekozen</span>
     38          <span class="ax-btn ax-btn-secondary ax-file-btn">🖼 <%= t('aaud.choose_cover') %></span>
     39          <span class="ax-file-name" data-empty><%= t('aaud.no_file') %></span>
    4040          <input type="file" id="batch-cover" accept="image/jpeg,image/png,image/webp,image/gif">
    4141        </span>
     
    4545      <label class="ax-dropzone" id="audio-dropzone" tabindex="0">
    4646        <span class="ax-dropzone-icon">🎵</span>
    47         <strong class="ax-dropzone-title">Sleep audio hierheen</strong>
    48         <span class="ax-dropzone-sub">of klik om bestanden te kiezen<br><small>mp3, m4a, ogg, opus, flac · max <%= maxBytesMb %> MB · wav max <%= maxWavMb %> MB</small></span>
     47        <strong class="ax-dropzone-title"><%= t('aaud.drag_here') %></strong>
     48        <span class="ax-dropzone-sub"><%= t('aaud.or_click') %><br><small>mp3, m4a, ogg, opus, flac · max <%= maxBytesMb %> MB · wav max <%= maxWavMb %> MB</small></span>
    4949        <input type="file" id="audio-files" accept="audio/*" multiple>
    5050      </label>
     
    5454
    5555      <div class="ax-form-actions" id="upload-actions" hidden>
    56         <button type="button" class="ax-btn ax-btn-primary" id="start-upload-btn">⬆ Start upload</button>
    57         <button type="button" class="ax-btn" id="clear-queue-btn">Wis lijst</button>
     56        <button type="button" class="ax-btn ax-btn-primary" id="start-upload-btn">⬆ <%= t('aaud.start_upload') %></button>
     57        <button type="button" class="ax-btn" id="clear-queue-btn"><%= t('aaud.clear_list') %></button>
    5858      </div>
    5959    </div>
     
    6363  <section class="ax-card">
    6464    <div class="ax-card-title" style="display:flex;align-items:center;gap:.5rem">
    65       Tracks <span class="ax-count"><%= tracks.length %></span>
     65      <%= t('aaud.tracks') %> <span class="ax-count"><%= tracks.length %></span>
    6666      <button type="button" class="ax-btn ax-btn-secondary" id="add-link-track-btn"
    67               style="margin-left:auto;font-size:.85rem" title="Een track zonder audiobestand — alleen titel + open-in links">
    68         + Track zonder audio
     67              style="margin-left:auto;font-size:.85rem" title="<%= t('aaud.add_link_track_title') %>">
     68        + <%= t('aaud.add_link_track') %>
    6969      </button>
    7070    </div>
     
    7373      <div class="ax-empty">
    7474        <div class="ax-empty-icon">♫</div>
    75         <p>Nog geen tracks. Upload er een hierboven.</p>
     75        <p><%= t('aaud.no_tracks') %></p>
    7676      </div>
    7777    <% } else { %>
     78      <%
     79        // Pre-fetch loop labels — `t` is shadowed by the loop var below.
     80        var _aaudUntitled = t('aaud.untitled');
     81        var _aaudPlay     = t('aaud.play');
     82        var _aaudEdit     = t('aaud.edit');
     83        var _aaudDlOn     = t('aaud.dl_on');
     84        var _aaudDlOff    = t('aaud.dl_off');
     85        var _aaudDelete   = t('aaud.delete');
     86        var _aaudCopy     = t('aaud.copy_click');
     87        var _aaudDelConfirm = t('aaud.delete_confirm');
     88      %>
    7889      <ul class="ax-list">
    7990        <% tracks.forEach(function(t) { %>
     
    8697
    8798            <div class="ax-track-meta">
    88               <div class="ax-track-title" data-cell="title"><%= t.title || '(zonder titel)' %></div>
     99              <div class="ax-track-title" data-cell="title"><%= t.title || _aaudUntitled %></div>
    89100              <div class="ax-track-sub">
    90101                <span data-cell="artist"><%= t.artist || '—' %></span>
     
    92103                <span class="ax-track-sep">·</span><span><%= t.size ? Math.round(t.size/1024) + ' KB' : '—' %></span>
    93104              </div>
    94               <code class="ax-embed" data-copy="[[track:<%= t.id %>]]" title="Klik om te kopiëren">[[track:<%= t.id %>]]</code>
     105              <code class="ax-embed" data-copy="[[track:<%= t.id %>]]" title="<%= _aaudCopy %>">[[track:<%= t.id %>]]</code>
    95106            </div>
    96107
     
    100111                        data-stream-url="<%= t.stream_url %>"
    101112                        data-track-id="<%= t.id %>"
    102                         aria-label="Afspelen" title="Afspelen">
     113                        aria-label="<%= _aaudPlay %>" title="<%= _aaudPlay %>">
    103114                  <span class="ax-track-play-icon" aria-hidden="true">▶</span>
    104115                </button>
    105116              <% } %>
    106               <button type="button" class="ax-icon-btn" data-track-edit data-id="<%= t.id %>" aria-label="Bewerken" title="Bewerken">✎</button>
     117              <button type="button" class="ax-icon-btn" data-track-edit data-id="<%= t.id %>" aria-label="<%= _aaudEdit %>" title="<%= _aaudEdit %>">✎</button>
    107118              <% if (typeof premiumUnlocked === 'undefined' || premiumUnlocked) { %>
    108119                <button type="button" class="ax-icon-btn" data-track-dl data-id="<%= t.id %>" data-on="<%= t.downloadable ? '1' : '0' %>"
    109                         aria-label="Download-voor-email" title="<%= t.downloadable ? 'Download-voor-email staat AAN — klik om uit te zetten' : 'Download-voor-email staat uit — klik om aan te zetten' %>"
     120                        aria-label="<%= t.downloadable ? _aaudDlOn : _aaudDlOff %>" title="<%= t.downloadable ? _aaudDlOn : _aaudDlOff %>"
    110121                        style="<%= t.downloadable ? 'color:var(--accent,#6b8f71)' : 'opacity:.5' %>">⬇</button>
    111122              <% } %>
    112               <form action="/admin/audio/<%= t.id %>/delete" method="post" onsubmit="return confirm('Track verwijderen?')" class="ax-track-delete">
    113                 <button type="submit" class="ax-icon-btn ax-icon-btn-danger" aria-label="Verwijderen" title="Verwijderen">🗑</button>
     123              <form action="/admin/audio/<%= t.id %>/delete" method="post" onsubmit="return confirm('<%= _aaudDelConfirm %>')" class="ax-track-delete">
     124                <button type="submit" class="ax-icon-btn ax-icon-btn-danger" aria-label="<%= _aaudDelete %>" title="<%= _aaudDelete %>">🗑</button>
    114125              </form>
    115126            </div>
     
    122133  <% if ((typeof premiumUnlocked === 'undefined' || premiumUnlocked) && typeof embedUrl !== 'undefined') { %>
    123134    <section class="ax-section" style="margin-top:1.5rem">
    124       <h2 style="margin:0 0 .4rem">🔊 Embedbare speler</h2>
    125       <p style="opacity:.75;font-size:.9rem;margin:0 0 .6rem">Plak deze code op je eigen website/blog om je muziek met deze speler in te sluiten:</p>
     135      <h2 style="margin:0 0 .4rem">🔊 <%= t('aaud.embed_player') %></h2>
     136      <p style="opacity:.75;font-size:.9rem;margin:0 0 .6rem"><%= t('aaud.embed_hint') %></p>
    126137      <textarea readonly rows="3" onclick="this.select()" style="width:100%;font-family:monospace;font-size:12px;padding:10px;border-radius:8px;border:1px solid var(--rule);background:var(--paper-2);color:inherit;">&lt;iframe src="<%= embedUrl %>" width="100%" height="420" frameborder="0" loading="lazy" style="border-radius:12px"&gt;&lt;/iframe&gt;</textarea>
    127       <p style="margin:.6rem 0 0"><a class="btn" href="<%= embedUrl %>" target="_blank">▶ Speler-voorbeeld openen</a></p>
     138      <p style="margin:.6rem 0 0"><a class="btn" href="<%= embedUrl %>" target="_blank">▶ <%= t('aaud.preview_player') %></a></p>
    128139    </section>
    129140  <% } %>
     
    573584        nameEl.removeAttribute('data-empty');
    574585      } else {
    575         nameEl.textContent = 'Geen bestand gekozen';
     586        nameEl.textContent = '<%= t('aaud.no_file') %>';
    576587        nameEl.setAttribute('data-empty', '');
    577588      }
     
    592603      if (icon) icon.textContent = playing ? '⏸' : '▶';
    593604      btn.classList.toggle('is-playing', playing);
    594       btn.setAttribute('aria-label', playing ? 'Pauzeren' : 'Afspelen');
     605      btn.setAttribute('aria-label', playing ? '<%= t('aaud.pause') %>' : '<%= t('aaud.play') %>');
    595606    }
    596607
     
    695706  function setItemStatus(item, status, label) {
    696707    const labels = {
    697       queued:      'Wachten',
    698       uploading:   'Uploaden…',
    699       transcoding: 'Converteren…',
    700       done:        '✓ Klaar',
    701       error:       '✗ Fout',
     708      queued:      '<%= t('aaud.st_queued') %>',
     709      uploading:   '<%= t('aaud.st_uploading') %>',
     710      transcoding: '<%= t('aaud.st_transcoding') %>',
     711      done:        '✓ <%= t('aaud.st_done') %>',
     712      error:       '✗ <%= t('aaud.st_error') %>',
    702713    };
    703714    item.status = status;
     
    726737      li.innerHTML =
    727738        '<div class="ax-queue-name"></div>' +
    728         '<span class="ax-queue-status ax-queue-status--queued">Wachten</span>';
     739        '<span class="ax-queue-status ax-queue-status--queued"><%= t('aaud.st_queued') %></span>';
    729740      // Use textContent to avoid HTML-injection if a filename contains markup.
    730741      li.querySelector('.ax-queue-name').textContent = file.name;
     
    813824      } catch (err) {
    814825        console.error('upload failed for', item.file.name, err);
    815         setItemStatus(item, 'error', '✗ ' + (err.message || 'Mislukt'));
     826        setItemStatus(item, 'error', '✗ ' + (err.message || '<%= t('aaud.failed') %>'));
    816827      }
    817828    }
     
    864875      let data;
    865876      try { data = await res.json(); }
    866       catch (_) { throw new Error('Onverwacht serverantwoord (' + res.status + ')'); }
     877      catch (_) { throw new Error('<%= t('aaud.err_unexpected') %> (' + res.status + ')'); }
    867878
    868879      if (!res.ok || !data.ok) {
     
    870881      }
    871882
    872       setItemStatus(item, 'done', '✓ ' + (data.title || 'Klaar'));
     883      setItemStatus(item, 'done', '✓ ' + (data.title || '<%= t('aaud.st_done') %>'));
    873884    } catch (err) {
    874885      clearTimeout(transcodeHint);
     
    885896        el.classList.add('is-copied');
    886897        const original = el.textContent;
    887         el.textContent = '✓ gekopieerd';
     898        el.textContent = '✓ <%= t('aaud.copied') %>';
    888899        setTimeout(() => {
    889900          el.classList.remove('is-copied');
     
    903914          method: 'POST', credentials: 'same-origin',
    904915          headers: { 'Content-Type': 'application/json' },
    905           body: JSON.stringify({ title: 'Nieuwe track' }),
     916          body: JSON.stringify({ title: '<%= t('aaud.new_track') %>' }),
    906917        });
    907918        const j = await r.json();
    908         if (!r.ok || !j.ok) throw new Error(j.error || 'Aanmaken mislukt');
     919        if (!r.ok || !j.ok) throw new Error(j.error || '<%= t('aaud.create_failed') %>');
    909920        if (typeof window.openTrackEditor !== 'function') { location.reload(); return; }
    910921        window.openTrackEditor({ id: j.id, onSaved: () => location.reload() });
    911922      } catch (err) {
    912         alert('Track aanmaken mislukt: ' + err.message);
     923        alert('<%= t('aaud.create_failed') %>: ' + err.message);
    913924      } finally {
    914925        addLinkBtn.disabled = false;
     
    923934    btn.addEventListener('click', () => {
    924935      if (typeof window.openTrackEditor !== 'function') {
    925         alert('Track editor niet geladen');
     936        alert('<%= t('aaud.editor_not_loaded') %>');
    926937        return;
    927938      }
     
    936947          const artistEl = row.querySelector('[data-cell="artist"]');
    937948          const albumEl  = row.querySelector('[data-cell="album"]');
    938           if (titleEl)  titleEl.textContent  = track.title  || '(zonder titel)';
     949          if (titleEl)  titleEl.textContent  = track.title  || '<%= t('aaud.untitled') %>';
    939950          if (artistEl) artistEl.textContent = track.artist || '—';
    940951          if (albumEl)  {
     
    984995        btn.style.opacity = want ? '1' : '.5';
    985996        btn.title = want
    986           ? 'Download-voor-email staat AAN — klik om uit te zetten'
    987           : 'Download-voor-email staat uit — klik om aan te zetten';
     997          ? '<%= t('aaud.dl_on') %>'
     998          : '<%= t('aaud.dl_off') %>';
    988999      } catch (e) {
    989         alert('Kon niet wijzigen: ' + (e.message || e));
     1000        alert('<%= t('aaud.change_failed') %>: ' + (e.message || e));
    9901001      } finally {
    9911002        btn.disabled = false;
Note: See TracChangeset for help on using the changeset viewer.