From 349977449bffccfbdfd2841038aa4b1ed28c3a67 Mon Sep 17 00:00:00 2001 From: Taxperia Date: Sat, 31 Oct 2026 23:48:40 +0300 Subject: [PATCH] i18n: refine az/ro translations and address review findings --- crates/ui-egui/src/i18n/az.tsv | 36 ++++++++--------- crates/ui-egui/src/i18n/live_tests.rs | 56 +++++++++++++++++++++++++-- crates/ui-egui/src/i18n/ro.tsv | 40 +++++++++---------- crates/ui-egui/src/i18n/tests.rs | 7 ++++ docs/gaps.md | 7 ++-- docs/localization-az-ro.md | 31 +++++++++++---- docs/localization-parity.md | 19 +++++---- docs/target-app-parity.md | 9 +++-- 8 files changed, 142 insertions(+), 63 deletions(-) diff --git a/crates/ui-egui/src/i18n/az.tsv b/crates/ui-egui/src/i18n/az.tsv index 37ef334ad0..f62db44e31 100644 --- a/crates/ui-egui/src/i18n/az.tsv +++ b/crates/ui-egui/src/i18n/az.tsv @@ -203,7 +203,7 @@ Create Clipping Mask Kəsici maska yarat Release Clipping Mask Kəsmə maskasını buraxın New Shape Layer Yeni Forma Layeri - New Type Layer Yeni tip qat + New Type Layer Yeni mətn qatı New Video Layer from File… Fayldan yeni video qatı… New Blank Video Layer Yeni boş video qatı Smart Filters Ağıllı filtrlər @@ -1390,19 +1390,19 @@ Breeze dark Qaranlıq meh Breeze Dark Theme Breeze Qaranlıq Mövzu Breeze light Külək işığı - Breeze Light Theme Breeze Light Theme + Breeze Light Theme Breeze açıq mövzusu Soft dark Yumşaq qaranlıq Soft light Yumşaq işıq Soft Dark Theme Soft Dark Theme - Soft Light Theme Yumşaq İşıq Mövzu + Soft Light Theme Soft açıq mövzusu Studio Theme Studiya mövzusu - Studio Light Theme Studio Yüngül Mövzu - Adwaita Light Theme Adwaita Yüngül Mövzu + Studio Light Theme Studio açıq mövzusu + Adwaita Light Theme Adwaita açıq mövzusu Adwaita Dark Theme Adwaita Qaranlıq Mövzu Pro Theme Pro Mövzu Pro Medium Gray Theme Pro Orta Boz Mövzu Pro Light Gray Theme Pro Açıq Boz Mövzu - Pro Light Theme Pro Yüngül Mövzu + Pro Light Theme Pro açıq mövzusu Workspace… İş sahəsi… Essentials Əsaslar New Workspace… Yeni İş Sahəsi… @@ -1513,7 +1513,7 @@ No matching commands Uyğun əmrlər yoxdur No document Sənəd yoxdur Pixel Layer Piksel qatı - Type Layer Layer yazın + Type Layer Mətn qatı Shape Layer Forma qatı Group Layer Qrup qatı Fill Layer Doldurma Layeri @@ -2211,7 +2211,7 @@ CPU (no GPU acceleration) CPU (GPU sürətləndirilməsi yoxdur) Cache levels Keş səviyyələri Cache tile size Keş kafel ölçüsü - Camera Raw Defaults Kamera Raw Defoltları + Camera Raw Defaults Camera Raw ilkin ayarları Canvas border Kətan haşiyəsi Show canvas dots Kətan nöqtələrini göstərin Clip thumbnails to layer bounds Miniatürləri qat sərhədlərinə kəsin @@ -2228,7 +2228,7 @@ Cursors Kursorlar Custom dark Fərdi qaranlıq Custom light Fərdi işıq - Dark Qaranlıq + Dark Tünd Dark gray Tünd boz Dashed lines kəsik xətlər Detailed Ətraflı @@ -2237,7 +2237,7 @@ Display p3 Ekran p3 Dots Nöqtələr Double click layer mask launches select and mask İki dəfə kliklənən qat maskası seçin və maskanı işə salır - Drop shadow Açılan kölgə + Drop shadow Düşən kölgə Dynamic color sliders Dinamik rəng sürgüləri East asian Şərqi Asiya Effect cache mb Effekt önbelleği mb @@ -2270,9 +2270,9 @@ Gridline every Gridline hər Guide color Bələdçi rəngi Guide style Bələdçi üslubu - Guides, Grid & Slices Bələdçilər, Torlar və Dilimlər + Guides, Grid & Slices Bələdçilər, tor və dilimlər Gutter Oluk - History Log Tarix jurnalı + History Log Tarixçə jurnalı History states Tarix dövlətləri Ignore exif profile tag Exif profil etiketinə məhəl qoymayın Image interpolation Şəkil interpolasiyası @@ -2341,7 +2341,7 @@ Show menu colors Menyu rənglərini göstərin Show only crosshair while painting Rəsm edərkən yalnız çarpaz işarəni göstərin Show slice numbers Dilim nömrələrini göstərin - Show tooltips Alət göstərişlərini göstərin + Show tooltips Alət ipuclarını göstər Enable animations Animasiyaları aktivləşdirin Show highlight on rollover Köçürmə zamanı vurğunu göstərin Show bounding box when dragging layer Qatı sürükləyərkən məhdudlaşdırıcı qutunu göstərin @@ -2366,7 +2366,7 @@ Traditional (72.27 points/inch) Ənənəvi (72,27 bal/düym) Transparency & Gamut Şəffaflıq və rəng əhatəsi Type units Növ vahidləri - UI font size UI şrift ölçüsü + UI font size UI şriftinin ölçüsü UI scale UI miqyası Units & Rulers Vahidlər və xətkeşlər Use additional plugins folder Əlavə plaginlər qovluğundan istifadə edin @@ -2667,8 +2667,8 @@ fontWeight Italic Kursiv Bundle Arrow-Key Nudges Ox düyməsi köçürmələrini qruplaşdır Nudge Bundle Pause (ms) Köçürmə qruplaşdırma fasiləsi (ms) Appearance Mode Görünüş rejimi - Dark Theme Qaranlıq Mövzu - Light Theme Yüngül Mövzu + Dark Theme Tünd mövzu + Light Theme Açıq mövzu Pro Medium Pro Orta Studio Light Studiya işığı Next Appearance Mode Növbəti Görünüş Rejimi @@ -2786,7 +2786,7 @@ fontWeight Italic Kursiv How often fastening points are placed by themselves Bərkitmə nöqtələri özləri tərəfindən nə qədər tez-tez yerləşdirilir Hue/Saturation Rəng/Doyma Intersect with selection ({key}) Seçimlə kəsişir ( {key} ) - Kind Xeyirxah + Kind Növ Linear Gradient Xətti Gradient Link layers Bağlantı qatları Lock Kilid @@ -2996,7 +2996,7 @@ curves Smooth Hamarlama Delete Contour Konturu silin Loaded contours from {file} {file} -dən yüklənmiş konturlar Saved contours to {file} Saxlanılan konturlar {file} - Show home recent files Evdəki son faylları göstərin + Show home recent files Ana səhifədə son faylları göstər Clear Brush Controls Fırça nəzarətlərini təmizləyin Reset All Locked Settings Bütün Kilidli Parametrləri Sıfırlayın Copy Texture to Other Tools Teksturanı Digər Alətlərə Kopyalayın diff --git a/crates/ui-egui/src/i18n/live_tests.rs b/crates/ui-egui/src/i18n/live_tests.rs index e43cd3e60c..a52be798eb 100644 --- a/crates/ui-egui/src/i18n/live_tests.rs +++ b/crates/ui-egui/src/i18n/live_tests.rs @@ -15,6 +15,7 @@ fn lang(code: &str) -> Lang { Lang::from_code(code).expect("registered language") } +/// Regional preferences resolve to their registered base catalog across all requested languages. #[test] fn requested_languages_resolve_regional_preferences() { for (tag, code) in [ @@ -39,21 +40,47 @@ fn requested_languages_resolve_regional_preferences() { assert_eq!(lang_from_tag("ko"), Some(lang("ko"))); } +/// Switching redraws the menu and persists through the shell's save/load services on restart. #[test] fn azerbaijani_and_romanian_draw_after_switching_and_restore_after_restart() { + use std::{cell::RefCell, rc::Rc}; + for (code, file) in [("az", "Fayl"), ("ro", "Fișier")] { with_language(Lang::EN, || { - let mut h = harness(); + let saved = Rc::new(RefCell::new(None::)); + let (load, store) = (saved.clone(), saved.clone()); + let services = Services { + load_prefs: Some(Box::new(move || load.borrow().clone())), + save_prefs: Some(Box::new(move |text| { + *store.borrow_mut() = Some(text.to_owned()); + Ok(()) + })), + ..Default::default() + }; + let mut h = harness_with_services(services); h.state_mut().run("prefs.set", json!({"path": "interface.language", "value": code})).expect("choose language"); h.run_steps(4); assert_eq!(current(), lang(code)); assert_eq!(tr(current(), "File"), file); assert!(drawn_text(&h).iter().any(|text| *text == file_title(lang(code)))); - let saved = h.state().session.prefs_to_json(); + let persisted = saved.borrow().clone().expect("shell saved preferences"); + let value: serde_json::Value = serde_json::from_str(&persisted).expect("saved JSON"); + assert_eq!(value["interface"]["language"], code); h.state_mut().run("prefs.set", json!({"path": "interface.language", "value": "en"})).expect("switch back"); h.run_steps(4); assert_eq!(current(), Lang::EN); - let services = Services { load_prefs: Some(Box::new(move || Some(saved.clone()))), ..Default::default() }; + assert_eq!( + serde_json::from_str::(&saved.borrow().clone().expect("saved English preferences")).expect("saved JSON")["interface"]["language"], + "en" + ); + h.state_mut().run("prefs.set", json!({"path": "interface.language", "value": code})).expect("choose language again"); + h.run_steps(4); + assert_eq!( + serde_json::from_str::(&saved.borrow().clone().expect("saved language preferences")).expect("saved JSON")["interface"]["language"], + code + ); + drop(h); + let services = Services { load_prefs: Some(Box::new(move || saved.borrow().clone())), ..Default::default() }; let restarted = harness_with_services(services); assert_eq!(restarted.state().session.prefs().interface.language, code); assert_eq!(current(), lang(code)); @@ -62,6 +89,28 @@ fn azerbaijani_and_romanian_draw_after_switching_and_restore_after_restart() { } } +/// Preferences draw the catalog terms verbatim and the bundled UI font covers their letters. +#[test] +fn azerbaijani_and_romanian_preferences_draw_terms_and_glyphs() { + for (code, interface, light) in [("az", "İnterfeys", "Açıq mövzu"), ("ro", "Interfață", "Temă deschisă")] { + with_language(Lang::EN, || { + let mut h = harness(); + h.state_mut().run("prefs.set", json!({"path": "interface.language", "value": code})).expect("choose language"); + crate::prefs_ui::open_preferences(h.state_mut(), "interface"); + h.run_steps(4); + let text = drawn_text(&h); + for expected in [interface, light, tr(lang(code), "UI font size"), tr(lang(code), "UI scale")] { + assert!(text.iter().any(|text| text == expected), "{code}: missing {expected:?}"); + h.ctx.fonts_mut(|fonts| { + for c in expected.chars().filter(|c| c.is_alphabetic()) { + assert!(fonts.has_glyph(&egui::FontId::proportional(12.0), c), "{code}: missing glyph {c}"); + } + }); + } + }); + } +} + #[test] fn ukrainian_native_app_menu_keeps_product_and_language_names() { with_language(Lang::EN, || { @@ -254,6 +303,7 @@ fn harness_with_services(services: Services) -> egui_kittest::Harness<'static, P harness } +/// Auto draws the detected system language on first launch while keeping the Auto preference. #[test] fn first_launch_renders_the_system_language_without_overwriting_auto() { for (tag, code) in [ diff --git a/crates/ui-egui/src/i18n/ro.tsv b/crates/ui-egui/src/i18n/ro.tsv index caece5ac0c..4215755add 100644 --- a/crates/ui-egui/src/i18n/ro.tsv +++ b/crates/ui-egui/src/i18n/ro.tsv @@ -203,7 +203,7 @@ Create Clipping Mask Creează o mască de decupare Release Clipping Mask Eliberați masca de tăiere New Shape Layer Strat de formă nou - New Type Layer Strat de tip nou + New Type Layer Strat de text nou New Video Layer from File… Strat video nou din fișier… New Blank Video Layer Strat video gol nou Smart Filters Filtre inteligente @@ -1390,19 +1390,19 @@ Breeze dark Briză întunecată Breeze Dark Theme Breeze Dark Theme Breeze light Briză lumină - Breeze Light Theme Tema Brizei Lumină + Breeze Light Theme Temă deschisă Breeze Soft dark Întuneric moale Soft light Lumină moale Soft Dark Theme Tema Soft Dark - Soft Light Theme Tema Lumină moale + Soft Light Theme Temă deschisă Soft Studio Theme Tema Studio - Studio Light Theme Studio Light Theme - Adwaita Light Theme Tema luminii Adwaita + Studio Light Theme Temă deschisă Studio + Adwaita Light Theme Temă deschisă Adwaita Adwaita Dark Theme Tema întunecată Adwaita Pro Theme Tema Pro Pro Medium Gray Theme Tema Pro Medium Gri Pro Light Gray Theme Temă Pro Gri deschis - Pro Light Theme Tema Pro Light + Pro Light Theme Temă deschisă Pro Workspace… Spațiu de lucru… Essentials Esențiale New Workspace… Nou spațiu de lucru… @@ -1513,7 +1513,7 @@ No matching commands Nu există comenzi care se potrivesc No document Niciun document Pixel Layer Strat de pixeli - Type Layer Tip strat + Type Layer Strat de text Shape Layer Strat de formă Group Layer Strat de grup Fill Layer Strat de umplere @@ -2211,7 +2211,7 @@ CPU (no GPU acceleration) CPU (fără accelerare GPU) Cache levels Niveluri cache Cache tile size Dimensiunea piesei cache - Camera Raw Defaults Camera Raw implicite + Camera Raw Defaults Setări implicite Camera Raw Canvas border Chenar pânză Show canvas dots Afișați puncte de pânză Clip thumbnails to layer bounds Decupați miniaturile la limitele stratului @@ -2228,7 +2228,7 @@ Cursors Cursore Custom dark Întuneric personalizat Custom light Lumină personalizată - Dark Întuneric + Dark Întunecat Dark gray Gri închis Dashed lines linii întrerupte Detailed Detaliat @@ -2238,7 +2238,7 @@ Dots puncte Double click layer mask launches select and mask Faceți dublu clic pe masca de strat lansează selectarea și masca Drop shadow Umbră - Dynamic color sliders glisoare de culoare dinamice + Dynamic color sliders Glisoare dinamice de culoare East asian Asia de Est Effect cache mb Cache efect mb Enable flick panning Activați flick panning @@ -2270,9 +2270,9 @@ Gridline every Gridline fiecare Guide color Culoare ghid Guide style Stilul ghid - Guides, Grid & Slices Ghiduri, grilă și felii + Guides, Grid & Slices Ghidaje, grilă și secțiuni Gutter Jgheab - History Log Jurnal istoric + History Log Jurnalul istoricului History states Istoria afirmă Ignore exif profile tag Ignorați eticheta de profil exif Image interpolation Interpolare imagine @@ -2341,7 +2341,7 @@ Show menu colors Afișați culorile meniului Show only crosshair while painting Afișați numai încrucișarea în timp ce pictați Show slice numbers Afișați numerele secțiunilor - Show tooltips Afișați sfaturi cu instrumente + Show tooltips Afișați indicii Enable animations Activați animațiile Show highlight on rollover Afișați evidențierea la trecere Show bounding box when dragging layer Afișează caseta de delimitare când trageți stratul @@ -2366,8 +2366,8 @@ Traditional (72.27 points/inch) Tradițional (72,27 puncte/inch) Transparency & Gamut Transparență și gamă Type units Tip unități - UI font size dimensiunea fontului UI - UI scale scară UI + UI font size Dimensiunea fontului UI + UI scale Scalare UI Units & Rulers Unități și rigle Use additional plugins folder Folosiți folderul de pluginuri suplimentare Use esc to commit Folosiți esc pentru a comite @@ -2666,9 +2666,9 @@ fontWeight Italic Cursiv Notification Duration (seconds) Durata notificării (secunde) Bundle Arrow-Key Nudges Grupează deplasările cu săgețile Nudge Bundle Pause (ms) Pauză de grupare a deplasărilor (ms) - Appearance Mode Modul aspect - Dark Theme Tema întunecată - Light Theme Tema ușoară + Appearance Mode Mod de afișare + Dark Theme Temă întunecată + Light Theme Temă deschisă Pro Medium Mediu Pro Studio Light Studio Light Next Appearance Mode Următorul mod de apariție @@ -2786,7 +2786,7 @@ fontWeight Italic Cursiv How often fastening points are placed by themselves Cât de des punctele de fixare sunt amplasate singure Hue/Saturation Nuanță/Saturație Intersect with selection ({key}) Se intersectează cu selecția ( {key} ) - Kind Amabil + Kind Tip Linear Gradient Gradient liniar Link layers Straturi de legătură Lock Blocare @@ -2996,7 +2996,7 @@ curves Smooth Netezire Delete Contour Șterge contur Loaded contours from {file} Contururi încărcate de la {file} Saved contours to {file} Contururi salvate în {file} - Show home recent files Afișați fișierele recente de acasă + Show home recent files Afișați fișierele recente pe pagina principală Clear Brush Controls Ștergeți comenzile pensulei Reset All Locked Settings Resetați toate setările blocate Copy Texture to Other Tools Copiați textura în alte instrumente diff --git a/crates/ui-egui/src/i18n/tests.rs b/crates/ui-egui/src/i18n/tests.rs index 1da3fdde1d..8f57ac63bb 100644 --- a/crates/ui-egui/src/i18n/tests.rs +++ b/crates/ui-egui/src/i18n/tests.rs @@ -292,6 +292,7 @@ fn serbian_plural_rule_handles_teens_and_compound_counts() { assert_eq!(forms, [2, 0, 1, 1, 2, 2, 2, 2, 0, 1, 2, 0, 2, 2, 1]); } +/// Both catalogs resolve regional tags, expose their native names and fall back to English. #[test] fn azerbaijani_and_romanian_resolve_locales_and_preferences() { for (code, name, tags) in [ @@ -310,6 +311,7 @@ fn azerbaijani_and_romanian_resolve_locales_and_preferences() { } } +/// Integer plural boundaries select the correct catalog form, including at `u64::MAX`. #[test] fn azerbaijani_and_romanian_plural_counts_include_hundreds_and_large_values() { let az = Lang::from_code("az").expect("az registered"); @@ -324,12 +326,16 @@ fn azerbaijani_and_romanian_plural_counts_include_hundreds_and_large_values() { assert_eq!(trn(az, 20, "{n} layer", "{n} layers"), "20 qat"); } +/// Context and command lookups retain distinct meanings while preserving product placeholders. #[test] fn azerbaijani_and_romanian_keep_context_placeholders_and_command_names() { for (code, file, layer, light, font_light) in [("az", "Fayl", "Qat", "İşıq", "İncə"), ("ro", "Fișier", "Strat", "Lumină", "Subțire")] { let lang = Lang::from_code(code).expect("registered language"); assert_eq!(tr(lang, "File"), file); assert_eq!(tr(lang, "Layer"), layer); + assert_eq!(tr(lang, "Kind"), if code == "az" { "Növ" } else { "Tip" }); + assert_eq!(tr(lang, "Type Layer"), if code == "az" { "Mətn qatı" } else { "Strat de text" }); + assert_eq!(tr(lang, "Light Theme"), if code == "az" { "Açıq mövzu" } else { "Temă deschisă" }); assert_eq!(tr_ctx(lang, "cameraRaw", "Light"), light); assert_eq!(tr_ctx(lang, "fontWeight", "Light"), font_light); assert_ne!(tr_id(lang, "edit.clear", "Clear"), "Clear"); @@ -337,6 +343,7 @@ fn azerbaijani_and_romanian_keep_context_placeholders_and_command_names() { } } +/// Every menu label and submenu segment has a lookup entry in both new catalogs. #[test] fn azerbaijani_and_romanian_cover_every_menu_label_and_path() { for code in ["az", "ro"] { diff --git a/docs/gaps.md b/docs/gaps.md index be3319e3a4..4f41fcb4a9 100644 --- a/docs/gaps.md +++ b/docs/gaps.md @@ -1,6 +1,6 @@ # Where PhotoCraft falls short of Photoshop -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-31 · **Change:** minor (Azerbaijani and Romanian UI catalogs; native review and Rust/offscreen verification pending; selected colour sets edit drawing, tone and supported filters; geometry remains open; G11: `.atn` document offsets preserve opening order after tab reordering, #7103; embedded AI/PDF/EPS save-back retains native format with a source-bound SVG display preview; Smooth and Feather command masks cropped to selection bounds plus blur reach; G1: Halftone Pattern Filter Gallery settings survive PSD/PSB saves (#8940); cropped selection morphology and command mask conversion performance evidence added; G9: 12 of 181 preferences do nothing, as the scorecard prefs audit counts them; G6: Art History Brush landed as partial, TOOL-213-20, #1610, so no toolbar tool is missing; G4: five hard-round built-in presets use 5% spacing to remove stacked-tip outlines and reduce pressure-opacity bands, #8842; G9: Crop tool canvas menu, #6561; G2: Content-Aware Scale seam carving 1.65–3.6× faster with unchanged output, P28 row 3.3 → 1.6 s on a 4-core VM, #5236; G9: UI scale presets done, 105–120% steps, #3222; G14: lossy DNG, raw sidecars, Camera Raw Defaults live, Calibration and Auto, CR3 assessed as not clean-room feasible; G8: PSD type on a path opens on its path and saves back into the file's `Txt2`, #7962; G11: a recorded Save As imports with its format and file (`file.saveAs`, `file.saveACopy` for As a Copy) instead of as a plain Save that wrote over the played file, #7102; folder destinations stop playback until #5430; adjustment clipping stays visible in compact Properties, #5231; G9: Color panel modes, ramp and copy commands, the same in every theme, UI-217-17, #6000; G17: Help › Check for Updates and an opt-out check once per launch, DIST-220-1 partial, #5485; G10: `.asl` style libraries landed, #1051; G8: area type (TYPE-218-10); G1: smart objects keep Photoshop's Cylinder warp, #4112; G8: paragraph direction buttons in the Paragraph panel and options bar, TYPE-218-2 partial; G1: 32-bit Hue/Saturation and Colorize as Photoshop does them, Photoshop-oracle floor 190/258, #5781; G11: compound layer properties export as separate Photoshop Set events, rename last, #8304; G11: Batch and droplets take the result at the action's Save step, so actions ending in Save As and Close work; G11: `.atn` steps that switch to another open document import, play and save back, #7103; G14: the Nikon D4 white level follows the sample depth, so 12-bit D4 NEFs no longer open 2.6 stops dark, #5978; G14: a Nikon NEF's Adobe RGB preview is read as Adobe RGB (maker-note ColorSpace 0x001E), #5987; Apply Layer Comp action mapping, #7101; G10: JPEG Options dialog landed, FILE-215-19 partial (no Matte, progressive Scans or size preview); G10: Cineon write; G10: DPX read and write, #1049, Cineon and Amiga IFF read; Classical Object Finder improvement; learned segmentation remains open; G4: named, stateful section locks and visible keyboard focus in Brush Settings; G4: independent Brush Pose tilt-axis overrides, #3825 partial; G4: Transfer Mixer-only controls, #3825 partial; G9: Preferences › File Handling › Save in Background takes effect, #6207, 24 of 169 preferences do nothing; G7: Content-Aware Move previews every selection the command takes, coarse then exact, TOOL-213-4 done, #6680; G4: Pattern Stamp and Background Eraser draw live, TOOL-213-7; G9: Animated Zoom pref read: holding the Zoom tool zooms continuously, #6210, prefs that do nothing 25 of 166; G2: a cancelled 24 MP Gaussian Blur job stops in at most 44 ms, P25 under its 200 ms budget in local runs, baseline not re-run, #6205; G1: generated large-file fixtures, a 4.38 GB PSB opens on Windows through io, FILE-216-2 done, #5107; G9: fill and adjustment layers copy and paste whole, #2344; G1: smart filter masks show on PSD open, Photoshop-oracle floor 188/258; G10: DPX read and write, #1049, Cineon read; G1: CMYK Hue/Saturation and Color Balance keep the black ink; G1: CMYK Posterize on the stored values, psd-tools oracle floor 242/309; G1: CMYK adjustment layers composite on the document's inks, Photoshop-oracle floor 165/258; G1: CMYK Brightness/Contrast on the inks, CMYK Threshold's luma; G1: modern Brightness/Contrast's brightness curve matches Photoshop, Photoshop-oracle floor 151/258; G1: Lab documents composite on their stored L, a, b, Photoshop-oracle floor 152/258; G1: Levels on whole levels at every step, Photoshop-oracle floor 146/258, psd-tools 238/309; G1: 32-bit Brightness/Contrast legacy, Channel Mixer, Photo Filter and Black & White tint unclipped as in Photoshop, Photoshop-oracle floor 149/258; G1: mask feathers blur with their exact variance, psd-tools oracle floor 240/309; G1: Threshold splits on Photoshop's luminosity, psd-tools oracle floor 241/309; G1: pass-through clipping base with an adjustment renders isolated, psd-tools oracle floor 240/309; G1: 16-bit Posterize as Photoshop bins it, psd-tools oracle floor 241/309; G1: Gradient Map driven by the W3C luminosity, Photoshop-oracle floor 147/258; G7: Field Blur's on-canvas pins, #3373; G4: Hue Jitter amount track, #3825 partial; G4: minimum field interaction, #3825 partial; G1: CMYK Invert, Posterize and Threshold on the inks, 16-bit Lab Invert; Photoshop-oracle floor 145/258; G11: `.atn` import maps 112 events: Select and Mask, Filter Gallery Spatter, Ripple, Crystallize, Pattern Fill layers, mask targeting, Step Backward, nudges in points, Bitmap mode; G5: Free Transform H/V skew fields; G9: toolbar tool shortcut editing; refreshed against main: G2, G5, G7, G9, G10, G13, G14, G17 items that landed; G10: DPX read and write, #1049; G4: checkbox-only Brush Settings rows; G11: Load/Save Actions `.atn` landed, AUTO-219-2 partial; G5: hold-a-key spring-loaded tools landed, #2672; G1: #2465 fixed; stale missing-tool rows corrected: Color Replacement, Red Eye and Add/Delete Anchor Point are live tools, so only Art History Brush is missing; no estimate changed; G10: Amiga IFF write; G10: PSD spot channels in Photoshop's polarity, earlier PhotoCraft PSDs keep their ink, #7415 split; G10: TIFF alpha and spot channels written and read as extra samples, FILE-215-7 done, FILE-215-17's TIFF part, #7415) · **Target:** Adobe Photoshop 2026 +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-31 · **Change:** minor (Azerbaijani and Romanian UI catalogs; native review pending; local build and focused checks verified; selected colour sets edit drawing, tone and supported filters; geometry remains open; G11: `.atn` document offsets preserve opening order after tab reordering, #7103; embedded AI/PDF/EPS save-back retains native format with a source-bound SVG display preview; Smooth and Feather command masks cropped to selection bounds plus blur reach; G1: Halftone Pattern Filter Gallery settings survive PSD/PSB saves (#8940); cropped selection morphology and command mask conversion performance evidence added; G9: 12 of 181 preferences do nothing, as the scorecard prefs audit counts them; G6: Art History Brush landed as partial, TOOL-213-20, #1610, so no toolbar tool is missing; G4: five hard-round built-in presets use 5% spacing to remove stacked-tip outlines and reduce pressure-opacity bands, #8842; G9: Crop tool canvas menu, #6561; G2: Content-Aware Scale seam carving 1.65–3.6× faster with unchanged output, P28 row 3.3 → 1.6 s on a 4-core VM, #5236; G9: UI scale presets done, 105–120% steps, #3222; G14: lossy DNG, raw sidecars, Camera Raw Defaults live, Calibration and Auto, CR3 assessed as not clean-room feasible; G8: PSD type on a path opens on its path and saves back into the file's `Txt2`, #7962; G11: a recorded Save As imports with its format and file (`file.saveAs`, `file.saveACopy` for As a Copy) instead of as a plain Save that wrote over the played file, #7102; folder destinations stop playback until #5430; adjustment clipping stays visible in compact Properties, #5231; G9: Color panel modes, ramp and copy commands, the same in every theme, UI-217-17, #6000; G17: Help › Check for Updates and an opt-out check once per launch, DIST-220-1 partial, #5485; G10: `.asl` style libraries landed, #1051; G8: area type (TYPE-218-10); G1: smart objects keep Photoshop's Cylinder warp, #4112; G8: paragraph direction buttons in the Paragraph panel and options bar, TYPE-218-2 partial; G1: 32-bit Hue/Saturation and Colorize as Photoshop does them, Photoshop-oracle floor 190/258, #5781; G11: compound layer properties export as separate Photoshop Set events, rename last, #8304; G11: Batch and droplets take the result at the action's Save step, so actions ending in Save As and Close work; G11: `.atn` steps that switch to another open document import, play and save back, #7103; G14: the Nikon D4 white level follows the sample depth, so 12-bit D4 NEFs no longer open 2.6 stops dark, #5978; G14: a Nikon NEF's Adobe RGB preview is read as Adobe RGB (maker-note ColorSpace 0x001E), #5987; Apply Layer Comp action mapping, #7101; G10: JPEG Options dialog landed, FILE-215-19 partial (no Matte, progressive Scans or size preview); G10: Cineon write; G10: DPX read and write, #1049, Cineon and Amiga IFF read; Classical Object Finder improvement; learned segmentation remains open; G4: named, stateful section locks and visible keyboard focus in Brush Settings; G4: independent Brush Pose tilt-axis overrides, #3825 partial; G4: Transfer Mixer-only controls, #3825 partial; G9: Preferences › File Handling › Save in Background takes effect, #6207, 24 of 169 preferences do nothing; G7: Content-Aware Move previews every selection the command takes, coarse then exact, TOOL-213-4 done, #6680; G4: Pattern Stamp and Background Eraser draw live, TOOL-213-7; G9: Animated Zoom pref read: holding the Zoom tool zooms continuously, #6210, prefs that do nothing 25 of 166; G2: a cancelled 24 MP Gaussian Blur job stops in at most 44 ms, P25 under its 200 ms budget in local runs, baseline not re-run, #6205; G1: generated large-file fixtures, a 4.38 GB PSB opens on Windows through io, FILE-216-2 done, #5107; G9: fill and adjustment layers copy and paste whole, #2344; G1: smart filter masks show on PSD open, Photoshop-oracle floor 188/258; G10: DPX read and write, #1049, Cineon read; G1: CMYK Hue/Saturation and Color Balance keep the black ink; G1: CMYK Posterize on the stored values, psd-tools oracle floor 242/309; G1: CMYK adjustment layers composite on the document's inks, Photoshop-oracle floor 165/258; G1: CMYK Brightness/Contrast on the inks, CMYK Threshold's luma; G1: modern Brightness/Contrast's brightness curve matches Photoshop, Photoshop-oracle floor 151/258; G1: Lab documents composite on their stored L, a, b, Photoshop-oracle floor 152/258; G1: Levels on whole levels at every step, Photoshop-oracle floor 146/258, psd-tools 238/309; G1: 32-bit Brightness/Contrast legacy, Channel Mixer, Photo Filter and Black & White tint unclipped as in Photoshop, Photoshop-oracle floor 149/258; G1: mask feathers blur with their exact variance, psd-tools oracle floor 240/309; G1: Threshold splits on Photoshop's luminosity, psd-tools oracle floor 241/309; G1: pass-through clipping base with an adjustment renders isolated, psd-tools oracle floor 240/309; G1: 16-bit Posterize as Photoshop bins it, psd-tools oracle floor 241/309; G1: Gradient Map driven by the W3C luminosity, Photoshop-oracle floor 147/258; G7: Field Blur's on-canvas pins, #3373; G4: Hue Jitter amount track, #3825 partial; G4: minimum field interaction, #3825 partial; G1: CMYK Invert, Posterize and Threshold on the inks, 16-bit Lab Invert; Photoshop-oracle floor 145/258; G11: `.atn` import maps 112 events: Select and Mask, Filter Gallery Spatter, Ripple, Crystallize, Pattern Fill layers, mask targeting, Step Backward, nudges in points, Bitmap mode; G5: Free Transform H/V skew fields; G9: toolbar tool shortcut editing; refreshed against main: G2, G5, G7, G9, G10, G13, G14, G17 items that landed; G10: DPX read and write, #1049; G4: checkbox-only Brush Settings rows; G11: Load/Save Actions `.atn` landed, AUTO-219-2 partial; G5: hold-a-key spring-loaded tools landed, #2672; G1: #2465 fixed; stale missing-tool rows corrected: Color Replacement, Red Eye and Add/Delete Anchor Point are live tools, so only Art History Brush is missing; no estimate changed; G10: Amiga IFF write; G10: PSD spot channels in Photoshop's polarity, earlier PhotoCraft PSDs keep their ink, #7415 split; G10: TIFF alpha and spot channels written and read as extra samples, FILE-215-7 done, FILE-215-17's TIFF part, #7415) · **Target:** Adobe Photoshop 2026 Every known shortfall against Photoshop 2026 (27.11.0), one entry each, ranked by user impact on the way to beta. This is the work list agents pick from: take the highest-ranked entry that @@ -398,7 +398,8 @@ what an agent can't finish alone. ### G13. Hindi, Arabic, Vietnamese, RTL and engine messages - **Progress (2026-10-31):** Azerbaijani (`az`, Latin script) and Romanian (`ro`) catalogs add 4,566 entries each, regional locale matching and plural messages (Romanian one/few/other). - Native-speaker review and Rust/offscreen verification remain pending; engine messages, + Native-speaker review remains pending. Local build and focused checks passed; the + localization notes record full-suite limits. Engine messages, estimated readiness and the remaining G13 scope are unchanged. See [notes](localization-az-ro.md). - **Missing:** catalogs for Hindi, Arabic and Vietnamese (3 of the 12 key languages); right-to- left UI layout; Devanagari fallback fonts (Thai #1909 and Khmer, Lao and Myanmar #3259 landed); engine error and status messages in @@ -428,7 +429,7 @@ what an agent can't finish alone. | Date | Change | Summary | |---|---|---| -| 2026-10-31 | minor | G13: Azerbaijani and Romanian catalogs, 4,566 entries each; regional tags and plural rules; native review and Rust/offscreen verification pending; estimates unchanged | +| 2026-10-31 | minor | G13: Azerbaijani and Romanian catalogs, 4,566 entries each; regional tags and plural rules; native review pending; local build and focused checks verified; estimates unchanged | | 2026-10-27 | minor | Smooth/Feather command reads and replacement use selection bounds plus blur reach; full-canvas reference and release comparisons extended; percentages and scorecard budgets unchanged | | 2026-10-27 | minor | G9: 12 of 181 preferences do nothing, the scorecard's count with integration batch 9 (#9775) on main; no estimate changed | | 2026-10-26 | minor | Expand/Contract/Border distance work, mask reads and replacement cropped to nonzero bounds; bounded distances for radii ≤5; 36 MP exact-output comparisons improve 1.54–37.82× across eight cases; percentages, estimates and scorecard budgets unchanged | diff --git a/docs/localization-az-ro.md b/docs/localization-az-ro.md index b5d69b4c98..b26022c2f2 100644 --- a/docs/localization-az-ro.md +++ b/docs/localization-az-ro.md @@ -59,11 +59,28 @@ fallback behavior, contextual terms, placeholders, menu coverage and plural coun 19/20, 100/101, 119/120 and `u64::MAX`. Shell tests cover language switching, English restoration, persisted preferences, restart and first-launch Auto detection for the new languages. -Static catalog checks inspect duplicate keys, TSV structure, placeholders in every plural form, -trailing ellipses, the complete menu tree and source-derived localization keys. Rust tests, -Clippy, wasm checks and offscreen app rendering require a Rust-equipped environment; the -implementation environment had neither `cargo` nor `rustc`, so those checks were not run here. +Local verification on 2026-10-31 used Rust 1.99.1 and MSVC on Windows x64: -Before merging, run `cargo test -p photocraft-ui-egui`, -`cargo clippy -p photocraft-ui-egui --all-targets -- -D warnings`, `cargo xtask layers`, -`cargo xtask wasm`, `cargo xtask i18n-coverage` and the offscreen snapshot example. +- The native `photocraft` binary and `snapshot` example built successfully with the locked + dependencies. These were unoptimized debug builds, not performance measurements. +- All six Azerbaijani/Romanian unit and shell tests passed, including saving through + `Services.save_prefs`, loading after restart, preference labels and UI glyph coverage. +- `cargo fmt --all -- --check`, `cargo clippy -p photocraft-ui-egui --all-targets -- -D warnings`, + `cargo xtask layers` and `cargo xtask wasm` passed. The wasm check covered 24 crates and + the optional HEIF feature. +- `cargo xtask i18n-coverage` reported 2,204 / 2,204 source keys for each new language. + Static TSV, duplicate, placeholder and ellipsis checks passed; the Rust test comparing + packed catalogs with their source files also passed. +- Editor and Interface Preferences screenshots for both languages were rendered offscreen + at 1440 × 900 logical pixels and 1.5× scale, using `--safe-gpu`, and visually inspected. + This caught incorrect translations of Kind, Type Layer, Light Theme and preference labels, + which were corrected and covered by regressions. These captures verify the CPU canvas + path, not hardware-GPU rendering or native window/input behavior. + +**The full test gate is not green.** The i18n subset finished with 59 passed and eight failed: +the existing Chinese/Japanese/Korean/Arabic shell checks reported missing glyphs in this +build without the optional `CRAFT_FONTS_DIR` input. All six new-language tests passed. +The full `cargo test -p photocraft-ui-egui --no-fail-fast` run terminated both its unit and +integration test processes with `0xc0000005` (`STATUS_ACCESS_VIOLATION`) during graphics tests; +it did not complete. These broader checks need a successful rerun before claiming full +validation. Native-speaker review also remains pending. diff --git a/docs/localization-parity.md b/docs/localization-parity.md index 06e894b4f6..44aaf5ba15 100644 --- a/docs/localization-parity.md +++ b/docs/localization-parity.md @@ -1,6 +1,6 @@ # Localization parity -> **Last reviewed:** 2026-10-12 · **Last updated:** 2026-10-31 · **Change:** minor (Azerbaijani and Romanian UI catalogs; native review and Rust/offscreen verification pending; Swedish, Finnish, Danish and Norwegian Bokmål UI catalogs; previously minor: Arabic UI catalog) · **Target:** Adobe Photoshop 2026 +> **Last reviewed:** 2026-10-12 · **Last updated:** 2026-10-31 · **Change:** minor (Azerbaijani and Romanian UI catalogs; native review pending; local build and focused checks verified; Swedish, Finnish, Danish and Norwegian Bokmål UI catalogs; previously minor: Arabic UI catalog) · **Target:** Adobe Photoshop 2026 Per-language status of PhotoCraft's UI. How catalogs work and how to add one: [`localization.md`](localization.md). Language notes: [Korean](localization-ko.md), [Spanish](localization-es.md), @@ -24,7 +24,7 @@ Vietnamese UI; Arabic and Hebrew users get English "Middle East" builds with the composer, and its text engine handles RTL, Devanagari and Indic scripts. **Summary.** 7 of the 12 key languages at 100% of measured keys (status `partial` because of the -engine messages), 2 absent (Hindi, Vietnamese), plus 8 other languages. Weighted by +engine messages), 2 absent (Hindi, Vietnamese), plus 17 other registered languages. Weighted by speakers, ~55% ready. Remaining: 80–140 h, plus native-speaker review (recorded only for Italian, 2026-10-12). @@ -47,11 +47,13 @@ speakers, ~55% ready. Remaining: 80–140 h, plus native-speaker review (recorde ## Other shipped languages -8 more, each at 1,608 / 1,608 measured keys, status `partial` for the same reason: Traditional +17 registered languages are outside the twelve-language table. The following eight have +1,608 / 1,608 measured keys, status `partial` for the same reason: Traditional Chinese `zh-hant` (2,329 rows), Russian `ru` (4,227 keys; also every dynamic label the other catalogs translate, [notes](localization-ru.md)), Ukrainian `uk` (3,375), Czech `cs` (3,522), Polish `pl` (3,366), Greek `el` (3,481), Dutch `nl` (3,568), Italian `it` (4,164; also every dynamic label the -other catalogs translate; reviewed by a native speaker, [notes](localization-it.md)). Turkish `tr` +other catalogs translate; reviewed by a native speaker, [notes](localization-it.md)). Hungarian +`hu` (4,347 rows) is also registered. Turkish `tr` (3,731 rows) also covers every measured key. Serbian `sr` (4,022 rows, Latin script, Ekavian, added 2026-10-12) covers every measured key and the union of the other catalogs' dynamic labels; its one/few/many plurals reuse the Russian rule. Swedish `sv` (4,552 rows, added 2026-10-28) covers every measured key and the @@ -66,9 +68,10 @@ Azerbaijani `az` (Latin script) and Romanian `ro` were added 2026-10-31, bringin registry to 29 languages including English. Each catalog has 4,566 entries, including the existing dynamic-label key union and 32 plural messages. Romanian uses one/few/other integer forms, including `20 de straturi`; Azerbaijani uses one/other. Both have complete menu keys, -regional locale matching and English fallback. Native-speaker review and Rust/offscreen -verification remain pending; this does not raise the estimated readiness percentage or close -G13. [Terminology, provenance and validation limits](localization-az-ro.md). +regional locale matching and English fallback. Native-speaker review remains pending. Local +build, six language tests, Clippy, wasm and CPU offscreen checks passed; broader-test limits +are documented in the language notes. This does not raise the estimated readiness percentage +or close G13. [Terminology, provenance and validation limits](localization-az-ro.md). Czech UI follow-up (2026-10-11): status and notice wrappers, hardware labels, brush and transform accessibility names, rasterization prompts, profile mismatch messages and preset @@ -94,7 +97,7 @@ engine localization or native review is complete. | Date | Change | Summary | |---|---|---| -| 2026-10-31 | minor | Azerbaijani and Romanian UI catalogs, 4,566 entries each; regional tags and plural rules; native review and Rust/offscreen verification pending; readiness estimates unchanged | +| 2026-10-31 | minor | Azerbaijani and Romanian UI catalogs, 4,566 entries each; regional tags and plural rules; registry summary corrected to 17 languages outside the table, including Hungarian; native review pending; local build and focused checks verified; readiness estimates unchanged | | 2026-10-10 | major | First version, measured from the catalogs with the `i18n-coverage` rules | | 2026-10-10 | minor | Right-to-left shell layout (menu bar, title, options and status bars, toolbox and dock sides) | | 2026-10-11 | minor | Czech dynamic status/notice and hardware text, render labels and Navigator zoom context; engine messages remain English | diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 33b7a02f00..16361544e3 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -1,6 +1,6 @@ # PhotoCraft vs Adobe Photoshop: target-app parity -> **Last reviewed:** 2026-10-12 · **Last updated:** 2026-10-31 · **Change:** minor (Azerbaijani and Romanian UI catalogs; native review and Rust/offscreen verification pending; selected colour sets edit drawing, tone and supported filters; geometry remains open; Smooth and Feather command masks cropped to selection bounds plus blur reach; cropped selection morphology and command mask conversion performance evidence added; 12 of 178 preferences do nothing, from the scorecard; the Camera Raw row names per-camera colour for non-DNG cameras as missing, not the Calibration panel, which landed 2026-10-13; Content-Aware Scale measurements added, #5236; Help › Check for Updates and an opt-out check once per launch, DIST-220-1 partial, #5485; area type, TYPE-218-10 partial, Type scorecard 5 / 5 / 0; compound layer properties export as separate Photoshop Set events, #8304; JPEG Options dialog landed, FILE-215-19 partial; Print › Functions › Bleed through `file.print`; Content-Aware Move live preview for every selection size, TOOL-213-4 done, #6680; retouching evidence: Pattern Stamp and Background Eraser draw live, only the Mixer Brush is left of TOOL-213-7; fill and adjustment layers copy and paste whole, UI-217-23 partial, #2344; shape layers take Density and Feather, TOOL-214-12 done, #5846; on-canvas corner radius widgets, TOOL-214-12; `.atn` import maps 112 events; Free Transform H/V skew fields; stale rows corrected against `main` and the scorecard, estimates unchanged; Photoshop Actions `.atn` load and save; 2026-10-10: Tilt-Shift measurements added; earlier the same day: major, full re-measure against Photoshop 2026 27.11.0; merged `parity-estimate.md` and the roadmap's honest assessment; stale missing-tool rows corrected: Color Replacement and Red Eye are live toolbar tools, Frame landed; percentages unchanged; PSD spot plates in Photoshop's polarity, #7415 split; TIFF spot plates, FILE-215-17's TIFF part, #7415) · **Target:** Adobe Photoshop 2026 +> **Last reviewed:** 2026-10-12 · **Last updated:** 2026-10-31 · **Change:** minor (Azerbaijani and Romanian UI catalogs; native review pending; local build and focused checks verified; selected colour sets edit drawing, tone and supported filters; geometry remains open; Smooth and Feather command masks cropped to selection bounds plus blur reach; cropped selection morphology and command mask conversion performance evidence added; 12 of 178 preferences do nothing, from the scorecard; the Camera Raw row names per-camera colour for non-DNG cameras as missing, not the Calibration panel, which landed 2026-10-13; Content-Aware Scale measurements added, #5236; Help › Check for Updates and an opt-out check once per launch, DIST-220-1 partial, #5485; area type, TYPE-218-10 partial, Type scorecard 5 / 5 / 0; compound layer properties export as separate Photoshop Set events, #8304; JPEG Options dialog landed, FILE-215-19 partial; Print › Functions › Bleed through `file.print`; Content-Aware Move live preview for every selection size, TOOL-213-4 done, #6680; retouching evidence: Pattern Stamp and Background Eraser draw live, only the Mixer Brush is left of TOOL-213-7; fill and adjustment layers copy and paste whole, UI-217-23 partial, #2344; shape layers take Density and Feather, TOOL-214-12 done, #5846; on-canvas corner radius widgets, TOOL-214-12; `.atn` import maps 112 events; Free Transform H/V skew fields; stale rows corrected against `main` and the scorecard, estimates unchanged; Photoshop Actions `.atn` load and save; 2026-10-10: Tilt-Shift measurements added; earlier the same day: major, full re-measure against Photoshop 2026 27.11.0; merged `parity-estimate.md` and the roadmap's honest assessment; stale missing-tool rows corrected: Color Replacement and Red Eye are live toolbar tools, Frame landed; percentages unchanged; PSD spot plates in Photoshop's polarity, #7415 split; TIFF spot plates, FILE-215-17's TIFF part, #7415) · **Target:** Adobe Photoshop 2026 The authoritative answer to "how close is PhotoCraft to Photoshop, and how much work is left". The one-page summary is [`ROADMAP.md`](../ROADMAP.md); the ranked work list is [`gaps.md`](gaps.md); @@ -584,14 +584,15 @@ See [method, raw measurements and reproduction](five-algorithm-performance.md). Azerbaijani (az, Latin script) and Romanian (ro) UI catalogs were added 2026-10-31, with 4,566 entries each and regional locale matching. Romanian has one/few/other integer -plurals. Native-speaker review and Rust/offscreen verification remain pending; readiness -estimates are unchanged. See [localization notes](localization-az-ro.md). +plurals. Native-speaker review remains pending. Local build and focused checks passed; +readiness estimates are unchanged. See [localization notes](localization-az-ro.md) for +the broader-test failures and verification limits. ## Revision history | Date | Change | Summary | |---|---|---| -| 2026-10-31 | minor | Azerbaijani and Romanian UI catalogs, 4,566 entries each; regional tags and plural rules; native review and Rust/offscreen verification pending; readiness estimates unchanged | +| 2026-10-31 | minor | Azerbaijani and Romanian UI catalogs, 4,566 entries each; regional tags and plural rules; native review pending; local build and focused checks verified; readiness estimates unchanged | | 2026-10-27 | minor | Smooth/Feather command reads and replacement use selection bounds plus blur reach; full-canvas reference and release comparisons extended; percentages and scorecard budgets unchanged | | 2026-10-26 | minor | Expand/Contract/Border distance work, mask reads and replacement cropped to nonzero bounds; bounded distances for radii ≤5; 36 MP exact-output comparisons improve 1.54–37.82× across eight cases; percentages, estimates and scorecard budgets unchanged | | 2026-10-25 | minor | Selected colour channel sets: drawing, combined and per-component Levels/Curves, adjustments and supported filters; [scope, evidence and benchmarks](multi-channel-editing.md). Move/transform remains open; percentages unchanged |