Fix: echte Umlaute in Einstellungen-UI

Sichtbare Texte in einstellungen/page.tsx und settings-tabs.tsx von
ASCII-Ersatz (ae/oe/ue/ss) auf echte Umlaute (ä/ö/ü/ß) umgestellt:
Ernährung, Präferenzen, persönlich, Straße, Eiweiß, Kochvorgänge,
Passwörter, für, über, groß u. a. Tab-id-Slug "ernaehrung" bleibt
ASCII (URL-Parameter).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sebastian Mayer 2026-05-29 10:20:24 +02:00
parent 36b92c07b7
commit e54a2ac223
2 changed files with 18 additions and 18 deletions

View file

@ -11,8 +11,8 @@ export type SettingsTabId =
| "anwendung"; | "anwendung";
const tabs: { id: SettingsTabId; label: string; description: string }[] = [ const tabs: { id: SettingsTabId; label: string; description: string }[] = [
{ id: "profil", label: "Profil", description: "Persoenliche Daten und Kontakt" }, { id: "profil", label: "Profil", description: "Persönliche Daten und Kontakt" },
{ id: "ernaehrung", label: "Ernaehrung", description: "Rezept- und Mahlzeiten-Praeferenzen" }, { id: "ernaehrung", label: "Ernährung", description: "Rezept- und Mahlzeiten-Präferenzen" },
{ id: "ziele", label: "Ziele", description: "Kalorien, Makros, Gewicht, Fasten" }, { id: "ziele", label: "Ziele", description: "Kalorien, Makros, Gewicht, Fasten" },
{ id: "sicherheit", label: "Sicherheit", description: "2FA, Passkeys, Passwort" }, { id: "sicherheit", label: "Sicherheit", description: "2FA, Passkeys, Passwort" },
{ id: "anwendung", label: "Anwendung", description: "Erscheinungsbild und Erinnerungen" }, { id: "anwendung", label: "Anwendung", description: "Erscheinungsbild und Erinnerungen" },

View file

@ -57,7 +57,7 @@ async function toOptimizedAvatarDataUrl(file: File) {
canvas.width = width; canvas.width = width;
canvas.height = height; canvas.height = height;
const ctx = canvas.getContext("2d"); const ctx = canvas.getContext("2d");
if (!ctx) throw new Error("Canvas nicht verfuegbar"); if (!ctx) throw new Error("Canvas nicht verfügbar");
ctx.drawImage(bitmap, 0, 0, width, height); ctx.drawImage(bitmap, 0, 0, width, height);
bitmap.close(); bitmap.close();
@ -67,7 +67,7 @@ async function toOptimizedAvatarDataUrl(file: File) {
throw new Error("Profilbild konnte nicht verarbeitet werden."); throw new Error("Profilbild konnte nicht verarbeitet werden.");
} }
if (dataUrl.length > 120_000) { if (dataUrl.length > 120_000) {
throw new Error("Profilbild ist nach Komprimierung noch zu gross."); throw new Error("Profilbild ist nach Komprimierung noch zu groß.");
} }
return dataUrl; return dataUrl;
} }
@ -175,7 +175,7 @@ export default function EinstellungenPage() {
onSuccess: async () => { onSuccess: async () => {
await healthOverviewQuery.refetch(); await healthOverviewQuery.refetch();
await utils.health.recipeRecommendations.invalidate(); await utils.health.recipeRecommendations.invalidate();
setStatusMessage("Essenspraeferenzen gespeichert."); setStatusMessage("Essenspräferenzen gespeichert.");
setErrorMessage(null); setErrorMessage(null);
}, },
onError: (error) => { onError: (error) => {
@ -289,11 +289,11 @@ export default function EinstellungenPage() {
async function handleImageUpload(file: File) { async function handleImageUpload(file: File) {
if (!file.type.startsWith("image/")) { if (!file.type.startsWith("image/")) {
setErrorMessage("Bitte eine Bilddatei auswaehlen."); setErrorMessage("Bitte eine Bilddatei auswählen.");
return; return;
} }
if (file.size > 5 * 1024 * 1024) { if (file.size > 5 * 1024 * 1024) {
setErrorMessage("Profilbild ist zu gross. Maximal 5 MB."); setErrorMessage("Profilbild ist zu groß. Maximal 5 MB.");
return; return;
} }
try { try {
@ -333,7 +333,7 @@ export default function EinstellungenPage() {
return ( return (
<DataPageLayout <DataPageLayout
title="Einstellungen" title="Einstellungen"
description="Profil, Sicherheit und persoenliche Vorlieben" description="Profil, Sicherheit und persönliche Vorlieben"
> >
<div className="space-y-6"> <div className="space-y-6">
<SettingsTabs active={active} onChange={setActive} /> <SettingsTabs active={active} onChange={setActive} />
@ -349,7 +349,7 @@ export default function EinstellungenPage() {
{active === "profil" ? ( {active === "profil" ? (
<SectionCard <SectionCard
title="Mein Profil" title="Mein Profil"
description="Persoenliche Daten und Kontakt. E-Mail-Aenderung erfolgt ueber das Konto." description="Persönliche Daten und Kontakt. E-Mail-Änderung erfolgt über das Konto."
footer={ footer={
<div className="flex justify-end"> <div className="flex justify-end">
<button <button
@ -447,12 +447,12 @@ export default function EinstellungenPage() {
/> />
</div> </div>
<div className="sm:col-span-2"> <div className="sm:col-span-2">
<label className={labelClass}>Strasse und Hausnummer</label> <label className={labelClass}>Straße und Hausnummer</label>
<input <input
className={inputClass} className={inputClass}
value={street} value={street}
onChange={(event) => setStreet(event.target.value)} onChange={(event) => setStreet(event.target.value)}
placeholder="Strasse und Hausnummer" placeholder="Straße und Hausnummer"
/> />
</div> </div>
<div> <div>
@ -480,7 +480,7 @@ export default function EinstellungenPage() {
{active === "ernaehrung" ? ( {active === "ernaehrung" ? (
<SectionCard <SectionCard
title="Rezept- und Mahlzeiten-Praeferenzen" title="Rezept- und Mahlzeiten-Präferenzen"
description="Beeinflusst Vorschlaege und automatische Wochenplanung." description="Beeinflusst Vorschlaege und automatische Wochenplanung."
footer={ footer={
<div className="flex justify-end"> <div className="flex justify-end">
@ -490,13 +490,13 @@ export default function EinstellungenPage() {
disabled={updateHealthProfile.isPending} disabled={updateHealthProfile.isPending}
className={buttonPrimary} className={buttonPrimary}
> >
Praeferenzen speichern Präferenzen speichern
</button> </button>
</div> </div>
} }
> >
<div> <div>
<p className={labelClass}>Ernaehrung</p> <p className={labelClass}>Ernährung</p>
<div className="grid grid-cols-2 gap-2 sm:grid-cols-4"> <div className="grid grid-cols-2 gap-2 sm:grid-cols-4">
{dietPreferenceOptions.map((option) => { {dietPreferenceOptions.map((option) => {
const selected = dietPreferences.includes(option.id); const selected = dietPreferences.includes(option.id);
@ -528,7 +528,7 @@ export default function EinstellungenPage() {
/> />
</label> </label>
<label className="block"> <label className="block">
<span className={labelClass}>Kochvorgaenge/Tag</span> <span className={labelClass}>Kochvorgänge/Tag</span>
<input <input
className={inputClass} className={inputClass}
inputMode="numeric" inputMode="numeric"
@ -580,7 +580,7 @@ export default function EinstellungenPage() {
}, },
{ {
icon: ScaleIcon, icon: ScaleIcon,
label: "Makros (KH / Fett / Eiweiss)", label: "Makros (KH / Fett / Eiweiß)",
value: `${healthGoals?.carbs ?? 200} / ${healthGoals?.fat ?? 67} / ${ value: `${healthGoals?.carbs ?? 200} / ${healthGoals?.fat ?? 67} / ${
healthGoals?.protein ?? 150 healthGoals?.protein ?? 150
} g`, } g`,
@ -659,7 +659,7 @@ export default function EinstellungenPage() {
</p> </p>
) : ( ) : (
<p className="mt-1 text-xs text-gray-500"> <p className="mt-1 text-xs text-gray-500">
Empfohlen, besonders fuer Admin-Accounts. Empfohlen, besonders für Admin-Accounts.
</p> </p>
)} )}
</div> </div>
@ -780,7 +780,7 @@ export default function EinstellungenPage() {
/> />
{passwordMismatch ? ( {passwordMismatch ? (
<p className="text-xs text-red-600"> <p className="text-xs text-red-600">
Die neuen Passwoerter stimmen nicht ueberein. Die neuen Passwörter stimmen nicht überein.
</p> </p>
) : null} ) : null}
</SectionCard> </SectionCard>