Merge pull request #1151 from keller-lukas/fix/1145-macos-app-menu-i18n
macOS: app menu follows the interface language (#1145)
This commit is contained in:
commit
9dc3421bb1
|
|
@ -4,7 +4,7 @@
|
|||
use std::sync::mpsc::{Receiver, channel};
|
||||
|
||||
use filmcraft_ui_egui::FilmcraftApp;
|
||||
use filmcraft_ui_egui::menus::{MENUS, MenuItem as Item, menu_items};
|
||||
use filmcraft_ui_egui::menus::{MENUS, MenuItem as Item, NATIVE_APP_MENU, menu_items};
|
||||
use muda::accelerator::Accelerator;
|
||||
use muda::{Menu, MenuEvent, MenuItem, PredefinedMenuItem, Submenu};
|
||||
|
||||
|
|
@ -28,25 +28,39 @@ pub fn install(app: &FilmcraftApp, ctx: egui::Context) -> (Receiver<String>, Sho
|
|||
let mut titles: Vec<(String, Submenu)> = Vec::new();
|
||||
let bar = Menu::new();
|
||||
let app_menu = Submenu::new("FilmCraft", true);
|
||||
let [settings_label, hide_label, hide_others_label, quit_label] = NATIVE_APP_MENU;
|
||||
// FilmCraft ▸ Settings ▸ <category> (Premiere's app-menu layout; General is Cmd+,)
|
||||
let settings = Submenu::new(app.ui.language.tr("Settings"), true);
|
||||
titles.push(("Settings".into(), settings.clone()));
|
||||
let settings = Submenu::new(app.ui.language.tr(settings_label), true);
|
||||
titles.push((settings_label.into(), settings.clone()));
|
||||
for it in items.iter().filter(|i| i.id.starts_with("app.settings.")) {
|
||||
let label = it.label.trim_end_matches('…').to_string();
|
||||
let mi = MenuItem::with_id(it.id.clone(), label, true, it.shortcut.as_deref().and_then(accel));
|
||||
native.push((it.id.clone(), mi.clone()));
|
||||
let _ = settings.append(&mi);
|
||||
}
|
||||
// About and Discord repeat Help menu commands: same translated labels, updated with them
|
||||
let label = |id: &str, english: &str| items.iter().find(|i| i.id == id).map_or_else(|| english.to_string(), |i| i.label.clone());
|
||||
let about = MenuItem::with_id("app.about", label("app.about", "About FilmCraft"), true, None);
|
||||
let discord = MenuItem::with_id("help.discord", label("help.discord", "Join the ArtCraft Discord…"), true, None);
|
||||
native.push(("app.about".into(), about.clone()));
|
||||
native.push(("help.discord".into(), discord.clone()));
|
||||
// the predefined items keep their native actions; our text lets them follow the language
|
||||
let predefined = [
|
||||
(hide_label, PredefinedMenuItem::hide(Some(app.ui.language.tr(hide_label)))),
|
||||
(hide_others_label, PredefinedMenuItem::hide_others(Some(app.ui.language.tr(hide_others_label)))),
|
||||
(quit_label, PredefinedMenuItem::quit(Some(app.ui.language.tr(quit_label)))),
|
||||
];
|
||||
let [(_, hide), (_, hide_others), (_, quit)] = &predefined;
|
||||
let _ = app_menu.append_items(&[
|
||||
&MenuItem::with_id("app.about", "About FilmCraft", true, None),
|
||||
&MenuItem::with_id("help.discord", "Join the ArtCraft Discord…", true, None),
|
||||
&about,
|
||||
&discord,
|
||||
&PredefinedMenuItem::separator(),
|
||||
&settings,
|
||||
&PredefinedMenuItem::separator(),
|
||||
&PredefinedMenuItem::hide(None),
|
||||
&PredefinedMenuItem::hide_others(None),
|
||||
hide,
|
||||
hide_others,
|
||||
&PredefinedMenuItem::separator(),
|
||||
&PredefinedMenuItem::quit(None),
|
||||
quit,
|
||||
]);
|
||||
let _ = bar.append(&app_menu);
|
||||
for top in MENUS {
|
||||
|
|
@ -115,6 +129,9 @@ pub fn install(app: &FilmcraftApp, ctx: egui::Context) -> (Receiver<String>, Sho
|
|||
for (title, submenu) in &titles {
|
||||
submenu.set_text(language.tr(title));
|
||||
}
|
||||
for (label, item) in &predefined {
|
||||
item.set_text(language.tr(label));
|
||||
}
|
||||
for (id, mi) in &native {
|
||||
if let Some(it) = items.iter().find(|i| &i.id == id) {
|
||||
mi.set_text(&it.label);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
# apps, text addresses the user informally ("du") and commands use the infinitive ("Projekt
|
||||
# öffnen…"). Product, format and feature names (FilmCraft, ArtCraft, Lumetri, Essential Graphics,
|
||||
# EDL, AAF…) keep their spelling. This catalog covers every menu, including the native macOS
|
||||
# Settings menu; anything else is shown in English.
|
||||
# app menu; anything else is shown in English.
|
||||
#
|
||||
# Terms: track = Spur, bin = Ablage, marker = Marker, playhead = Abspielposition, In/Out = In-/Out-
|
||||
# Punkt, transition = Übergang, frame hold = Standbild, panel = Bedienfeld, workspace =
|
||||
|
|
@ -180,6 +180,8 @@
|
|||
Guide Templates Hilfslinienvorlagen
|
||||
Help Hilfe
|
||||
Hide All Caption Tracks Alle Untertitelspuren ausblenden
|
||||
Hide FilmCraft FilmCraft ausblenden
|
||||
Hide Others Andere ausblenden
|
||||
High Quality Playback Wiedergabe in hoher Qualität
|
||||
History Verlauf
|
||||
Import From Importieren aus
|
||||
|
|
@ -269,6 +271,7 @@
|
|||
Properties Eigenschaften
|
||||
Proxy Proxy
|
||||
Purple Lila
|
||||
Quit FilmCraft FilmCraft beenden
|
||||
Reconnect Full Resolution Media… Medien in voller Auflösung neu verknüpfen…
|
||||
Recover Unsaved Changes… Nicht gespeicherte Änderungen wiederherstellen…
|
||||
Rectangle Rechteck
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 6 and column 45.
|
|
|
@ -931,7 +931,7 @@ mod tests {
|
|||
}
|
||||
|
||||
/// German covers at least the core menus (the Brazilian Portuguese entries, the top-level menus
|
||||
/// and the native macOS Settings menu); every entry must still be a menu label.
|
||||
/// and the native macOS app menu); every entry must still be a menu label.
|
||||
#[test]
|
||||
fn german_entries_cover_the_original_menu_catalog() {
|
||||
let app = crate::FilmcraftApp::new(filmcraft_engine::Session::default());
|
||||
|
|
@ -939,17 +939,18 @@ mod tests {
|
|||
let known = |text: &str| crate::menus::MENUS.contains(&text) || items.iter().any(|it| it.label == text || it.path.iter().any(|p| p == text));
|
||||
let (entries, _) = catalog::parse_entries(include_str!("de.tsv"));
|
||||
for (_, en, _) in entries {
|
||||
assert!(known(&en) || en == "Settings", "not a menu label: {en}");
|
||||
assert!(known(&en) || crate::menus::NATIVE_APP_MENU.contains(&en.as_str()), "not a menu label: {en}");
|
||||
}
|
||||
let (portuguese, _) = catalog::parse_entries(include_str!("pt-br.tsv"));
|
||||
for (_, en, _) in portuguese {
|
||||
assert!(Language::De.has(&en), "missing German menu label: {en}");
|
||||
}
|
||||
// "Clip" and "Audio" are the same word in German, so check for an entry, not a change
|
||||
for en in crate::menus::MENUS.into_iter().chain(["Audio", "Settings"]) {
|
||||
for en in crate::menus::MENUS.into_iter().chain(["Audio"]).chain(crate::menus::NATIVE_APP_MENU) {
|
||||
assert!(Language::De.has(en), "untranslated German menu: {en}");
|
||||
}
|
||||
assert_eq!(Language::De.tr("Settings"), "Voreinstellungen");
|
||||
assert_eq!(Language::De.tr("Quit FilmCraft"), "FilmCraft beenden");
|
||||
}
|
||||
|
||||
/// French covers the whole interface (the Spanish catalog's entries and the native macOS
|
||||
|
|
|
|||
|
|
@ -622,6 +622,11 @@ pub struct MenuItem {
|
|||
|
||||
pub const MENUS: [&str; 9] = ["File", "Edit", "Clip", "Sequence", "Markers", "Graphics and Titles", "View", "Window", "Help"];
|
||||
|
||||
/// Labels only the native macOS app menu shows, with no command in the menu tree behind them: the
|
||||
/// Settings submenu and the predefined Hide, Hide Others and Quit items. They are translated through
|
||||
/// the catalogs like menu labels (`apps/filmcraft/src/native_menu.rs`).
|
||||
pub const NATIVE_APP_MENU: [&str; 4] = ["Settings", "Hide FilmCraft", "Hide Others", "Quit FilmCraft"];
|
||||
|
||||
pub fn menu_items(app: &FilmcraftApp) -> Vec<MenuItem> {
|
||||
let mut v = menu_items_for(&app.session);
|
||||
for it in &mut v {
|
||||
|
|
|
|||
Loading…
Reference in New Issue