From d35725200d644d6737f2b14a1532908a2cb5f64c Mon Sep 17 00:00:00 2001 From: DomiLuebben <92845669+DomiLuebben@users.noreply.github.com> Date: Tue, 13 Oct 2026 19:21:38 +0200 Subject: [PATCH] i18n(de): translate the labels that were still English (344 entries) and fix three display bugs (#4008) * i18n(de): translate the labels that were still English A walk through the German UI with a lookup recorder (every menu item, every tool's options bar, every dialog it opens) found 344 texts that fell through to English. They were invisible to the existing i18n test, which only scans tl!("literal") calls: the labels of the schema-driven filter dialogs are built from the engine's parameter names ("centerX" -> "Center X"), and status and system-info templates are looked up at run time. - de.tsv: 344 new entries. Filter, Render (Flame, Picture Frame, Tree, Lighting Effects), Blur Gallery, Lens Correction, Color Lookup, Shadows/Highlights, Apply Image / Calculations, Layer Style blending options, Find and Replace, Document Info, the keyboard shortcut editor's commands, system info and the tool names that were missing (Color Sampler, Vertical Type, Patch). Written from the plain meaning of each label, in the vocabulary the catalog already uses (see its header: no proprietary localisation resource consulted). - "Light" was translated as "Light": the appearance mode in Preferences and the toolbar tooltip read "Light" next to "Dunkel". It is "Hell" now (Camera Raw's Light section and the font weight have their own contexts). - Export As: the dialog title was built with format!() and could not be translated; it goes through the catalog now. - Names such as "iPhone 14 Pro Max" (the artboard dialog's preset choices) were split like camelCase keys and read "I Phone 14 Pro Max"; a name with a space is shown as it is. Test: display_names_are_not_split_like_camel_case_keys. Left English on purpose: product and file-format names, units, document and layer names the engine creates ("Untitled", "Background": other code compares the latter), brush and swatch preset names (data), and words that are the same in German (Filter, Info, Zoom, Median...). Co-Authored-By: Claude Sonnet 5.5 * i18n(de): drop the process note and give the new sections English headings The first heading described how the entries were found (and when), which is not catalog content, and the other headings were German while the rest of the file uses short English section comments. Only comment lines change. Co-Authored-By: Claude Sonnet 5.5 * i18n(de): US spelling in a section heading Co-Authored-By: Claude Sonnet 5.5 --------- Co-authored-by: Claude Sonnet 5.5 --- crates/ui-egui/src/export_dialog.rs | 2 +- crates/ui-egui/src/filter_dialog.rs | 13 + crates/ui-egui/src/i18n/de.tsv | 356 +++++++++++++++++++++++++++- crates/ui-egui/src/rotate_view.rs | 5 +- crates/ui-egui/src/view_cmds.rs | 5 + crates/ui-egui/src/wheel_nav.rs | 3 +- 6 files changed, 376 insertions(+), 8 deletions(-) diff --git a/crates/ui-egui/src/export_dialog.rs b/crates/ui-egui/src/export_dialog.rs index 6cb0e310d..a322ee108 100644 --- a/crates/ui-egui/src/export_dialog.rs +++ b/crates/ui-egui/src/export_dialog.rs @@ -80,7 +80,7 @@ pub fn open_layer(app: &mut PhotocraftApp, layer: photocraft_doc::LayerId) -> Re let id = open(app)?; if let Some(d) = app.ui.dialogs.iter_mut().find(|d| d.id == id) { d.fields.insert("__layer".into(), json!(layer.0)); - d.fields.insert("__label".into(), json!(format!("Export As: {}", ldoc.name))); + d.fields.insert("__label".into(), json!(crate::i18n::fmt(crate::i18n::t("Export As: {name}"), &[("name", &ldoc.name)]))); d.fields.insert("__w".into(), json!(ldoc.size.width)); d.fields.insert("__h".into(), json!(ldoc.size.height)); } diff --git a/crates/ui-egui/src/filter_dialog.rs b/crates/ui-egui/src/filter_dialog.rs index 89fcd3ef9..f4dacbc57 100644 --- a/crates/ui-egui/src/filter_dialog.rs +++ b/crates/ui-egui/src/filter_dialog.rs @@ -556,6 +556,10 @@ pub(crate) fn source_label(key: &str) -> String { if key == "localized" { return "Localized Color Clusters".into(); } + // A name that already reads as text ("iPhone 14 Pro Max", "Web 1920") is not camelCase. + if key.contains(' ') { + return key.to_string(); + } // camelCase → "Camel Case" let mut s = String::new(); for (i, ch) in key.chars().enumerate() { @@ -975,6 +979,15 @@ pub struct FilterPreviewKey { mod tests { use super::*; + /// Preset names such as "iPhone 14 Pro Max" (the artboard dialog's choices) are text, not + /// camelCase keys: they used to read "I Phone 14 Pro Max". + #[test] + fn display_names_are_not_split_like_camel_case_keys() { + assert_eq!(source_label("iPhone 14 Pro Max"), "iPhone 14 Pro Max"); + assert_eq!(source_label("Web 1920"), "Web 1920"); + assert_eq!(source_label("centerX"), "Center X"); + } + #[test] fn only_direction_angles_get_the_dial() { // Every listed command still has a degree `angle` range, so the list cannot go stale. diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index cccbfbb35..88ef95767 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -1711,7 +1711,7 @@ Layer Knocks Out Drop Shadow Ebene stanzt den Schlagschatten aus Legal Legal Letter Letter - Light Light + Light Hell Line Tool Linien-Werkzeug Linear Linear Linear Burn Linear nachbelichten @@ -2748,6 +2748,360 @@ curves Smooth Glätten Light Red Hellrot Medium Blue Mittelblau Light Gray Hellgrau +# Status messages, system info and tool names + Join the ArtCraft Discord ({url}) Dem ArtCraft-Discord beitreten ({url}) + Nothing to paste: the clipboard holds no image Nichts einzufügen: Die Zwischenablage enthält kein Bild + Graphics adapter: {adapter} Grafikadapter: {adapter} + Backend: {backend} ({type}) Backend: {backend} ({type}) + Driver: {driver} Treiber: {driver} + GPU backend preference: {backend} Bevorzugtes GPU-Backend: {backend} + Selected at launch: {backend} Beim Start gewählt: {backend} + Image compositor: {processor} Bildkompositor: {processor} + Window renderer: {renderer} Fensterdarstellung: {renderer} + Graphics adapter Grafikadapter + Platform: {os} {arch} Plattform: {os} {arch} + Monitor profile: {profile} Monitorprofil: {profile} + Opened {url} {url} geöffnet + {index} of {total} {index} von {total} + Export As: {name} Exportieren als: {name} + Color Sampler Tool Farbaufnahme-Werkzeug + Vertical Type Tool Vertikales Text-Werkzeug + Patch Tool Ausbessern-Werkzeug + Grid view Rasteransicht + List view Listenansicht + Proof Setup… Proof einrichten… + 32-bit Preview Options 32-Bit-Vorschauoptionen +# Shortcut editor commands without a menu item + Toggle Layer Mask Overlay Ebenenmasken-Überlagerung umschalten + Increase Type Size Schriftgröße erhöhen + Decrease Type Size Schriftgröße verringern + Increase Type Size by 10 Schriftgröße um 10 erhöhen + Decrease Type Size by 10 Schriftgröße um 10 verringern + Make Selection from Path Auswahl aus Pfad erstellen + Target Composite Channel Gesamtkanal als Ziel festlegen + Target Channel 3 Kanal 3 als Ziel festlegen + Target Channel 4 Kanal 4 als Ziel festlegen + Target Channel 5 Kanal 5 als Ziel festlegen + Target Channel 6 Kanal 6 als Ziel festlegen + Target Channel 7 Kanal 7 als Ziel festlegen + Target Channel 8 Kanal 8 als Ziel festlegen + Target Channel 9 Kanal 9 als Ziel festlegen + Switch Foreground and Background Colors Vorder- und Hintergrundfarbe vertauschen + Default Foreground and Background Colors Standardfarben für Vorder- und Hintergrund + Fill with Foreground Color Mit Vordergrundfarbe füllen + Fill with Background Color Mit Hintergrundfarbe füllen + Fill with Foreground Color, Preserve Transparency Mit Vordergrundfarbe füllen, Transparenz erhalten + Fill with Background Color, Preserve Transparency Mit Hintergrundfarbe füllen, Transparenz erhalten + Decrease Brush Size Pinselgröße verringern + Increase Brush Size Pinselgröße erhöhen + Decrease Brush Hardness Pinselhärte verringern + Increase Brush Hardness Pinselhärte erhöhen + Temporary Vorübergehend + Space Leertaste + Hand Tool (hold) Hand-Werkzeug (gedrückt halten) + Zoom In (hold, drag to scrub) Vergrößern (gedrückt halten, zum Zoomen ziehen) + Zoom Out (hold) Verkleinern (gedrückt halten) + Erase with Brush or Pencil (hold) Mit Pinsel oder Buntstift radieren (gedrückt halten) +# Document Info and color profiles + Author Title Position des Urhebers + Copyright Status Urheberrechtsstatus + Unknown Unbekannt + Copyrighted Urheberrechtlich geschützt + Public Domain Gemeinfrei + Copyright Url Urheberrechts-URL + Keywords Stichwörter + Title Titel + Profile Profil + Working Arbeitsfarbraum + Working-cmyk Arbeits-CMYK + Coated-cmyk CMYK (gestrichen) + Linear-srgb Lineares sRGB + Gray-gamma-2.2 Grau (Gamma 2,2) + Adobe-rgb-compat Adobe-RGB-kompatibel + Prophoto-compat ProPhoto-kompatibel + Display-p3 Display P3 + Intent Renderpriorität + Bpc Schwarzpunktkompensierung + Simulate Paper Papierfarbe simulieren +# Color Lookup, Shadows/Highlights, channels and Calculations + Warming Filter (85) Wärmefilter (85) + Lut LUT + Cool Kühl + Teal Orange Türkis-Orange + Faded Film Verblasster Film + Day For Night Tag für Nacht + Mono Contrast Mono-Kontrast + Cross Process Kreuzentwicklung + Shadow Amount Stärke der Tiefen + Shadow Tone Tonbreite der Tiefen + Shadow Radius Radius der Tiefen + Highlight Amount Stärke der Lichter + Highlight Tone Tonbreite der Lichter + Highlight Radius Radius der Lichter + Midtone Mitteltöne + Black Clip Schwarz beschneiden + White Clip Weiß beschneiden + Strength Stärke + Shadow Schatten + Highlight Lichter + Intensity Intensität + Neutralize Neutralisieren + Use Selection In Source Auswahl in der Quelle verwenden + Use Selection In Target Auswahl im Ziel verwenden + Cw Im Uhrzeigersinn + Ccw Gegen den Uhrzeigersinn + Nearest Nächster Nachbar + Linear Dodge Linear abwedeln + Mask Channel Maskenkanal + R G B RGB + Source Channel Quellkanal + Source Invert Quelle invertieren + Result Ergebnis + Source1 Channel Kanal der Quelle 1 + Source1 Invert Quelle 1 invertieren + Source2 Channel Kanal der Quelle 2 + Source2 Invert Quelle 2 invertieren + Channel1 Kanal 1 + Channel2 Kanal 2 + Channel3 Kanal 3 + Channel4 Kanal 4 +# Measurement log + Label Beschriftung + Date and Time Datum und Uhrzeit + Scale Units Maßstabseinheiten + Scale Factor Maßstabsfaktor + Area Fläche + Perimeter Umfang + Circularity Kreisförmigkeit + Gray Value (Minimum) Grauwert (Minimum) + Gray Value (Maximum) Grauwert (Maximum) + Gray Value (Mean) Grauwert (Mittelwert) + Gray Value (Median) Grauwert (Median) + Integrated Density Integrierte Dichte + Length Länge + W: B: + A: Wi: +# Lens Correction, Blur Gallery and distortion filters + Pattern Fill… Musterfüllung… + Generic Allgemein + Correct Distortion Verzerrung korrigieren + Correct Vignette Vignette korrigieren + Correct C A Chromatische Aberration korrigieren + Auto Scale Automatisch skalieren + Distortion Verzerrung + Red Cyan Rot/Cyan + Blue Yellow Blau/Gelb + Vignette Amount Stärke der Vignette + Vignette Midpoint Mittelpunkt der Vignette + Edge Extension Kantenerweiterung + Hexagon Sechseck + Pentagon Fünfeck + Heptagon Siebeneck + Octagon Achteck + Blade Curvature Krümmung der Blendenlamellen + Depth Map Tiefenkarte + Focal Distance Fokusabstand + Distribution Verteilung + Uniform Gleichmäßig + Monochromatic Monochrom + Seed Startwert + Spin Drehen + Draft Entwurf + Good Gut + Best Beste + Center X Mitte X + Center Y Mitte Y + Circle Kreis + Star Stern + Cross Kreuz + High Hoch + Low Niedrig + Edge Only Nur Kanten + Overlay Edge Kante überlagern + Focus Fokus + Transition Übergang + Speed Geschwindigkeit + Taper Verjüngung + End X Ende X + End Y Ende Y + Blur Angle Weichzeichnungswinkel + Rectangular To Polar Rechteckig zu Polar + Polar To Rectangular Polar zu Rechteckig + Undefined Areas Undefinierte Bereiche + Wrap Umbrechen + Repeat Wiederholen + Horizontal Only Nur horizontal + Vertical Only Nur vertikal + Generators Generatoren + Wavelength Min Wellenlänge min. + Wavelength Max Wellenlänge max. + Amplitude Min Amplitude min. + Amplitude Max Amplitude max. + Sine Sinus + Ridges Grate + Pond Ripples Teichwellen + Out From Center Von der Mitte nach außen + Around Center Um die Mitte + Reduce Color Noise Farbrauschen reduzieren + Sharpen Details Details schärfen + Remove Jpeg Artifact JPEG-Artefakte entfernen + Max Radius Maximaler Radius + Cell Size Zellengröße + Fine Dots Feine Punkte + Medium Dots Mittlere Punkte + Grainy Dots Körnige Punkte + Coarse Dots Grobe Punkte + Short Lines Kurze Linien + Medium Lines Mittlere Linien + Long Lines Lange Linien + Short Strokes Kurze Striche + Medium Strokes Mittlere Striche + Long Strokes Lange Striche +# Render filters: Flame, Picture Frame, Tree + Flame Type Flammentyp + One Flame Along Path Eine Flamme entlang des Pfads + Multiple Flames Along Path Mehrere Flammen entlang des Pfads + Multiple Flames Path Directions Mehrere Flammen, Pfadrichtungen + Multiple Flames Various Length Mehrere Flammen, unterschiedliche Längen + Candle Light Kerzenlicht + Multiple Flames One Direction Mehrere Flammen, eine Richtung + Randomize Length Länge zufällig variieren + Interval Abstand + Adjust Interval For Loops Abstand für Schleifen anpassen + Use Custom Color Eigene Farbe verwenden + Jag Zacken + Flame Lines Flammenlinien + Flame Bottom Alignment Ausrichtung des Flammenfußes + Flame Style Flammenstil + Violent Heftig + Flat Flach + Flame Shape Flammenform + To Center Zur Mitte + Pointed Spitz + Randomize Shapes Formen zufällig variieren + Fine Fein + Frame Rahmen + Vine With Flowers Ranke mit Blumen + Vine With Leaves Ranke mit Blättern + Ivy Efeu + Roses Rosen + Daisies Gänseblümchen + Berries Beeren + Bamboo Bambus + Waves Wellen + Zigzag Zickzack + Rope Seil + Scallops Muschelrand + Double Line Doppellinie + Snowflakes Schneeflocken + Stars Sterne + Hearts Herzen + Arrangement Anordnung + Thickness Dicke + Vine Color Rankenfarbe + Flower Color Blütenfarbe + Leaf Color Blattfarbe + Base Tree Type Baumtyp + Light Direction Lichtrichtung + Leaves Amount Blattmenge + Leaves Size Blattgröße + Branches Height Asthöhe + Branches Thickness Aststärke + Default Leaves Standardblätter + Leaves Color Blattfarbe + Custom Branch Color Eigene Astfarbe + Branches Color Astfarbe + Flat Shading Flächige Schattierung +# Lens, Lighting Effects, depth and mosaic filters + Lens Objektiv + Prime35 Festbrennweite 35 + Prime105 Festbrennweite 105 + Movie Prime Film-Festbrennweite + Light Type Lichttyp + Point Punkt + Infinite Unendlich + Light X Licht X + Light Y Licht Y + Light Z Licht Z + Target X Ziel X + Target Y Ziel Y + Elevation Höhe + Gloss Glanz + Metallic Metallisch + Ambience Umgebungslicht + White Is High Weiß ist hoch + Ambient Umgebung + Warmth Wärme + Softness Weichheit + Reduce Noise Rauschen reduzieren + Darken Only Nur abdunkeln + Lighten Only Nur aufhellen + Anisotropic Anisotrop + Blocks Blöcke + Pyramids Pyramiden + Depth Mode Tiefenmodus + Random Zufällig + Level Based Stufenbasiert + Solid Front Faces Einfarbige Vorderseiten + Mask Incomplete Blocks Unvollständige Blöcke maskieren + Stylization Stilisierung + Cleanliness Sauberkeit + Bristle Detail Borstendetail + Lighting Beleuchtung + Shine Glanz + Max Offset Maximaler Versatz + Unaltered Unverändert + Lower Untere + Upper Obere + Blast Böe + Stagger Versetzt + From Right Von rechts + From Left Von links + Eliminate Entfernen + Odd Fields Ungerade Halbbilder + Even Fields Gerade Halbbilder + Create By Erzeugen durch + Duplication Duplizieren + Kernel Kern + Input Mode Eingabemodus + Rgb RGB + Hsb HSB + Hsl HSL + Row Order Zeilenreihenfolge + Preserve Erhalten + Squareness Rechteckigkeit +# Find and Replace, image size, Warp and Layer Style blending options + Any Beliebig + Cmyk CMYK + To Nach + Dont Enlarge Nicht vergrößern + Find Suchen + Replace Ersetzen + Change Find Ersetzen und suchen + Case Sensitive Groß-/Kleinschreibung beachten + Whole Word Ganzes Wort + Forward Vorwärts + Based On Basierend auf + General Blending Allgemeine Füllmethode + Advanced Blending Erweiterte Füllmethode + Blend Interior Effects as Group Innere Effekte als Gruppe überblenden + Blend Clipped Layers as Group Ebenen der Schnittmaske als Gruppe überblenden + Transparency Shapes Layer Transparenz bestimmt die Ebenenform + Layer Mask Hides Effects Ebenenmaske blendet Effekte aus + Vector Mask Hides Effects Vektormaske blendet Effekte aus + This Layer: Diese Ebene: + Underlying Layer: Darunterliegende Ebene: + Up Nach oben + Checkerboard Schachbrett + Arc Lower Bogen unten + Arc Upper Bogen oben + Bend Biegen + Horizontal Distortion Horizontale Verzerrung + Vertical Distortion Vertikale Verzerrung + Stretch Strecken + Map Document Dokument zuordnen + Map Path Pfad zuordnen + Fuzziness Toleranz Object Finder Objektsucher No object found there — drag a rectangle around it Dort wurde kein Objekt gefunden — ziehen Sie ein Rechteck darum Remove after each stroke Nach jedem Strich entfernen diff --git a/crates/ui-egui/src/rotate_view.rs b/crates/ui-egui/src/rotate_view.rs index fa22ac986..5e173da5d 100644 --- a/crates/ui-egui/src/rotate_view.rs +++ b/crates/ui-egui/src/rotate_view.rs @@ -322,10 +322,7 @@ mod tests { let ctx = egui::Context::default(); let mut enabled = None; let mut disabled = None; - let raw = egui::RawInput { - events: vec![Event::Rotate(std::f32::consts::FRAC_PI_6)], - ..Default::default() - }; + let raw = egui::RawInput { events: vec![Event::Rotate(std::f32::consts::FRAC_PI_6)], ..Default::default() }; let mut output = ctx.run_ui(raw, |ui| { enabled = trackpad_delta(ui.ctx(), true); disabled = trackpad_delta(ui.ctx(), false); diff --git a/crates/ui-egui/src/view_cmds.rs b/crates/ui-egui/src/view_cmds.rs index 5e6d25233..793257be8 100644 --- a/crates/ui-egui/src/view_cmds.rs +++ b/crates/ui-egui/src/view_cmds.rs @@ -996,6 +996,10 @@ pub fn reset_form(f: &mut Map) { } fn label_of(key: &str) -> String { + // A name that already reads as text ("iPhone 14 Pro Max", "Web 1920") is not camelCase. + if key.contains(' ') { + return tl!(key).to_owned(); + } let mut out = String::new(); for (i, c) in key.chars().enumerate() { if i == 0 { @@ -1026,6 +1030,7 @@ mod label_tests { crate::i18n::with_language(crate::i18n::Lang::EN, || { assert_eq!(label_of("useAntialias"), "Use antialias"); assert_eq!(label_of("radius"), "Radius"); + assert_eq!(label_of("iPhone 14 Pro Max"), "iPhone 14 Pro Max"); }); } } diff --git a/crates/ui-egui/src/wheel_nav.rs b/crates/ui-egui/src/wheel_nav.rs index 4523fbc64..c65ad1fe2 100644 --- a/crates/ui-egui/src/wheel_nav.rs +++ b/crates/ui-egui/src/wheel_nav.rs @@ -381,8 +381,7 @@ mod tests { assert!(matches!(legacy.events[0], Event::Zoom(_))); // Native magnification takes over before the synthetic gesture times out. - let mut native = - RawInput { time: Some(1.1), events: vec![Event::Zoom(1.1), wheel(Vec2::new(0.0, 0.25), CTRL)], ..Default::default() }; + let mut native = RawInput { time: Some(1.1), events: vec![Event::Zoom(1.1), wheel(Vec2::new(0.0, 0.25), CTRL)], ..Default::default() }; fold_legacy_pinch(&ctx, &mut native); assert!(matches!(native.events[0], Event::Zoom(f) if (f - 1.1).abs() < 1e-5)); assert!(matches!(native.events[1], Event::MouseWheel { .. }));