diff --git a/crates/ui-egui/locales/de.json b/crates/ui-egui/locales/de.json index ebd52e71d..cb40d0200 100644 --- a/crates/ui-egui/locales/de.json +++ b/crates/ui-egui/locales/de.json @@ -1900,5 +1900,25 @@ "Choose an album…": "Album wählen…", "No albums yet": "Noch keine Alben", "Fix the marked rules to save this album.": "Korrigiere die markierten Regeln, um dieses Album zu speichern.", - "Some rules need fixing (Edit Smart Album…):": "Einige Regeln müssen korrigiert werden (Smart-Album bearbeiten…):" + "Some rules need fixing (Edit Smart Album…):": "Einige Regeln müssen korrigiert werden (Smart-Album bearbeiten…):", + "isn't a field rules know": "ist kein bekanntes Feld", + "can't be tested that way": "lässt sich so nicht prüfen", + "is yes or no": "ist Ja oder Nein", + "needs a number": "braucht eine Zahl", + "needs a time like 1/250 or 2": "braucht eine Zeit wie 1/250 oder 2", + "needs a date like 2026, 2026-08 or 2026-08-14": "braucht ein Datum wie 2026, 2026-08 oder 2026-08-14", + "has the first date after the second": "das erste Datum liegt nach dem zweiten", + "needs two values": "braucht zwei Werte", + "needs a number more than 0": "braucht eine Zahl größer als 0", + "needs hours, days, weeks, months or years": "braucht Stunden, Tage, Wochen, Monate oder Jahre", + "isn't one of the choices": "ist keine der Auswahlmöglichkeiten", + "needs something to look for (or use “is empty”)": "braucht einen Suchbegriff (oder „ist leer“)", + "needs something to look for (or use “isn't empty”)": "braucht einen Suchbegriff (oder „ist nicht leer“)", + "needs text": "braucht Text", + "can't match any rating from 0 to 5": "passt zu keiner Bewertung von 0 bis 5", + "needs an album": "braucht ein Album", + "names an album that no longer exists": "nennt ein Album, das es nicht mehr gibt", + "can't test a smart album; choose a plain album": "kann kein Smart-Album prüfen; wähle ein normales Album", + "can't test a folder; choose an album in it": "kann keinen Ordner prüfen; wähle ein Album darin", + "This group is empty: add a rule or remove it.": "Diese Gruppe ist leer: Füge eine Regel hinzu oder entferne sie." } diff --git a/crates/ui-egui/locales/es.json b/crates/ui-egui/locales/es.json index 77e4959f1..a894ed841 100644 --- a/crates/ui-egui/locales/es.json +++ b/crates/ui-egui/locales/es.json @@ -1745,5 +1745,25 @@ "Choose an album…": "Elige un álbum…", "No albums yet": "Aún no hay álbumes", "Fix the marked rules to save this album.": "Corrige las reglas marcadas para guardar este álbum.", - "Some rules need fixing (Edit Smart Album…):": "Hay reglas que corregir (Editar álbum inteligente…):" + "Some rules need fixing (Edit Smart Album…):": "Hay reglas que corregir (Editar álbum inteligente…):", + "isn't a field rules know": "no es un campo conocido", + "can't be tested that way": "no se puede comprobar así", + "is yes or no": "es Sí o No", + "needs a number": "necesita un número", + "needs a time like 1/250 or 2": "necesita un tiempo como 1/250 o 2", + "needs a date like 2026, 2026-08 or 2026-08-14": "necesita una fecha como 2026, 2026-08 o 2026-08-14", + "has the first date after the second": "la primera fecha es posterior a la segunda", + "needs two values": "necesita dos valores", + "needs a number more than 0": "necesita un número mayor que 0", + "needs hours, days, weeks, months or years": "necesita horas, días, semanas, meses o años", + "isn't one of the choices": "no es ninguna de las opciones", + "needs something to look for (or use “is empty”)": "necesita algo que buscar (o usa «está vacío»)", + "needs something to look for (or use “isn't empty”)": "necesita algo que buscar (o usa «no está vacío»)", + "needs text": "necesita texto", + "can't match any rating from 0 to 5": "no coincide con ninguna valoración de 0 a 5", + "needs an album": "necesita un álbum", + "names an album that no longer exists": "indica un álbum que ya no existe", + "can't test a smart album; choose a plain album": "no puede comprobar un álbum inteligente; elige un álbum normal", + "can't test a folder; choose an album in it": "no puede comprobar una carpeta; elige un álbum dentro", + "This group is empty: add a rule or remove it.": "Este grupo está vacío: añade una regla o quítalo." } diff --git a/crates/ui-egui/locales/ja.json b/crates/ui-egui/locales/ja.json index 94f96729b..b7fa0316b 100644 --- a/crates/ui-egui/locales/ja.json +++ b/crates/ui-egui/locales/ja.json @@ -1749,5 +1749,25 @@ "Choose an album…": "アルバムを選択…", "No albums yet": "アルバムはまだありません", "Fix the marked rules to save this album.": "このアルバムを保存するには、印の付いた条件を修正してください。", - "Some rules need fixing (Edit Smart Album…):": "修正が必要な条件があります(スマートアルバムを編集…):" + "Some rules need fixing (Edit Smart Album…):": "修正が必要な条件があります(スマートアルバムを編集…):", + "isn't a field rules know": "不明な項目です", + "can't be tested that way": "この方法では判定できません", + "is yes or no": "「あり」か「なし」を選んでください", + "needs a number": "数値が必要です", + "needs a time like 1/250 or 2": "1/250 や 2 のようなシャッター速度が必要です", + "needs a date like 2026, 2026-08 or 2026-08-14": "2026、2026-08、2026-08-14 のような日付が必要です", + "has the first date after the second": "最初の日付が2番目の日付より後です", + "needs two values": "2つの値が必要です", + "needs a number more than 0": "0より大きい数値が必要です", + "needs hours, days, weeks, months or years": "時間・日・週・月・年のいずれかが必要です", + "isn't one of the choices": "選択肢にありません", + "needs something to look for (or use “is empty”)": "検索する語句が必要です(または「が空」を使用)", + "needs something to look for (or use “isn't empty”)": "検索する語句が必要です(または「が空でない」を使用)", + "needs text": "文字列が必要です", + "can't match any rating from 0 to 5": "0〜5 のどの評価にも一致しません", + "needs an album": "アルバムを選択してください", + "names an album that no longer exists": "存在しないアルバムが指定されています", + "can't test a smart album; choose a plain album": "スマートアルバムは判定できません。通常のアルバムを選択してください", + "can't test a folder; choose an album in it": "フォルダーは判定できません。中のアルバムを選択してください", + "This group is empty: add a rule or remove it.": "このグループは空です。条件を追加するか、グループを削除してください。" } diff --git a/crates/ui-egui/locales/pt-br.json b/crates/ui-egui/locales/pt-br.json index 1abdf4e48..7e06fa7ae 100644 --- a/crates/ui-egui/locales/pt-br.json +++ b/crates/ui-egui/locales/pt-br.json @@ -1745,5 +1745,25 @@ "Choose an album…": "Escolha um álbum…", "No albums yet": "Ainda não há álbuns", "Fix the marked rules to save this album.": "Corrija as regras marcadas para salvar este álbum.", - "Some rules need fixing (Edit Smart Album…):": "Algumas regras precisam ser corrigidas (Editar álbum inteligente…):" + "Some rules need fixing (Edit Smart Album…):": "Algumas regras precisam ser corrigidas (Editar álbum inteligente…):", + "isn't a field rules know": "não é um campo conhecido", + "can't be tested that way": "não pode ser testado assim", + "is yes or no": "é Sim ou Não", + "needs a number": "precisa de um número", + "needs a time like 1/250 or 2": "precisa de um tempo como 1/250 ou 2", + "needs a date like 2026, 2026-08 or 2026-08-14": "precisa de uma data como 2026, 2026-08 ou 2026-08-14", + "has the first date after the second": "a primeira data é posterior à segunda", + "needs two values": "precisa de dois valores", + "needs a number more than 0": "precisa de um número maior que 0", + "needs hours, days, weeks, months or years": "precisa de horas, dias, semanas, meses ou anos", + "isn't one of the choices": "não é nenhuma das opções", + "needs something to look for (or use “is empty”)": "precisa de algo para procurar (ou use “está vazio”)", + "needs something to look for (or use “isn't empty”)": "precisa de algo para procurar (ou use “não está vazio”)", + "needs text": "precisa de texto", + "can't match any rating from 0 to 5": "não corresponde a nenhuma avaliação de 0 a 5", + "needs an album": "precisa de um álbum", + "names an album that no longer exists": "indica um álbum que não existe mais", + "can't test a smart album; choose a plain album": "não pode testar um álbum inteligente; escolha um álbum comum", + "can't test a folder; choose an album in it": "não pode testar uma pasta; escolha um álbum dentro dela", + "This group is empty: add a rule or remove it.": "Este grupo está vazio: adicione uma regra ou remova-o." } diff --git a/crates/ui-egui/locales/ru.json b/crates/ui-egui/locales/ru.json index 61fed8b4d..fd289e141 100644 --- a/crates/ui-egui/locales/ru.json +++ b/crates/ui-egui/locales/ru.json @@ -1417,5 +1417,25 @@ "Choose an album…": "Выберите альбом…", "No albums yet": "Альбомов пока нет", "Fix the marked rules to save this album.": "Исправьте отмеченные условия, чтобы сохранить альбом.", - "Some rules need fixing (Edit Smart Album…):": "Некоторые условия нужно исправить (Изменить смарт-альбом…):" + "Some rules need fixing (Edit Smart Album…):": "Некоторые условия нужно исправить (Изменить смарт-альбом…):", + "isn't a field rules know": "неизвестное поле", + "can't be tested that way": "так проверить нельзя", + "is yes or no": "только «Да» или «Нет»", + "needs a number": "нужно число", + "needs a time like 1/250 or 2": "нужна выдержка вроде 1/250 или 2", + "needs a date like 2026, 2026-08 or 2026-08-14": "нужна дата вроде 2026, 2026-08 или 2026-08-14", + "has the first date after the second": "первая дата позже второй", + "needs two values": "нужны два значения", + "needs a number more than 0": "нужно число больше 0", + "needs hours, days, weeks, months or years": "нужны часы, дни, недели, месяцы или годы", + "isn't one of the choices": "нет среди вариантов", + "needs something to look for (or use “is empty”)": "нужно указать, что искать (или выберите «пусто»)", + "needs something to look for (or use “isn't empty”)": "нужно указать, что искать (или выберите «не пусто»)", + "needs text": "нужен текст", + "can't match any rating from 0 to 5": "не подходит ни к одной оценке от 0 до 5", + "needs an album": "нужен альбом", + "names an album that no longer exists": "указан альбом, которого больше нет", + "can't test a smart album; choose a plain album": "нельзя проверять смарт-альбом; выберите обычный альбом", + "can't test a folder; choose an album in it": "нельзя проверять папку; выберите альбом в ней", + "This group is empty: add a rule or remove it.": "Эта группа пуста: добавьте условие или удалите её." } diff --git a/crates/ui-egui/locales/zh-hans.json b/crates/ui-egui/locales/zh-hans.json index 65e9b1944..335867dc8 100644 --- a/crates/ui-egui/locales/zh-hans.json +++ b/crates/ui-egui/locales/zh-hans.json @@ -1745,5 +1745,25 @@ "Choose an album…": "选择相册…", "No albums yet": "还没有相册", "Fix the marked rules to save this album.": "请修正标记的规则以保存此相册。", - "Some rules need fixing (Edit Smart Album…):": "有些规则需要修正(编辑智能相册…):" + "Some rules need fixing (Edit Smart Album…):": "有些规则需要修正(编辑智能相册…):", + "isn't a field rules know": "不是已知的字段", + "can't be tested that way": "不能这样判断", + "is yes or no": "只能是“是”或“否”", + "needs a number": "需要一个数字", + "needs a time like 1/250 or 2": "需要类似 1/250 或 2 的快门速度", + "needs a date like 2026, 2026-08 or 2026-08-14": "需要类似 2026、2026-08 或 2026-08-14 的日期", + "has the first date after the second": "第一个日期晚于第二个", + "needs two values": "需要两个值", + "needs a number more than 0": "需要大于 0 的数字", + "needs hours, days, weeks, months or years": "需要小时、天、周、月或年", + "isn't one of the choices": "不是可选项之一", + "needs something to look for (or use “is empty”)": "需要要查找的内容(或使用“为空”)", + "needs something to look for (or use “isn't empty”)": "需要要查找的内容(或使用“不为空”)", + "needs text": "需要文本", + "can't match any rating from 0 to 5": "不匹配 0 到 5 的任何星级", + "needs an album": "需要选择相册", + "names an album that no longer exists": "指定的相册已不存在", + "can't test a smart album; choose a plain album": "无法判断智能相册;请选择普通相册", + "can't test a folder; choose an album in it": "无法判断文件夹;请选择其中的相册", + "This group is empty: add a rule or remove it.": "此组为空:请添加规则或将其删除。" } diff --git a/crates/ui-egui/locales/zh-hant.json b/crates/ui-egui/locales/zh-hant.json index 12487b4b1..1995f4e8b 100644 --- a/crates/ui-egui/locales/zh-hant.json +++ b/crates/ui-egui/locales/zh-hant.json @@ -1745,5 +1745,25 @@ "Choose an album…": "選擇相簿…", "No albums yet": "還沒有相簿", "Fix the marked rules to save this album.": "請修正標記的規則以儲存此相簿。", - "Some rules need fixing (Edit Smart Album…):": "有些規則需要修正(編輯智慧相簿…):" + "Some rules need fixing (Edit Smart Album…):": "有些規則需要修正(編輯智慧型相簿…):", + "isn't a field rules know": "不是已知的欄位", + "can't be tested that way": "無法這樣判斷", + "is yes or no": "只能是「是」或「否」", + "needs a number": "需要一個數字", + "needs a time like 1/250 or 2": "需要類似 1/250 或 2 的快門速度", + "needs a date like 2026, 2026-08 or 2026-08-14": "需要類似 2026、2026-08 或 2026-08-14 的日期", + "has the first date after the second": "第一個日期晚於第二個", + "needs two values": "需要兩個值", + "needs a number more than 0": "需要大於 0 的數字", + "needs hours, days, weeks, months or years": "需要小時、天、週、月或年", + "isn't one of the choices": "不是可選項之一", + "needs something to look for (or use “is empty”)": "需要要尋找的內容(或使用「為空」)", + "needs something to look for (or use “isn't empty”)": "需要要尋找的內容(或使用「不為空」)", + "needs text": "需要文字", + "can't match any rating from 0 to 5": "不符合 0 到 5 的任何評分", + "needs an album": "需要選擇相簿", + "names an album that no longer exists": "指定的相簿已不存在", + "can't test a smart album; choose a plain album": "無法判斷智慧型相簿;請選擇一般相簿", + "can't test a folder; choose an album in it": "無法判斷資料夾;請選擇其中的相簿", + "This group is empty: add a rule or remove it.": "此群組為空:請新增規則或將其移除。" } diff --git a/crates/ui-egui/src/i18n.rs b/crates/ui-egui/src/i18n.rs index 67ae99f3f..21ce65ddd 100644 --- a/crates/ui-egui/src/i18n.rs +++ b/crates/ui-egui/src/i18n.rs @@ -328,9 +328,29 @@ pub fn filter_label(filter: &lightcraft_catalog::Filter, catalog: &lightcraft_ca } } -/// What people read for a smart-album rule problem (`rule 2: …`). +/// What people read for a smart-album rule's problem, under its row: the field's name and the +/// issue, in the current language ("Title: needs something to look for"). pub fn problem_text(problem: &lightcraft_catalog::rules::Problem) -> String { - problem.to_string() + problem_text_in(language(), problem) +} + +/// [`problem_text`] with the rule's position first ("#2.1 Rating: …"), for lists away from the +/// editor (the sidebar's tooltip). +pub fn problem_line(problem: &lightcraft_catalog::rules::Problem) -> String { + problem_line_in(language(), problem) +} + +fn problem_text_in(language: Locale, problem: &lightcraft_catalog::rules::Problem) -> String { + let issue = tr_in(language, problem.issue.text()); + match problem.field.as_deref() { + Some(field) => format!("{}: {issue}", tr_in(language, lightcraft_catalog::rules::field_label(field).unwrap_or(field))), + None => issue.to_string(), + } +} + +fn problem_line_in(language: Locale, problem: &lightcraft_catalog::rules::Problem) -> String { + let at: Vec = problem.path.iter().map(|i| i.saturating_add(1).to_string()).collect(); + format!("#{} {}", at.join("."), problem_text_in(language, problem)) } /// What people read for smart-album choice `id` of `field` ("Gemeinfrei" for `publicDomain` in @@ -721,6 +741,29 @@ mod tests { } } + /// A rule's problem reads as the field's name and the issue, in the editor's language ("Titel: + /// braucht einen Suchbegriff …"); the sidebar's list puts the rule's position first. Every issue + /// is translated in every language: an English one would stand out in a translated editor. + #[test] + fn problems_read_in_every_language() { + use lightcraft_catalog::rules::Issue; + let cat = lightcraft_catalog::Catalog::new(); + let problems = |rules: serde_json::Value| { + serde_json::from_value::(serde_json::json!({"rules": rules})).unwrap().check(&cat) + }; + let p = problems(serde_json::json!([{"field": "rating", "op": "gte", "value": 3}, {"field": "title", "op": "contains", "value": ""}])); + assert_eq!(problem_text_in(Locale::En, &p[0]), "Title: needs something to look for (or use “is empty”)"); + assert_eq!(problem_text_in(Locale::De, &p[0]), "Titel: braucht einen Suchbegriff (oder „ist leer“)"); + assert_eq!(problem_line_in(Locale::En, &p[0]), "#2 Title: needs something to look for (or use “is empty”)"); + let g = problems(serde_json::json!([{"group": {"rules": []}}])); + assert_eq!(problem_text_in(Locale::En, &g[0]), "This group is empty: add a rule or remove it."); + for language in Locale::ALL.iter().filter(|l| **l != Locale::En) { + for issue in Issue::ALL { + assert!(language.catalog().contains_key(issue.text()), "{} lacks {:?}", language.code(), issue.text()); + } + } + } + /// Every command, control, rule and rename label, and every line of What's New, is a message a /// catalog can translate. What a language lacks is reported, not failed (like /// `catalogs_agree_on_placeholders_and_report_gaps`): a feature PR doesn't have to ship every diff --git a/crates/ui-egui/src/panels/left.rs b/crates/ui-egui/src/panels/left.rs index 7b9584e7d..8f843d946 100644 --- a/crates/ui-egui/src/panels/left.rs +++ b/crates/ui-egui/src/panels/left.rs @@ -749,7 +749,7 @@ fn albums_tree(app: &mut LightcraftApp, ui: &mut egui::Ui, all: &AlbumKids, pare tip.push_str(crate::i18n::tr("Some rules need fixing (Edit Smart Album…):")); for p in &problems { tip.push('\n'); - tip.push_str(&crate::i18n::problem_text(p)); + tip.push_str(&crate::i18n::problem_line(p)); } } resp = resp.on_hover_text(tip); diff --git a/crates/ui-egui/src/panels/rules_editor.rs b/crates/ui-egui/src/panels/rules_editor.rs index 3d6bdd243..cae91ac20 100644 --- a/crates/ui-egui/src/panels/rules_editor.rs +++ b/crates/ui-egui/src/panels/rules_editor.rs @@ -266,7 +266,7 @@ fn field_menu(ui: &mut egui::Ui, salt: &str, field: &mut String, op: &mut String fn problem_note(ui: &mut egui::Ui, problem: Option<&Problem>, salt: &str) { let Some(p) = problem else { return }; let t = Tokens::get(ui.ctx()); - let r = ui.label(RichText::new(format!("⚠ {}", p.message)).color(t.caution).small()); + let r = ui.label(RichText::new(format!("⚠ {}", crate::i18n::problem_text(p))).color(t.caution).small()); register(ui.ctx(), format!("ruleProblem:{salt}"), r.rect); } diff --git a/docs/parity.md b/docs/parity.md index 4a86dee53..6aee99619 100644 --- a/docs/parity.md +++ b/docs/parity.md @@ -744,7 +744,7 @@ From `08-lightroom-classic-extras.md` (Classic-only features) and part 2 of `06- | LRC-LIB-FOLDERS | Disk folder tree | P1 | ✅ | `cmd:library.browse`, `cmd:folder.rename`, `cmd:folder.move`, `crates/ui-egui/src/panels/left.rs` (`local_section`, `folder_tree`) | standard places + the browsed folder + Browse Folder…, each an expandable tree of subfolders, breadcrumb navigation; context menu: Rename Folder…, Move Folder To…, Show in Finder (on disk, photos relinked; one undo step each: undo / redo move the folder back and forth, refused when the destination is taken) | | LRC-LIB-LIBFOLDERS | Library folders | P1 | ✅ | `cmd:library.folders`, `cmd:library.source` (`libraryFolder`), `cmd:library.filter` (`libraryFolder`), `cmd:library.removeFolder`, `cmd:folder.rename`, `cmd:folder.move`, `crates/catalog/src/folders.rs`, `crates/ui-egui/src/panels/left.rs` (`folders_section`, `folder_menu_for_library`, `row_menu`), `crates/ui-egui/src/tests_panels.rs` | sidebar Folders: the folders the library's photos were imported from, with photo counts (subfolders included), as another way to explore the library next to By Date and Keywords; Local stays the way to browse any folder on disk. Each disk is a row of its own and starts open: a drive letter, a Windows share (`\\server\share`, `\\?\UNC\…`, `\\wsl$\…`), a macOS share or disk (`/Volumes/tokyo`), a Linux mount (`/media`, `/run/media`, `/mnt`, gvfs `smb-share:…`, `/net/`), NFS (`/Network/Servers/`), else “This Computer” — told apart from the stored paths alone, so an offline share lists like a mounted one and the sidebar never waits on a network. Only folders that hold imported photos are listed (browsed, deleted and demo photos don't count); every folder is a row of its own (`Users` → `me` → `Pictures`), and a folder that holds nothing itself and leads to one folder starts open; every level indents one step more than its parent however deep it goes: the whole sidebar (Albums, Local, By Date, Folders, Keywords) scrolls sideways when its widest row is wider than the panel (rows are measured one frame behind; Create Album and the counts stay at the visible edge); a name that does not fit ends in an ellipsis at the count (the full name on hover; a row asks for room for at most 140 pt of its name, so depth, not one long name, makes the sidebar scroll), as does the grid title; selection and hover bars end at the panel's edge; click makes the folder the source, like an album or a Local folder (its photos and those of the folders inside it fill the grid, titled “photos/travel”; the filter bar narrows it further; `library.source` `libraryFolder` + `path`; kept with the library's view), and its row is always opened into view; the shown folder follows a rename or move and their undo (engine-tested; the sidebar test covers rename), is dropped back to All Photos when it is removed or no longer holds a photo, and a smart album made from the view keeps it, also when its rules are edited (the editor has no folder field). Only a folder that shows exactly its own photos can be shown (not the startup disk, not `/Volumes`-like folders above disks, not an empty one). A row that would cover other disks' photos too (the startup disk, `/Volumes`, `/mnt`…) only opens. Right-click (row or triangle): Rename Folder…, Move Folder To…, Show in Finder (on disk, photos relinked, as in Local) and Remove from Library… (asks; moves the folder's photos to Recently Deleted, one undo step, no file touched); a disk row offers Remove Disk from Library… (the command needs `disk: true` for a whole disk or a folder spanning several). Limits: names are compared as written (macOS `/Volumes/tokyo` vs `/Volumes/TOKYO` are two disks; `/Volumes/Macintosh HD/…` and `/…` two rows); no disk capacity / online status; no “include subfolders” switch (a folder always includes the ones inside it); creating a folder and moving photos between folders are not here | | LRC-LIB-COLLECTIONS | Collections & sets | P1 | ✅ | `cmd:album.create`, `cmd:album.createSmart`, `cmd:album.toggleTarget`, `cmd:album.setTarget`, `cmd:album.clearQuick` | albums + folders (sets), smart albums (rule editor; rules incl. File Path — the whole string, either separator, case-insensitive), Quick Collection and target album (B in the grids adds / removes the selection; album menu ▸ Set as Target Album; the target is marked +) | -| LRC-LIB-SMARTCOLL | Smart-collection rules | P1 | ✅ | `cmd:album.createSmart`, `cmd:album.setRules`, `cmd:album.ruleFields`, `cmd:dialog.smartAlbum`, `crates/catalog/src/rules.rs`, `crates/ui-egui/src/panels/rules_editor.rs` | rule editor: match all / any / none, nested groups (⌥+ or + Group), 47 fields. The field menu shows Rating, Pick Flag, Color Label and Any Searchable Text, then one submenu per group (`FIELD_GROUPS`; `album.ruleFields` reports each field's `group`): Source (album, virtual copy, copy name, in a stack), File (filename, extension, file path, type, format, video duration), Date (capture / import / edit), Keywords & People (keywords, keyword count, people on named faces, people count), Description (title, caption, alt text, creator, copyright, copyright status), Camera Info (camera, lens, focal length, aperture, shutter speed as 1/250, ISO), Location (any, city, state / province, country, GPS), Size (long / short edge, aspect ratio and megapixels, all of the photo as shown: oriented and cropped), Develop (has edits, cropped, treatment), Assisted Culling (focus, best of similar shots); text / number / date / in-the-last operators; live match count; also usable as a library filter (`ruleSet`). Missing vs Classic: develop preset, flash, exposure bias, camera serial, colour profile / bit depth, IPTC Job, smart-preview status, membership of smart collections. Choice values read as words in every language (Public Domain, Black & White; `album.ruleFields` gives `choiceLabels`), while rules store the ids; yes/no fields read Yes / No and take `true`/`false`, `"yes"`/`"no"`, `"true"`/`"false"` or 1/0. Every value is checked against its field (`RuleSet::check`): numbers (rating 0–5), shutter speeds, dates (`2026`, `2026-08`, `2026-08-14`, a full time; no 30 February), between = two bounds in order, in the last = more than 0 of a known unit, choices (ids, the flag's picked / rejected, a colour label's custom name), text with something to look for, an existing plain album, no empty groups; each problem names its rule (`rule 2.1: rating is 0–5, not 9`), and album.createSmart / setRules / library.filter refuse them. The editor marks each bad rule under its row (inside groups too), keeps OK disabled until they are fixed (and its confirm refuses them before renaming anything), picks an Album rule's album from a list of the plain albums, named with their folder and sorted by name (is / isn't; a folder can't be tested), and drawing it never changes a rule: a rating of 9, "in the last 0 days" or 20,000 days, a "between" that isn't a pair all stay as stored (and are marked where they can't match) until edited. Album rules saved with an old operator (≥, between) are read as the "isn't" they always meant (`RuleSet::upgrade`). Saved smart albums whose rules no longer check are marked ⚠ in the sidebar (tooltip: what to fix; `albums.list` `problems`, `Catalog::smart_album_problems`); the name and rules of an album are saved as one undo step (`album.setRules` `name`). Not yet: problem messages are English and name fields by id | +| LRC-LIB-SMARTCOLL | Smart-collection rules | P1 | ✅ | `cmd:album.createSmart`, `cmd:album.setRules`, `cmd:album.ruleFields`, `cmd:dialog.smartAlbum`, `crates/catalog/src/rules.rs`, `crates/ui-egui/src/panels/rules_editor.rs` | rule editor: match all / any / none, nested groups (⌥+ or + Group), 47 fields. The field menu shows Rating, Pick Flag, Color Label and Any Searchable Text, then one submenu per group (`FIELD_GROUPS`; `album.ruleFields` reports each field's `group`): Source (album, virtual copy, copy name, in a stack), File (filename, extension, file path, type, format, video duration), Date (capture / import / edit), Keywords & People (keywords, keyword count, people on named faces, people count), Description (title, caption, alt text, creator, copyright, copyright status), Camera Info (camera, lens, focal length, aperture, shutter speed as 1/250, ISO), Location (any, city, state / province, country, GPS), Size (long / short edge, aspect ratio and megapixels, all of the photo as shown: oriented and cropped), Develop (has edits, cropped, treatment), Assisted Culling (focus, best of similar shots); text / number / date / in-the-last operators; live match count; also usable as a library filter (`ruleSet`). Missing vs Classic: develop preset, flash, exposure bias, camera serial, colour profile / bit depth, IPTC Job, smart-preview status, membership of smart collections. Choice values read as words in every language (Public Domain, Black & White; `album.ruleFields` gives `choiceLabels`), while rules store the ids; yes/no fields read Yes / No and take `true`/`false`, `"yes"`/`"no"`, `"true"`/`"false"` or 1/0. Every value is checked against its field (`RuleSet::check`): numbers (rating 0–5), shutter speeds, dates (`2026`, `2026-08`, `2026-08-14`, a full time; no 30 February), between = two bounds in order, in the last = more than 0 of a known unit, choices (ids, the flag's picked / rejected, a colour label's custom name), text with something to look for, an existing plain album, no empty groups; each problem names its rule (`rule 2.1: rating is 0–5, not 9`), and album.createSmart / setRules / library.filter refuse them. The editor marks each bad rule under its row (inside groups too), keeps OK disabled until they are fixed (and its confirm refuses them before renaming anything), picks an Album rule's album from a list of the plain albums, named with their folder and sorted by name (is / isn't; a folder can't be tested), and drawing it never changes a rule: a rating of 9, "in the last 0 days" or 20,000 days, a "between" that isn't a pair all stay as stored (and are marked where they can't match) until edited. Album rules saved with an old operator (≥, between) are read as the "isn't" they always meant (`RuleSet::upgrade`). Saved smart albums whose rules no longer check are marked ⚠ in the sidebar (tooltip: what to fix; `albums.list` `problems`, `Catalog::smart_album_problems`); the name and rules of an album are saved as one undo step (`album.setRules` `name`). Problems read as the field's name and the issue in the editor's language (“Titel: braucht einen Suchbegriff”; `Problem::issue` / `field`, `Issue::text`, translated in every locale), while commands keep the English `rule 2.1: …` message for agents | | LRC-LIB-PUBLISH | Publish services | P2 | ⬜ | | | | LRC-LIB-FILTERBAR | Library filter bar | P1 | ✅ | `cmd:view.filterBar`, `cmd:filter.applyPreset`, `cmd:library.filter` (`labels`, `ruleSet`), `crates/ui-egui/src/panels/filterbar.rs` | rating/flag/label (several labels at once = any of them)/kind/edited/camera/lens/keyword, clear, save as smart album, filter presets; the filter stays as you change albums (always locked, as in the desktop app); arbitrary conditions via the smart-album rule editor | | LRC-LIB-STACKS | Stacks (full) | P1 | ✅ | `crates/catalog/src/stacks.rs`, `cmd:stack.group`, `cmd:stack.split`, `cmd:stack.moveUp`, `cmd:stack.moveDown` | group / ungroup / toggle / set top / remove / auto by time / split / move up and down | diff --git a/docs/whats-new.md b/docs/whats-new.md index 8252f9c31..e4d12673f 100644 --- a/docs/whats-new.md +++ b/docs/whats-new.md @@ -17,6 +17,8 @@ way round, "in the last 0 days", a colour label that doesn't exist, "contains" with nothing to look for, an album that is gone or smart, or an empty group is reported with its rule ("rule 2.1: rating is 0–5, not 9") instead of quietly matching nothing or everything. New date rules start at the current year. +- Rule problems are shown in your language and name the field the way the menu does ("Title: needs something to look + for"). - A smart album whose rules no longer check (an album they test was deleted) is marked ⚠ in the sidebar; its tooltip says what to fix, and `albums.list` reports `problems`. - Editing a smart album's name and rules is one step: both or neither, and one undo takes both back