[dyad] Anwendung ins Deutsche übersetzen - wrote 17 file(s)

This commit is contained in:
[dyad]
2026-01-18 17:28:41 +01:00
parent 212d13d5db
commit 26a947c9b7
17 changed files with 135 additions and 129 deletions

View File

@@ -10,7 +10,7 @@ export default function ChangelogPage() {
<Button asChild variant="ghost" className="mb-4 -ml-4"> <Button asChild variant="ghost" className="mb-4 -ml-4">
<Link href="/"> <Link href="/">
<ArrowLeft className="mr-2 h-4 w-4" /> <ArrowLeft className="mr-2 h-4 w-4" />
Back to Converter Zurück zum Konverter
</Link> </Link>
</Button> </Button>
</div> </div>

View File

@@ -10,34 +10,34 @@ export default function ImprintPage() {
<Button asChild variant="ghost" className="mb-4 -ml-4"> <Button asChild variant="ghost" className="mb-4 -ml-4">
<Link href="/"> <Link href="/">
<ArrowLeft className="mr-2 h-4 w-4" /> <ArrowLeft className="mr-2 h-4 w-4" />
Back to Converter Zurück zum Konverter
</Link> </Link>
</Button> </Button>
<main className="w-full"> <main className="w-full">
<Card> <Card>
<CardHeader> <CardHeader>
<CardTitle className="text-3xl font-bold">Imprint</CardTitle> <CardTitle className="text-3xl font-bold">Impressum</CardTitle>
</CardHeader> </CardHeader>
<CardContent className="space-y-6 text-muted-foreground"> <CardContent className="space-y-6 text-muted-foreground">
<p> <p>
Information according to § 5 TMG (German Telemedia Act) Angaben gemäß § 5 TMG
</p> </p>
<div className="space-y-2"> <div className="space-y-2">
<h3 className="font-semibold text-foreground">Contact Information:</h3> <h3 className="font-semibold text-foreground">Kontakt:</h3>
<p>[Your Company Name]</p> <p>[Ihr Firmenname]</p>
<p>[Street Name & Number]</p> <p>[Stre & Hausnummer]</p>
<p>[Postal Code & City]</p> <p>[PLZ & Stadt]</p>
<p>Email: [your-email@example.com]</p> <p>E-Mail: [ihre-email@example.com]</p>
<p>Phone: [your-phone-number]</p> <p>Telefon: [ihre-telefonnummer]</p>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<h3 className="font-semibold text-foreground">Represented by:</h3> <h3 className="font-semibold text-foreground">Vertreten durch:</h3>
<p>[Your Name/CEO's Name]</p> <p>[Ihr Name/Name des Geschäftsführers]</p>
</div> </div>
<div className="pt-4 border-t"> <div className="pt-4 border-t">
<h3 className="font-semibold text-foreground">Disclaimer:</h3> <h3 className="font-semibold text-foreground">Haftungsausschluss:</h3>
<p className="text-sm"> <p className="text-sm">
This is a sample imprint and not legally binding. Please replace the placeholder content with your own information and consult a legal professional to ensure compliance with all applicable laws. Dies ist ein Musterimpressum und nicht rechtsverbindlich. Bitte ersetzen Sie die Platzhalterinhalte durch Ihre eigenen Informationen und konsultieren Sie einen Rechtsexperten, um die Einhaltung aller geltenden Gesetze sicherzustellen.
</p> </p>
</div> </div>
</CardContent> </CardContent>

View File

@@ -16,8 +16,8 @@ const geistMono = Geist_Mono({
}); });
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Image Web Exporter", title: "Bild Web Exporter",
description: "Upload a picture, then export it in a different resolution and format.", description: "Laden Sie ein Bild hoch und exportieren Sie es in einer anderen Auflösung und einem anderen Format.",
}; };
export default function RootLayout({ export default function RootLayout({
@@ -26,7 +26,7 @@ export default function RootLayout({
children: React.ReactNode; children: React.ReactNode;
}>) { }>) {
return ( return (
<html lang="en" suppressHydrationWarning> <html lang="de" suppressHydrationWarning>
<body <body
className={`${geistSans.variable} ${geistMono.variable} antialiased`} className={`${geistSans.variable} ${geistMono.variable} antialiased`}
> >

View File

@@ -6,10 +6,10 @@ export default function Home() {
<main className="flex flex-col items-center w-full max-w-6xl z-10"> <main className="flex flex-col items-center w-full max-w-6xl z-10">
<div className="text-center mb-8"> <div className="text-center mb-8">
<h1 className="text-4xl font-bold tracking-tight text-gray-900 dark:text-gray-100 sm:text-5xl"> <h1 className="text-4xl font-bold tracking-tight text-gray-900 dark:text-gray-100 sm:text-5xl">
Image Web Exporter Bild Web Exporter
</h1> </h1>
<p className="mt-3 text-lg text-gray-600 dark:text-gray-400"> <p className="mt-3 text-lg text-gray-600 dark:text-gray-400">
Upload a picture, then export it in a different resolution and format. Laden Sie ein Bild hoch und exportieren Sie es in einer anderen Auflösung und einem anderen Format.
</p> </p>
</div> </div>
<ImageConverter /> <ImageConverter />

View File

@@ -10,37 +10,37 @@ export default function PrivacyPage() {
<Button asChild variant="ghost" className="mb-4 -ml-4"> <Button asChild variant="ghost" className="mb-4 -ml-4">
<Link href="/"> <Link href="/">
<ArrowLeft className="mr-2 h-4 w-4" /> <ArrowLeft className="mr-2 h-4 w-4" />
Back to Converter Zurück zum Konverter
</Link> </Link>
</Button> </Button>
<main className="w-full"> <main className="w-full">
<Card> <Card>
<CardHeader> <CardHeader>
<CardTitle className="text-3xl font-bold">Data Privacy Policy</CardTitle> <CardTitle className="text-3xl font-bold">Datenschutzerklärung</CardTitle>
</CardHeader> </CardHeader>
<CardContent className="space-y-6 text-muted-foreground"> <CardContent className="space-y-6 text-muted-foreground">
<div className="space-y-2"> <div className="space-y-2">
<h3 className="font-semibold text-foreground">1. General Information</h3> <h3 className="font-semibold text-foreground">1. Allgemeine Informationen</h3>
<p> <p>
This is a placeholder for your data privacy policy. It outlines how personal data is collected, used, and protected when you use this website. Dies ist ein Platzhalter r Ihre Datenschutzerklärung. Sie beschreibt, wie personenbezogene Daten erfasst, verwendet und geschützt werden, wenn Sie diese Website nutzen.
</p> </p>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<h3 className="font-semibold text-foreground">2. Data Collection on This Website</h3> <h3 className="font-semibold text-foreground">2. Datenerfassung auf dieser Website</h3>
<p> <p>
All image processing happens directly in your browser. The images you upload are not sent to any server and are not stored by us. We do not collect any personal data from the images. Die gesamte Bildverarbeitung findet direkt in Ihrem Browser statt. Die von Ihnen hochgeladenen Bilder werden an keinen Server gesendet und von uns nicht gespeichert. Wir erheben keine personenbezogenen Daten aus den Bildern.
</p> </p>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<h3 className="font-semibold text-foreground">3. Your Rights</h3> <h3 className="font-semibold text-foreground">3. Ihre Rechte</h3>
<p> <p>
As no personal data is collected, rights regarding access, rectification, or erasure of personal data are not applicable in this context. Da keine personenbezogenen Daten erhoben werden, sind die Rechte auf Auskunft, Berichtigung oder Löschung personenbezogener Daten in diesem Zusammenhang nicht anwendbar.
</p> </p>
</div> </div>
<div className="pt-4 border-t"> <div className="pt-4 border-t">
<h3 className="font-semibold text-foreground">Disclaimer:</h3> <h3 className="font-semibold text-foreground">Haftungsausschluss:</h3>
<p className="text-sm"> <p className="text-sm">
This is a sample privacy policy and not legally binding. It is crucial to adapt this text to your specific data processing activities and to consult with a legal professional to ensure full GDPR compliance. Dies ist eine Muster-Datenschutzerklärung und nicht rechtsverbindlich. Es ist entscheidend, diesen Text an Ihre spezifischen Datenverarbeitungsaktivitäten anzupassen und einen Rechtsexperten zu konsultieren, um die vollständige Einhaltung der DSGVO sicherzustellen.
</p> </p>
</div> </div>
</CardContent> </CardContent>

View File

@@ -11,7 +11,7 @@ interface ActionButtonsProps {
export function ActionButtons({ onReset }: ActionButtonsProps) { export function ActionButtons({ onReset }: ActionButtonsProps) {
const handleApply = () => { const handleApply = () => {
toast.info("Settings updated and will be used for all downloads."); toast.info("Einstellungen aktualisiert und werden für alle Downloads verwendet.");
}; };
return ( return (
@@ -20,18 +20,18 @@ export function ActionButtons({ onReset }: ActionButtonsProps) {
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button onClick={onReset} className="w-full" variant="outline"> <Button onClick={onReset} className="w-full" variant="outline">
<RotateCcw className="mr-2 h-4 w-4" /> Reset <RotateCcw className="mr-2 h-4 w-4" /> Zurücksetzen
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent><p>Reset all settings to their default values.</p></TooltipContent> <TooltipContent><p>Alle Einstellungen auf ihre Standardwerte zurücksetzen.</p></TooltipContent>
</Tooltip> </Tooltip>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button onClick={handleApply} className="w-full"> <Button onClick={handleApply} className="w-full">
<Check className="mr-2 h-4 w-4" /> Apply <Check className="mr-2 h-4 w-4" /> Anwenden
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent><p>Confirm and apply all the settings above. This does not download the images.</p></TooltipContent> <TooltipContent><p>Bestätigen und alle oben genannten Einstellungen anwenden. Dies lädt die Bilder nicht herunter.</p></TooltipContent>
</Tooltip> </Tooltip>
</div> </div>
</TooltipProvider> </TooltipProvider>

View File

@@ -6,14 +6,20 @@ import { cn } from "@/lib/utils";
import { changelogData } from "@/lib/changelog-data"; import { changelogData } from "@/lib/changelog-data";
export function Changelog() { export function Changelog() {
const typeTranslations: { [key: string]: string } = {
"New": "Neu",
"Improved": "Verbessert",
"Fixed": "Behoben",
};
return ( return (
<div className="w-full max-w-4xl mx-auto py-12 px-4 sm:px-6 lg:px-8"> <div className="w-full max-w-4xl mx-auto py-12 px-4 sm:px-6 lg:px-8">
<div className="text-center mb-12"> <div className="text-center mb-12">
<h1 className="text-4xl font-bold tracking-tight text-gray-900 dark:text-gray-100 sm:text-5xl"> <h1 className="text-4xl font-bold tracking-tight text-gray-900 dark:text-gray-100 sm:text-5xl">
Changelog Änderungsprotokoll
</h1> </h1>
<p className="mt-3 text-lg text-gray-600 dark:text-gray-400"> <p className="mt-3 text-lg text-gray-600 dark:text-gray-400">
Tracking all the new features, improvements, and bug fixes. Verfolgung aller neuen Funktionen, Verbesserungen und Fehlerbehebungen.
</p> </p>
</div> </div>
<div className="space-y-8"> <div className="space-y-8">
@@ -32,12 +38,12 @@ export function Changelog() {
<Badge <Badge
variant="outline" variant="outline"
className={cn("mt-1 shrink-0 font-semibold", { className={cn("mt-1 shrink-0 font-semibold", {
"border-blue-500/50 bg-blue-500/10 text-blue-700 dark:text-blue-300": change.type === "Improved", "border-blue-500/50 bg-blue-500/10 text-blue-700 dark:text-blue-300": change.type === "Verbessert",
"border-green-500/50 bg-green-500/10 text-green-700 dark:text-green-300": change.type === "New", "border-green-500/50 bg-green-500/10 text-green-700 dark:text-green-300": change.type === "Neu",
"border-red-500/50 bg-red-500/10 text-red-700 dark:text-red-300": change.type === "Fixed", "border-red-500/50 bg-red-500/10 text-red-700 dark:text-red-300": change.type === "Behoben",
})} })}
> >
{change.type} {typeTranslations[change.type] || change.type}
</Badge> </Badge>
<p className="text-foreground">{change.text}</p> <p className="text-foreground">{change.text}</p>
</li> </li>

View File

@@ -27,8 +27,8 @@ export function Footer() {
</div> </div>
<div className="flex items-center gap-4 text-sm text-muted-foreground"> <div className="flex items-center gap-4 text-sm text-muted-foreground">
<Link href="/imprint" className="hover:text-primary transition-colors">Imprint</Link> <Link href="/imprint" className="hover:text-primary transition-colors">Impressum</Link>
<Link href="/privacy" className="hover:text-primary transition-colors">Privacy</Link> <Link href="/privacy" className="hover:text-primary transition-colors">Datenschutz</Link>
{latestVersion && ( {latestVersion && (
<Link <Link
href="/changelog" href="/changelog"

View File

@@ -34,7 +34,7 @@ export function ImageListItem({
<div className="p-4 border rounded-lg flex items-center gap-4"> <div className="p-4 border rounded-lg flex items-center gap-4">
<img src={image.previewUrl} alt={`Preview ${index + 1}`} className="w-20 h-20 object-cover rounded-md shrink-0" /> <img src={image.previewUrl} alt={`Preview ${index + 1}`} className="w-20 h-20 object-cover rounded-md shrink-0" />
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<Label htmlFor={`filename-${index}`} className="text-xs text-muted-foreground">Base Name</Label> <Label htmlFor={`filename-${index}`} className="text-xs text-muted-foreground">Basisname</Label>
<Input <Input
id={`filename-${index}`} id={`filename-${index}`}
value={image.filename} value={image.filename}
@@ -42,7 +42,7 @@ export function ImageListItem({
className="text-sm font-medium h-8 mt-1" className="text-sm font-medium h-8 mt-1"
/> />
<p className="text-xs text-muted-foreground truncate mt-1" title={`${finalFilename}.${settings.format}`}> <p className="text-xs text-muted-foreground truncate mt-1" title={`${finalFilename}.${settings.format}`}>
Final name: {finalFilename}.{settings.format} Finaler Name: {finalFilename}.{settings.format}
</p> </p>
</div> </div>
<div className="flex items-center shrink-0"> <div className="flex items-center shrink-0">
@@ -58,7 +58,7 @@ export function ImageListItem({
<Download className="h-4 w-4" /> <Download className="h-4 w-4" />
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent><p>Download this image</p></TooltipContent> <TooltipContent><p>Dieses Bild herunterladen</p></TooltipContent>
</Tooltip> </Tooltip>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
@@ -72,7 +72,7 @@ export function ImageListItem({
<X className="h-4 w-4" /> <X className="h-4 w-4" />
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent><p>Remove this image</p></TooltipContent> <TooltipContent><p>Dieses Bild entfernen</p></TooltipContent>
</Tooltip> </Tooltip>
</div> </div>
</div> </div>

View File

@@ -41,24 +41,24 @@ export function ImageList({
<Card> <Card>
<CardHeader> <CardHeader>
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<CardTitle>Uploaded Images</CardTitle> <CardTitle>Hochgeladene Bilder</CardTitle>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button variant="ghost" size="sm" onClick={onClearAll} disabled={isProcessing}> <Button variant="ghost" size="sm" onClick={onClearAll} disabled={isProcessing}>
<Trash2 className="mr-2 h-4 w-4" />Clear All <Trash2 className="mr-2 h-4 w-4" />Alle löschen
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent><p>Remove all uploaded images.</p></TooltipContent> <TooltipContent><p>Alle hochgeladenen Bilder entfernen.</p></TooltipContent>
</Tooltip> </Tooltip>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button onClick={onDownloadAll} disabled={isProcessing}> <Button onClick={onDownloadAll} disabled={isProcessing}>
<Download className="mr-2 h-4 w-4" /> <Download className="mr-2 h-4 w-4" />
{isConverting ? "Converting..." : `Download All (${images.length})`} {isConverting ? "Konvertiere..." : `Alle herunterladen (${images.length})`}
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent><p>Convert and download all images with the current settings.</p></TooltipContent> <TooltipContent><p>Alle Bilder mit den aktuellen Einstellungen konvertieren und herunterladen.</p></TooltipContent>
</Tooltip> </Tooltip>
</div> </div>
</div> </div>

View File

@@ -39,7 +39,7 @@ export function ImageUploadArea({ onFilesSelected }: ImageUploadAreaProps) {
<Card> <Card>
<CardContent className="pt-6"> <CardContent className="pt-6">
<div className="space-y-4"> <div className="space-y-4">
<h3 className="text-lg font-medium">Upload Images</h3> <h3 className="text-lg font-medium">Bilder hochladen</h3>
<div <div
className={cn( className={cn(
"w-full h-48 rounded-lg border-2 border-dashed flex items-center justify-center relative transition-colors cursor-pointer hover:border-primary/60", "w-full h-48 rounded-lg border-2 border-dashed flex items-center justify-center relative transition-colors cursor-pointer hover:border-primary/60",
@@ -52,8 +52,8 @@ export function ImageUploadArea({ onFilesSelected }: ImageUploadAreaProps) {
> >
<div className="flex flex-col items-center justify-center text-center text-muted-foreground"> <div className="flex flex-col items-center justify-center text-center text-muted-foreground">
<Upload className="w-8 h-8 mb-2" /> <Upload className="w-8 h-8 mb-2" />
<p className="font-semibold">Click or drag and drop to upload</p> <p className="font-semibold">Klicken oder per Drag & Drop hochladen</p>
<p className="text-xs text-muted-foreground mt-1">PNG, JPG, WEBP supported</p> <p className="text-xs text-muted-foreground mt-1">PNG, JPG, WEBP werden unterstützt</p>
</div> </div>
<Input type="file" ref={fileInputRef} onChange={handleImageChange} className="hidden" accept="image/*" multiple /> <Input type="file" ref={fileInputRef} onChange={handleImageChange} className="hidden" accept="image/*" multiple />
</div> </div>

View File

@@ -28,8 +28,8 @@ export function SettingsPanel({
<AccordionItem value="image-settings" className="border rounded-lg bg-card"> <AccordionItem value="image-settings" className="border rounded-lg bg-card">
<AccordionTrigger className="p-6 hover:no-underline"> <AccordionTrigger className="p-6 hover:no-underline">
<div className="text-left"> <div className="text-left">
<h3 className="text-lg font-medium leading-none">Image Settings</h3> <h3 className="text-lg font-medium leading-none">Bildeinstellungen</h3>
<p className="text-sm text-muted-foreground mt-1">Adjust resolution and scaling for all images.</p> <p className="text-sm text-muted-foreground mt-1">Auflösung und Skalierung r alle Bilder anpassen.</p>
</div> </div>
</AccordionTrigger> </AccordionTrigger>
<AccordionContent className="px-6 pb-6"> <AccordionContent className="px-6 pb-6">
@@ -45,8 +45,8 @@ export function SettingsPanel({
<AccordionItem value="filename-settings" className="border rounded-lg bg-card"> <AccordionItem value="filename-settings" className="border rounded-lg bg-card">
<AccordionTrigger className="p-6 hover:no-underline"> <AccordionTrigger className="p-6 hover:no-underline">
<div className="text-left"> <div className="text-left">
<h3 className="text-lg font-medium leading-none">Filename Settings</h3> <h3 className="text-lg font-medium leading-none">Dateinameneinstellungen</h3>
<p className="text-sm text-muted-foreground mt-1">Customize the output filenames.</p> <p className="text-sm text-muted-foreground mt-1">Die Ausgabe-Dateinamen anpassen.</p>
</div> </div>
</AccordionTrigger> </AccordionTrigger>
<AccordionContent className="px-6 pb-6"> <AccordionContent className="px-6 pb-6">
@@ -62,8 +62,8 @@ export function SettingsPanel({
<AccordionItem value="quality-settings" className="border rounded-lg bg-card"> <AccordionItem value="quality-settings" className="border rounded-lg bg-card">
<AccordionTrigger className="p-6 hover:no-underline"> <AccordionTrigger className="p-6 hover:no-underline">
<div className="text-left"> <div className="text-left">
<h3 className="text-lg font-medium leading-none">Quality Settings</h3> <h3 className="text-lg font-medium leading-none">Qualitätseinstellungen</h3>
<p className="text-sm text-muted-foreground mt-1">Choose format and compression level.</p> <p className="text-sm text-muted-foreground mt-1">Format und Komprimierungsstufe auswählen.</p>
</div> </div>
</AccordionTrigger> </AccordionTrigger>
<AccordionContent className="px-6 pb-6"> <AccordionContent className="px-6 pb-6">

View File

@@ -26,61 +26,61 @@ export function FilenameSettings({
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<Switch id="use-default-base-name" checked={settings.useDefaultBaseName} onCheckedChange={(checked) => onSettingsChange({ useDefaultBaseName: checked })} /> <Switch id="use-default-base-name" checked={settings.useDefaultBaseName} onCheckedChange={(checked) => onSettingsChange({ useDefaultBaseName: checked })} />
<Label htmlFor="use-default-base-name" className="flex items-center gap-1.5 cursor-pointer"> <Label htmlFor="use-default-base-name" className="flex items-center gap-1.5 cursor-pointer">
Use default base name Standard-Basisnamen verwenden
<Tooltip> <Tooltip>
<TooltipTrigger onClick={(e) => e.preventDefault()}><HelpCircle className="h-4 w-4 text-muted-foreground" /></TooltipTrigger> <TooltipTrigger onClick={(e) => e.preventDefault()}><HelpCircle className="h-4 w-4 text-muted-foreground" /></TooltipTrigger>
<TooltipContent><p>When enabled, all newly uploaded images will use the specified default base name.</p></TooltipContent> <TooltipContent><p>Wenn aktiviert, verwenden alle neu hochgeladenen Bilder den angegebenen Standard-Basisnamen.</p></TooltipContent>
</Tooltip> </Tooltip>
</Label> </Label>
</div> </div>
{settings.useDefaultBaseName && ( {settings.useDefaultBaseName && (
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="default-base-name">Default base name</Label> <Label htmlFor="default-base-name">Standard-Basisname</Label>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Input <Input
id="default-base-name" id="default-base-name"
placeholder="e.g., new-york-trip" placeholder="z.B. new-york-reise"
value={settings.defaultBaseName} value={settings.defaultBaseName}
onChange={(e) => onSettingsChange({ defaultBaseName: e.target.value })} onChange={(e) => onSettingsChange({ defaultBaseName: e.target.value })}
/> />
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button variant="outline" size="sm" onClick={onApplyDefaultBaseNameToAll} disabled={!settings.defaultBaseName || !hasImages}> <Button variant="outline" size="sm" onClick={onApplyDefaultBaseNameToAll} disabled={!settings.defaultBaseName || !hasImages}>
Apply to all Auf alle anwenden
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent><p>Apply this base name to all currently uploaded images.</p></TooltipContent> <TooltipContent><p>Diesen Basisnamen auf alle aktuell hochgeladenen Bilder anwenden.</p></TooltipContent>
</Tooltip> </Tooltip>
</div> </div>
</div> </div>
)} )}
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center gap-1.5"> <div className="flex items-center gap-1.5">
<Label htmlFor="prefix">Prefix</Label> <Label htmlFor="prefix">Präfix</Label>
<Tooltip> <Tooltip>
<TooltipTrigger><HelpCircle className="h-4 w-4 text-muted-foreground" /></TooltipTrigger> <TooltipTrigger><HelpCircle className="h-4 w-4 text-muted-foreground" /></TooltipTrigger>
<TooltipContent><p>Add text to the beginning of every filename.</p></TooltipContent> <TooltipContent><p>Text am Anfang jedes Dateinamens hinzufügen.</p></TooltipContent>
</Tooltip> </Tooltip>
</div> </div>
<Input id="prefix" placeholder="e.g., travel-" value={settings.prefix} onChange={(e) => onSettingsChange({ prefix: e.target.value })} /> <Input id="prefix" placeholder="z.B. reise-" value={settings.prefix} onChange={(e) => onSettingsChange({ prefix: e.target.value })} />
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center gap-1.5"> <div className="flex items-center gap-1.5">
<Label htmlFor="suffix">Suffix</Label> <Label htmlFor="suffix">Suffix</Label>
<Tooltip> <Tooltip>
<TooltipTrigger><HelpCircle className="h-4 w-4 text-muted-foreground" /></TooltipTrigger> <TooltipTrigger><HelpCircle className="h-4 w-4 text-muted-foreground" /></TooltipTrigger>
<TooltipContent><p>Add text to the end of every filename (before the number).</p></TooltipContent> <TooltipContent><p>Text am Ende jedes Dateinamens (vor der Nummer) hinzufügen.</p></TooltipContent>
</Tooltip> </Tooltip>
</div> </div>
<Input id="suffix" placeholder="e.g., -edit" value={settings.suffix} onChange={(e) => onSettingsChange({ suffix: e.target.value })} /> <Input id="suffix" placeholder="z.B. -bearbeitet" value={settings.suffix} onChange={(e) => onSettingsChange({ suffix: e.target.value })} />
</div> </div>
<div className="flex items-center space-x-2 pt-2"> <div className="flex items-center space-x-2 pt-2">
<Switch id="use-counter" checked={settings.useCounter} onCheckedChange={(checked) => onSettingsChange({ useCounter: checked })} /> <Switch id="use-counter" checked={settings.useCounter} onCheckedChange={(checked) => onSettingsChange({ useCounter: checked })} />
<Label htmlFor="use-counter" className="flex items-center gap-1.5 cursor-pointer"> <Label htmlFor="use-counter" className="flex items-center gap-1.5 cursor-pointer">
Add sequential number Fortlaufende Nummer hinzufügen
<Tooltip> <Tooltip>
<TooltipTrigger onClick={(e) => e.preventDefault()}><HelpCircle className="h-4 w-4 text-muted-foreground" /></TooltipTrigger> <TooltipTrigger onClick={(e) => e.preventDefault()}><HelpCircle className="h-4 w-4 text-muted-foreground" /></TooltipTrigger>
<TooltipContent><p>Append a numbered sequence to each filename.</p></TooltipContent> <TooltipContent><p>Eine nummerierte Sequenz an jeden Dateinamen anhängen.</p></TooltipContent>
</Tooltip> </Tooltip>
</Label> </Label>
</div> </div>
@@ -88,10 +88,10 @@ export function FilenameSettings({
<div className="grid grid-cols-2 gap-4 pt-2"> <div className="grid grid-cols-2 gap-4 pt-2">
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center gap-1.5"> <div className="flex items-center gap-1.5">
<Label htmlFor="counter-start">Start number</Label> <Label htmlFor="counter-start">Startnummer</Label>
<Tooltip> <Tooltip>
<TooltipTrigger><HelpCircle className="h-4 w-4 text-muted-foreground" /></TooltipTrigger> <TooltipTrigger><HelpCircle className="h-4 w-4 text-muted-foreground" /></TooltipTrigger>
<TooltipContent><p>The first number to use in the sequence.</p></TooltipContent> <TooltipContent><p>Die erste Nummer, die in der Sequenz verwendet wird.</p></TooltipContent>
</Tooltip> </Tooltip>
</div> </div>
<Input <Input
@@ -104,10 +104,10 @@ export function FilenameSettings({
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center gap-1.5"> <div className="flex items-center gap-1.5">
<Label htmlFor="counter-digits">Padding digits</Label> <Label htmlFor="counter-digits">Auffüllende Ziffern</Label>
<Tooltip> <Tooltip>
<TooltipTrigger><HelpCircle className="h-4 w-4 text-muted-foreground" /></TooltipTrigger> <TooltipTrigger><HelpCircle className="h-4 w-4 text-muted-foreground" /></TooltipTrigger>
<TooltipContent><p>Total number of digits for the counter, padded with leading zeros (e.g., 3 for 001).</p></TooltipContent> <TooltipContent><p>Gesamtzahl der Ziffern für den Zähler, mit führenden Nullen aufgefüllt (z.B. 3 r 001).</p></TooltipContent>
</Tooltip> </Tooltip>
</div> </div>
<Input <Input

View File

@@ -11,11 +11,11 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip
import { ObjectPositionControl } from "@/components/object-position-control"; import { ObjectPositionControl } from "@/components/object-position-control";
const aspectRatios = [ const aspectRatios = [
{ name: "Custom", value: "custom" }, { name: "Benutzerdefiniert", value: "custom" },
{ name: "1:1 (Square)", value: "1/1" }, { name: "1:1 (Quadratisch)", value: "1/1" },
{ name: "4:3 (Standard)", value: "4/3" }, { name: "4:3 (Standard)", value: "4/3" },
{ name: "3:2 (Photography)", value: "3/2" }, { name: "3:2 (Fotografie)", value: "3/2" },
{ name: "16:9 (Widescreen)", value: "16/9" }, { name: "16:9 (Breitbild)", value: "16/9" },
]; ];
interface ImageSettingsProps { interface ImageSettingsProps {
@@ -35,14 +35,14 @@ export function ImageSettings({
<div className="space-y-4"> <div className="space-y-4">
<div> <div>
<div className="flex items-center gap-1.5"> <div className="flex items-center gap-1.5">
<Label htmlFor="aspect-ratio">Aspect Ratio</Label> <Label htmlFor="aspect-ratio">Seitenverhältnis</Label>
<Tooltip> <Tooltip>
<TooltipTrigger><HelpCircle className="h-4 w-4 text-muted-foreground" /></TooltipTrigger> <TooltipTrigger><HelpCircle className="h-4 w-4 text-muted-foreground" /></TooltipTrigger>
<TooltipContent><p>Choose a preset aspect ratio or select 'Custom' to enter dimensions manually.</p></TooltipContent> <TooltipContent><p>Wählen Sie ein voreingestelltes Seitenverhältnis oder 'Benutzerdefiniert', um die Abmessungen manuell einzugeben.</p></TooltipContent>
</Tooltip> </Tooltip>
</div> </div>
<Select value={settings.aspectRatio} onValueChange={onAspectRatioChange}> <Select value={settings.aspectRatio} onValueChange={onAspectRatioChange}>
<SelectTrigger id="aspect-ratio" className="mt-2"><SelectValue placeholder="Select aspect ratio" /></SelectTrigger> <SelectTrigger id="aspect-ratio" className="mt-2"><SelectValue placeholder="Seitenverhältnis auswählen" /></SelectTrigger>
<SelectContent> <SelectContent>
{aspectRatios.map((ratio) => ( {aspectRatios.map((ratio) => (
<SelectItem key={ratio.value} value={ratio.value}>{ratio.name}</SelectItem> <SelectItem key={ratio.value} value={ratio.value}>{ratio.name}</SelectItem>
@@ -53,28 +53,28 @@ export function ImageSettings({
<div className="flex items-end gap-2"> <div className="flex items-end gap-2">
<div className="space-y-2 flex-1"> <div className="space-y-2 flex-1">
<div className="flex items-center gap-1.5"> <div className="flex items-center gap-1.5">
<Label htmlFor="width">Width (px)</Label> <Label htmlFor="width">Breite (px)</Label>
<Tooltip> <Tooltip>
<TooltipTrigger><HelpCircle className="h-4 w-4 text-muted-foreground" /></TooltipTrigger> <TooltipTrigger><HelpCircle className="h-4 w-4 text-muted-foreground" /></TooltipTrigger>
<TooltipContent><p>Set the output width in pixels. Leave blank to use the original width.</p></TooltipContent> <TooltipContent><p>Legen Sie die Ausgabebreite in Pixeln fest. Leer lassen, um die Originalbreite zu verwenden.</p></TooltipContent>
</Tooltip> </Tooltip>
</div> </div>
<Input id="width" type="number" placeholder="Auto" value={settings.width} onChange={(e) => { onSettingsChange({ width: e.target.value, aspectRatio: 'custom' }) }} /> <Input id="width" type="number" placeholder="Auto" value={settings.width} onChange={(e) => { onSettingsChange({ width: e.target.value, aspectRatio: 'custom' }) }} />
</div> </div>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button variant="outline" size="icon" onClick={onSwapDimensions} className="shrink-0" aria-label="Swap width and height"> <Button variant="outline" size="icon" onClick={onSwapDimensions} className="shrink-0" aria-label="Breite und Höhe tauschen">
<ArrowRightLeft className="h-4 w-4" /> <ArrowRightLeft className="h-4 w-4" />
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent><p>Swap the entered width and height values.</p></TooltipContent> <TooltipContent><p>Die eingegebenen Werte für Breite und Höhe tauschen.</p></TooltipContent>
</Tooltip> </Tooltip>
<div className="space-y-2 flex-1"> <div className="space-y-2 flex-1">
<div className="flex items-center gap-1.5"> <div className="flex items-center gap-1.5">
<Label htmlFor="height">Height (px)</Label> <Label htmlFor="height">Höhe (px)</Label>
<Tooltip> <Tooltip>
<TooltipTrigger><HelpCircle className="h-4 w-4 text-muted-foreground" /></TooltipTrigger> <TooltipTrigger><HelpCircle className="h-4 w-4 text-muted-foreground" /></TooltipTrigger>
<TooltipContent><p>Set the output height in pixels. Leave blank to use the original height.</p></TooltipContent> <TooltipContent><p>Legen Sie die Ausgabehöhe in Pixeln fest. Leer lassen, um die Originalhöhe zu verwenden.</p></TooltipContent>
</Tooltip> </Tooltip>
</div> </div>
<Input id="height" type="number" placeholder="Auto" value={settings.height} onChange={(e) => { onSettingsChange({ height: e.target.value, aspectRatio: 'custom' }) }} /> <Input id="height" type="number" placeholder="Auto" value={settings.height} onChange={(e) => { onSettingsChange({ height: e.target.value, aspectRatio: 'custom' }) }} />
@@ -83,27 +83,27 @@ export function ImageSettings({
<div className="flex items-center space-x-2 pt-2"> <div className="flex items-center space-x-2 pt-2">
<Checkbox id="keep-orientation" checked={settings.keepOrientation} onCheckedChange={(checked) => onSettingsChange({ keepOrientation: Boolean(checked) })} /> <Checkbox id="keep-orientation" checked={settings.keepOrientation} onCheckedChange={(checked) => onSettingsChange({ keepOrientation: Boolean(checked) })} />
<Label htmlFor="keep-orientation" className="cursor-pointer flex items-center gap-1.5"> <Label htmlFor="keep-orientation" className="cursor-pointer flex items-center gap-1.5">
Keep original orientation Originalausrichtung beibehalten
<Tooltip> <Tooltip>
<TooltipTrigger onClick={(e) => e.preventDefault()}><HelpCircle className="h-4 w-4 text-muted-foreground" /></TooltipTrigger> <TooltipTrigger onClick={(e) => e.preventDefault()}><HelpCircle className="h-4 w-4 text-muted-foreground" /></TooltipTrigger>
<TooltipContent><p>Automatically swaps width and height to match the original image's orientation.</p></TooltipContent> <TooltipContent><p>Tauscht automatisch Breite und Höhe, um der Ausrichtung des Originalbildes zu entsprechen.</p></TooltipContent>
</Tooltip> </Tooltip>
</Label> </Label>
</div> </div>
<div className="mt-4 space-y-2"> <div className="mt-4 space-y-2">
<div className="flex items-center gap-1.5"> <div className="flex items-center gap-1.5">
<Label htmlFor="scale-mode">Scaling</Label> <Label htmlFor="scale-mode">Skalierung</Label>
<Tooltip> <Tooltip>
<TooltipTrigger><HelpCircle className="h-4 w-4 text-muted-foreground" /></TooltipTrigger> <TooltipTrigger><HelpCircle className="h-4 w-4 text-muted-foreground" /></TooltipTrigger>
<TooltipContent><p>Determines how the image fits into the new dimensions.</p></TooltipContent> <TooltipContent><p>Bestimmt, wie das Bild in die neuen Abmessungen passt.</p></TooltipContent>
</Tooltip> </Tooltip>
</div> </div>
<Select value={settings.scaleMode} onValueChange={(value) => onSettingsChange({ scaleMode: value as any })}> <Select value={settings.scaleMode} onValueChange={(value) => onSettingsChange({ scaleMode: value as any })}>
<SelectTrigger id="scale-mode"><SelectValue placeholder="Select scaling mode" /></SelectTrigger> <SelectTrigger id="scale-mode"><SelectValue placeholder="Skalierungsmodus auswählen" /></SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="fill">Fill (stretch to fit)</SelectItem> <SelectItem value="fill">Füllen (strecken)</SelectItem>
<SelectItem value="cover">Cover (crop to fit)</SelectItem> <SelectItem value="cover">Abdecken (zuschneiden)</SelectItem>
<SelectItem value="contain">Contain (letterbox)</SelectItem> <SelectItem value="contain">Enthalten (Letterbox)</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
@@ -113,7 +113,7 @@ export function ImageSettings({
<Label>Position</Label> <Label>Position</Label>
<Tooltip> <Tooltip>
<TooltipTrigger><HelpCircle className="h-4 w-4 text-muted-foreground" /></TooltipTrigger> <TooltipTrigger><HelpCircle className="h-4 w-4 text-muted-foreground" /></TooltipTrigger>
<TooltipContent><p>Sets the anchor point for 'Cover' or 'Contain' scaling.</p></TooltipContent> <TooltipContent><p>Legt den Ankerpunkt für die Skalierung 'Abdecken' oder 'Enthalten' fest.</p></TooltipContent>
</Tooltip> </Tooltip>
</div> </div>
<ObjectPositionControl value={settings.objectPosition} onChange={(pos) => onSettingsChange({ objectPosition: pos as ObjectPosition })} /> <ObjectPositionControl value={settings.objectPosition} onChange={(pos) => onSettingsChange({ objectPosition: pos as ObjectPosition })} />

View File

@@ -20,11 +20,11 @@ export function QualitySettings({ settings, onSettingsChange }: QualitySettingsP
<Label htmlFor="format">Format</Label> <Label htmlFor="format">Format</Label>
<Tooltip> <Tooltip>
<TooltipTrigger><HelpCircle className="h-4 w-4 text-muted-foreground" /></TooltipTrigger> <TooltipTrigger><HelpCircle className="h-4 w-4 text-muted-foreground" /></TooltipTrigger>
<TooltipContent><p>Choose the output file format for the images.</p></TooltipContent> <TooltipContent><p>Wählen Sie das Ausgabedateiformat r die Bilder.</p></TooltipContent>
</Tooltip> </Tooltip>
</div> </div>
<Select value={settings.format} onValueChange={(value: ImageFormat) => onSettingsChange({ format: value })}> <Select value={settings.format} onValueChange={(value: ImageFormat) => onSettingsChange({ format: value })}>
<SelectTrigger id="format"><SelectValue placeholder="Select format" /></SelectTrigger> <SelectTrigger id="format"><SelectValue placeholder="Format auswählen" /></SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="png">PNG</SelectItem> <SelectItem value="png">PNG</SelectItem>
<SelectItem value="jpeg">JPEG</SelectItem> <SelectItem value="jpeg">JPEG</SelectItem>
@@ -35,10 +35,10 @@ export function QualitySettings({ settings, onSettingsChange }: QualitySettingsP
<div className="space-y-2"> <div className="space-y-2">
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<div className="flex items-center gap-1.5"> <div className="flex items-center gap-1.5">
<Label htmlFor="quality">Quality</Label> <Label htmlFor="quality">Qualität</Label>
<Tooltip> <Tooltip>
<TooltipTrigger><HelpCircle className="h-4 w-4 text-muted-foreground" /></TooltipTrigger> <TooltipTrigger><HelpCircle className="h-4 w-4 text-muted-foreground" /></TooltipTrigger>
<TooltipContent><p>Set compression quality for JPEG/WEBP. Higher is better quality but larger file size.</p></TooltipContent> <TooltipContent><p>Stellen Sie die Komprimierungsqualität r JPEG/WEBP ein. Höher bedeutet bessere Qualität, aber größere Dateigröße.</p></TooltipContent>
</Tooltip> </Tooltip>
</div> </div>
<span className="text-sm text-muted-foreground">{settings.quality}%</span> <span className="text-sm text-muted-foreground">{settings.quality}%</span>
@@ -53,7 +53,7 @@ export function QualitySettings({ settings, onSettingsChange }: QualitySettingsP
disabled={settings.format === 'png'} disabled={settings.format === 'png'}
/> />
{settings.format === 'png' && ( {settings.format === 'png' && (
<p className="text-xs text-muted-foreground pt-1">Quality slider is disabled for PNG (lossless format).</p> <p className="text-xs text-muted-foreground pt-1">Der Qualitätsregler ist r PNG (verlustfreies Format) deaktiviert.</p>
)} )}
</div> </div>
</div> </div>

View File

@@ -43,7 +43,7 @@ export function useImageConverter() {
const imageFiles = Array.from(files).filter(file => file.type.startsWith("image/")); const imageFiles = Array.from(files).filter(file => file.type.startsWith("image/"));
if (imageFiles.length === 0) { if (imageFiles.length === 0) {
toast.error("No valid image files found."); toast.error("Keine gültigen Bilddateien gefunden.");
return; return;
} }
@@ -56,7 +56,7 @@ export function useImageConverter() {
})); }));
setImages(prev => [...prev, ...newImageFiles]); setImages(prev => [...prev, ...newImageFiles]);
toast.success(`${imageFiles.length} image(s) added.`); toast.success(`${imageFiles.length} Bild(er) hinzugefügt.`);
}, [settings.useDefaultBaseName, settings.defaultBaseName]); }, [settings.useDefaultBaseName, settings.defaultBaseName]);
const handleRemoveImage = useCallback((indexToRemove: number) => { const handleRemoveImage = useCallback((indexToRemove: number) => {
@@ -72,7 +72,7 @@ export function useImageConverter() {
const handleClearAll = useCallback(() => { const handleClearAll = useCallback(() => {
setImages([]); setImages([]);
updateSettings({ width: initialSettings.width, height: initialSettings.height }); updateSettings({ width: initialSettings.width, height: initialSettings.height });
toast.info("All images cleared."); toast.info("Alle Bilder gelöscht.");
}, [updateSettings]); }, [updateSettings]);
const handleFilenameChange = useCallback((index: number, newName: string) => { const handleFilenameChange = useCallback((index: number, newName: string) => {
@@ -87,15 +87,15 @@ export function useImageConverter() {
const handleConvertAndDownloadSingle = useCallback(async (index: number) => { const handleConvertAndDownloadSingle = useCallback(async (index: number) => {
setConvertingIndex(index); setConvertingIndex(index);
toast.info(`Starting conversion for ${images[index].filename}...`); toast.info(`Starte Konvertierung für ${images[index].filename}...`);
try { try {
const imageToConvert = images[index]; const imageToConvert = images[index];
const dataUrl = await processImage(imageToConvert, settings); const dataUrl = await processImage(imageToConvert, settings);
const finalFilename = generateFinalFilename(imageToConvert.filename, settings, index); const finalFilename = generateFinalFilename(imageToConvert.filename, settings, index);
downloadDataUrl(dataUrl, `${finalFilename}.${settings.format}`); downloadDataUrl(dataUrl, `${finalFilename}.${settings.format}`);
toast.success(`Successfully exported ${imageToConvert.filename}!`); toast.success(`${imageToConvert.filename} erfolgreich exportiert!`);
} catch (error) { } catch (error) {
const message = error instanceof Error ? error.message : "An unknown error occurred."; const message = error instanceof Error ? error.message : "Ein unbekannter Fehler ist aufgetreten.";
toast.error(message); toast.error(message);
} finally { } finally {
setConvertingIndex(null); setConvertingIndex(null);
@@ -104,11 +104,11 @@ export function useImageConverter() {
const handleConvertAndDownloadAll = useCallback(async () => { const handleConvertAndDownloadAll = useCallback(async () => {
if (images.length === 0) { if (images.length === 0) {
toast.error("Please upload images first."); toast.error("Bitte laden Sie zuerst Bilder hoch.");
return; return;
} }
setIsConverting(true); setIsConverting(true);
toast.info(`Starting conversion for ${images.length} images...`); toast.info(`Starte Konvertierung für ${images.length} Bilder...`);
const conversionPromises = images.map(async (image, index) => { const conversionPromises = images.map(async (image, index) => {
try { try {
@@ -116,7 +116,7 @@ export function useImageConverter() {
const finalFilename = generateFinalFilename(image.filename, settings, index); const finalFilename = generateFinalFilename(image.filename, settings, index);
downloadDataUrl(dataUrl, `${finalFilename}.${settings.format}`); downloadDataUrl(dataUrl, `${finalFilename}.${settings.format}`);
} catch (error) { } catch (error) {
const message = error instanceof Error ? error.message : `Failed to process ${image.filename}`; const message = error instanceof Error ? error.message : `Verarbeitung von ${image.filename} fehlgeschlagen`;
toast.error(message); toast.error(message);
throw error; throw error;
} }
@@ -124,9 +124,9 @@ export function useImageConverter() {
try { try {
await Promise.all(conversionPromises); await Promise.all(conversionPromises);
toast.success(`Successfully exported all ${images.length} images!`); toast.success(`Alle ${images.length} Bilder erfolgreich exportiert!`);
} catch (error) { } catch (error) {
toast.error("Some images failed to convert. See individual errors."); toast.error("Einige Bilder konnten nicht konvertiert werden. Siehe einzelne Fehler.");
} finally { } finally {
setIsConverting(false); setIsConverting(false);
} }
@@ -134,7 +134,7 @@ export function useImageConverter() {
const handleResetSettings = useCallback(() => { const handleResetSettings = useCallback(() => {
setSettings(initialSettings); setSettings(initialSettings);
toast.success("All settings have been reset to their defaults."); toast.success("Alle Einstellungen wurden auf ihre Standardwerte zurückgesetzt.");
}, []); }, []);
const handleAspectRatioChange = useCallback((value: string) => { const handleAspectRatioChange = useCallback((value: string) => {
@@ -159,15 +159,15 @@ export function useImageConverter() {
const handleApplyDefaultBaseNameToAll = useCallback(() => { const handleApplyDefaultBaseNameToAll = useCallback(() => {
if (!settings.defaultBaseName) { if (!settings.defaultBaseName) {
toast.error("Please enter a default base name to apply."); toast.error("Bitte geben Sie einen Standard-Basisnamen zum Anwenden ein.");
return; return;
} }
if (images.length === 0) { if (images.length === 0) {
toast.info("Upload some images first."); toast.info("Laden Sie zuerst einige Bilder hoch.");
return; return;
} }
setImages(prev => prev.map(img => ({ ...img, filename: settings.defaultBaseName }))); setImages(prev => prev.map(img => ({ ...img, filename: settings.defaultBaseName })));
toast.success(`Set base name to "${settings.defaultBaseName}" for all ${images.length} images.`); toast.success(`Basisname für alle ${images.length} Bilder auf "${settings.defaultBaseName}" gesetzt.`);
}, [images.length, settings.defaultBaseName]); }, [images.length, settings.defaultBaseName]);
return { return {

View File

@@ -1,27 +1,27 @@
export const changelogData = [ export const changelogData = [
{ {
version: "1.2.0", version: "1.2.0",
date: "July 26, 2024", date: "26. Juli 2024",
changes: [ changes: [
{ type: "New", text: "Added a changelog page to track updates and new features." }, { type: "Neu", text: "Eine Changelog-Seite hinzugefügt, um Updates und neue Funktionen zu verfolgen." },
{ type: "Improved", text: "Implemented 'auto' dimensioning for batch processing. When one dimension (width or height) is set, the other is calculated automatically for each image to maintain its aspect ratio." }, { type: "Verbessert", text: "'Auto'-Dimensionierung für die Stapelverarbeitung implementiert. Wenn eine Dimension (Breite oder Höhe) festgelegt wird, wird die andere für jedes Bild automatisch berechnet, um das Seitenverhältnis beizubehalten." },
{ type: "Fixed", text: "Enabled the 'Reset' and 'Apply' buttons to be used even when no images are uploaded." }, { type: "Behoben", text: "Die Schaltflächen 'Zurücksetzen' und 'Anwenden' können nun auch ohne hochgeladene Bilder verwendet werden." },
], ],
}, },
{ {
version: "1.1.0", version: "1.1.0",
date: "July 25, 2024", date: "25. Juli 2024",
changes: [ changes: [
{ type: "Improved", text: "Aspect ratio is now automatically detected from the first uploaded image and can be locked." }, { type: "Verbessert", text: "Das Seitenverhältnis wird jetzt automatisch vom ersten hochgeladenen Bild erkannt und kann gesperrt werden." },
{ type: "Fixed", text: "Corrected an issue where swapping dimensions would not update the aspect ratio to 'Custom'."} { type: "Behoben", text: "Ein Problem wurde behoben, bei dem das Tauschen der Dimensionen das Seitenverhältnis nicht auf 'Benutzerdefiniert' aktualisierte."}
], ],
}, },
{ {
version: "1.0.0", version: "1.0.0",
date: "July 24, 2024", date: "24. Juli 2024",
changes: [ changes: [
{ type: "New", text: "Initial release of the Image Web Exporter." }, { type: "Neu", text: "Erstveröffentlichung des Bild Web Exporters." },
{ type: "New", text: "Features include image uploading, format conversion (PNG, JPEG, WEBP), resizing, quality adjustment, and filename customization." }, { type: "Neu", text: "Zu den Funktionen gehören das Hochladen von Bildern, die Konvertierung von Formaten (PNG, JPEG, WEBP), die Größenänderung, die Anpassung der Qualität und die Anpassung von Dateinamen." },
], ],
}, },
]; ];