const UMLAUT_REPLACEMENTS: Record = { Ae: "Ae", Oe: "Oe", Ue: "Ue", ae: "ae", oe: "oe", ue: "ue", ss: "ss", Ä: "Ae", Ö: "Oe", Ü: "Ue", ä: "ae", ö: "oe", ü: "ue", ß: "ss" } function replaceGermanCharacters(value: string) { return value.replace(/[ÄÖÜäöüß]/g, (char) => UMLAUT_REPLACEMENTS[char] || char) } function sanitizeFileNamePart(value: string) { return value .normalize("NFKD") .replace(/[\u0300-\u036f]/g, "") .replace(/[^A-Za-z0-9._-]+/g, "-") .replace(/-+/g, "-") .replace(/[._-]{2,}/g, (match) => match[0]) .replace(/^[._-]+|[._-]+$/g, "") } export function sanitizeFilename(filename?: string | null, fallback = "file") { const rawName = (filename || "").trim() if (!rawName) { return fallback } const normalized = replaceGermanCharacters(rawName) .replace(/[\u0000-\u001f\u007f]/g, "") .replace(/[\\/]/g, "-") const lastDotIndex = normalized.lastIndexOf(".") const hasExtension = lastDotIndex > 0 && lastDotIndex < normalized.length - 1 const basename = hasExtension ? normalized.slice(0, lastDotIndex) : normalized const extension = hasExtension ? normalized.slice(lastDotIndex + 1) : "" const safeBasename = sanitizeFileNamePart(basename) || fallback const safeExtension = extension ? sanitizeFileNamePart(extension).toLowerCase() : "" return safeExtension ? `${safeBasename}.${safeExtension}` : safeBasename }