i18n(cards+finance+mood): translate 3 list/detail views via $_()

- cards/views/DetailView: route through cards.detail.* (deck-name
  fallback, prop labels, meta, confirm/toast strings)
- finance/ListView: route through finance.page.* (re-uses existing
  page namespace) + finance.list_view.empty_no_tx; drops unused
  Transaction + FinanceCategory type imports
- mood/ListView: route through mood.list_view.* (new namespace)

Baseline 899 → 881 (-18).
This commit is contained in:
Till JS 2026-04-27 18:38:06 +02:00
parent 70a06d1d9f
commit b99dd60ad0
4 changed files with 56 additions and 40 deletions

View file

@ -12,6 +12,7 @@
import { VisibilityPicker, type VisibilityLevel } from '@mana/shared-privacy'; import { VisibilityPicker, type VisibilityLevel } from '@mana/shared-privacy';
import type { ViewProps } from '$lib/app-registry'; import type { ViewProps } from '$lib/app-registry';
import type { LocalDeck, LocalCard } from '../types'; import type { LocalDeck, LocalCard } from '../types';
import { _ } from 'svelte-i18n';
let { params, goBack }: ViewProps = $props(); let { params, goBack }: ViewProps = $props();
let deckId = $derived(params.deckId as string); let deckId = $derived(params.deckId as string);
@ -48,7 +49,7 @@
async function saveField() { async function saveField() {
detail.blur(); detail.blur();
await deckStore.updateDeck(deckId, { await deckStore.updateDeck(deckId, {
title: editName.trim() || detail.entity?.name || 'Unbenannt', title: editName.trim() || detail.entity?.name || $_('cards.detail.name_fallback'),
description: editDescription.trim() || undefined, description: editDescription.trim() || undefined,
}); });
// Color is not in UpdateDeckInput, update directly // Color is not in UpdateDeckInput, update directly
@ -61,14 +62,14 @@
<DetailViewShell <DetailViewShell
entity={detail.entity} entity={detail.entity}
loading={detail.loading} loading={detail.loading}
notFoundLabel="Deck nicht gefunden" notFoundLabel={$_('cards.detail.not_found')}
confirmDelete={detail.confirmDelete} confirmDelete={detail.confirmDelete}
onAskDelete={detail.askDelete} onAskDelete={detail.askDelete}
onCancelDelete={detail.cancelDelete} onCancelDelete={detail.cancelDelete}
confirmDeleteLabel="Deck wirklich löschen?" confirmDeleteLabel={$_('cards.detail.confirm_delete')}
onConfirmDelete={() => onConfirmDelete={() =>
detail.deleteWithUndo({ detail.deleteWithUndo({
label: 'Deck gelöscht', label: $_('cards.detail.toast_deleted'),
delete: () => deckStore.deleteDeck(deckId), delete: () => deckStore.deleteDeck(deckId),
goBack, goBack,
})} })}
@ -79,12 +80,12 @@
bind:value={editName} bind:value={editName}
onfocus={detail.focus} onfocus={detail.focus}
onblur={saveField} onblur={saveField}
placeholder="Deck-Name..." placeholder={$_('cards.detail.placeholder_name')}
/> />
<div class="properties"> <div class="properties">
<div class="prop-row"> <div class="prop-row">
<span class="prop-label">Farbe</span> <span class="prop-label">{$_('cards.detail.prop_color')}</span>
<input <input
type="color" type="color"
class="color-input" class="color-input"
@ -95,7 +96,7 @@
</div> </div>
<div class="prop-row"> <div class="prop-row">
<span class="prop-label">Sichtbarkeit</span> <span class="prop-label">{$_('cards.detail.prop_visibility')}</span>
<VisibilityPicker <VisibilityPicker
level={deck.visibility ?? 'space'} level={deck.visibility ?? 'space'}
onChange={(next: VisibilityLevel) => deckStore.setVisibility(deckId, next)} onChange={(next: VisibilityLevel) => deckStore.setVisibility(deckId, next)}
@ -104,34 +105,42 @@
</div> </div>
<div class="prop-row"> <div class="prop-row">
<span class="prop-label">Karten</span> <span class="prop-label">{$_('cards.detail.prop_cards')}</span>
<span class="prop-value">{cardCount}</span> <span class="prop-value">{cardCount}</span>
</div> </div>
{#if deck.lastStudied} {#if deck.lastStudied}
<div class="prop-row"> <div class="prop-row">
<span class="prop-label">Zuletzt gelernt</span> <span class="prop-label">{$_('cards.detail.prop_last_studied')}</span>
<span class="prop-value">{formatDate(new Date(deck.lastStudied))}</span> <span class="prop-value">{formatDate(new Date(deck.lastStudied))}</span>
</div> </div>
{/if} {/if}
</div> </div>
<div class="section"> <div class="section">
<span class="section-label">Beschreibung</span> <span class="section-label">{$_('cards.detail.section_description')}</span>
<textarea <textarea
class="description-input" class="description-input"
bind:value={editDescription} bind:value={editDescription}
onfocus={detail.focus} onfocus={detail.focus}
onblur={saveField} onblur={saveField}
placeholder="Beschreibung hinzufügen..." placeholder={$_('cards.detail.placeholder_description')}
rows={3} rows={3}
></textarea> ></textarea>
</div> </div>
<div class="meta"> <div class="meta">
<span>Erstellt: {formatDate(new Date(deck.createdAt ?? ''))}</span> <span
>{$_('cards.detail.meta_created', {
values: { date: formatDate(new Date(deck.createdAt ?? '')) },
})}</span
>
{#if deck.updatedAt} {#if deck.updatedAt}
<span>Bearbeitet: {formatDate(new Date(deck.updatedAt))}</span> <span
>{$_('cards.detail.meta_updated', {
values: { date: formatDate(new Date(deck.updatedAt)) },
})}</span
>
{/if} {/if}
</div> </div>
{/snippet} {/snippet}

View file

@ -15,8 +15,9 @@
groupByDate, groupByDate,
} from './queries'; } from './queries';
import { financeStore } from './stores/finance.svelte'; import { financeStore } from './stores/finance.svelte';
import type { Transaction, FinanceCategory, TransactionType } from './types'; import type { TransactionType } from './types';
import type { ViewProps } from '$lib/app-registry'; import type { ViewProps } from '$lib/app-registry';
import { _ } from 'svelte-i18n';
let { navigate, goBack, params }: ViewProps = $props(); let { navigate, goBack, params }: ViewProps = $props();
@ -75,7 +76,7 @@
onclick={() => { onclick={() => {
addType = 'expense'; addType = 'expense';
addCatId = null; addCatId = null;
}}>Ausgabe</button }}>{$_('finance.page.type_expense')}</button
> >
<button <button
type="button" type="button"
@ -84,7 +85,7 @@
onclick={() => { onclick={() => {
addType = 'income'; addType = 'income';
addCatId = null; addCatId = null;
}}>Einnahme</button }}>{$_('finance.page.type_income')}</button
> >
</div> </div>
<div class="input-row"> <div class="input-row">
@ -92,7 +93,7 @@
class="amount-input" class="amount-input"
type="text" type="text"
inputmode="decimal" inputmode="decimal"
placeholder="0,00" placeholder={$_('finance.page.placeholder_amount')}
bind:value={addAmount} bind:value={addAmount}
onkeydown={handleKeydown} onkeydown={handleKeydown}
/> />
@ -100,7 +101,7 @@
<input <input
class="desc-input" class="desc-input"
type="text" type="text"
placeholder="Beschreibung..." placeholder={$_('finance.page.placeholder_description')}
bind:value={addDesc} bind:value={addDesc}
onkeydown={handleKeydown} onkeydown={handleKeydown}
/> />
@ -123,17 +124,17 @@
<div class="summary"> <div class="summary">
<div class="summary-col"> <div class="summary-col">
<span class="s-value income">+{formatCurrency(income)}</span> <span class="s-value income">+{formatCurrency(income)}</span>
<span class="s-label">Einnahmen</span> <span class="s-label">{$_('finance.page.summary_income')}</span>
</div> </div>
<div class="summary-col"> <div class="summary-col">
<span class="s-value expense">-{formatCurrency(expenses)}</span> <span class="s-value expense">-{formatCurrency(expenses)}</span>
<span class="s-label">Ausgaben</span> <span class="s-label">{$_('finance.page.summary_expenses')}</span>
</div> </div>
<div class="summary-col"> <div class="summary-col">
<span class="s-value" class:income={balance >= 0} class:expense={balance < 0}> <span class="s-value" class:income={balance >= 0} class:expense={balance < 0}>
{balance >= 0 ? '+' : ''}{formatCurrency(balance)} {balance >= 0 ? '+' : ''}{formatCurrency(balance)}
</span> </span>
<span class="s-label">Bilanz</span> <span class="s-label">{$_('finance.page.summary_balance')}</span>
</div> </div>
</div> </div>
@ -161,7 +162,7 @@
{/each} {/each}
{#if recentTxs.length === 0} {#if recentTxs.length === 0}
<p class="empty">Noch keine Transaktionen diesen Monat.</p> <p class="empty">{$_('finance.list_view.empty_no_tx')}</p>
{/if} {/if}
</div> </div>
</div> </div>

View file

@ -19,6 +19,7 @@
} from './queries'; } from './queries';
import { EMOTION_META, ACTIVITY_LABELS } from './types'; import { EMOTION_META, ACTIVITY_LABELS } from './types';
import QuickLog from './components/QuickLog.svelte'; import QuickLog from './components/QuickLog.svelte';
import { _ } from 'svelte-i18n';
const entriesQuery = useAllMoodEntries(); const entriesQuery = useAllMoodEntries();
const settingsQuery = useMoodSettings(); const settingsQuery = useMoodSettings();
@ -63,9 +64,11 @@
😊 😊
{/if} {/if}
</span> </span>
<span class="cta-text">Wie geht es dir?</span> <span class="cta-text">{$_('mood.list_view.cta_question')}</span>
<span class="cta-sub"> <span class="cta-sub">
{todayEntries.length}/{settings.dailyTarget} Check-ins heute {$_('mood.list_view.cta_count', {
values: { n: todayEntries.length, target: settings.dailyTarget },
})}
</span> </span>
</button> </button>
{/if} {/if}
@ -73,7 +76,7 @@
<!-- Today's Entries --> <!-- Today's Entries -->
{#if todayEntries.length > 0} {#if todayEntries.length > 0}
<div class="today-section"> <div class="today-section">
<span class="section-label">Heute</span> <span class="section-label">{$_('mood.list_view.section_today')}</span>
<div class="today-entries"> <div class="today-entries">
{#each todayEntries as entry (entry.id)} {#each todayEntries as entry (entry.id)}
<div class="entry-pill"> <div class="entry-pill">
@ -93,22 +96,22 @@
<div class="stats-row"> <div class="stats-row">
<div class="stat"> <div class="stat">
<span class="stat-val" style:color={levelColor(avgLevel7)}>{avgLevel7 || '—'}</span> <span class="stat-val" style:color={levelColor(avgLevel7)}>{avgLevel7 || '—'}</span>
<span class="stat-lbl">Ø 7 Tage</span> <span class="stat-lbl">{$_('mood.list_view.stat_avg_7')}</span>
</div> </div>
<div class="stat"> <div class="stat">
<span class="stat-val" style:color={levelColor(avgLevel30)}>{avgLevel30 || '—'}</span> <span class="stat-val" style:color={levelColor(avgLevel30)}>{avgLevel30 || '—'}</span>
<span class="stat-lbl">Ø 30 Tage</span> <span class="stat-lbl">{$_('mood.list_view.stat_avg_30')}</span>
</div> </div>
<div class="stat"> <div class="stat">
<span class="stat-val">{streak}</span> <span class="stat-val">{streak}</span>
<span class="stat-lbl">Streak</span> <span class="stat-lbl">{$_('mood.list_view.stat_streak')}</span>
</div> </div>
</div> </div>
<!-- Week Mood Chart --> <!-- Week Mood Chart -->
{#if weekData.some((d) => d.avgLevel > 0)} {#if weekData.some((d) => d.avgLevel > 0)}
<div class="week-section"> <div class="week-section">
<span class="section-label">Diese Woche</span> <span class="section-label">{$_('mood.list_view.section_week')}</span>
<div class="week-chart"> <div class="week-chart">
{#each weekData as day} {#each weekData as day}
<div class="week-col"> <div class="week-col">
@ -136,15 +139,19 @@
<!-- Valence Bar --> <!-- Valence Bar -->
{#if entries.length >= 5} {#if entries.length >= 5}
<div class="valence-section"> <div class="valence-section">
<span class="section-label">Stimmungsbilanz</span> <span class="section-label">{$_('mood.list_view.section_valence')}</span>
<div class="valence-bar"> <div class="valence-bar">
<div class="v-pos" style:width="{valence.positive}%"></div> <div class="v-pos" style:width="{valence.positive}%"></div>
<div class="v-neu" style:width="{valence.neutral}%"></div> <div class="v-neu" style:width="{valence.neutral}%"></div>
<div class="v-neg" style:width="{valence.negative}%"></div> <div class="v-neg" style:width="{valence.negative}%"></div>
</div> </div>
<div class="valence-labels"> <div class="valence-labels">
<span class="vl-pos">{valence.positive}% positiv</span> <span class="vl-pos"
<span class="vl-neg">{valence.negative}% negativ</span> >{$_('mood.list_view.valence_positive', { values: { n: valence.positive } })}</span
>
<span class="vl-neg"
>{$_('mood.list_view.valence_negative', { values: { n: valence.negative } })}</span
>
</div> </div>
</div> </div>
{/if} {/if}
@ -152,7 +159,7 @@
<!-- Top Emotions --> <!-- Top Emotions -->
{#if distribution.length > 0} {#if distribution.length > 0}
<div class="dist-section"> <div class="dist-section">
<span class="section-label">Häufigste Emotionen</span> <span class="section-label">{$_('mood.list_view.section_distribution')}</span>
<div class="dist-list"> <div class="dist-list">
{#each distribution.slice(0, 5) as item} {#each distribution.slice(0, 5) as item}
<div class="dist-row"> <div class="dist-row">
@ -175,7 +182,7 @@
<!-- Weekday Pattern --> <!-- Weekday Pattern -->
{#if weekdayPattern.some((d) => d.avgLevel > 0)} {#if weekdayPattern.some((d) => d.avgLevel > 0)}
<div class="pattern-section"> <div class="pattern-section">
<span class="section-label">Wochentag-Muster</span> <span class="section-label">{$_('mood.list_view.section_weekday_pattern')}</span>
<div class="pattern-row"> <div class="pattern-row">
{#each weekdayPattern as day} {#each weekdayPattern as day}
<div class="pattern-col"> <div class="pattern-col">
@ -196,15 +203,17 @@
<!-- Activity Insights --> <!-- Activity Insights -->
{#if activityInsights.length >= 2} {#if activityInsights.length >= 2}
<div class="insights-section"> <div class="insights-section">
<span class="section-label">Aktivitäten & Stimmung</span> <span class="section-label">{$_('mood.list_view.section_activities')}</span>
{#each activityInsights.slice(0, 4) as insight} {#each activityInsights.slice(0, 4) as insight}
<div class="insight-row"> <div class="insight-row">
<span class="ins-emoji">{ACTIVITY_LABELS[insight.activity]?.emoji ?? ''}</span> <span class="ins-emoji">{ACTIVITY_LABELS[insight.activity]?.emoji ?? ''}</span>
<span class="ins-name">{ACTIVITY_LABELS[insight.activity]?.de ?? insight.activity}</span> <span class="ins-name">{ACTIVITY_LABELS[insight.activity]?.de ?? insight.activity}</span>
<span class="ins-val" style:color={levelColor(insight.avgLevel)} <span class="ins-val" style:color={levelColor(insight.avgLevel)}
{insight.avgLevel}</span >{$_('mood.list_view.insight_avg', { values: { n: insight.avgLevel } })}</span
>
<span class="ins-count"
>{$_('mood.list_view.insight_count', { values: { n: insight.count } })}</span
> >
<span class="ins-count">({insight.count}×)</span>
</div> </div>
{/each} {/each}
</div> </div>

View file

@ -68,7 +68,6 @@
"apps/mana/apps/web/src/lib/modules/broadcast/widgets/BroadcastsWidget.svelte": 3, "apps/mana/apps/web/src/lib/modules/broadcast/widgets/BroadcastsWidget.svelte": 3,
"apps/mana/apps/web/src/lib/modules/calc/ListView.svelte": 1, "apps/mana/apps/web/src/lib/modules/calc/ListView.svelte": 1,
"apps/mana/apps/web/src/lib/modules/cards/components/CreateDeckModal.svelte": 3, "apps/mana/apps/web/src/lib/modules/cards/components/CreateDeckModal.svelte": 3,
"apps/mana/apps/web/src/lib/modules/cards/views/DetailView.svelte": 6,
"apps/mana/apps/web/src/lib/modules/chat/ListView.svelte": 1, "apps/mana/apps/web/src/lib/modules/chat/ListView.svelte": 1,
"apps/mana/apps/web/src/lib/modules/chat/views/DetailView.svelte": 1, "apps/mana/apps/web/src/lib/modules/chat/views/DetailView.svelte": 1,
"apps/mana/apps/web/src/lib/modules/citycorners/views/DetailView.svelte": 3, "apps/mana/apps/web/src/lib/modules/citycorners/views/DetailView.svelte": 3,
@ -111,7 +110,6 @@
"apps/mana/apps/web/src/lib/modules/core/widgets/TasksTodayWidget.svelte": 1, "apps/mana/apps/web/src/lib/modules/core/widgets/TasksTodayWidget.svelte": 1,
"apps/mana/apps/web/src/lib/modules/core/widgets/UpcomingEventsWidget.svelte": 1, "apps/mana/apps/web/src/lib/modules/core/widgets/UpcomingEventsWidget.svelte": 1,
"apps/mana/apps/web/src/lib/modules/drink/ListView.svelte": 5, "apps/mana/apps/web/src/lib/modules/drink/ListView.svelte": 5,
"apps/mana/apps/web/src/lib/modules/finance/ListView.svelte": 6,
"apps/mana/apps/web/src/lib/modules/goals/ListView.svelte": 1, "apps/mana/apps/web/src/lib/modules/goals/ListView.svelte": 1,
"apps/mana/apps/web/src/lib/modules/guides/ListView.svelte": 1, "apps/mana/apps/web/src/lib/modules/guides/ListView.svelte": 1,
"apps/mana/apps/web/src/lib/modules/habits/components/HabitDetail.svelte": 5, "apps/mana/apps/web/src/lib/modules/habits/components/HabitDetail.svelte": 5,
@ -130,7 +128,6 @@
"apps/mana/apps/web/src/lib/modules/meditate/components/StatsOverview.svelte": 3, "apps/mana/apps/web/src/lib/modules/meditate/components/StatsOverview.svelte": 3,
"apps/mana/apps/web/src/lib/modules/meditate/ListView.svelte": 1, "apps/mana/apps/web/src/lib/modules/meditate/ListView.svelte": 1,
"apps/mana/apps/web/src/lib/modules/mood/components/QuickLog.svelte": 6, "apps/mana/apps/web/src/lib/modules/mood/components/QuickLog.svelte": 6,
"apps/mana/apps/web/src/lib/modules/mood/ListView.svelte": 6,
"apps/mana/apps/web/src/lib/modules/moodlit/components/mood/CreateMoodDialog.svelte": 3, "apps/mana/apps/web/src/lib/modules/moodlit/components/mood/CreateMoodDialog.svelte": 3,
"apps/mana/apps/web/src/lib/modules/music/ListView.svelte": 3, "apps/mana/apps/web/src/lib/modules/music/ListView.svelte": 3,
"apps/mana/apps/web/src/lib/modules/music/views/DetailView.svelte": 6, "apps/mana/apps/web/src/lib/modules/music/views/DetailView.svelte": 6,