mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 19:01:08 +02:00
i18n(memoro): translate views/DetailView via $_() — title sources, statuses, fields, transcript
- TITLE_SOURCE_LABELS map → TITLE_SOURCE_KEYS routing through $_(memoro.detail_view.title_sources.*)
- statusLabels map → STATUS_KEYS routing through $_(memoro.detail_view.statuses.*)
- Shell labels (notFound/confirmDelete/toast_deleted)
- Title placeholder: idle vs generating variant
- 4 prop rows (Status/Dauer/Sprache/Sichtbarkeit) + lang placeholder
- Section labels (Zusammenfassung/Transkript) + transcript states (transcribing/failed/empty/source)
- Meta-row Erstellt/Bearbeitet with {date}
Baselines: hardcoded 1025 → 1017 (8 cleared); missing-keys baseline unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d391a603f7
commit
98ce33e788
2 changed files with 44 additions and 37 deletions
|
|
@ -15,18 +15,16 @@
|
||||||
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 { LocalMemo, ProcessingStatus } from '../types';
|
import type { LocalMemo, ProcessingStatus } from '../types';
|
||||||
|
import { _ } from 'svelte-i18n';
|
||||||
|
|
||||||
// Human-readable labels for the title-source badge below the title
|
// Map LlmTier → i18n key (mana-server keyed as 'mana_server' since dots are
|
||||||
// input. We use these specific strings (not @mana/shared-llm's
|
// reserved path separators). Strings live in memoro.detail_view.title_sources.
|
||||||
// generic tierLabel) so we can surface the actual model family
|
const TITLE_SOURCE_KEYS: Record<LlmTier, string> = {
|
||||||
// — both browser and mana-server now run Gemma 4 variants, so the
|
none: 'memoro.detail_view.title_sources.none',
|
||||||
// label stays coherent across tiers.
|
browser: 'memoro.detail_view.title_sources.browser',
|
||||||
const TITLE_SOURCE_LABELS: Record<LlmTier, string> = {
|
'mana-server': 'memoro.detail_view.title_sources.mana_server',
|
||||||
none: 'Lokal (regelbasiert)',
|
byok: 'memoro.detail_view.title_sources.byok',
|
||||||
browser: 'Auf deinem Gerät (Gemma 4 E2B)',
|
cloud: 'memoro.detail_view.title_sources.cloud',
|
||||||
'mana-server': 'Mana-Server (Gemma 4 E4B)',
|
|
||||||
byok: 'Dein API-Key',
|
|
||||||
cloud: 'Google Gemini',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function isLlmTier(value: unknown): value is LlmTier {
|
function isLlmTier(value: unknown): value is LlmTier {
|
||||||
|
|
@ -84,11 +82,11 @@
|
||||||
return `${m}:${String(s).padStart(2, '0')}`;
|
return `${m}:${String(s).padStart(2, '0')}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const statusLabels: Record<ProcessingStatus, string> = {
|
const STATUS_KEYS: Record<ProcessingStatus, string> = {
|
||||||
pending: 'Ausstehend',
|
pending: 'memoro.detail_view.statuses.pending',
|
||||||
processing: 'Wird verarbeitet',
|
processing: 'memoro.detail_view.statuses.processing',
|
||||||
completed: 'Fertig',
|
completed: 'memoro.detail_view.statuses.completed',
|
||||||
failed: 'Fehlgeschlagen',
|
failed: 'memoro.detail_view.statuses.failed',
|
||||||
};
|
};
|
||||||
|
|
||||||
const statusColors: Record<ProcessingStatus, string> = {
|
const statusColors: Record<ProcessingStatus, string> = {
|
||||||
|
|
@ -130,21 +128,21 @@
|
||||||
if (detail.focused) return null;
|
if (detail.focused) return null;
|
||||||
const metadata = (memo.metadata as Record<string, unknown> | null) ?? {};
|
const metadata = (memo.metadata as Record<string, unknown> | null) ?? {};
|
||||||
const source = metadata.titleSource;
|
const source = metadata.titleSource;
|
||||||
return isLlmTier(source) ? TITLE_SOURCE_LABELS[source] : null;
|
return isLlmTier(source) ? $_(TITLE_SOURCE_KEYS[source]) : null;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<DetailViewShell
|
<DetailViewShell
|
||||||
entity={detail.entity}
|
entity={detail.entity}
|
||||||
loading={detail.loading}
|
loading={detail.loading}
|
||||||
notFoundLabel="Memo nicht gefunden"
|
notFoundLabel={$_('memoro.detail_view.not_found')}
|
||||||
confirmDelete={detail.confirmDelete}
|
confirmDelete={detail.confirmDelete}
|
||||||
onAskDelete={detail.askDelete}
|
onAskDelete={detail.askDelete}
|
||||||
onCancelDelete={detail.cancelDelete}
|
onCancelDelete={detail.cancelDelete}
|
||||||
confirmDeleteLabel="Memo wirklich löschen?"
|
confirmDeleteLabel={$_('memoro.detail_view.confirm_delete')}
|
||||||
onConfirmDelete={() =>
|
onConfirmDelete={() =>
|
||||||
detail.deleteWithUndo({
|
detail.deleteWithUndo({
|
||||||
label: 'Memo gelöscht',
|
label: $_('memoro.detail_view.toast_deleted'),
|
||||||
delete: () => memosStore.delete(memoId),
|
delete: () => memosStore.delete(memoId),
|
||||||
goBack,
|
goBack,
|
||||||
})}
|
})}
|
||||||
|
|
@ -156,7 +154,9 @@
|
||||||
bind:value={editTitle}
|
bind:value={editTitle}
|
||||||
onfocus={detail.focus}
|
onfocus={detail.focus}
|
||||||
onblur={saveField}
|
onblur={saveField}
|
||||||
placeholder={titleIsGenerating && !editTitle ? 'Titel wird generiert…' : 'Titel…'}
|
placeholder={titleIsGenerating && !editTitle
|
||||||
|
? $_('memoro.detail_view.placeholder_title_generating')
|
||||||
|
: $_('memoro.detail_view.placeholder_title_idle')}
|
||||||
/>
|
/>
|
||||||
<button class="pin-btn" class:pinned={memo.isPinned} onclick={togglePin}>
|
<button class="pin-btn" class:pinned={memo.isPinned} onclick={togglePin}>
|
||||||
<PushPin size={16} />
|
<PushPin size={16} />
|
||||||
|
|
@ -169,30 +169,30 @@
|
||||||
|
|
||||||
<div class="properties">
|
<div class="properties">
|
||||||
<div class="prop-row">
|
<div class="prop-row">
|
||||||
<span class="prop-label">Status</span>
|
<span class="prop-label">{$_('memoro.detail_view.label_status')}</span>
|
||||||
<span class="prop-value" style="color: {statusColors[memo.processingStatus]}">
|
<span class="prop-value" style="color: {statusColors[memo.processingStatus]}">
|
||||||
{statusLabels[memo.processingStatus]}
|
{$_(STATUS_KEYS[memo.processingStatus])}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="prop-row">
|
<div class="prop-row">
|
||||||
<span class="prop-label">Dauer</span>
|
<span class="prop-label">{$_('memoro.detail_view.label_duration')}</span>
|
||||||
<span class="prop-value">{formatDuration(memo.audioDurationMs)}</span>
|
<span class="prop-value">{formatDuration(memo.audioDurationMs)}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="prop-row">
|
<div class="prop-row">
|
||||||
<span class="prop-label">Sprache</span>
|
<span class="prop-label">{$_('memoro.detail_view.label_language')}</span>
|
||||||
<input
|
<input
|
||||||
class="prop-input"
|
class="prop-input"
|
||||||
bind:value={editLanguage}
|
bind:value={editLanguage}
|
||||||
onfocus={detail.focus}
|
onfocus={detail.focus}
|
||||||
onblur={saveField}
|
onblur={saveField}
|
||||||
placeholder="z.B. de"
|
placeholder={$_('memoro.detail_view.placeholder_language')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="prop-row">
|
<div class="prop-row">
|
||||||
<span class="prop-label">Sichtbarkeit</span>
|
<span class="prop-label">{$_('memoro.detail_view.label_visibility')}</span>
|
||||||
<VisibilityPicker
|
<VisibilityPicker
|
||||||
level={memo.visibility ?? 'space'}
|
level={memo.visibility ?? 'space'}
|
||||||
onChange={(next: VisibilityLevel) => memosStore.setVisibility(memoId, next)}
|
onChange={(next: VisibilityLevel) => memosStore.setVisibility(memoId, next)}
|
||||||
|
|
@ -202,42 +202,50 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<span class="section-label">Zusammenfassung</span>
|
<span class="section-label">{$_('memoro.detail_view.section_summary')}</span>
|
||||||
<textarea
|
<textarea
|
||||||
class="description-input"
|
class="description-input"
|
||||||
bind:value={editIntro}
|
bind:value={editIntro}
|
||||||
onfocus={detail.focus}
|
onfocus={detail.focus}
|
||||||
onblur={saveField}
|
onblur={saveField}
|
||||||
placeholder="Zusammenfassung hinzufügen..."
|
placeholder={$_('memoro.detail_view.placeholder_summary')}
|
||||||
rows={2}
|
rows={2}
|
||||||
></textarea>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<span class="section-label">Transkript</span>
|
<span class="section-label">{$_('memoro.detail_view.section_transcript')}</span>
|
||||||
{#if memo.processingStatus === 'processing'}
|
{#if memo.processingStatus === 'processing'}
|
||||||
<div class="transcript transcript-loading">
|
<div class="transcript transcript-loading">
|
||||||
<span class="loading-dot"></span>
|
<span class="loading-dot"></span>
|
||||||
<span class="loading-dot"></span>
|
<span class="loading-dot"></span>
|
||||||
<span class="loading-dot"></span>
|
<span class="loading-dot"></span>
|
||||||
<span>Wird transkribiert…</span>
|
<span>{$_('memoro.detail_view.transcribing')}</span>
|
||||||
</div>
|
</div>
|
||||||
{:else if memo.processingStatus === 'failed'}
|
{:else if memo.processingStatus === 'failed'}
|
||||||
<div class="transcript transcript-failed">
|
<div class="transcript transcript-failed">
|
||||||
Transkription fehlgeschlagen. Versuche es erneut oder gib das Transkript manuell ein.
|
{$_('memoro.detail_view.transcript_failed')}
|
||||||
</div>
|
</div>
|
||||||
{:else if memo.transcript}
|
{:else if memo.transcript}
|
||||||
<div class="transcript">{memo.transcript}</div>
|
<div class="transcript">{memo.transcript}</div>
|
||||||
<div class="source-label">Voxtral via mana-stt</div>
|
<div class="source-label">{$_('memoro.detail_view.transcript_source')}</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="transcript transcript-empty">Kein Transkript vorhanden.</div>
|
<div class="transcript transcript-empty">{$_('memoro.detail_view.transcript_empty')}</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="meta">
|
<div class="meta">
|
||||||
<span>Erstellt: {formatDate(new Date(memo.createdAt ?? ''))}</span>
|
<span
|
||||||
|
>{$_('memoro.detail_view.meta_created', {
|
||||||
|
values: { date: formatDate(new Date(memo.createdAt ?? '')) },
|
||||||
|
})}</span
|
||||||
|
>
|
||||||
{#if memo.updatedAt}
|
{#if memo.updatedAt}
|
||||||
<span>Bearbeitet: {formatDate(new Date(memo.updatedAt))}</span>
|
<span
|
||||||
|
>{$_('memoro.detail_view.meta_updated', {
|
||||||
|
values: { date: formatDate(new Date(memo.updatedAt)) },
|
||||||
|
})}</span
|
||||||
|
>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,6 @@
|
||||||
"apps/mana/apps/web/src/lib/modules/meditate/components/SessionPlayer.svelte": 3,
|
"apps/mana/apps/web/src/lib/modules/meditate/components/SessionPlayer.svelte": 3,
|
||||||
"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/memoro/views/DetailView.svelte": 8,
|
|
||||||
"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/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,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue