i18n(wallpaper): translate WallpaperPicker via $_() — scope toggle, tabs, sections, upload, overlay

Adds wallpaper.picker namespace covering:
- Scope toggle (Alle Szenen / Nur diese Szene), Reset action
- 3 tabs (Farben/Bilder/Upload) routed via labelKey on the tab data
- Section labels (Empfohlen + Weitere)
- "Hintergrundbilder kommen bald" placeholder for empty images tab
- Upload zone (in-progress, drop, prompt, hint with formats)
- Upload error templates (failed with {status}, generic fallback)
- Loading gallery + "Eigene Bilder" section + delete-image title
- Overlay section + Weichzeichner/Abdunklung labels
- "Bild" alt fallback for media originalName

Baselines: hardcoded 975 → 968 (7 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 16:08:37 +02:00
parent 4357433e7b
commit aa96cae8a0
7 changed files with 163 additions and 25 deletions

View file

@ -15,6 +15,7 @@
import { wallpaperStore } from '$lib/stores/wallpaper.svelte';
import { theme } from '$lib/stores/theme';
import { workbenchScenesStore } from '$lib/stores/workbench-scenes.svelte';
import { _ } from 'svelte-i18n';
// ── Media URL ───────────────────────────────────────────────
@ -81,7 +82,7 @@
id: m.id,
url: `${MEDIA_URL}/api/v1/media/${m.id}/file/large`,
thumbUrl: `${MEDIA_URL}/api/v1/media/${m.id}/file/thumb`,
originalName: m.originalName ?? 'Bild',
originalName: m.originalName ?? $_('wallpaper.picker.alt_image'),
})
);
uploadedWallpapers = items;
@ -205,7 +206,9 @@
});
if (!res.ok) {
throw new Error(`Upload fehlgeschlagen (${res.status})`);
throw new Error(
$_('wallpaper.picker.err_upload_failed', { values: { status: res.status } })
);
}
const data = await res.json();
@ -226,7 +229,7 @@
await applyWallpaper(buildConfig({ type: 'upload', mediaId, url }));
} catch (err) {
uploadError = err instanceof Error ? err.message : 'Upload fehlgeschlagen';
uploadError = err instanceof Error ? err.message : $_('wallpaper.picker.err_upload_generic');
} finally {
uploading = false;
}
@ -272,11 +275,11 @@
}
}
// Tab items
const tabs: { id: Tab; label: string; icon: typeof Image }[] = [
{ id: 'gradients', label: 'Farben', icon: Palette },
{ id: 'images', label: 'Bilder', icon: Image },
{ id: 'upload', label: 'Upload', icon: UploadSimple },
// Tab items — labelKey routed through $_() at render time
const tabs: { id: Tab; labelKey: string; icon: typeof Image }[] = [
{ id: 'gradients', labelKey: 'wallpaper.picker.tab_gradients', icon: Palette },
{ id: 'images', labelKey: 'wallpaper.picker.tab_images', icon: Image },
{ id: 'upload', labelKey: 'wallpaper.picker.tab_upload', icon: UploadSimple },
];
</script>
@ -294,7 +297,7 @@
class:text-muted-foreground={scope !== 'global'}
onclick={() => (scope = 'global')}
>
Alle Szenen
{$_('wallpaper.picker.scope_global')}
</button>
<button
type="button"
@ -305,7 +308,7 @@
class:text-muted-foreground={scope !== 'scene'}
onclick={() => (scope = 'scene')}
>
Nur diese Szene
{$_('wallpaper.picker.scope_scene')}
</button>
</div>
{:else}
@ -319,7 +322,7 @@
onclick={clearWallpaper}
>
<Prohibit size={12} />
Zurücksetzen
{$_('wallpaper.picker.action_reset')}
</button>
{/if}
</div>
@ -337,7 +340,7 @@
onclick={() => (activeTab = tab.id)}
>
<tab.icon size={15} weight={activeTab === tab.id ? 'fill' : 'regular'} />
{tab.label}
{$_(tab.labelKey)}
</button>
{/each}
</div>
@ -346,7 +349,7 @@
{#if activeTab === 'gradients'}
<!-- Current theme gradients (prominent) -->
<p class="mb-2 text-xs font-medium text-muted-foreground uppercase tracking-wider">
Empfohlen
{$_('wallpaper.picker.section_recommended')}
<span class="normal-case tracking-normal font-normal">({currentVariant})</span>
</p>
<div class="grid grid-cols-2 gap-2.5 mb-5">
@ -396,7 +399,7 @@
{#if PREDEFINED_WALLPAPERS.length === 0}
<div class="flex flex-col items-center justify-center py-8 text-muted-foreground">
<Image size={32} class="mb-2 opacity-40" />
<p class="text-sm">Hintergrundbilder kommen bald</p>
<p class="text-sm">{$_('wallpaper.picker.images_coming_soon')}</p>
</div>
{:else}
{#if variantWallpapers.length > 0}
@ -428,7 +431,7 @@
{#if otherWallpapers.length > 0}
<p class="mb-2 text-xs font-medium text-muted-foreground uppercase tracking-wider">
Weitere
{$_('wallpaper.picker.section_others')}
</p>
<div class="grid grid-cols-4 gap-2 mb-4">
{#each otherWallpapers as wp}
@ -473,13 +476,13 @@
>
{#if uploading}
<SpinnerGap size={28} class="text-primary mb-1 animate-spin" />
<p class="text-sm text-foreground">Wird hochgeladen...</p>
<p class="text-sm text-foreground">{$_('wallpaper.picker.upload_in_progress')}</p>
{:else}
<UploadSimple size={28} class="text-muted-foreground mb-1" />
<p class="text-sm text-muted-foreground">
{isDragging ? 'Hier ablegen' : 'Bild hochladen'}
{isDragging ? $_('wallpaper.picker.upload_drop') : $_('wallpaper.picker.upload_prompt')}
</p>
<p class="text-xs text-muted-foreground/60">JPG, PNG, WebP — Drag & Drop oder Klick</p>
<p class="text-xs text-muted-foreground/60">{$_('wallpaper.picker.upload_hint')}</p>
{/if}
</div>
<input
@ -506,11 +509,11 @@
{#if loadingGallery}
<div class="mt-4 flex items-center justify-center py-4 text-muted-foreground">
<SpinnerGap size={20} class="animate-spin mr-2" />
<span class="text-sm">Lade Bilder...</span>
<span class="text-sm">{$_('wallpaper.picker.loading_gallery')}</span>
</div>
{:else if uploadedWallpapers.length > 0}
<p class="mt-4 mb-2 text-xs font-medium text-muted-foreground uppercase tracking-wider">
Eigene Bilder
{$_('wallpaper.picker.section_my_images')}
</p>
<div class="grid grid-cols-3 gap-2">
{#each uploadedWallpapers as media (media.id)}
@ -539,7 +542,7 @@
<button
type="button"
class="delete-btn"
title="Bild löschen"
title={$_('wallpaper.picker.action_delete_image')}
onclick={() => deleteUpload(media.id)}
>
<Trash size={12} />
@ -552,11 +555,15 @@
<!-- Overlay controls (always visible, disabled when no wallpaper) -->
<div class="mt-4 border-t border-border pt-4" class:opacity-40={currentSource.type === 'none'}>
<p class="mb-3 text-xs font-medium text-muted-foreground uppercase tracking-wider">Overlay</p>
<p class="mb-3 text-xs font-medium text-muted-foreground uppercase tracking-wider">
{$_('wallpaper.picker.section_overlay')}
</p>
<div class="mb-3">
<div class="flex items-center justify-between mb-1">
<label for="wp-blur" class="text-sm text-foreground">Weichzeichner</label>
<label for="wp-blur" class="text-sm text-foreground"
>{$_('wallpaper.picker.label_blur')}</label
>
<span class="text-xs text-muted-foreground tabular-nums">{blur}px</span>
</div>
<input
@ -574,7 +581,9 @@
<div>
<div class="flex items-center justify-between mb-1">
<label for="wp-opacity" class="text-sm text-foreground">Abdunklung</label>
<label for="wp-opacity" class="text-sm text-foreground"
>{$_('wallpaper.picker.label_dim')}</label
>
<span class="text-xs text-muted-foreground tabular-nums"
>{Math.round(overlayOpacity * 100)}%</span
>

View file

@ -0,0 +1,26 @@
{
"picker": {
"scope_global": "Alle Szenen",
"scope_scene": "Nur diese Szene",
"action_reset": "Zurücksetzen",
"tab_gradients": "Farben",
"tab_images": "Bilder",
"tab_upload": "Upload",
"section_recommended": "Empfohlen",
"section_others": "Weitere",
"images_coming_soon": "Hintergrundbilder kommen bald",
"upload_in_progress": "Wird hochgeladen...",
"upload_drop": "Hier ablegen",
"upload_prompt": "Bild hochladen",
"upload_hint": "JPG, PNG, WebP — Drag & Drop oder Klick",
"err_upload_failed": "Upload fehlgeschlagen ({status})",
"err_upload_generic": "Upload fehlgeschlagen",
"loading_gallery": "Lade Bilder...",
"section_my_images": "Eigene Bilder",
"action_delete_image": "Bild löschen",
"section_overlay": "Overlay",
"label_blur": "Weichzeichner",
"label_dim": "Abdunklung",
"alt_image": "Bild"
}
}

View file

@ -0,0 +1,26 @@
{
"picker": {
"scope_global": "All scenes",
"scope_scene": "Just this scene",
"action_reset": "Reset",
"tab_gradients": "Colors",
"tab_images": "Images",
"tab_upload": "Upload",
"section_recommended": "Recommended",
"section_others": "Others",
"images_coming_soon": "Wallpapers coming soon",
"upload_in_progress": "Uploading…",
"upload_drop": "Drop here",
"upload_prompt": "Upload image",
"upload_hint": "JPG, PNG, WebP — drag & drop or click",
"err_upload_failed": "Upload failed ({status})",
"err_upload_generic": "Upload failed",
"loading_gallery": "Loading images…",
"section_my_images": "My images",
"action_delete_image": "Delete image",
"section_overlay": "Overlay",
"label_blur": "Blur",
"label_dim": "Dim",
"alt_image": "Image"
}
}

View file

@ -0,0 +1,26 @@
{
"picker": {
"scope_global": "Todas las escenas",
"scope_scene": "Solo esta escena",
"action_reset": "Restablecer",
"tab_gradients": "Colores",
"tab_images": "Imágenes",
"tab_upload": "Subir",
"section_recommended": "Recomendados",
"section_others": "Otros",
"images_coming_soon": "Fondos próximamente",
"upload_in_progress": "Subiendo…",
"upload_drop": "Soltar aquí",
"upload_prompt": "Subir imagen",
"upload_hint": "JPG, PNG, WebP — arrastra y suelta o haz clic",
"err_upload_failed": "Error de subida ({status})",
"err_upload_generic": "Error de subida",
"loading_gallery": "Cargando imágenes…",
"section_my_images": "Mis imágenes",
"action_delete_image": "Eliminar imagen",
"section_overlay": "Superposición",
"label_blur": "Desenfoque",
"label_dim": "Atenuación",
"alt_image": "Imagen"
}
}

View file

@ -0,0 +1,26 @@
{
"picker": {
"scope_global": "Toutes les scènes",
"scope_scene": "Seulement cette scène",
"action_reset": "Réinitialiser",
"tab_gradients": "Couleurs",
"tab_images": "Images",
"tab_upload": "Upload",
"section_recommended": "Recommandé",
"section_others": "Autres",
"images_coming_soon": "Fonds d'écran bientôt disponibles",
"upload_in_progress": "Téléversement…",
"upload_drop": "Déposer ici",
"upload_prompt": "Téléverser une image",
"upload_hint": "JPG, PNG, WebP — glisser-déposer ou clic",
"err_upload_failed": "Échec du téléversement ({status})",
"err_upload_generic": "Échec du téléversement",
"loading_gallery": "Chargement des images…",
"section_my_images": "Mes images",
"action_delete_image": "Supprimer l'image",
"section_overlay": "Surimpression",
"label_blur": "Flou",
"label_dim": "Atténuation",
"alt_image": "Image"
}
}

View file

@ -0,0 +1,26 @@
{
"picker": {
"scope_global": "Tutte le scene",
"scope_scene": "Solo questa scena",
"action_reset": "Ripristina",
"tab_gradients": "Colori",
"tab_images": "Immagini",
"tab_upload": "Upload",
"section_recommended": "Consigliati",
"section_others": "Altri",
"images_coming_soon": "Sfondi in arrivo",
"upload_in_progress": "Caricamento…",
"upload_drop": "Rilascia qui",
"upload_prompt": "Carica immagine",
"upload_hint": "JPG, PNG, WebP — drag & drop o clic",
"err_upload_failed": "Caricamento non riuscito ({status})",
"err_upload_generic": "Caricamento non riuscito",
"loading_gallery": "Caricamento immagini…",
"section_my_images": "Le mie immagini",
"action_delete_image": "Elimina immagine",
"section_overlay": "Overlay",
"label_blur": "Sfocatura",
"label_dim": "Oscuramento",
"alt_image": "Immagine"
}
}