mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 19:01:08 +02:00
i18n(music+profile): translate detail/hub views via $_()
- music/views/DetailView: route through music.detail.* (new namespace) - profile/ListView: route through profile.hub.* (new sub-namespace) + reuse existing profile.* keys for account-section actions; TABS refactored from literal label → labelKey routing through $_() Baseline 881 → 869 (-12).
This commit is contained in:
parent
fa401cfeec
commit
a5cef980ae
3 changed files with 61 additions and 45 deletions
|
|
@ -10,6 +10,7 @@
|
|||
import { Heart } from '@mana/shared-icons';
|
||||
import type { ViewProps } from '$lib/app-registry';
|
||||
import type { LocalSong } from '../types';
|
||||
import { _ } from 'svelte-i18n';
|
||||
|
||||
let { params, goBack }: ViewProps = $props();
|
||||
let songId = $derived(params.songId as string);
|
||||
|
|
@ -38,7 +39,7 @@
|
|||
async function saveField() {
|
||||
detail.blur();
|
||||
await libraryStore.updateMetadata(songId, {
|
||||
title: editTitle.trim() || detail.entity?.title || 'Ohne Titel',
|
||||
title: editTitle.trim() || detail.entity?.title || $_('music.detail.title_fallback'),
|
||||
artist: editArtist.trim() || undefined,
|
||||
album: editAlbum.trim() || undefined,
|
||||
genre: editGenre.trim() || undefined,
|
||||
|
|
@ -62,14 +63,14 @@
|
|||
<DetailViewShell
|
||||
entity={detail.entity}
|
||||
loading={detail.loading}
|
||||
notFoundLabel="Song nicht gefunden"
|
||||
notFoundLabel={$_('music.detail.not_found')}
|
||||
confirmDelete={detail.confirmDelete}
|
||||
onAskDelete={detail.askDelete}
|
||||
onCancelDelete={detail.cancelDelete}
|
||||
confirmDeleteLabel="Song wirklich löschen?"
|
||||
confirmDeleteLabel={$_('music.detail.confirm_delete')}
|
||||
onConfirmDelete={() =>
|
||||
detail.deleteWithUndo({
|
||||
label: 'Song gelöscht',
|
||||
label: $_('music.detail.toast_deleted'),
|
||||
delete: () => libraryStore.delete(songId),
|
||||
goBack,
|
||||
})}
|
||||
|
|
@ -81,7 +82,7 @@
|
|||
bind:value={editTitle}
|
||||
onfocus={detail.focus}
|
||||
onblur={saveField}
|
||||
placeholder="Titel..."
|
||||
placeholder={$_('music.detail.placeholder_title')}
|
||||
/>
|
||||
<button class="fav-btn" class:active={song.favorite} onclick={toggleFavorite}>
|
||||
<Heart size={18} weight={song.favorite ? 'fill' : 'regular'} />
|
||||
|
|
@ -90,18 +91,18 @@
|
|||
|
||||
<div class="properties">
|
||||
<div class="prop-row">
|
||||
<span class="prop-label">Künstler</span>
|
||||
<span class="prop-label">{$_('music.detail.prop_artist')}</span>
|
||||
<input
|
||||
class="prop-input"
|
||||
bind:value={editArtist}
|
||||
onfocus={detail.focus}
|
||||
onblur={saveField}
|
||||
placeholder="Unbekannt"
|
||||
placeholder={$_('music.detail.prop_artist_placeholder')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="prop-row">
|
||||
<span class="prop-label">Album</span>
|
||||
<span class="prop-label">{$_('music.detail.prop_album')}</span>
|
||||
<input
|
||||
class="prop-input"
|
||||
bind:value={editAlbum}
|
||||
|
|
@ -112,7 +113,7 @@
|
|||
</div>
|
||||
|
||||
<div class="prop-row">
|
||||
<span class="prop-label">Genre</span>
|
||||
<span class="prop-label">{$_('music.detail.prop_genre')}</span>
|
||||
<input
|
||||
class="prop-input"
|
||||
bind:value={editGenre}
|
||||
|
|
@ -123,7 +124,7 @@
|
|||
</div>
|
||||
|
||||
<div class="prop-row">
|
||||
<span class="prop-label">Jahr</span>
|
||||
<span class="prop-label">{$_('music.detail.prop_year')}</span>
|
||||
<input
|
||||
type="number"
|
||||
class="prop-input"
|
||||
|
|
@ -135,7 +136,7 @@
|
|||
</div>
|
||||
|
||||
<div class="prop-row">
|
||||
<span class="prop-label">BPM</span>
|
||||
<span class="prop-label">{$_('music.detail.prop_bpm')}</span>
|
||||
<input
|
||||
type="number"
|
||||
class="prop-input"
|
||||
|
|
@ -147,23 +148,35 @@
|
|||
</div>
|
||||
|
||||
<div class="prop-row">
|
||||
<span class="prop-label">Dauer</span>
|
||||
<span class="prop-label">{$_('music.detail.prop_duration')}</span>
|
||||
<span class="prop-value">{formatDuration(song.duration)}</span>
|
||||
</div>
|
||||
|
||||
<div class="prop-row">
|
||||
<span class="prop-label">Wiedergaben</span>
|
||||
<span class="prop-label">{$_('music.detail.prop_play_count')}</span>
|
||||
<span class="prop-value">{song.playCount}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="meta">
|
||||
<span>Erstellt: {formatDate(new Date(song.createdAt ?? ''))}</span>
|
||||
<span
|
||||
>{$_('music.detail.meta_created', {
|
||||
values: { date: formatDate(new Date(song.createdAt ?? '')) },
|
||||
})}</span
|
||||
>
|
||||
{#if song.updatedAt}
|
||||
<span>Bearbeitet: {formatDate(new Date(song.updatedAt))}</span>
|
||||
<span
|
||||
>{$_('music.detail.meta_updated', {
|
||||
values: { date: formatDate(new Date(song.updatedAt)) },
|
||||
})}</span
|
||||
>
|
||||
{/if}
|
||||
{#if song.lastPlayedAt}
|
||||
<span>Zuletzt gehört: {formatDate(new Date(song.lastPlayedAt))}</span>
|
||||
<span
|
||||
>{$_('music.detail.meta_last_played', {
|
||||
values: { date: formatDate(new Date(song.lastPlayedAt)) },
|
||||
})}</span
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
{/snippet}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
import ContextFreeform from './ContextFreeform.svelte';
|
||||
import { useUserContext } from './queries';
|
||||
import { getProgress } from './questions';
|
||||
import { _ } from 'svelte-i18n';
|
||||
|
||||
type Tab = 'overview' | 'interview' | 'freeform' | 'account';
|
||||
type InterviewStartMode = 'text' | 'voice' | 'conversation';
|
||||
|
|
@ -44,11 +45,11 @@
|
|||
}
|
||||
});
|
||||
|
||||
const TABS: { key: Tab; label: string }[] = [
|
||||
{ key: 'overview', label: 'Übersicht' },
|
||||
{ key: 'interview', label: 'Interview' },
|
||||
{ key: 'freeform', label: 'Freitext' },
|
||||
{ key: 'account', label: 'Konto' },
|
||||
const TABS: { key: Tab; labelKey: string }[] = [
|
||||
{ key: 'overview', labelKey: 'profile.hub.tab_overview' },
|
||||
{ key: 'interview', labelKey: 'profile.hub.tab_interview' },
|
||||
{ key: 'freeform', labelKey: 'profile.hub.tab_freeform' },
|
||||
{ key: 'account', labelKey: 'profile.hub.tab_account' },
|
||||
];
|
||||
|
||||
function startInterview(mode: InterviewStartMode) {
|
||||
|
|
@ -58,15 +59,15 @@
|
|||
|
||||
function handleProfileUpdate(user: ApiUserProfile) {
|
||||
apiProfile = user;
|
||||
toast.success('Profil erfolgreich aktualisiert');
|
||||
toast.success($_('profile.profile_updated'));
|
||||
}
|
||||
|
||||
function handlePasswordChange() {
|
||||
toast.success('Passwort erfolgreich geändert');
|
||||
toast.success($_('profile.password_changed'));
|
||||
}
|
||||
|
||||
async function handleAccountDeleted() {
|
||||
toast.info('Konto wird gelöscht...');
|
||||
toast.info($_('profile.hub.toast_account_deleting'));
|
||||
await authStore.signOut();
|
||||
goto('/login');
|
||||
}
|
||||
|
|
@ -89,7 +90,7 @@
|
|||
if (tab.key !== 'interview') interviewStartMode = null;
|
||||
}}
|
||||
>
|
||||
{tab.label}
|
||||
{$_(tab.labelKey)}
|
||||
</button>
|
||||
{/each}
|
||||
</nav>
|
||||
|
|
@ -102,12 +103,14 @@
|
|||
<!-- Interview start hero -->
|
||||
<div class="interview-hero">
|
||||
<div class="hero-header">
|
||||
<h3 class="hero-title">Interview starten</h3>
|
||||
<h3 class="hero-title">{$_('profile.hub.hero_title')}</h3>
|
||||
<p class="hero-subtitle">
|
||||
{#if progress.percent > 0}
|
||||
{progress.answered} von {progress.total} Fragen beantwortet — mach weiter!
|
||||
{$_('profile.hub.hero_subtitle_progress', {
|
||||
values: { answered: progress.answered, total: progress.total },
|
||||
})}
|
||||
{:else}
|
||||
Erzähl Mana mehr über dich, damit die App besser zu dir passt.
|
||||
{$_('profile.hub.hero_subtitle_initial')}
|
||||
{/if}
|
||||
</p>
|
||||
{#if progress.percent > 0}
|
||||
|
|
@ -133,8 +136,8 @@
|
|||
</svg>
|
||||
</span>
|
||||
<span class="hero-option-text">
|
||||
<strong>Per Text</strong>
|
||||
<span>Fragen lesen und tippen</span>
|
||||
<strong>{$_('profile.hub.option_text_title')}</strong>
|
||||
<span>{$_('profile.hub.option_text_hint')}</span>
|
||||
</span>
|
||||
</button>
|
||||
<button class="hero-option voice" onclick={() => startInterview('voice')}>
|
||||
|
|
@ -156,8 +159,8 @@
|
|||
</svg>
|
||||
</span>
|
||||
<span class="hero-option-text">
|
||||
<strong>Per Sprache</strong>
|
||||
<span>Fragen hören und sprechen</span>
|
||||
<strong>{$_('profile.hub.option_voice_title')}</strong>
|
||||
<span>{$_('profile.hub.option_voice_hint')}</span>
|
||||
</span>
|
||||
</button>
|
||||
<button class="hero-option conversation" onclick={() => startInterview('conversation')}>
|
||||
|
|
@ -176,8 +179,8 @@
|
|||
</svg>
|
||||
</span>
|
||||
<span class="hero-option-text">
|
||||
<strong>Als Gespräch</strong>
|
||||
<span>Fließend — Antworten werden automatisch gespeichert</span>
|
||||
<strong>{$_('profile.hub.option_conversation_title')}</strong>
|
||||
<span>{$_('profile.hub.option_conversation_hint')}</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -197,7 +200,11 @@
|
|||
<div class="account-card">
|
||||
<div class="account-header">
|
||||
{#if apiProfile?.image}
|
||||
<img src={apiProfile.image} alt="Avatar" class="account-avatar" />
|
||||
<img
|
||||
src={apiProfile.image}
|
||||
alt={$_('profile.hub.avatar_alt')}
|
||||
class="account-avatar"
|
||||
/>
|
||||
{:else}
|
||||
<div class="account-avatar-placeholder">
|
||||
{(apiProfile?.name ?? 'U').slice(0, 2).toUpperCase()}
|
||||
|
|
@ -212,16 +219,14 @@
|
|||
|
||||
<div class="account-actions">
|
||||
<button class="account-btn" onclick={() => goto('/profile/me-images')}>
|
||||
Meine Bilder
|
||||
<span class="account-btn-hint">
|
||||
Gesichts- und Ganzkörperbilder für KI-Bildgenerierung
|
||||
</span>
|
||||
{$_('profile.hub.action_my_images')}
|
||||
<span class="account-btn-hint">{$_('profile.hub.action_my_images_hint')}</span>
|
||||
</button>
|
||||
<button class="account-btn" onclick={() => (showEditModal = true)}>
|
||||
Profil bearbeiten
|
||||
{$_('profile.edit')}
|
||||
</button>
|
||||
<button class="account-btn" onclick={() => (showPasswordModal = true)}>
|
||||
Passwort ändern
|
||||
{$_('profile.change_password')}
|
||||
</button>
|
||||
<button
|
||||
class="account-btn"
|
||||
|
|
@ -230,10 +235,10 @@
|
|||
goto('/login');
|
||||
}}
|
||||
>
|
||||
Abmelden
|
||||
{$_('profile.logout')}
|
||||
</button>
|
||||
<button class="account-btn danger" onclick={() => (showDeleteModal = true)}>
|
||||
Konto löschen
|
||||
{$_('profile.delete_account')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -130,7 +130,6 @@
|
|||
"apps/mana/apps/web/src/lib/modules/mood/components/QuickLog.svelte": 6,
|
||||
"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/views/DetailView.svelte": 6,
|
||||
"apps/mana/apps/web/src/lib/modules/news-research/ListView.svelte": 3,
|
||||
"apps/mana/apps/web/src/lib/modules/news/widgets/NewsUnreadWidget.svelte": 2,
|
||||
"apps/mana/apps/web/src/lib/modules/notes/ListView.svelte": 4,
|
||||
|
|
@ -147,7 +146,6 @@
|
|||
"apps/mana/apps/web/src/lib/modules/profile/components/MeImageSlotCard.svelte": 3,
|
||||
"apps/mana/apps/web/src/lib/modules/profile/components/MeImageTile.svelte": 3,
|
||||
"apps/mana/apps/web/src/lib/modules/profile/ContextInterview.svelte": 6,
|
||||
"apps/mana/apps/web/src/lib/modules/profile/ListView.svelte": 6,
|
||||
"apps/mana/apps/web/src/lib/modules/profile/MeImagesView.svelte": 2,
|
||||
"apps/mana/apps/web/src/lib/modules/questions/ListView.svelte": 1,
|
||||
"apps/mana/apps/web/src/lib/modules/questions/views/DetailView.svelte": 6,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue