From ed535180cbdfcf5540662fa3c0e0a24b0def9ec7 Mon Sep 17 00:00:00 2001 From: carlitosteso3 <47792165+carlitosteso3@users.noreply.github.com> Date: Tue, 6 Oct 2026 22:06:02 -0500 Subject: [PATCH] i18n: Spanish interface language MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Español next to English and 日本語 (Edit ▸ Language ▸ Español, also in the native menu bar). SPANISH covers every menu, submenu and menu item label (347 entries, Latin American Spanish); names, formats and percentages stay as they are. A new test checks that every entry is a current menu label, so a renamed command cannot leave a dead entry behind. Co-Authored-By: Claude Opus 5.5 --- apps/filmcraft/src/native_menu.rs | 5 +- crates/ui-egui/src/i18n.rs | 388 +++++++++++++++++++++++++++++- crates/ui-egui/src/menus.rs | 10 +- 3 files changed, 393 insertions(+), 10 deletions(-) diff --git a/apps/filmcraft/src/native_menu.rs b/apps/filmcraft/src/native_menu.rs index 52d31f786..d044f1734 100644 --- a/apps/filmcraft/src/native_menu.rs +++ b/apps/filmcraft/src/native_menu.rs @@ -92,8 +92,11 @@ pub fn install(app: &FilmcraftApp, ctx: egui::Context) -> (Receiver, Sho } }); let update: ShortcutUpdater = Box::new(move |items: &[Item]| { - let language = if items.iter().any(|it| it.id == "app.language.japanese" && it.checked == Some(true)) { + let checked = |id: &str| items.iter().any(|it| it.id == id && it.checked == Some(true)); + let language = if checked("app.language.japanese") { filmcraft_ui_egui::i18n::Language::Ja + } else if checked("app.language.spanish") { + filmcraft_ui_egui::i18n::Language::Es } else { filmcraft_ui_egui::i18n::Language::En }; diff --git a/crates/ui-egui/src/i18n.rs b/crates/ui-egui/src/i18n.rs index 644268b2b..a18ced6ef 100644 --- a/crates/ui-egui/src/i18n.rs +++ b/crates/ui-egui/src/i18n.rs @@ -14,15 +14,17 @@ pub enum Language { #[default] En, Ja, + Es, } impl Language { - pub const ALL: [Self; 2] = [Self::En, Self::Ja]; + pub const ALL: [Self; 3] = [Self::En, Self::Ja, Self::Es]; pub fn name(self) -> &'static str { match self { Self::En => "English", Self::Ja => "日本語", + Self::Es => "Español", } } @@ -30,17 +32,18 @@ impl Language { match code { "en" => Some(Self::En), "ja" => Some(Self::Ja), + "es" => Some(Self::Es), _ => None, } } pub fn tr(self, text: &str) -> &str { - if self == Self::Ja - && let Some((_, japanese)) = JAPANESE.iter().find(|(english, _)| *english == text) - { - return japanese; - } - text + let table = match self { + Self::En => return text, + Self::Ja => JAPANESE, + Self::Es => SPANISH, + }; + table.iter().find(|(english, _)| *english == text).map_or(text, |(_, translated)| translated) } } @@ -120,6 +123,356 @@ pub fn install_japanese_font(ctx: &egui::Context) -> bool { true } +const SPANISH: &[(&str, &str)] = &[ + ("About FilmCraft", "Acerca de FilmCraft"), + ("Add Audio Submix Track", "Añadir pista de submezcla de audio"), + ("Add Caption at Playhead", "Añadir subtítulo en el cabezal"), + ("Add Chapter Marker…", "Añadir marcador de capítulo…"), + ("Add Edit", "Añadir edición"), + ("Add Edit to All Tracks", "Añadir edición a todas las pistas"), + ("Add Flash Cue Marker…", "Añadir marcador de referencia Flash…"), + ("Add Frame Hold", "Añadir fotograma congelado"), + ("Add Guide…", "Añadir guía…"), + ("Add Marker", "Añadir marcador"), + ("Add New Caption Track…", "Añadir nueva pista de subtítulos…"), + ("Add Range Marker", "Añadir marcador de rango"), + ("Add Range Marker to In and Out", "Añadir marcador de rango de entrada a salida"), + ("Add Tracks…", "Añadir pistas…"), + ("Adjustment Layer…", "Capa de ajuste…"), + ("Align to Selection", "Alinear con la selección"), + ("Align to Video Frame", "Alinear con el fotograma de video"), + ("Align to Video Frame as Group", "Alinear con el fotograma de video como grupo"), + ("All Panels", "Todos los paneles"), + ("Alpha", "Alfa"), + ("Appearance", "Apariencia"), + ("Appearance…", "Apariencia…"), + ("Apply Audio Transition", "Aplicar transición de audio"), + ("Apply Default Transitions to Selection", "Aplicar transiciones predeterminadas a la selección"), + ("Apply Video Transition", "Aplicar transición de video"), + ("Arrange", "Organizar"), + ("ArtCraft Website", "Sitio web de ArtCraft"), + ("Assembly", "Ensamblaje"), + ("Attach Proxies…", "Adjuntar proxies…"), + ("Audio Channels…", "Canales de audio…"), + ("Audio Clip Mixer", "Mezclador de clips de audio"), + ("Audio Gain…", "Ganancia de audio…"), + ("Audio Hardware…", "Hardware de audio…"), + ("Audio In", "Entrada de audio"), + ("Audio Meters", "Medidores de audio"), + ("Audio Options", "Opciones de audio"), + ("Audio Out", "Salida de audio"), + ("Audio Track Mixer", "Mezclador de pistas de audio"), + ("Audio Waveform", "Forma de onda de audio"), + ("Auto Save…", "Guardado automático…"), + ("Automate to Sequence…", "Automatizar a secuencia…"), + ("Bars and Tone…", "Barras y tono…"), + ("Bin", "Bandeja"), + ("Bin From Selection", "Bandeja a partir de la selección"), + ("Black Video…", "Video negro…"), + ("Blue", "Azul"), + ("Bottom", "Abajo"), + ("Breakout to Mono", "Separar en mono"), + ("Bring Forward", "Traer adelante"), + ("Bring to Front", "Traer al frente"), + ("Brown", "Marrón"), + ("Captions", "Subtítulos"), + ("Captions and Graphics", "Subtítulos y gráficos"), + ("Captions…", "Subtítulos…"), + ("Caribbean", "Caribe"), + ("Center Horizontally", "Centrar horizontalmente"), + ("Center Vertically", "Centrar verticalmente"), + ("Cerulean", "Cerúleo"), + ("Clear", "Borrar"), + ("Clear Guides", "Borrar guías"), + ("Clear In", "Borrar entrada"), + ("Clear In and Out", "Borrar entrada y salida"), + ("Clear Markers", "Borrar marcadores"), + ("Clear Out", "Borrar salida"), + ("Clear Selected Marker", "Borrar marcador seleccionado"), + ("Close", "Cerrar"), + ("Close All Other Projects", "Cerrar todos los demás proyectos"), + ("Close All Projects", "Cerrar todos los proyectos"), + ("Close Gap", "Cerrar hueco"), + ("Close Project", "Cerrar proyecto"), + ("Color Management…", "Gestión del color…"), + ("Color Matte…", "Mate de color…"), + ("Comparison View", "Vista de comparación"), + ("Composite Video", "Video compuesto"), + ("Consolidate Duplicates", "Consolidar duplicados"), + ("Copy", "Copiar"), + ("Copy Paste Includes Sequence Markers", "Copiar y pegar incluye marcadores de secuencia"), + ("Create Captions from Transcript…", "Crear subtítulos a partir de la transcripción…"), + ("Create Multi-Camera Source Sequence…", "Crear secuencia de origen multicámara…"), + ("Create Proxies…", "Crear proxies…"), + ("Cut", "Cortar"), + ("Darkest", "Más oscuro"), + ("Delete Render Files", "Eliminar archivos de procesamiento"), + ("Delete Render Files In to Out", "Eliminar archivos de procesamiento de entrada a salida"), + ("Delete Tracks…", "Eliminar pistas…"), + ("Delete Transcript", "Eliminar transcripción"), + ("Demo Footage", "Material de demostración"), + ("Demo Project", "Proyecto de demostración"), + ("Deselect All", "Deseleccionar todo"), + ("Detach Proxies", "Separar proxies"), + ("Display Mode", "Modo de visualización"), + ("Distribute", "Distribuir"), + ("Distribute Horizontally", "Distribuir horizontalmente"), + ("Distribute Space Horizontally", "Distribuir espacio horizontalmente"), + ("Distribute Space Vertically", "Distribuir espacio verticalmente"), + ("Distribute Vertically", "Distribuir verticalmente"), + ("Duplicate", "Duplicar"), + ("Dynamic Audio Waveforms", "Formas de onda de audio dinámicas"), + ("Edit", "Edición"), + ("Edit Offline…", "Editar sin conexión…"), + ("Edit Original", "Editar original"), + ("Edit Subclip…", "Editar subclip…"), + ("Editing", "Edición"), + ("Effect Controls", "Controles de efectos"), + ("Effects", "Efectos"), + ("Ellipse", "Elipse"), + ("Enable", "Activar"), + ("Enable Remix", "Activar remezcla"), + ("Essential Graphics", "Gráficos esenciales"), + ("Essential Sound", "Sonido esencial"), + ("Events", "Eventos"), + ("Export", "Exportar"), + ("Export As Motion Graphics Template…", "Exportar como plantilla de animación…"), + ("Extract", "Extraer"), + ("Extract Audio", "Extraer audio"), + ("Field Options…", "Opciones de campo…"), + ("File", "Archivo"), + ("File…", "Archivo…"), + ("Fill frame", "Rellenar fotograma"), + ("FilmCraft Help…", "Ayuda de FilmCraft…"), + ("FilmCraft on GitHub", "FilmCraft en GitHub"), + ("FilmCraft on getartcraft.com", "FilmCraft en getartcraft.com"), + ("Find Next", "Buscar siguiente"), + ("Find…", "Buscar…"), + ("Fit", "Ajustar"), + ("Fit to frame", "Ajustar al fotograma"), + ("Flatten", "Acoplar"), + ("Forest", "Bosque"), + ("Frame Blending", "Fusión de fotogramas"), + ("Frame Hold Options…", "Opciones de fotograma congelado…"), + ("Frame Sampling", "Muestreo de fotogramas"), + ("From Bin", "Desde la bandeja"), + ("From Source Monitor", "Desde el monitor de origen"), + ("From Source Monitor, Match Frame", "Desde el monitor de origen, fotograma coincidente"), + ("From file…", "Desde archivo…"), + ("Full", "Completa"), + ("Generate Audio Waveform", "Generar forma de onda de audio"), + ("Get Media File Properties for", "Obtener propiedades del archivo multimedia de"), + ("Go to Gap", "Ir al hueco"), + ("Go to In", "Ir a la entrada"), + ("Go to Next Caption Segment", "Ir al siguiente segmento de subtítulo"), + ("Go to Next Marker", "Ir al siguiente marcador"), + ("Go to Out", "Ir a la salida"), + ("Go to Previous Caption Segment", "Ir al segmento de subtítulo anterior"), + ("Go to Previous Marker", "Ir al marcador anterior"), + ("Go to Split", "Ir a la división"), + ("Graphics and Titles", "Gráficos y títulos"), + ("Graphics…", "Gráficos…"), + ("Green", "Verde"), + ("Group", "Agrupar"), + ("Guide Templates", "Plantillas de guías"), + ("Help", "Ayuda"), + ("Hide All Caption Tracks", "Ocultar todas las pistas de subtítulos"), + ("High Quality Playback", "Reproducción de alta calidad"), + ("History", "Historial"), + ("Import From", "Importar desde"), + ("Import Image Sequence…", "Importar secuencia de imágenes…"), + ("Import from Media Browser", "Importar desde el navegador de medios"), + ("Import…", "Importar…"), + ("Info", "Información"), + ("Ingest Settings…", "Ajustes de ingesta…"), + ("Insert", "Insertar"), + ("Insert Frame Hold Segment", "Insertar segmento de fotograma congelado"), + ("Install Motion Graphics Template…", "Instalar plantilla de animación…"), + ("Interpret Footage…", "Interpretar material…"), + ("Join the ArtCraft Discord…", "Únete al Discord de ArtCraft…"), + ("Keyboard Shortcuts…", "Atajos de teclado…"), + ("Label", "Etiqueta"), + ("Labels…", "Etiquetas…"), + ("Language", "Idioma"), + ("Lavender", "Lavanda"), + ("Learning", "Aprendizaje"), + ("Left", "Izquierda"), + ("Libraries", "Bibliotecas"), + ("Lift", "Levantar"), + ("Light", "Claro"), + ("Link", "Vincular"), + ("Link Media…", "Vincular medios…"), + ("Linked Selection", "Selección vinculada"), + ("Lock Guides", "Bloquear guías"), + ("Lumetri Color", "Color Lumetri"), + ("Lumetri Scopes", "Ámbitos Lumetri"), + ("Magnification", "Ampliación"), + ("Make Offline…", "Desconectar…"), + ("Make Subclip…", "Crear subclip…"), + ("Make Subsequence", "Crear subsecuencia"), + ("Manage Guides…", "Gestionar guías…"), + ("Mark Clip", "Marcar clip"), + ("Mark In", "Marcar entrada"), + ("Mark Out", "Marcar salida"), + ("Mark Selection", "Marcar selección"), + ("Mark Split", "Marcar división"), + ("Markers", "Marcadores"), + ("Match Frame", "Fotograma coincidente"), + ("Media Analysis & Transcription…", "Análisis y transcripción de medios…"), + ("Media Browser", "Navegador de medios"), + ("Media Cache…", "Caché de medios…"), + ("Media…", "Medios…"), + ("Medium", "Medio"), + ("Memory…", "Memoria…"), + ("Merge Clips…", "Combinar clips…"), + ("Metadata", "Metadatos"), + ("Modify", "Modificar"), + ("Motion Graphics Template…", "Plantilla de animación…"), + ("Multi-Camera", "Multicámara"), + ("Multi-Camera View", "Vista multicámara"), + ("Nest…", "Anidar…"), + ("New", "Nuevo"), + ("New Layer", "Nueva capa"), + ("New Project Panel", "Nuevo panel de proyecto"), + ("Next in Sequence", "Siguiente en la secuencia"), + ("Next in Track", "Siguiente en la pista"), + ("Normalize Mix Track…", "Normalizar pista de mezcla…"), + ("Offline File…", "Archivo sin conexión…"), + ("Open Project…", "Abrir proyecto…"), + ("Optical Flow", "Flujo óptico"), + ("Overwrite", "Sobrescribir"), + ("Paste", "Pegar"), + ("Paste Attributes…", "Pegar atributos…"), + ("Paste Insert", "Pegar e insertar"), + ("Paused Resolution", "Resolución en pausa"), + ("Playback Resolution", "Resolución de reproducción"), + ("Playback…", "Reproducción…"), + ("Plugins…", "Complementos…"), + ("Polygon", "Polígono"), + ("Preferences", "Preferencias"), + ("Previous in Sequence", "Anterior en la secuencia"), + ("Previous in Track", "Anterior en la pista"), + ("Program", "Programa"), + ("Progress", "Progreso"), + ("Project", "Proyecto"), + ("Project Manager…", "Administrador de proyectos…"), + ("Project Settings", "Ajustes del proyecto"), + ("Project…", "Proyecto…"), + ("Properties", "Propiedades"), + ("Purple", "Púrpura"), + ("Reconnect Full Resolution Media…", "Reconectar medios de resolución completa…"), + ("Recover Unsaved Changes…", "Recuperar cambios no guardados…"), + ("Rectangle", "Rectángulo"), + ("Red", "Rojo"), + ("Redo", "Rehacer"), + ("Reference Monitor", "Monitor de referencia"), + ("Remix", "Remezcla"), + ("Remix Properties…", "Propiedades de remezcla…"), + ("Remove Attributes…", "Quitar atributos…"), + ("Remove Filler Words", "Quitar muletillas"), + ("Remove Pauses", "Quitar pausas"), + ("Remove Unused", "Quitar no utilizados"), + ("Rename…", "Cambiar nombre…"), + ("Render Audio", "Procesar audio"), + ("Render Effects In to Out", "Procesar efectos de entrada a salida"), + ("Render In to Out", "Procesar de entrada a salida"), + ("Render Selection", "Procesar selección"), + ("Replace Fonts in Projects…", "Reemplazar fuentes en proyectos…"), + ("Replace With Clip", "Reemplazar con clip"), + ("Report an Issue…", "Informar de un problema…"), + ("Reset All Parameters", "Restablecer todos los parámetros"), + ("Reset Duration", "Restablecer duración"), + ("Reset to Saved Layout", "Restablecer al diseño guardado"), + ("Restore Captions from Source Clip", "Restaurar subtítulos del clip de origen"), + ("Reveal Log Files…", "Mostrar archivos de registro…"), + ("Reverse Match Frame", "Fotograma coincidente inverso"), + ("Revert", "Revertir"), + ("Revert Remix", "Revertir remezcla"), + ("Review", "Revisión"), + ("Right", "Derecha"), + ("Ripple Delete", "Eliminar y cerrar hueco"), + ("Ripple Sequence Markers", "Desplazar marcadores de secuencia"), + ("Rose", "Rosa"), + ("Safe Margins", "Márgenes seguros"), + ("Save", "Guardar"), + ("Save All", "Guardar todo"), + ("Save As…", "Guardar como…"), + ("Save Guides as Template…", "Guardar guías como plantilla…"), + ("Save a Copy…", "Guardar una copia…"), + ("Save as Template…", "Guardar como plantilla…"), + ("Scale to Frame Size", "Escalar al tamaño del fotograma"), + ("Scene Edit Detection…", "Detección de cambios de escena…"), + ("Scratch Disks…", "Discos de memoria virtual…"), + ("Search Bin", "Bandeja de búsqueda"), + ("Select", "Seleccionar"), + ("Select All", "Seleccionar todo"), + ("Select All Matching", "Seleccionar todos los coincidentes"), + ("Select Label Group", "Seleccionar grupo de etiquetas"), + ("Select Next Graphic", "Seleccionar siguiente gráfico"), + ("Select Next Layer", "Seleccionar siguiente capa"), + ("Select Previous Graphic", "Seleccionar gráfico anterior"), + ("Select Previous Layer", "Seleccionar capa anterior"), + ("Selection Follows Playhead", "La selección sigue al cabezal"), + ("Selection as FilmCraft Project…", "Selección como proyecto de FilmCraft…"), + ("Selection…", "Selección…"), + ("Send Backward", "Enviar atrás"), + ("Send to Back", "Enviar al fondo"), + ("Sequence", "Secuencia"), + ("Sequence From Clip", "Secuencia a partir de clip"), + ("Sequence Settings…", "Ajustes de secuencia…"), + ("Sequence…", "Secuencia…"), + ("Show Active Caption Tracks Only", "Mostrar solo pistas de subtítulos activas"), + ("Show All Caption Tracks", "Mostrar todas las pistas de subtítulos"), + ("Show All Marker Colors", "Mostrar todos los colores de marcador"), + ("Show Guides", "Mostrar guías"), + ("Show Rulers", "Mostrar reglas"), + ("Show Through Edits", "Mostrar ediciones pasantes"), + ("Simplify Sequence…", "Simplificar secuencia…"), + ("Snap in Program Monitor", "Ajustar en el monitor de programa"), + ("Snap in Timeline", "Ajustar en la línea de tiempo"), + ("Source", "Origen"), + ("Source Settings…", "Ajustes de origen…"), + ("Speed/Duration…", "Velocidad/duración…"), + ("Synchronize…", "Sincronizar…"), + ("System Compatibility Report…", "Informe de compatibilidad del sistema…"), + ("Tan", "Canela"), + ("Teal", "Verde azulado"), + ("Text", "Texto"), + ("Time Interpolation", "Interpolación de tiempo"), + ("Timecode", "Código de tiempo"), + ("Timecode…", "Código de tiempo…"), + ("Timeline", "Línea de tiempo"), + ("Timeline…", "Línea de tiempo…"), + ("Toggle Proxies", "Alternar proxies"), + ("Tools", "Herramientas"), + ("Top", "Arriba"), + ("Transcribe Sequence…", "Transcribir secuencia…"), + ("Transcribe…", "Transcribir…"), + ("Transcript", "Transcripción"), + ("Transparent Video…", "Video transparente…"), + ("Trim Edit", "Recortar edición"), + ("Trim…", "Recorte…"), + ("Undo", "Deshacer"), + ("Ungroup", "Desagrupar"), + ("Universal Counting Leader…", "Cuenta atrás universal…"), + ("Update Metadata…", "Actualizar metadatos…"), + ("Upgrade Caption to Graphic", "Convertir subtítulo en gráfico"), + ("Upgrade to Source Graphic", "Convertir en gráfico de origen"), + ("Vertical Text", "Texto vertical"), + ("Video In", "Entrada de video"), + ("Video Options", "Opciones de video"), + ("Video Out", "Salida de video"), + ("Video and Audio Waveform Split", "Video y forma de onda de audio divididos"), + ("View", "Ver"), + ("Violet", "Violeta"), + ("Window", "Ventana"), + ("Workspaces", "Espacios de trabajo"), + ("Yellow", "Amarillo"), + ("Zoom In", "Acercar"), + ("Zoom Out", "Alejar"), + ("Zoom to Sequence", "Ajustar zoom a la secuencia"), +]; + const JAPANESE: &[(&str, &str)] = &[ ("Clip", "クリップ"), ("Sequence", "シーケンス"), @@ -222,10 +575,28 @@ mod tests { assert_eq!(Language::En.tr(en), *en); } assert_eq!(Language::Ja.tr("File"), "ファイル"); + for (i, (en, es)) in SPANISH.iter().enumerate() { + assert!(!es.is_empty()); + assert!(SPANISH.iter().take(i).all(|(other, _)| en != other)); + } + assert_eq!(Language::Es.tr("File"), "Archivo"); + assert_eq!(Language::Es.tr("mi video.mp4"), "mi video.mp4"); + assert_eq!(Language::parse("es"), Some(Language::Es)); assert_eq!(Language::Ja.tr("日本語の文書.pdf"), "日本語の文書.pdf"); assert_eq!(Language::parse("xx"), None); } + #[test] + fn spanish_entries_are_menu_labels() { + // a renamed command would otherwise leave a dead entry and an untranslated menu item + let app = crate::FilmcraftApp::new(filmcraft_engine::Session::default()); + let items = crate::menus::menu_items(&app); + let known = |text: &str| crate::menus::MENUS.contains(&text) || items.iter().any(|it| it.label == text || it.path.iter().any(|p| p == text)); + for (en, _) in SPANISH { + assert!(known(en), "not a menu label: {en}"); + } + } + #[test] fn language_commands_switch_and_persist_without_a_document() { let mut app = crate::FilmcraftApp::new(filmcraft_engine::Session::default()); @@ -235,6 +606,9 @@ mod tests { let saved = serde_json::to_string(&app.ui).unwrap(); let restored: crate::state::UiState = serde_json::from_str(&saved).unwrap(); assert_eq!(restored.language, Language::Ja); + crate::menus::invoke(&mut app, &ctx, "app.language.spanish", serde_json::json!({})).unwrap(); + assert_eq!(app.ui.language, Language::Es); + assert!(crate::menus::menu_items(&app).iter().any(|it| it.id == "app.language.spanish" && it.checked == Some(true))); crate::menus::invoke(&mut app, &ctx, "app.language.english", serde_json::json!({})).unwrap(); assert_eq!(app.ui.language, Language::En); } diff --git a/crates/ui-egui/src/menus.rs b/crates/ui-egui/src/menus.rs index 14966f38e..ab0776c07 100644 --- a/crates/ui-egui/src/menus.rs +++ b/crates/ui-egui/src/menus.rs @@ -24,6 +24,7 @@ macro_rules! uic { pub const UI_COMMANDS: &[UiCommand] = &[ uic!("app.language.english", "English", ["Edit", "Language"], None), uic!("app.language.japanese", "日本語", ["Edit", "Language"], None), + uic!("app.language.spanish", "Español", ["Edit", "Language"], None), uic!("playback.toggle", "Play/Stop", [], Some("Space")), uic!("playback.forward", "Shuttle Right", [], Some("L")), uic!("playback.stop", "Shuttle Stop", [], Some("K")), @@ -146,12 +147,16 @@ pub fn panel_command_id(p: PanelKind) -> String { /// Execute a UI or engine command by id. pub fn invoke(app: &mut FilmcraftApp, ctx: &egui::Context, id: &str, params: Value) -> Result { - if matches!(id, "app.language.english" | "app.language.japanese") { + if matches!(id, "app.language.english" | "app.language.japanese" | "app.language.spanish") { // Japanese needs the craft-fonts (built with CRAFT_FONTS_DIR) or a font installed on the system if id == "app.language.japanese" && !crate::i18n::install_japanese_font(ctx) { return Err("no Japanese font is installed on this system (for example Noto Sans CJK JP); the interface stays in English".into()); } - app.ui.language = if id == "app.language.japanese" { crate::i18n::Language::Ja } else { crate::i18n::Language::En }; + app.ui.language = match id { + "app.language.japanese" => crate::i18n::Language::Ja, + "app.language.spanish" => crate::i18n::Language::Es, + _ => crate::i18n::Language::En, + }; let items = menu_items(app); if let Some(hook) = app.hooks.shortcuts_changed.as_mut() { hook(&items); @@ -401,6 +406,7 @@ pub fn menu_items(app: &FilmcraftApp) -> Vec { match it.id.as_str() { "app.language.english" => it.checked = Some(app.ui.language == crate::i18n::Language::En), "app.language.japanese" => it.checked = Some(app.ui.language == crate::i18n::Language::Ja), + "app.language.spanish" => it.checked = Some(app.ui.language == crate::i18n::Language::Es), _ => {} } if it.id.starts_with("view.") {