Changeset 70677e96 in Klonkt
- Timestamp:
- 07/28/2026 07:43:49 PM (6 weeks ago)
- Branches:
- main
- Children:
- 742ba7e
- Parents:
- a7bcf66
- Files:
-
- 1 added
- 5 edited
-
src/assets/css/guardian.css (modified) (1 diff)
-
src/assets/js/guardian.js (modified) (6 diffs)
-
src/routes/guardian.js (modified) (5 diffs)
-
src/services/i18n.js (modified) (3 diffs)
-
src/views/pages/guardian.ejs (modified) (2 diffs)
-
test/guardian-panel.test.js (added)
Legend:
- Unmodified
- Added
- Removed
-
src/assets/css/guardian.css
ra7bcf66 r70677e96 118 118 .g-note .tl-media-video, .g-note .tl-media-audio { width: 100%; margin: 8px 0 0; border-radius: 10px; display: block; } 119 119 .g-note .tl-media-video { max-height: 320px; background: #000; } 120 121 /* ── Paneel per ward ─────────────────────────────────────────────────────── 122 Een guardian denkt per kind, niet per functie: achter de regel van een kind 123 zit alles over dat kind. De regel zelf draagt de tellers, zodat een dicht 124 paneel nooit iets verbergt dat een antwoord nodig heeft. */ 125 .g-card.ward > .row { gap: 8px; } 126 .g-panel { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line); } 127 .g-panel[hidden] { display: none; } 128 .g-panel-sec + .g-panel-sec { margin-top: 16px; } 129 .g-panel-sec h3 { margin: 0 0 6px; font-size: .82rem; font-weight: 600; letter-spacing: .03em; 130 text-transform: uppercase; color: var(--sub); } 131 .g-panel-sec .g-card { background: transparent; border-color: var(--line); } 132 .g-panel-sec .g-card + .g-card { margin-top: 6px; } 133 .g-panel-sec .row { flex-wrap: wrap; } 134 .g-empty.small { margin: 0; font-size: .85rem; } 135 .tag.help { color: var(--accent); border-color: var(--accent); } -
src/assets/js/guardian.js
ra7bcf66 r70677e96 43 43 44 44 // ── 1. Help requests ─────────────────────────────────────────────────── 45 // A call for help is not an alarm: it may well be settled quietly between a 46 // guardian and the child. So the card carries no siren, it just has to be 47 // impossible to miss. It shows up twice on purpose (Robins keuze): the recent 48 // ones across all children at the top, the full history of one child in that 49 // child's panel. 50 var HELP_TOP = 5; 51 52 function helpCard(h) { 53 var card = el('div', 'g-card help'); 54 var row = el('div', 'row'); 55 var who = el('span', 'who grow'); 56 // name_html carries the custom emojis (FEP-9098) of the display name, the 57 // same way de Krant renders a byline. Falls back to the plain name. 58 if (h.name_html) who.innerHTML = h.name_html; 59 else who.textContent = h.actor_name || handleOf(h.actor_uri, h.actor_handle); 60 row.appendChild(who); 61 row.appendChild(el('span', 'when', when(h, h.published || h.created_at))); 62 card.appendChild(row); 63 var body = el('div', 'body g-note'); 64 // body_html is the shared note-body partial, rendered server-side: the 65 // content with its emojis, the quote / link-preview card and the media. 66 // Falls back to the bare content for rows stored before that existed. 67 body.innerHTML = h.body_html || h.content || ''; // sanitized server-side on ingest 68 card.appendChild(body); 69 if (h.note_url) { 70 var a = el('a', 'g-link', T.open || 'open'); 71 a.href = h.note_url; a.target = '_blank'; a.rel = 'noopener'; 72 card.appendChild(a); 73 } 74 return card; 75 } 76 45 77 function renderHelp() { 46 78 var list = document.getElementById('help-list'); 47 79 list.textContent = ''; 48 80 var help = S.help || []; 49 help.forEach(function (h) { 50 var card = el('div', 'g-card help'); 51 var row = el('div', 'row'); 52 var who = el('span', 'who grow'); 53 // name_html carries the custom emojis (FEP-9098) of the display name, the 54 // same way de Krant renders a byline. Falls back to the plain name. 55 if (h.name_html) who.innerHTML = h.name_html; 56 else who.textContent = h.actor_name || handleOf(h.actor_uri, h.actor_handle); 57 row.appendChild(who); 58 row.appendChild(el('span', 'when', when(h, h.published || h.created_at))); 59 card.appendChild(row); 60 var body = el('div', 'body g-note'); 61 // body_html is the shared note-body partial, rendered server-side: the 62 // content with its emojis, the quote / link-preview card and the media. 63 // Falls back to the bare content for rows stored before that existed. 64 body.innerHTML = h.body_html || h.content || ''; // sanitized server-side on ingest 65 card.appendChild(body); 66 if (h.note_url) { 67 var a = el('a', 'g-link', T.open || 'open'); 68 a.href = h.note_url; a.target = '_blank'; a.rel = 'noopener'; 69 card.appendChild(a); 70 } 71 list.appendChild(card); 72 }); 81 help.slice(0, HELP_TOP).forEach(function (h) { list.appendChild(helpCard(h)); }); 82 if (help.length > HELP_TOP) { 83 list.appendChild(el('p', 'g-sec-sub', '+ ' + (help.length - HELP_TOP) + ' — ' + (T.panel_help || ''))); 84 } 73 85 var badge = document.getElementById('help-count'); 74 86 badge.textContent = help.length; badge.hidden = help.length === 0; … … 119 131 } 120 132 121 // ── 4. Accepted wards ────────────────────────────────────────────────── 133 // ── 4. Accepted wards: one panel per child ───────────────────────────── 134 // A guardian thinks per child, not per function, so everything about one 135 // child sits behind that child's row: the gated settings, the follow requests 136 // waiting on them, their calls for help, their recent posts. The row itself 137 // carries counts, so nothing that needs an answer hides inside a closed 138 // panel. 139 var openPanels = {}; // ward uri -> open, so a refresh does not close it 140 // The follow requests and the wards' posts arrive from their own endpoints 141 // and are grouped into the panels by ward, so they are cached here rather 142 // than rendered into a section of their own. 143 var FEED = [], FOLLOWS = []; 144 145 function sectionInto(panel, title, items, empty, build) { 146 var h = el('div', 'g-panel-sec'); 147 h.appendChild(el('h3', null, title)); 148 if (!items.length) h.appendChild(el('p', 'g-empty small', empty)); 149 else items.forEach(function (it) { h.appendChild(build(it)); }); 150 panel.appendChild(h); 151 return h; 152 } 153 154 function embedsButton(w) { 155 // Gated feature: external (non-fediverse) embeds. Off by default for a 156 // ward; only a guardian can open it, and the gate is enforced server-side 157 // when the feed is built, so this button is the only thing that moves it. 158 // Shown for EVERY ward, including one on another server. There the value is 159 // unknown (it lives on the ward's server), but proposing is exactly as 160 // possible: the proposal travels, the ward's server tallies the guardians 161 // and enforces. A guardian next door must not have more say than one far 162 // away. 163 var known = w.embeds === true || w.embeds === false; 164 var emb = el('button', 'quiet small', 165 (known ? (w.embeds ? T.embeds_on : T.embeds_off) : T.embeds_propose) || 'Link previews'); 166 emb.addEventListener('click', function () { 167 emb.disabled = true; 168 fetch('/guardian/wards/embeds', { 169 method: 'POST', headers: { 'Content-Type': 'application/json' }, 170 body: JSON.stringify({ uri: w.other_uri, allow: known ? !w.embeds : true }), 171 }).then(function (r) { return r.json(); }) 172 .then(function (j) { 173 // Not settled yet: the other guardians still have to answer. 174 if (j && j.state === 'open') { 175 emb.textContent = (T.embeds_waiting || 'waiting for the other guardians'); 176 emb.disabled = true; 177 return; 178 } 179 refresh(); 180 }) 181 .catch(function () { emb.disabled = false; }); 182 }); 183 return emb; 184 } 185 186 function wardPanel(w) { 187 var uri = w.other_uri; 188 var panel = el('div', 'g-panel'); 189 panel.hidden = !openPanels[uri]; 190 191 var set = el('div', 'g-panel-sec'); 192 set.appendChild(el('h3', null, T.settings_title || 'Settings')); 193 var setRow = el('div', 'row'); 194 setRow.appendChild(embedsButton(w)); 195 set.appendChild(setRow); 196 panel.appendChild(set); 197 198 sectionInto(panel, T.panel_follow || 'Follow requests', 199 FOLLOWS.filter(function (f) { return f.wardUri === uri; }), 200 T.panel_follow_empty || '', followCard); 201 202 sectionInto(panel, T.panel_help || 'Calls for help', 203 (S.help || []).filter(function (h) { return h.actor_uri === uri; }), 204 T.panel_help_empty || '', helpCard); 205 206 sectionInto(panel, T.panel_posts || 'Recent posts', 207 FEED.filter(function (p) { return p.authorUri === uri; }), 208 T.panel_posts_empty || '', feedCard); 209 210 var act = el('div', 'g-panel-sec'); 211 act.appendChild(el('h3', null, T.panel_actions || 'Actions')); 212 var actRow = el('div', 'row'); 213 var wave = el('button', 'small', T.wave || '👋 Wave'); 214 wave.addEventListener('click', function () { sendWave(uri, wave); }); 215 actRow.appendChild(wave); 216 var rel = el('button', 'quiet small', T.release); 217 // Releasing a ward is heavy and hard to undo (coming back needs a fresh 218 // offer the ward accepts), so it asks first and spells out what changes. 219 rel.addEventListener('click', function () { 220 var who = handleOf(uri, w.other_handle); 221 var msg = (T.release_confirm || 'Release {who}?').replace('{who}', who); 222 if (window.confirm(msg)) remove(uri, rel); 223 }); 224 actRow.appendChild(rel); 225 act.appendChild(actRow); 226 panel.appendChild(act); 227 return panel; 228 } 229 122 230 function renderWards() { 123 231 var list = document.getElementById('wards-list'); … … 125 233 var wards = S.wards || []; 126 234 wards.forEach(function (w) { 127 var card = el('div', 'g-card'); 235 var uri = w.other_uri; 236 var card = el('div', 'g-card ward'); 128 237 var row = el('div', 'row'); 129 row.appendChild(el('span', 'who grow', handleOf(w.other_uri, w.other_handle))); 238 row.appendChild(el('span', 'who grow', handleOf(uri, w.other_handle))); 239 // Counts on the row: whatever is waiting must be visible with the panel shut. 240 var nHelp = (S.help || []).filter(function (h) { return h.actor_uri === uri; }).length; 241 var nFollow = FOLLOWS.filter(function (f) { return f.wardUri === uri; }).length; 242 if (nHelp) row.appendChild(el('span', 'tag help', '🛟 ' + nHelp)); 243 if (nFollow) row.appendChild(el('span', 'tag co', nFollow + ' ' + (nFollow === 1 ? (T.badge_follow_one || '') : (T.badge_follow || '')))); 130 244 row.appendChild(el('span', 'tag ok', T.active)); 131 var wave = el('button', 'small', T.wave || '👋 Wave'); 132 wave.addEventListener('click', function () { sendWave(w.other_uri, wave); }); 133 row.appendChild(wave); 134 // Gated feature: external (non-fediverse) embeds. Off by default for a 135 // ward; only a guardian can open it, and the gate is enforced server-side 136 // when the feed is built, so this button is the only thing that moves it. 137 // Shown for EVERY ward, including one on another server. There the value 138 // is unknown (it lives on the ward's server), but proposing is exactly as 139 // possible: the proposal travels, the ward's server tallies the guardians 140 // and enforces. A guardian next door must not have more say than one far 141 // away. 142 var known = w.embeds === true || w.embeds === false; 143 var emb = el('button', 'quiet small', 144 (known ? (w.embeds ? T.embeds_on : T.embeds_off) : T.embeds_propose) || 'Link previews'); 145 emb.addEventListener('click', function () { 146 emb.disabled = true; 147 fetch('/guardian/wards/embeds', { 148 method: 'POST', headers: { 'Content-Type': 'application/json' }, 149 body: JSON.stringify({ uri: w.other_uri, allow: known ? !w.embeds : true }), 150 }).then(function (r) { return r.json(); }) 151 .then(function (j) { 152 // Not settled yet: the other guardians still have to answer. 153 if (j && j.state === 'open') { 154 emb.textContent = (T.embeds_waiting || 'waiting for the other guardians'); 155 emb.disabled = true; 156 return; 157 } 158 refresh(); 159 }) 160 .catch(function () { emb.disabled = false; }); 245 var toggle = el('button', 'quiet small', openPanels[uri] ? T.panel_close : T.panel_open); 246 row.appendChild(toggle); 247 card.appendChild(row); 248 var panel = wardPanel(w); 249 card.appendChild(panel); 250 toggle.addEventListener('click', function () { 251 openPanels[uri] = !openPanels[uri]; 252 panel.hidden = !openPanels[uri]; 253 toggle.textContent = openPanels[uri] ? T.panel_close : T.panel_open; 161 254 }); 162 row.appendChild(emb);163 var btn = el('button', 'quiet small', T.release);164 // Releasing a ward is heavy and hard to undo (coming back needs a fresh165 // offer the ward accepts), so it asks first and spells out what changes.166 btn.addEventListener('click', function () {167 var who = handleOf(w.other_uri, w.other_handle);168 var msg = (T.release_confirm || 'Release {who}?').replace('{who}', who);169 if (window.confirm(msg)) remove(w.other_uri, btn);170 });171 row.appendChild(btn);172 card.appendChild(row);173 255 list.appendChild(card); 174 256 }); … … 197 279 198 280 // ── 0. Wards' corner: read-only feed of your wards' posts ─────────────── 199 function renderFeed(items) { 200 var list = document.getElementById('feed-list'); 201 list.textContent = ''; 202 (items || []).forEach(function (p) { 203 var card = el('div', 'g-card feed'); 204 var head = el('div', 'row'); 205 head.appendChild(el('span', 'who grow', p.author)); 206 if (p.published) head.appendChild(el('span', 'g-when', when(p, p.published))); 207 card.appendChild(head); 208 var body = el('div', 'feed-body'); 209 if (p.cw) { 210 var d = document.createElement('details'); 211 var sum = document.createElement('summary'); sum.textContent = p.cw; d.appendChild(sum); 212 var inner = el('div'); inner.innerHTML = p.content || ''; d.appendChild(inner); 213 body.appendChild(d); 214 } else { 215 body.innerHTML = p.content || ''; // server-sanitized HTML (same as Berichten) 216 } 217 card.appendChild(body); 218 list.appendChild(card); 219 }); 220 show('feed-section', (items || []).length > 0); 281 // Lives inside each child's panel now, so the fetches only fill a cache and 282 // ask the ward list to redraw. A guardian watches, it does not publish. 283 function feedCard(p) { 284 var card = el('div', 'g-card feed'); 285 var head = el('div', 'row'); 286 head.appendChild(el('span', 'who grow', p.author)); 287 if (p.published) head.appendChild(el('span', 'g-when', when(p, p.published))); 288 card.appendChild(head); 289 var body = el('div', 'feed-body'); 290 if (p.cw) { 291 var d = document.createElement('details'); 292 var sum = document.createElement('summary'); sum.textContent = p.cw; d.appendChild(sum); 293 var inner = el('div'); inner.innerHTML = p.content || ''; d.appendChild(inner); 294 body.appendChild(d); 295 } else { 296 body.innerHTML = p.content || ''; // server-sanitized HTML (same as Berichten) 297 } 298 card.appendChild(body); 299 return card; 221 300 } 222 301 … … 224 303 return fetch('/guardian/api/feed?site=' + encodeURIComponent(S.site)) 225 304 .then(function (r) { return r.json(); }) 226 .then(function (f) { if (f && !f.error) renderFeed(f.items);})227 .catch(function () { /* corner just stays hidden*/ });305 .then(function (f) { if (f && !f.error) { FEED = f.items || []; renderWards(); } }) 306 .catch(function () { /* panels just show "nothing yet" */ }); 228 307 } 229 308 … … 236 315 }).then(loadFollowReqs); 237 316 } 238 function renderFollowReqs(items) { 239 var list = document.getElementById('follow-list'); 240 list.textContent = ''; 241 (items || []).forEach(function (f) { 242 var card = el('div', 'g-card'); 243 var row = el('div', 'row'); 244 row.appendChild(el('span', 'who grow', f.follower + ' → ' + f.ward)); 245 var ok = el('button', 'small', T.accept || 'Accept'); 246 ok.addEventListener('click', function () { answerFollow(f.id, 'approve', ok); }); 247 var no = el('button', 'quiet small', T.reject || 'Deny'); 248 no.addEventListener('click', function () { answerFollow(f.id, 'reject', no); }); 249 row.appendChild(ok); row.appendChild(no); 250 card.appendChild(row); 251 list.appendChild(card); 252 }); 253 show('follow-section', (items || []).length > 0); 317 function followCard(f) { 318 var card = el('div', 'g-card'); 319 var row = el('div', 'row'); 320 // Inside the child's own panel the ward name is a given, so only the 321 // person asking is named here. 322 row.appendChild(el('span', 'who grow', f.follower)); 323 var ok = el('button', 'small', T.accept || 'Accept'); 324 ok.addEventListener('click', function () { answerFollow(f.id, 'approve', ok); }); 325 var no = el('button', 'quiet small', T.reject || 'Deny'); 326 no.addEventListener('click', function () { answerFollow(f.id, 'reject', no); }); 327 row.appendChild(ok); row.appendChild(no); 328 card.appendChild(row); 329 return card; 254 330 } 255 331 function loadFollowReqs() { 256 332 return fetch('/guardian/api/follow-requests?site=' + encodeURIComponent(S.site)) 257 333 .then(function (r) { return r.json(); }) 258 .then(function (f) { if (f && !f.error) renderFollowReqs(f.items);})259 .catch(function () { /* stays hidden*/ });334 .then(function (f) { if (f && !f.error) { FOLLOWS = f.items || []; renderWards(); } }) 335 .catch(function () { /* panels just show "none waiting" */ }); 260 336 } 261 337 -
src/routes/guardian.js
ra7bcf66 r70677e96 41 41 'pending', 'active', 'retract', 'release', 'release_confirm', 'open', 'push_unavailable', 42 42 'embeds_on', 'embeds_off', 'embeds_propose', 'embeds_waiting', 43 'accept', 'reject', 'complete', 'awaiting_others', 'coguard']; 43 'accept', 'reject', 'complete', 'awaiting_others', 'coguard', 44 // The per-ward panel: everything about one child in one place. 45 'settings_title', 'panel_open', 'panel_close', 'panel_help', 'panel_help_empty', 46 'panel_follow', 'panel_follow_empty', 'panel_posts', 'panel_posts_empty', 47 'panel_actions', 'badge_help', 'badge_follow', 'badge_follow_one', 'help_empty']; 44 48 const s = Object.fromEntries(keys.map((k) => [k, i18nT(L, `guardian.${k}`)])); 45 49 s.wave = i18nT(L, 'guardian.wave'); … … 139 143 id: p.id, 140 144 author: p.author_handle || p.author_name || p.author_uri, 145 authorUri: p.author_uri, // the grouping key: which child's panel this belongs in 141 146 authorName: p.author_name, 142 147 authorIcon: p.author_icon, … … 157 162 const base = (process.env.PUBLIC_BASE_URL || '').replace(/\/+$/, ''); 158 163 return Guardianship.listWards(site.slug) 159 .map((w) => (w.other_uri.startsWith(base) ? w.other_uri.split('/').pop(): null))164 .map((w) => (w.other_uri.startsWith(base) ? { slug: w.other_uri.split('/').pop(), uri: w.other_uri } : null)) 160 165 .filter(Boolean); 161 166 } … … 166 171 const items = []; 167 172 const host = (() => { try { return new URL(process.env.PUBLIC_BASE_URL || '').host; } catch { return ''; } })(); 173 // wardUri is the grouping key for the per-ward panel: the handle is for 174 // reading, the URI is what identifies the child across both cases below. 168 175 // Local wards (guardian co-located): read the pending follows directly. 169 for (const w ardSlugof wardSlugsOf(site)) {170 for (const f of Guardianship.follows.listForWard(w ardSlug)) {171 items.push({ id: f.id, ward: `@${w ardSlug}@${host}`, follower: f.follower_handle || f.follower_name || f.follower_uri, followerIcon: f.follower_icon, remote: false, created: f.created_at });176 for (const w of wardSlugsOf(site)) { 177 for (const f of Guardianship.follows.listForWard(w.slug)) { 178 items.push({ id: f.id, ward: `@${w.slug}@${host}`, wardUri: w.uri, follower: f.follower_handle || f.follower_name || f.follower_uri, followerIcon: f.follower_icon, remote: false, created: f.created_at }); 172 179 } 173 180 } … … 175 182 for (const rev of Guardianship.follows.listReviews(site.slug)) { 176 183 const wardName = (() => { try { const u = new URL(rev.ward_uri); return `@${u.pathname.split('/').pop()}@${u.host}`; } catch { return rev.ward_uri; } })(); 177 items.push({ id: rev.id, ward: wardName, follower: rev.follower_handle || rev.follower_uri, followerIcon: rev.follower_icon, remote: true, created: rev.created_at });184 items.push({ id: rev.id, ward: wardName, wardUri: rev.ward_uri, follower: rev.follower_handle || rev.follower_uri, followerIcon: rev.follower_icon, remote: true, created: rev.created_at }); 178 185 } 179 186 res.json({ items }); -
src/services/i18n.js
ra7bcf66 r70677e96 67 67 'admin.b_paid': 'Betaalde posts', 'admin.b_push': 'Notificaties', 'admin.back': 'Terug naar Beheer', 68 68 'push.t': 'Notificaties', 'push.intro': 'Krijg een melding op dit apparaat bij nieuwe volgers, reacties en berichten, ook als de site niet open staat. Versleuteld tot in je browser; wij sturen zo min mogelijk inhoud mee.', 'push.unavailable': 'Push is op deze server niet beschikbaar (sleutel kon niet worden aangemaakt of de dependency ontbreekt).', 'push.unsupported': 'Deze browser ondersteunt geen push-notificaties.', 'push.ios_hint': 'Op iPhone/iPad werkt dit alleen als de site op je beginscherm staat: deel-knop, dan "Zet op beginscherm", en open de site daarna vanaf daar.', 'push.this_device': 'Dit apparaat:', 'push.checking': 'controleren…', 'push.state_on': 'meldingen staan aan', 'push.state_off': 'meldingen staan uit', 'push.state_denied': 'geblokkeerd in de browserinstellingen', 'push.state_unknown': 'status onbekend', 'push.state_unsupported': 'niet ondersteund', 'push.enable': 'Zet aan op dit apparaat', 'push.disable': 'Zet uit', 'push.test': 'Stuur testmelding', 'push.what': 'Waarvoor wil je een melding?', 'push.a_follow': 'Nieuwe volger', 'push.a_reply': 'Reactie of vermelding', 'push.a_like': 'Waardering (ster)', 'push.a_boost': 'Boost', 'push.a_dm': 'Privébericht', 'push.saved': 'Opgeslagen.', 'push.devices': 'Gekoppelde apparaten', 'push.device': 'Apparaat', 'push.since': 'sinds', 'push.remove': 'Verwijder', 'push.enable_failed': 'aanzetten mislukt', 'push.on_short': 'Word supporter', 69 'push.n_follow_t': 'Nieuwe volger', 'push.n_follow_b': '{who} volgt je nu', 'push.n_reply_t': 'Reactie op "{title}"', 'push.n_mention_t': 'Vermelding', 'push.n_dm_t': 'Privébericht', 'push.n_dm_b': 'Nieuw bericht van {who}', 'push.n_like_t': 'Nieuwe waardering', 'push.n_like_b': '{who} waardeerde "{title}"', 'push.n_boost_t': 'Geboost', 'push.n_boost_b': '{who} boostte "{title}"', 'msg.guard_offer': 'wil je guardian worden. Bespreek dit met je ouders of verzorgers voordat je beslist.', 'msg.guard_accept': 'Accepteer', 'msg.guard_reject': 'Weiger', 'msg.guard_accepted': 'Guardian geaccepteerd. Jullie zijn nu verbonden.', 'msg.guard_rejected': 'Aanvraag geweigerd.', 'msg.guard_failed': 'Dat lukte niet; probeer het opnieuw.', 'msg.guardians_label': 'Jouw guardians', 'msg.waved_at_you': 'zwaaide naar je', 'msg.help_request': 'vroeg om hulp', 'msg.wave_r1': 'Wat leuk!', 'msg.wave_r2': 'Bel me even', 'msg.wave_back': '👋 Terug', 'msg.wave_sent': 'Zwaai verstuurd.', 'guardian.feed_title': 'Van je wards', 'guardian.feed_sub': 'Meelezen met wat je wards plaatsen. Alleen kijken.', 'guardian.follow_title': 'Volgverzoeken', 'guardian.follow_sub': 'Iemand wil een van je wards volgen. Jij beslist.', 'guardian.wave': '👋 Zwaai', 'guardian.waved': '👋 verstuurd', 'guardian.app_name': 'Klonkt Guardian', 'guardian.tagline': 'Wards beheren en hulpverzoeken opvangen.', 'guardian.acting_as': 'Je handelt als', 'guardian.help_title': 'Hulpverzoeken', 'guardian.help_sub': 'Als een ward de reddingsboei gebruikt, verschijnt het hier.', 'guardian.help_empty': 'Geen hulpverzoeken. Mooi zo.', 'guardian.adopt_title': 'Ward adopteren', 'guardian.adopt_sub': 'Vul de handle van het kind in (@kind@server.eu). Ze krijgen een aanvraag in hun Klonkt die ze accepteren.', 'guardian.adopt_label': 'Handle van de ward', 'guardian.adopt_btn': 'Verstuur aanvraag', 'guardian.pending_title': 'Verzonden aanvragen', 'guardian.pending_sub': 'Wacht tot de ward accepteert.', 'guardian.wards_title': 'Mijn wards', 'guardian. wards_empty': 'Nog geen wards. Adopteer er hierboven een.', 'guardian.push_title': 'Meldingen', 'guardian.push_sub': 'Ontvang een melding bij een hulpverzoek of voogdij-antwoord, ook als de app dicht is.', 'guardian.push_on': 'Zet meldingen aan', 'guardian.push_off': 'Meldingen staan aan; tik om uit te zetten', 'guardian.sent': 'Aanvraag verstuurd. Zie hieronder bij Verzonden aanvragen.', 'guardian.sent_retry': 'Aanvraag opgeslagen; we blijven proberen te bezorgen.', 'guardian.sending': 'Versturen…', 'guardian.not_found': 'Die handle konden we niet vinden.', 'guardian.failed': 'Mislukt', 'guardian.network': 'Netwerkfout.', 'guardian.pending': 'wacht op antwoord', 'guardian.active': 'actief', 'guardian.retract': 'Intrekken', 'guardian.release': 'Loslaten', 'guardian.embeds_on': 'Linkvoorbeelden: aan', 'guardian.embeds_off': 'Linkvoorbeelden: uit', 'guardian.embeds_propose': 'Linkvoorbeelden voorstellen', 'guardian.embeds_waiting': 'wacht op de andere guardians', 'guardian.release_confirm': '{who} loslaten?\n\nJe stopt dan als guardian. Je ziet hun berichten niet meer, je krijgt geen hulpverzoeken meer van ze, en je kunt volgverzoeken niet meer voor ze beoordelen.\n\nTerugkomen kan alleen met een nieuwe aanvraag die zij accepteren.', 'guardian.open': 'open', 'guardian.accept': 'Accepteer', 'guardian.reject': 'Weiger', 'guardian.complete': 'Voltooien', 'guardian.awaiting_others': 'wacht op de andere partijen', 'guardian.coguard': 'mede-voogdij-aanvraag', 'guardian.push_unavailable': 'Push niet beschikbaar', 'push.n_help_t': 'Hulpvraag', 'push.n_help_b': '{who} vraagt om je hulp', 'push.n_guard_offer_t': 'Voogdij-aanvraag', 'push.n_guard_offer_b': '{who} wil je guardian worden', 'push.n_guard_ward_t': 'Ward geaccepteerd', 'push.n_guard_ward_b': '{who} accepteerde je als guardian', 'push.n_guard_cog_t': 'Mede-voogdij gevraagd', 'push.n_guard_cog_b': 'Er is een guardian-aanvraag voor {who}', 'push.n_test_t': 'Klonkt-testnotificatie', 'push.n_test_b': 'Werkt. Zo komen meldingen binnen op dit apparaat.',69 'push.n_follow_t': 'Nieuwe volger', 'push.n_follow_b': '{who} volgt je nu', 'push.n_reply_t': 'Reactie op "{title}"', 'push.n_mention_t': 'Vermelding', 'push.n_dm_t': 'Privébericht', 'push.n_dm_b': 'Nieuw bericht van {who}', 'push.n_like_t': 'Nieuwe waardering', 'push.n_like_b': '{who} waardeerde "{title}"', 'push.n_boost_t': 'Geboost', 'push.n_boost_b': '{who} boostte "{title}"', 'msg.guard_offer': 'wil je guardian worden. Bespreek dit met je ouders of verzorgers voordat je beslist.', 'msg.guard_accept': 'Accepteer', 'msg.guard_reject': 'Weiger', 'msg.guard_accepted': 'Guardian geaccepteerd. Jullie zijn nu verbonden.', 'msg.guard_rejected': 'Aanvraag geweigerd.', 'msg.guard_failed': 'Dat lukte niet; probeer het opnieuw.', 'msg.guardians_label': 'Jouw guardians', 'msg.waved_at_you': 'zwaaide naar je', 'msg.help_request': 'vroeg om hulp', 'msg.wave_r1': 'Wat leuk!', 'msg.wave_r2': 'Bel me even', 'msg.wave_back': '👋 Terug', 'msg.wave_sent': 'Zwaai verstuurd.', 'guardian.feed_title': 'Van je wards', 'guardian.feed_sub': 'Meelezen met wat je wards plaatsen. Alleen kijken.', 'guardian.follow_title': 'Volgverzoeken', 'guardian.follow_sub': 'Iemand wil een van je wards volgen. Jij beslist.', 'guardian.wave': '👋 Zwaai', 'guardian.waved': '👋 verstuurd', 'guardian.app_name': 'Klonkt Guardian', 'guardian.tagline': 'Wards beheren en hulpverzoeken opvangen.', 'guardian.acting_as': 'Je handelt als', 'guardian.help_title': 'Hulpverzoeken', 'guardian.help_sub': 'Als een ward de reddingsboei gebruikt, verschijnt het hier.', 'guardian.help_empty': 'Geen hulpverzoeken. Mooi zo.', 'guardian.adopt_title': 'Ward adopteren', 'guardian.adopt_sub': 'Vul de handle van het kind in (@kind@server.eu). Ze krijgen een aanvraag in hun Klonkt die ze accepteren.', 'guardian.adopt_label': 'Handle van de ward', 'guardian.adopt_btn': 'Verstuur aanvraag', 'guardian.pending_title': 'Verzonden aanvragen', 'guardian.pending_sub': 'Wacht tot de ward accepteert.', 'guardian.wards_title': 'Mijn wards', 'guardian.settings_title': 'Instellingen', 'guardian.panel_open': 'Bekijken', 'guardian.panel_close': 'Sluiten', 'guardian.panel_help': 'Hulpvragen van dit kind', 'guardian.panel_help_empty': 'Nog geen hulpvragen.', 'guardian.panel_follow': 'Volgverzoeken', 'guardian.panel_follow_empty': 'Geen openstaande volgverzoeken.', 'guardian.panel_posts': 'Recente berichten', 'guardian.panel_posts_empty': 'Nog niets te zien.', 'guardian.panel_actions': 'Acties', 'guardian.badge_help': 'hulpvragen', 'guardian.badge_follow': 'volgverzoeken', 'guardian.badge_follow_one': 'volgverzoek', 'guardian.wards_empty': 'Nog geen wards. Adopteer er hierboven een.', 'guardian.push_title': 'Meldingen', 'guardian.push_sub': 'Ontvang een melding bij een hulpverzoek of voogdij-antwoord, ook als de app dicht is.', 'guardian.push_on': 'Zet meldingen aan', 'guardian.push_off': 'Meldingen staan aan; tik om uit te zetten', 'guardian.sent': 'Aanvraag verstuurd. Zie hieronder bij Verzonden aanvragen.', 'guardian.sent_retry': 'Aanvraag opgeslagen; we blijven proberen te bezorgen.', 'guardian.sending': 'Versturen…', 'guardian.not_found': 'Die handle konden we niet vinden.', 'guardian.failed': 'Mislukt', 'guardian.network': 'Netwerkfout.', 'guardian.pending': 'wacht op antwoord', 'guardian.active': 'actief', 'guardian.retract': 'Intrekken', 'guardian.release': 'Loslaten', 'guardian.embeds_on': 'Linkvoorbeelden: aan', 'guardian.embeds_off': 'Linkvoorbeelden: uit', 'guardian.embeds_propose': 'Linkvoorbeelden voorstellen', 'guardian.embeds_waiting': 'wacht op de andere guardians', 'guardian.release_confirm': '{who} loslaten?\n\nJe stopt dan als guardian. Je ziet hun berichten niet meer, je krijgt geen hulpverzoeken meer van ze, en je kunt volgverzoeken niet meer voor ze beoordelen.\n\nTerugkomen kan alleen met een nieuwe aanvraag die zij accepteren.', 'guardian.open': 'open', 'guardian.accept': 'Accepteer', 'guardian.reject': 'Weiger', 'guardian.complete': 'Voltooien', 'guardian.awaiting_others': 'wacht op de andere partijen', 'guardian.coguard': 'mede-voogdij-aanvraag', 'guardian.push_unavailable': 'Push niet beschikbaar', 'push.n_help_t': 'Hulpvraag', 'push.n_help_b': '{who} vraagt om je hulp', 'push.n_guard_offer_t': 'Voogdij-aanvraag', 'push.n_guard_offer_b': '{who} wil je guardian worden', 'push.n_guard_ward_t': 'Ward geaccepteerd', 'push.n_guard_ward_b': '{who} accepteerde je als guardian', 'push.n_guard_cog_t': 'Mede-voogdij gevraagd', 'push.n_guard_cog_b': 'Er is een guardian-aanvraag voor {who}', 'push.n_test_t': 'Klonkt-testnotificatie', 'push.n_test_b': 'Werkt. Zo komen meldingen binnen op dit apparaat.', 70 70 'apaid.t': 'Betaalde posts', 'apaid.intro': 'Koppel je eigen Patreon-campagne. Supporters ontgrendelen betaalde posts met een passkey, zonder account en zonder cookie. Wij bewaren geen namen of e-mailadressen van supporters, alleen het versleutelde token van jouw campagne.', 'apaid.saved': 'Opgeslagen.', 'apaid.nokey': 'Let op: de encryptiesleutel kon niet worden aangemaakt of gelezen (schrijfrechten op de opslagmap?). Zonder sleutel kunnen secrets niet veilig worden opgeslagen.', 'apaid.status': 'Status:', 'apaid.connected': 'verbonden', 'apaid.campaign': 'campagne', 'apaid.configured': 'ingesteld, nog niet verbonden (vul een token in)', 'apaid.notyet': 'nog niet ingesteld', 'apaid.redirect_h': 'Zet deze redirect-URI in je Patreon-client', 'apaid.redirect_p': 'Bij je Patreon API-client, onder Redirect URIs, moet exact deze regel staan. Klopt hij niet, dan geeft Patreon een foutmelding in plaats van je supporters terug te sturen.', 'apaid.copy': 'Kopieer', 'apaid.copied': 'Gekopieerd', 'apaid.client_id': 'Patreon client id', 'apaid.client_secret': 'Patreon client secret', 'apaid.keep': 'Leeg laten = huidige waarde behouden.', 'apaid.campaign_id': 'Campagne-id', 'apaid.public_page': 'Openbare Patreon-pagina', 'apaid.public_help': 'De link waar bezoekers supporter kunnen worden. Getoond als "Word supporter" wanneer iemand nog niet doneert.', 'apaid.access': 'Creator access token', 'apaid.refresh': 'Creator refresh token', 'apaid.token_help': 'De access + refresh token krijg je op je Patreon API-clientpagina. Wij versleutelen ze en verversen automatisch.', 'apaid.min_eur': 'Standaard-steunbedrag voor een betaalde post (euro)', 'apaid.save': 'Opslaan', 'apaid.disconnect': 'Koppeling verwijderen', 'apaid.disconnect_confirm': 'Patreon-koppeling verwijderen?', 'apaid.unchanged': 'blijft ongewijzigd', 71 71 'pgate.h': 'Voor supporters', 'pgate.sub': 'Deze post is voor supporters van deze site. Word supporter en ontgrendel hem daarna met een passkey. Geen account op deze site, geen cookie.', 'pgate.sub_cents': 'Deze post is voor supporters van deze site (vanaf €{eur} per maand op Patreon). Word supporter en ontgrendel hem daarna met een passkey. Geen account op deze site, geen cookie.', 'pgate.join': 'Word supporter op Patreon', 'pgate.unlock_have': 'Al supporter? Ontgrendelen', 'pgate.unlock': 'Ontgrendelen met Patreon', 'pgate.join_short': 'Word supporter', 'pgate.confirm': 'Bevestig met je passkey…', 'pgate.failed': 'Ontgrendelen mislukt. Probeer opnieuw.', 'pgate.error': 'Er ging iets mis. Probeer opnieuw.', … … 1008 1008 'admin.b_paid': 'Paid posts', 'admin.b_push': 'Notifications', 'admin.back': 'Back to Admin', 1009 1009 'push.t': 'Notifications', 'push.intro': 'Get a notification on this device for new followers, replies and messages, even when the site is closed. Encrypted all the way to your browser; we send as little content as possible.', 'push.unavailable': 'Push is unavailable on this server (the key could not be created or the dependency is missing).', 'push.unsupported': 'This browser does not support push notifications.', 'push.ios_hint': 'On iPhone/iPad this only works when the site is on your home screen: share button, then "Add to Home Screen", and open it from there.', 'push.this_device': 'This device:', 'push.checking': 'checking…', 'push.state_on': 'notifications are on', 'push.state_off': 'notifications are off', 'push.state_denied': 'blocked in the browser settings', 'push.state_unknown': 'status unknown', 'push.state_unsupported': 'not supported', 'push.enable': 'Turn on for this device', 'push.disable': 'Turn off', 'push.test': 'Send a test notification', 'push.what': 'What do you want to be notified about?', 'push.a_follow': 'New follower', 'push.a_reply': 'Reply or mention', 'push.a_like': 'Like (star)', 'push.a_boost': 'Boost', 'push.a_dm': 'Private message', 'push.saved': 'Saved.', 'push.devices': 'Linked devices', 'push.device': 'Device', 'push.since': 'since', 'push.remove': 'Remove', 'push.enable_failed': 'turning on failed', 1010 'push.n_follow_t': 'New follower', 'push.n_follow_b': '{who} now follows you', 'push.n_reply_t': 'Reply to "{title}"', 'push.n_mention_t': 'Mention', 'push.n_dm_t': 'Private message', 'push.n_dm_b': 'New message from {who}', 'push.n_like_t': 'New like', 'push.n_like_b': '{who} liked "{title}"', 'push.n_boost_t': 'Boosted', 'push.n_boost_b': '{who} boosted "{title}"', 'msg.guard_offer': 'wants to become your guardian. Talk this over with your parents or carers before you decide.', 'msg.guard_accept': 'Accept', 'msg.guard_reject': 'Reject', 'msg.guard_accepted': 'Guardian accepted. You are now connected.', 'msg.guard_rejected': 'Offer rejected.', 'msg.guard_failed': 'That did not work; try again.', 'msg.guardians_label': 'Your guardians', 'msg.waved_at_you': 'waved at you', 'msg.help_request': 'asked for help', 'msg.wave_r1': 'Lovely!', 'msg.wave_r2': 'Call me', 'msg.wave_back': '👋 Back', 'msg.wave_sent': 'Wave sent.', 'guardian.feed_title': 'Your wards', 'guardian.feed_sub': 'Read along with what your wards post. Watch only.', 'guardian.follow_title': 'Follow requests', 'guardian.follow_sub': 'Someone wants to follow one of your wards. You decide.', 'guardian.wave': '👋 Wave', 'guardian.waved': '👋 sent', 'guardian.app_name': 'Klonkt Guardian', 'guardian.tagline': 'Manage wards and catch calls for help.', 'guardian.acting_as': 'You act as', 'guardian.help_title': 'Help requests', 'guardian.help_sub': 'When a ward uses the help buoy, it shows up here.', 'guardian.help_empty': 'No help requests. Good.', 'guardian.adopt_title': 'Adopt a ward', 'guardian.adopt_sub': 'Enter the child handle (@kid@server.eu). They get an offer in their Klonkt to accept.', 'guardian.adopt_label': 'Ward handle', 'guardian.adopt_btn': 'Send offer', 'guardian.pending_title': 'Sent offers', 'guardian.pending_sub': 'Waiting for the ward to accept.', 'guardian.wards_title': 'My wards', 'guardian. wards_empty': 'No wards yet. Adopt one above.', 'guardian.push_title': 'Notifications', 'guardian.push_sub': 'Get notified on a call for help or a guardianship answer, even with the app closed.', 'guardian.push_on': 'Turn on notifications', 'guardian.push_off': 'Notifications are on; tap to turn off', 'guardian.sent': 'Offer sent. See it below under Sent offers.', 'guardian.sent_retry': 'Offer saved; we keep trying to deliver it.', 'guardian.sending': 'Sending…', 'guardian.not_found': 'We could not find that handle.', 'guardian.failed': 'Failed', 'guardian.network': 'Network error.', 'guardian.pending': 'awaiting answer', 'guardian.active': 'active', 'guardian.retract': 'Retract', 'guardian.release': 'Release', 'guardian.embeds_on': 'Link previews: on', 'guardian.embeds_off': 'Link previews: off', 'guardian.embeds_propose': 'Propose link previews', 'guardian.embeds_waiting': 'waiting for the other guardians', 'guardian.release_confirm': 'Release {who}?\n\nYou stop being their guardian. You will no longer see their posts, no longer receive their calls for help, and no longer decide on follow requests for them.\n\nComing back means a fresh offer that they accept.', 'guardian.open': 'open', 'guardian.accept': 'Accept', 'guardian.reject': 'Reject', 'guardian.complete': 'Complete', 'guardian.awaiting_others': 'awaiting the other parties', 'guardian.coguard': 'co-guardianship offer', 'guardian.push_unavailable': 'Push unavailable', 'push.n_help_t': 'Call for help', 'push.n_help_b': '{who} is asking for your help', 'push.n_guard_offer_t': 'Guardianship offer', 'push.n_guard_offer_b': '{who} wants you as their guardian', 'push.n_guard_ward_t': 'Ward accepted', 'push.n_guard_ward_b': '{who} accepted you as guardian', 'push.n_guard_cog_t': 'Co-guardianship asked', 'push.n_guard_cog_b': 'A guardian offer for {who} needs you', 'push.n_test_t': 'Klonkt test notification', 'push.n_test_b': 'It works. This is how notifications arrive on this device.',1010 'push.n_follow_t': 'New follower', 'push.n_follow_b': '{who} now follows you', 'push.n_reply_t': 'Reply to "{title}"', 'push.n_mention_t': 'Mention', 'push.n_dm_t': 'Private message', 'push.n_dm_b': 'New message from {who}', 'push.n_like_t': 'New like', 'push.n_like_b': '{who} liked "{title}"', 'push.n_boost_t': 'Boosted', 'push.n_boost_b': '{who} boosted "{title}"', 'msg.guard_offer': 'wants to become your guardian. Talk this over with your parents or carers before you decide.', 'msg.guard_accept': 'Accept', 'msg.guard_reject': 'Reject', 'msg.guard_accepted': 'Guardian accepted. You are now connected.', 'msg.guard_rejected': 'Offer rejected.', 'msg.guard_failed': 'That did not work; try again.', 'msg.guardians_label': 'Your guardians', 'msg.waved_at_you': 'waved at you', 'msg.help_request': 'asked for help', 'msg.wave_r1': 'Lovely!', 'msg.wave_r2': 'Call me', 'msg.wave_back': '👋 Back', 'msg.wave_sent': 'Wave sent.', 'guardian.feed_title': 'Your wards', 'guardian.feed_sub': 'Read along with what your wards post. Watch only.', 'guardian.follow_title': 'Follow requests', 'guardian.follow_sub': 'Someone wants to follow one of your wards. You decide.', 'guardian.wave': '👋 Wave', 'guardian.waved': '👋 sent', 'guardian.app_name': 'Klonkt Guardian', 'guardian.tagline': 'Manage wards and catch calls for help.', 'guardian.acting_as': 'You act as', 'guardian.help_title': 'Help requests', 'guardian.help_sub': 'When a ward uses the help buoy, it shows up here.', 'guardian.help_empty': 'No help requests. Good.', 'guardian.adopt_title': 'Adopt a ward', 'guardian.adopt_sub': 'Enter the child handle (@kid@server.eu). They get an offer in their Klonkt to accept.', 'guardian.adopt_label': 'Ward handle', 'guardian.adopt_btn': 'Send offer', 'guardian.pending_title': 'Sent offers', 'guardian.pending_sub': 'Waiting for the ward to accept.', 'guardian.wards_title': 'My wards', 'guardian.settings_title': 'Settings', 'guardian.panel_open': 'Open', 'guardian.panel_close': 'Close', 'guardian.panel_help': 'Calls for help from this child', 'guardian.panel_help_empty': 'No calls for help yet.', 'guardian.panel_follow': 'Follow requests', 'guardian.panel_follow_empty': 'No follow requests waiting.', 'guardian.panel_posts': 'Recent posts', 'guardian.panel_posts_empty': 'Nothing here yet.', 'guardian.panel_actions': 'Actions', 'guardian.badge_help': 'calls for help', 'guardian.badge_follow': 'follow requests', 'guardian.badge_follow_one': 'follow request', 'guardian.wards_empty': 'No wards yet. Adopt one above.', 'guardian.push_title': 'Notifications', 'guardian.push_sub': 'Get notified on a call for help or a guardianship answer, even with the app closed.', 'guardian.push_on': 'Turn on notifications', 'guardian.push_off': 'Notifications are on; tap to turn off', 'guardian.sent': 'Offer sent. See it below under Sent offers.', 'guardian.sent_retry': 'Offer saved; we keep trying to deliver it.', 'guardian.sending': 'Sending…', 'guardian.not_found': 'We could not find that handle.', 'guardian.failed': 'Failed', 'guardian.network': 'Network error.', 'guardian.pending': 'awaiting answer', 'guardian.active': 'active', 'guardian.retract': 'Retract', 'guardian.release': 'Release', 'guardian.embeds_on': 'Link previews: on', 'guardian.embeds_off': 'Link previews: off', 'guardian.embeds_propose': 'Propose link previews', 'guardian.embeds_waiting': 'waiting for the other guardians', 'guardian.release_confirm': 'Release {who}?\n\nYou stop being their guardian. You will no longer see their posts, no longer receive their calls for help, and no longer decide on follow requests for them.\n\nComing back means a fresh offer that they accept.', 'guardian.open': 'open', 'guardian.accept': 'Accept', 'guardian.reject': 'Reject', 'guardian.complete': 'Complete', 'guardian.awaiting_others': 'awaiting the other parties', 'guardian.coguard': 'co-guardianship offer', 'guardian.push_unavailable': 'Push unavailable', 'push.n_help_t': 'Call for help', 'push.n_help_b': '{who} is asking for your help', 'push.n_guard_offer_t': 'Guardianship offer', 'push.n_guard_offer_b': '{who} wants you as their guardian', 'push.n_guard_ward_t': 'Ward accepted', 'push.n_guard_ward_b': '{who} accepted you as guardian', 'push.n_guard_cog_t': 'Co-guardianship asked', 'push.n_guard_cog_b': 'A guardian offer for {who} needs you', 'push.n_test_t': 'Klonkt test notification', 'push.n_test_b': 'It works. This is how notifications arrive on this device.', 1011 1011 'apaid.t': 'Paid posts', 'apaid.intro': 'Connect your own Patreon campaign. Supporters unlock paid posts with a passkey, no account and no cookie. We store no supporter names or email addresses, only the encrypted token of your campaign.', 'apaid.saved': 'Saved.', 'apaid.nokey': 'Note: the encryption key could not be created or read (write permissions on the storage directory?). Without a key, secrets cannot be stored safely.', 'apaid.status': 'Status:', 'apaid.connected': 'connected', 'apaid.campaign': 'campaign', 'apaid.configured': 'configured, not connected yet (enter a token)', 'apaid.notyet': 'not configured yet', 'apaid.redirect_h': 'Put this redirect URI in your Patreon client', 'apaid.redirect_p': 'In your Patreon API client, under Redirect URIs, exactly this line must be present. If it does not match, Patreon shows an error instead of sending your supporters back.', 'apaid.copy': 'Copy', 'apaid.copied': 'Copied', 'apaid.client_id': 'Patreon client id', 'apaid.client_secret': 'Patreon client secret', 'apaid.keep': 'Leave empty = keep the current value.', 'apaid.campaign_id': 'Campaign id', 'apaid.public_page': 'Public Patreon page', 'apaid.public_help': 'The link where visitors can become a supporter. Shown as "Become a supporter" when someone does not pledge yet.', 'apaid.access': 'Creator access token', 'apaid.refresh': 'Creator refresh token', 'apaid.token_help': 'You get the access + refresh token on your Patreon API client page. We encrypt them and refresh automatically.', 'apaid.min_eur': 'Default support amount for a paid post (euro)', 'apaid.save': 'Save', 'apaid.disconnect': 'Remove connection', 'apaid.disconnect_confirm': 'Remove the Patreon connection?', 'apaid.unchanged': 'stays unchanged', 1012 1012 'pgate.h': 'For supporters', 'pgate.sub': 'This post is for supporters of this site. Become a supporter and then unlock it with a passkey. No account on this site, no cookie.', 'pgate.sub_cents': 'This post is for supporters of this site (from €{eur} per month on Patreon). Become a supporter and then unlock it with a passkey. No account on this site, no cookie.', 'pgate.join': 'Become a supporter on Patreon', 'pgate.unlock_have': 'Already a supporter? Unlock', 'pgate.unlock': 'Unlock with Patreon', 'pgate.join_short': 'Become a supporter', 'pgate.confirm': 'Confirm with your passkey…', 'pgate.failed': 'Unlocking failed. Try again.', 'pgate.error': 'Something went wrong. Try again.', … … 1943 1943 'admin.b_paid': 'Bezahlte Beiträge', 'admin.b_push': 'Benachrichtigungen', 'admin.back': 'Zurück zur Verwaltung', 1944 1944 'push.t': 'Benachrichtigungen', 'push.intro': 'Erhalte auf diesem Gerät eine Meldung bei neuen Followern, Antworten und Nachrichten, auch wenn die Seite geschlossen ist. Verschlüsselt bis in deinen Browser; wir senden so wenig Inhalt wie möglich mit.', 'push.unavailable': 'Push ist auf diesem Server nicht verfügbar (Schlüssel konnte nicht erstellt werden oder die Abhängigkeit fehlt).', 'push.unsupported': 'Dieser Browser unterstützt keine Push-Benachrichtigungen.', 'push.ios_hint': 'Auf iPhone/iPad funktioniert das nur, wenn die Seite auf deinem Home-Bildschirm liegt: Teilen-Knopf, dann "Zum Home-Bildschirm", und öffne sie danach von dort.', 'push.this_device': 'Dieses Gerät:', 'push.checking': 'prüfen…', 'push.state_on': 'Benachrichtigungen sind an', 'push.state_off': 'Benachrichtigungen sind aus', 'push.state_denied': 'in den Browser-Einstellungen blockiert', 'push.state_unknown': 'Status unbekannt', 'push.state_unsupported': 'nicht unterstützt', 'push.enable': 'Auf diesem Gerät einschalten', 'push.disable': 'Ausschalten', 'push.test': 'Testmeldung senden', 'push.what': 'Wofür möchtest du eine Meldung?', 'push.a_follow': 'Neuer Follower', 'push.a_reply': 'Antwort oder Erwähnung', 'push.a_like': 'Like (Stern)', 'push.a_boost': 'Boost', 'push.a_dm': 'Private Nachricht', 'push.saved': 'Gespeichert.', 'push.devices': 'Verbundene Geräte', 'push.device': 'Gerät', 'push.since': 'seit', 'push.remove': 'Entfernen', 'push.enable_failed': 'Einschalten fehlgeschlagen', 1945 'push.n_follow_t': 'Neuer Follower', 'push.n_follow_b': '{who} folgt dir jetzt', 'push.n_reply_t': 'Antwort auf "{title}"', 'push.n_mention_t': 'Erwähnung', 'push.n_dm_t': 'Private Nachricht', 'push.n_dm_b': 'Neue Nachricht von {who}', 'push.n_like_t': 'Neues Like', 'push.n_like_b': '{who} gefällt "{title}"', 'push.n_boost_t': 'Geboostet', 'push.n_boost_b': '{who} hat "{title}" geboostet', 'msg.guard_offer': 'möchte dein Guardian werden. Besprich das mit deinen Eltern oder Betreuern, bevor du entscheidest.', 'msg.guard_accept': 'Annehmen', 'msg.guard_reject': 'Ablehnen', 'msg.guard_accepted': 'Guardian angenommen. Ihr seid jetzt verbunden.', 'msg.guard_rejected': 'Angebot abgelehnt.', 'msg.guard_failed': 'Das hat nicht geklappt; versuch es erneut.', 'msg.guardians_label': 'Deine Guardians', 'msg.waved_at_you': 'hat dir zugewinkt', 'msg.help_request': 'hat um Hilfe gebeten', 'msg.wave_r1': 'Wie schön!', 'msg.wave_r2': 'Ruf mich an', 'msg.wave_back': '👋 Zurück', 'msg.wave_sent': 'Winken gesendet.', 'guardian.feed_title': 'Deine Wards', 'guardian.feed_sub': 'Lies mit, was deine Wards posten. Nur schauen.', 'guardian.follow_title': 'Follow-Anfragen', 'guardian.follow_sub': 'Jemand möchte einem deiner Wards folgen. Du entscheidest.', 'guardian.wave': '👋 Winken', 'guardian.waved': '👋 gesendet', 'guardian.app_name': 'Klonkt Guardian', 'guardian.tagline': 'Wards verwalten und Hilferufe auffangen.', 'guardian.acting_as': 'Du handelst als', 'guardian.help_title': 'Hilferufe', 'guardian.help_sub': 'Wenn ein Ward die Rettungsboje nutzt, erscheint es hier.', 'guardian.help_empty': 'Keine Hilferufe. Gut so.', 'guardian.adopt_title': 'Ward adoptieren', 'guardian.adopt_sub': 'Gib das Handle des Kindes ein (@kind@server.eu). Es bekommt ein Angebot in seinem Klonkt zum Annehmen.', 'guardian.adopt_label': 'Ward-Handle', 'guardian.adopt_btn': 'Angebot senden', 'guardian.pending_title': 'Gesendete Angebote', 'guardian.pending_sub': 'Warten, bis der Ward annimmt.', 'guardian.wards_title': 'Meine Wards', 'guardian. wards_empty': 'Noch keine Wards. Adoptiere oben eins.', 'guardian.push_title': 'Meldungen', 'guardian.push_sub': 'Erhalte eine Meldung bei einem Hilferuf oder einer Vormundschafts-Antwort, auch bei geschlossener App.', 'guardian.push_on': 'Meldungen einschalten', 'guardian.push_off': 'Meldungen sind an; tippen zum Ausschalten', 'guardian.sent': 'Angebot gesendet. Siehe unten bei Gesendete Angebote.', 'guardian.sent_retry': 'Angebot gespeichert; wir versuchen weiter zuzustellen.', 'guardian.sending': 'Senden…', 'guardian.not_found': 'Dieses Handle konnten wir nicht finden.', 'guardian.failed': 'Fehlgeschlagen', 'guardian.network': 'Netzwerkfehler.', 'guardian.pending': 'wartet auf Antwort', 'guardian.active': 'aktiv', 'guardian.retract': 'Zurückziehen', 'guardian.release': 'Loslassen', 'guardian.embeds_on': 'Linkvorschauen: an', 'guardian.embeds_off': 'Linkvorschauen: aus', 'guardian.embeds_propose': 'Linkvorschauen vorschlagen', 'guardian.embeds_waiting': 'wartet auf die anderen Guardians', 'guardian.release_confirm': '{who} loslassen?\n\nDu bist dann nicht mehr Guardian. Du siehst ihre Beitraege nicht mehr, erhaeltst keine Hilferufe mehr von ihnen und entscheidest nicht mehr ueber Follow-Anfragen fuer sie.\n\nZurueck geht nur mit einem neuen Angebot, das sie annehmen.', 'guardian.open': 'öffnen', 'guardian.accept': 'Annehmen', 'guardian.reject': 'Ablehnen', 'guardian.complete': 'Abschließen', 'guardian.awaiting_others': 'wartet auf die anderen Parteien', 'guardian.coguard': 'Mit-Vormundschaftsangebot', 'guardian.push_unavailable': 'Push nicht verfügbar', 'push.n_help_t': 'Hilferuf', 'push.n_help_b': '{who} bittet um deine Hilfe', 'push.n_guard_offer_t': 'Vormundschaftsangebot', 'push.n_guard_offer_b': '{who} möchte dich als Guardian', 'push.n_guard_ward_t': 'Ward akzeptiert', 'push.n_guard_ward_b': '{who} hat dich als Guardian akzeptiert', 'push.n_guard_cog_t': 'Mit-Vormundschaft gefragt', 'push.n_guard_cog_b': 'Ein Guardian-Angebot für {who} braucht dich', 'push.n_test_t': 'Klonkt-Testmeldung', 'push.n_test_b': 'Funktioniert. So kommen Meldungen auf diesem Gerät an.',1945 'push.n_follow_t': 'Neuer Follower', 'push.n_follow_b': '{who} folgt dir jetzt', 'push.n_reply_t': 'Antwort auf "{title}"', 'push.n_mention_t': 'Erwähnung', 'push.n_dm_t': 'Private Nachricht', 'push.n_dm_b': 'Neue Nachricht von {who}', 'push.n_like_t': 'Neues Like', 'push.n_like_b': '{who} gefällt "{title}"', 'push.n_boost_t': 'Geboostet', 'push.n_boost_b': '{who} hat "{title}" geboostet', 'msg.guard_offer': 'möchte dein Guardian werden. Besprich das mit deinen Eltern oder Betreuern, bevor du entscheidest.', 'msg.guard_accept': 'Annehmen', 'msg.guard_reject': 'Ablehnen', 'msg.guard_accepted': 'Guardian angenommen. Ihr seid jetzt verbunden.', 'msg.guard_rejected': 'Angebot abgelehnt.', 'msg.guard_failed': 'Das hat nicht geklappt; versuch es erneut.', 'msg.guardians_label': 'Deine Guardians', 'msg.waved_at_you': 'hat dir zugewinkt', 'msg.help_request': 'hat um Hilfe gebeten', 'msg.wave_r1': 'Wie schön!', 'msg.wave_r2': 'Ruf mich an', 'msg.wave_back': '👋 Zurück', 'msg.wave_sent': 'Winken gesendet.', 'guardian.feed_title': 'Deine Wards', 'guardian.feed_sub': 'Lies mit, was deine Wards posten. Nur schauen.', 'guardian.follow_title': 'Follow-Anfragen', 'guardian.follow_sub': 'Jemand möchte einem deiner Wards folgen. Du entscheidest.', 'guardian.wave': '👋 Winken', 'guardian.waved': '👋 gesendet', 'guardian.app_name': 'Klonkt Guardian', 'guardian.tagline': 'Wards verwalten und Hilferufe auffangen.', 'guardian.acting_as': 'Du handelst als', 'guardian.help_title': 'Hilferufe', 'guardian.help_sub': 'Wenn ein Ward die Rettungsboje nutzt, erscheint es hier.', 'guardian.help_empty': 'Keine Hilferufe. Gut so.', 'guardian.adopt_title': 'Ward adoptieren', 'guardian.adopt_sub': 'Gib das Handle des Kindes ein (@kind@server.eu). Es bekommt ein Angebot in seinem Klonkt zum Annehmen.', 'guardian.adopt_label': 'Ward-Handle', 'guardian.adopt_btn': 'Angebot senden', 'guardian.pending_title': 'Gesendete Angebote', 'guardian.pending_sub': 'Warten, bis der Ward annimmt.', 'guardian.wards_title': 'Meine Wards', 'guardian.settings_title': 'Einstellungen', 'guardian.panel_open': 'Ansehen', 'guardian.panel_close': 'Schliessen', 'guardian.panel_help': 'Hilferufe dieses Kindes', 'guardian.panel_help_empty': 'Noch keine Hilferufe.', 'guardian.panel_follow': 'Folgeanfragen', 'guardian.panel_follow_empty': 'Keine offenen Folgeanfragen.', 'guardian.panel_posts': 'Neueste Beitraege', 'guardian.panel_posts_empty': 'Noch nichts zu sehen.', 'guardian.panel_actions': 'Aktionen', 'guardian.badge_help': 'Hilferufe', 'guardian.badge_follow': 'Folgeanfragen', 'guardian.badge_follow_one': 'Folgeanfrage', 'guardian.wards_empty': 'Noch keine Wards. Adoptiere oben eins.', 'guardian.push_title': 'Meldungen', 'guardian.push_sub': 'Erhalte eine Meldung bei einem Hilferuf oder einer Vormundschafts-Antwort, auch bei geschlossener App.', 'guardian.push_on': 'Meldungen einschalten', 'guardian.push_off': 'Meldungen sind an; tippen zum Ausschalten', 'guardian.sent': 'Angebot gesendet. Siehe unten bei Gesendete Angebote.', 'guardian.sent_retry': 'Angebot gespeichert; wir versuchen weiter zuzustellen.', 'guardian.sending': 'Senden…', 'guardian.not_found': 'Dieses Handle konnten wir nicht finden.', 'guardian.failed': 'Fehlgeschlagen', 'guardian.network': 'Netzwerkfehler.', 'guardian.pending': 'wartet auf Antwort', 'guardian.active': 'aktiv', 'guardian.retract': 'Zurückziehen', 'guardian.release': 'Loslassen', 'guardian.embeds_on': 'Linkvorschauen: an', 'guardian.embeds_off': 'Linkvorschauen: aus', 'guardian.embeds_propose': 'Linkvorschauen vorschlagen', 'guardian.embeds_waiting': 'wartet auf die anderen Guardians', 'guardian.release_confirm': '{who} loslassen?\n\nDu bist dann nicht mehr Guardian. Du siehst ihre Beitraege nicht mehr, erhaeltst keine Hilferufe mehr von ihnen und entscheidest nicht mehr ueber Follow-Anfragen fuer sie.\n\nZurueck geht nur mit einem neuen Angebot, das sie annehmen.', 'guardian.open': 'öffnen', 'guardian.accept': 'Annehmen', 'guardian.reject': 'Ablehnen', 'guardian.complete': 'Abschließen', 'guardian.awaiting_others': 'wartet auf die anderen Parteien', 'guardian.coguard': 'Mit-Vormundschaftsangebot', 'guardian.push_unavailable': 'Push nicht verfügbar', 'push.n_help_t': 'Hilferuf', 'push.n_help_b': '{who} bittet um deine Hilfe', 'push.n_guard_offer_t': 'Vormundschaftsangebot', 'push.n_guard_offer_b': '{who} möchte dich als Guardian', 'push.n_guard_ward_t': 'Ward akzeptiert', 'push.n_guard_ward_b': '{who} hat dich als Guardian akzeptiert', 'push.n_guard_cog_t': 'Mit-Vormundschaft gefragt', 'push.n_guard_cog_b': 'Ein Guardian-Angebot für {who} braucht dich', 'push.n_test_t': 'Klonkt-Testmeldung', 'push.n_test_b': 'Funktioniert. So kommen Meldungen auf diesem Gerät an.', 1946 1946 'apaid.t': 'Bezahlte Beiträge', 'apaid.intro': 'Verbinde deine eigene Patreon-Kampagne. Unterstützer entsperren bezahlte Beiträge mit einem Passkey, ohne Konto und ohne Cookie. Wir speichern keine Namen oder E-Mail-Adressen von Unterstützern, nur das verschlüsselte Token deiner Kampagne.', 'apaid.saved': 'Gespeichert.', 'apaid.nokey': 'Achtung: der Verschlüsselungsschlüssel konnte nicht erstellt oder gelesen werden (Schreibrechte auf dem Speicherordner?). Ohne Schlüssel können Secrets nicht sicher gespeichert werden.', 'apaid.status': 'Status:', 'apaid.connected': 'verbunden', 'apaid.campaign': 'Kampagne', 'apaid.configured': 'eingerichtet, noch nicht verbunden (Token eintragen)', 'apaid.notyet': 'noch nicht eingerichtet', 'apaid.redirect_h': 'Trage diese Redirect-URI in deinen Patreon-Client ein', 'apaid.redirect_p': 'In deinem Patreon-API-Client muss unter Redirect URIs genau diese Zeile stehen. Stimmt sie nicht, zeigt Patreon eine Fehlermeldung statt deine Unterstützer zurückzuschicken.', 'apaid.copy': 'Kopieren', 'apaid.copied': 'Kopiert', 'apaid.client_id': 'Patreon Client-ID', 'apaid.client_secret': 'Patreon Client-Secret', 'apaid.keep': 'Leer lassen = aktuellen Wert behalten.', 'apaid.campaign_id': 'Kampagnen-ID', 'apaid.public_page': 'Öffentliche Patreon-Seite', 'apaid.public_help': 'Der Link, unter dem Besucher Unterstützer werden können. Wird als "Unterstützer werden" gezeigt, wenn jemand noch nicht spendet.', 'apaid.access': 'Creator Access-Token', 'apaid.refresh': 'Creator Refresh-Token', 'apaid.token_help': 'Access- und Refresh-Token bekommst du auf deiner Patreon-API-Client-Seite. Wir verschlüsseln sie und erneuern automatisch.', 'apaid.min_eur': 'Standard-Unterstützungsbetrag für einen bezahlten Beitrag (Euro)', 'apaid.save': 'Speichern', 'apaid.disconnect': 'Verbindung entfernen', 'apaid.disconnect_confirm': 'Patreon-Verbindung entfernen?', 'apaid.unchanged': 'bleibt unverändert', 1947 1947 'pgate.h': 'Für Unterstützer', 'pgate.sub': 'Dieser Beitrag ist für Unterstützer dieser Seite. Werde Unterstützer und entsperre ihn danach mit einem Passkey. Kein Konto auf dieser Seite, kein Cookie.', 'pgate.sub_cents': 'Dieser Beitrag ist für Unterstützer dieser Seite (ab €{eur} pro Monat auf Patreon). Werde Unterstützer und entsperre ihn danach mit einem Passkey. Kein Konto auf dieser Seite, kein Cookie.', 'pgate.join': 'Unterstützer werden auf Patreon', 'pgate.unlock_have': 'Schon Unterstützer? Entsperren', 'pgate.unlock': 'Mit Patreon entsperren', 'pgate.join_short': 'Unterstützer werden', 'pgate.confirm': 'Bestätige mit deinem Passkey…', 'pgate.failed': 'Entsperren fehlgeschlagen. Versuch es erneut.', 'pgate.error': 'Etwas ist schiefgegangen. Versuch es erneut.', -
src/views/pages/guardian.ejs
ra7bcf66 r70677e96 32 32 33 33 <main> 34 <!-- 0. Wards' corner: meelezen met je wards (read-only). --> 35 <section id="feed-section" hidden> 36 <div class="g-sec-head"><h2><%= t('guardian.feed_title') %></h2></div> 37 <p class="g-sec-sub"><%= t('guardian.feed_sub') %></p> 38 <div id="feed-list" class="g-list"></div> 39 </section> 40 41 <!-- 0b. Follow-verzoeken op je wards (FEP-633c §5.3). --> 42 <section id="follow-section" hidden> 43 <div class="g-sec-head"><h2><%= t('guardian.follow_title') %></h2></div> 44 <p class="g-sec-sub"><%= t('guardian.follow_sub') %></p> 45 <div id="follow-list" class="g-list"></div> 46 </section> 47 48 <!-- 1. Berichtencentrum: hulpverzoeken. Dat is waar dit voor bestaat. --> 34 <!-- 1. Hulpverzoeken, uitgelicht over alle wards heen. Dat is waar dit voor 35 bestaat, en het moet opvallen zonder dat je eerst een kind opent. 36 De volledige geschiedenis per kind staat in dat kind zijn paneel. --> 49 37 <section id="help-section"> 50 38 <div class="g-sec-head"> … … 76 64 </section> 77 65 78 <!-- 4. Mijn wards (geaccepteerd). --> 66 <!-- 4. Mijn wards. Elk kind is een regel die opent naar een paneel met 67 alles over dat kind: instellingen, volgverzoeken, hulpvragen en 68 recente berichten. Een guardian denkt per kind, niet per functie. --> 79 69 <section id="wards-section"> 80 70 <div class="g-sec-head"><h2><%= t('guardian.wards_title') %></h2></div>
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)