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