i18n(news): translate workbench ListView via $_() — onboarding CTA, toolbar, list rows

Adds news.workbench sub-namespace (cta_title/hint/action, err_short,
empty_short, open_aria); reuses news.feed.* (articles count, refresh,
savedLink, settingsLink, loading) and news.reactions.* (interested/
notInterested/blockSource) for the workbench-embedded ListView.

Baselines: hardcoded 1066 → 1058 (8 cleared); missing-keys baseline unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-27 14:43:18 +02:00
parent 258edaa07d
commit c0bf9aad1c
7 changed files with 61 additions and 16 deletions

View file

@ -133,5 +133,13 @@
"title": "News",
"empty": "Keine ungelesenen News.",
"viewAll": "Alle ansehen"
},
"workbench": {
"cta_title": "News Hub einrichten",
"cta_hint": "Wähle Themen, Sprachen und Quellen — danach erscheinen hier deine Artikel.",
"cta_action": "Jetzt einrichten",
"err_short": "Fehler",
"empty_short": "Keine neuen Artikel.",
"open_aria": "Öffnen"
}
}

View file

@ -133,5 +133,13 @@
"title": "News",
"empty": "No unread news.",
"viewAll": "View all"
},
"workbench": {
"cta_title": "Set up the News Hub",
"cta_hint": "Pick topics, languages and sources — after that your articles appear here.",
"cta_action": "Set up now",
"err_short": "Error",
"empty_short": "No new articles.",
"open_aria": "Open"
}
}

View file

@ -133,5 +133,13 @@
"title": "Noticias",
"empty": "Sin noticias por leer.",
"viewAll": "Ver todo"
},
"workbench": {
"cta_title": "Configurar el News Hub",
"cta_hint": "Elige temas, idiomas y fuentes — luego tus artículos aparecerán aquí.",
"cta_action": "Configurar ahora",
"err_short": "Error",
"empty_short": "No hay artículos nuevos.",
"open_aria": "Abrir"
}
}

View file

@ -133,5 +133,13 @@
"title": "Actualités",
"empty": "Aucune actualité non lue.",
"viewAll": "Tout voir"
},
"workbench": {
"cta_title": "Configurer le News Hub",
"cta_hint": "Choisis thèmes, langues et sources — ensuite tes articles apparaîtront ici.",
"cta_action": "Configurer maintenant",
"err_short": "Erreur",
"empty_short": "Aucun nouvel article.",
"open_aria": "Ouvrir"
}
}

View file

@ -133,5 +133,13 @@
"title": "Notizie",
"empty": "Nessuna notizia da leggere.",
"viewAll": "Vedi tutte"
},
"workbench": {
"cta_title": "Configura il News Hub",
"cta_hint": "Scegli temi, lingue e fonti — poi i tuoi articoli appariranno qui.",
"cta_action": "Configura ora",
"err_short": "Errore",
"empty_short": "Nessun nuovo articolo.",
"open_aria": "Apri"
}
}

View file

@ -29,6 +29,7 @@
import { articlesStore } from '$lib/modules/news/stores/articles.svelte';
import { feedCacheStore } from '$lib/modules/news/stores/feed-cache.svelte';
import type { LocalCachedArticle } from '$lib/modules/news/types';
import { _ } from 'svelte-i18n';
// We accept ViewProps for protocol compatibility but the workbench
// view doesn't navigate within itself — every "open" jumps to the
@ -97,18 +98,18 @@
<div class="wb-news">
{#if !prefs.onboardingCompleted}
<div class="cta">
<p class="cta-title">News Hub einrichten</p>
<p class="cta-title">{$_('news.workbench.cta_title')}</p>
<p class="cta-hint">
Wähle Themen, Sprachen und Quellen — danach erscheinen hier deine Artikel.
{$_('news.workbench.cta_hint')}
</p>
<a class="cta-btn" href="/news">Jetzt einrichten</a>
<a class="cta-btn" href="/news">{$_('news.workbench.cta_action')}</a>
</div>
{:else}
<div class="toolbar">
<div class="counts">
{ranked.length} Artikel
{$_('news.feed.articles', { values: { count: ranked.length } })}
{#if feedCacheStore.lastError}
· <span class="err">Fehler</span>
· <span class="err">{$_('news.workbench.err_short')}</span>
{/if}
</div>
<div class="tools">
@ -117,22 +118,22 @@
class="tool"
onclick={refresh}
disabled={feedCacheStore.inFlight}
title="Neu laden"
title={$_('news.feed.refresh')}
>
{feedCacheStore.inFlight ? '…' : '↻'}
</button>
<a class="tool" href="/news/saved" title="Gespeichert">📑</a>
<a class="tool" href="/news/preferences" title="Einstellungen"></a>
<a class="tool" href="/news/saved" title={$_('news.feed.savedLink')}>📑</a>
<a class="tool" href="/news/preferences" title={$_('news.feed.settingsLink')}>⚙</a>
</div>
</div>
{#if ranked.length === 0}
<div class="empty">
{#if pool.length === 0}
<p>Lade Artikel…</p>
<p>{$_('news.feed.loading')}</p>
{:else}
<p>Keine neuen Artikel.</p>
<button type="button" class="link" onclick={refresh}>Neu laden</button>
<p>{$_('news.workbench.empty_short')}</p>
<button type="button" class="link" onclick={refresh}>{$_('news.feed.refresh')}</button>
{/if}
</div>
{:else}
@ -140,7 +141,12 @@
{#each ranked.slice(0, 30) as { article } (article.id)}
<li class="item">
{#if article.imageUrl}
<button type="button" class="thumb" onclick={() => open(article)} aria-label="Öffnen">
<button
type="button"
class="thumb"
onclick={() => open(article)}
aria-label={$_('news.workbench.open_aria')}
>
<img src={article.imageUrl} alt="" loading="lazy" />
</button>
{/if}
@ -158,7 +164,7 @@
type="button"
class="rxn"
onclick={() => react(article, 'interested')}
title="Interessiert"
title={$_('news.reactions.interested')}
>
❤️
</button>
@ -166,7 +172,7 @@
type="button"
class="rxn"
onclick={() => react(article, 'not_interested')}
title="Nicht für mich"
title={$_('news.reactions.notInterested')}
>
👎
</button>
@ -174,7 +180,7 @@
type="button"
class="rxn"
onclick={() => react(article, 'source_blocked')}
title="Quelle ausblenden"
title={$_('news.reactions.blockSource')}
>
🚫
</button>