Keep plug-in menu rows under Filter › Plug-ins in every language (#2540) (#2627)

* Keep plug-in menu rows under Filter › Plug-ins in every language (#2540)

plugin_ui::insert_menu_items built the insertion path from translated
strings (tl!("Filter"), tl!("Plug-ins")), but MenuItem::path is the
untranslated English path from a command's engine spec: the renderer
groups rows by it and translates only at display time. Under any
language whose catalog translates those menu names (ja, fr, ...) the
plug-in rows got a path no menu node matched, so installed WebAssembly
plug-ins were never drawn. Take the path from the "Install Plug-in…"
item the rows sit above instead, and cover it with a test that installs
the invert.wasm fixture under a Japanese language scope.

* cargo fmt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jason 2026-10-10 20:00:36 +02:00 committed by GitHub
parent ee4373521e
commit caa43fa3f6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 24 additions and 1 deletions

View File

@ -13,7 +13,11 @@ const PREFIX: &str = "plugin.filter.";
pub fn insert_menu_items(app: &PhotocraftApp, items: &mut Vec<MenuItem>) {
let plugins = photocraft_plugins::registry::list();
let Some(at) = items.iter().position(|i| i.id == "plugin.install") else { return };
let path: Vec<String> = vec![tl!("Filter").into(), tl!("Plug-ins").into()];
// `MenuItem::path` is the untranslated English path from the command's engine spec: the menu
// renderer groups rows by it and translates only at display time. Translating it here left the
// plug-in rows under a path no menu node matched, so under any non-English language they were
// never drawn (#2540). Borrow the path of "Install Plug-in…", the item we sit above.
let Some(path) = items.get(at).map(|i| i.path.clone()) else { return };
let enabled = app.session.is_enabled("plugin.run");
let mut extra: Vec<MenuItem> = plugins
.iter()
@ -93,4 +97,23 @@ mod tests {
let r = crate::menus::invoke(&mut app, &ctx, "plugin.install", json!({})).unwrap();
assert!(r.get("dialog").is_some());
}
/// Plug-in rows carry the untranslated English menu path like every other item, so they still
/// land under Filter › Plug-ins when the UI language translates those menu names (#2540).
#[test]
fn plugin_menu_path_stays_untranslated_in_other_languages() {
let ja = crate::i18n::Lang::from_code("ja").expect("registered language");
// The names on the path must really differ from the English, or the test proves nothing.
assert_ne!(crate::i18n::tr(ja, "Filter"), "Filter");
assert_ne!(crate::i18n::tr(ja, "Plug-ins"), "Plug-ins");
let _lang = crate::i18n::language_scope(ja);
let mut app = PhotocraftApp::new(photocraft_engine::Session::new(), crate::Services::default());
let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../plugins/tests/fixtures/invert.wasm");
app.run("plugin.install", json!({"path": path})).unwrap();
let items = crate::menus::menu_items(&app);
let me = items.iter().find(|i| i.id == "plugin.filter.org.photocraft.example.invert").expect("plug-in listed");
assert_eq!(me.path, ["Filter", "Plug-ins"], "plug-in row sits where the menu renderer looks for it");
let install = items.iter().find(|i| i.id == "plugin.install").expect("Install Plug-in… listed");
assert_eq!(me.path, install.path, "same submenu as Install Plug-in…");
}
}