diff --git a/crates/ui-egui/src/panels/content_fill_panel.rs b/crates/ui-egui/src/panels/content_fill_panel.rs index 4b87d6786..24bd88549 100644 --- a/crates/ui-egui/src/panels/content_fill_panel.rs +++ b/crates/ui-egui/src/panels/content_fill_panel.rs @@ -4,6 +4,7 @@ //! live in `EditorState::content_fill` (`contentFill.set`); the button runs //! `contentFill.generate` as a background job (Window ▸ Progress). +use crate::i18n::tr; use egui::{Align2, Rect, pos2, vec2}; use serde_json::json; @@ -25,7 +26,7 @@ pub fn show(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) { let comp = app.session.active_comp_arc(); let layer = comp.as_ref().and_then(|c| app.session.state.selected_layers.first().and_then(|l| c.layer(*l)).cloned()); // Fill Target. - p.text(pos2(x0, y + 9.0), Align2::LEFT_CENTER, "Fill Target", Tokens::semibold(12.0), t.text); + p.text(pos2(x0, y + 9.0), Align2::LEFT_CENTER, tr("Fill Target"), Tokens::semibold(12.0), t.text); y += ROW - 6.0; let target = match &layer { Some(l) => { diff --git a/crates/ui-egui/src/panels/delete_items.rs b/crates/ui-egui/src/panels/delete_items.rs index dbda93d7e..7ccc936a1 100644 --- a/crates/ui-egui/src/panels/delete_items.rs +++ b/crates/ui-egui/src/panels/delete_items.rs @@ -5,6 +5,7 @@ //! //! Automation ids: `dialog.deleteItems.delete`, `dialog.deleteItems.cancel`. +use crate::i18n::tr; use egui::{Color32, vec2}; use serde_json::Value; @@ -56,15 +57,15 @@ pub fn show(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) { ); ui.label(egui::RichText::new(text).color(t.tab_text_active)); ui.add_space(4.0); - ui.label(egui::RichText::new("You can undo this.").color(t.text_dim).size(11.0)); + ui.label(egui::RichText::new(tr("You can undo this.")).color(t.text_dim).size(11.0)); ui.add_space(18.0); ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { - let r = ui.add(egui::Button::new(egui::RichText::new(" Delete ").color(Color32::WHITE)).fill(t.danger)); + let r = ui.add(egui::Button::new(egui::RichText::new(tr(" Delete ")).color(Color32::WHITE)).fill(t.danger)); app.auto.add("dialog.deleteItems.delete", r.rect, "Delete"); if r.clicked() { answer = Some(true); } - let r = ui.button("Cancel"); + let r = ui.button(tr("Cancel")); app.auto.add("dialog.deleteItems.cancel", r.rect, "Cancel"); if r.clicked() { answer = Some(false); diff --git a/crates/ui-egui/src/panels/dialogs.rs b/crates/ui-egui/src/panels/dialogs.rs index 80a3e88c2..dce4642d0 100644 --- a/crates/ui-egui/src/panels/dialogs.rs +++ b/crates/ui-egui/src/panels/dialogs.rs @@ -1,6 +1,7 @@ //! Modal dialogs: About (community links, Contributors and Models credits), New Composition / //! Composition Settings, Solid Settings and the command palette (Camera/Light Settings live in `dialogs_3d`). +use crate::i18n::{tr, tr_args}; use effectcraft_engine::project::{ItemId, ItemKind, LayerId, LayerSource}; use egui::{Align2, Color32, Rect, Sense, pos2, vec2}; use serde_json::{Value, json}; @@ -232,9 +233,9 @@ fn about(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) { } ui.add_space(8.0); ui.horizontal(|ui| { - ui.label(egui::RichText::new("MIT OR Apache-2.0 • Fonts: Inter, JetBrains Mono (OFL)").small().color(t.text_faint)); + ui.label(egui::RichText::new(tr("MIT OR Apache-2.0 • Fonts: Inter, JetBrains Mono (OFL)")).small().color(t.text_faint)); ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { - let r = ui.button("Close"); + let r = ui.button(tr("Close")); app.auto.add("about.close", r.rect, "Close"); if r.clicked() { close = true; @@ -265,7 +266,7 @@ fn about_main(app: &mut EffectcraftApp, ui: &mut egui::Ui, t: &Tokens, cmd: &mut t.text_dim, ); ui.add_space(12.0); - ui.label("A clean-room, open-source compositor for motion graphics and visual effects: native on macOS, Windows and Linux, and in the browser. Part of the ArtCraft family of creative apps."); + ui.label(tr("A clean-room, open-source compositor for motion graphics and visual effects: native on macOS, Windows and Linux, and in the browser. Part of the ArtCraft family of creative apps.")); ui.add_space(14.0); let links: [(Icon, &str, &str, &'static str); 4] = [ (Icon::Chat, "Join the ArtCraft Discord", effectcraft_engine::links::DISCORD, "help.discord"), @@ -349,23 +350,23 @@ fn solid(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) { let auto = &mut app.auto; modal(ctx, "Solid Settings", vec2(480.0, if editing.is_some() { 430.0 } else { 390.0 }), t, |ui| { ui.horizontal(|ui| { - ui.label("Name:"); + ui.label(tr("Name:")); let r = ui.add(egui::TextEdit::singleline(&mut name).desired_width(320.0)); auto.add("dialog.solid.name", r.rect, "Name"); }); ui.add_space(8.0); - ui.label(egui::RichText::new("Size").strong()); + ui.label(egui::RichText::new(tr("Size")).strong()); egui::Grid::new("solid-grid").num_columns(2).spacing([14.0, 8.0]).show(ui, |ui| { let (ow, oh) = (size[0], size[1]); - ui.label("Width:"); + ui.label(tr("Width:")); ui.horizontal(|ui| { let r = ui.add(egui::DragValue::new(&mut size[0]).range(1..=30000).suffix(" px")); auto.add("dialog.solid.width", r.rect, "Width"); - let r = ui.checkbox(&mut lock, format!("Lock Aspect Ratio to {}", super::comp_settings::aspect_label(ow as f64, oh as f64))); + let r = ui.checkbox(&mut lock, tr_args("Lock Aspect Ratio to {}", &[&(super::comp_settings::aspect_label(ow as f64, oh as f64))])); auto.add("dialog.solid.lockAspect", r.rect, "Lock Aspect Ratio"); }); ui.end_row(); - ui.label("Height:"); + ui.label(tr("Height:")); let r = ui.add(egui::DragValue::new(&mut size[1]).range(1..=30000).suffix(" px")); auto.add("dialog.solid.height", r.rect, "Height"); ui.end_row(); @@ -376,7 +377,7 @@ fn solid(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) { size[0] = ((size[1] as f64) * ow as f64 / oh as f64).round().max(1.0) as u32; } } - ui.label("Pixel Aspect Ratio:"); + ui.label(tr("Pixel Aspect Ratio:")); let cur = super::comp_settings::PIXEL_ASPECTS .iter() .find(|(_, v)| (v - par).abs() < 1e-3) @@ -403,14 +404,14 @@ fn solid(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) { .color(Color32::GRAY), ); ui.add_space(4.0); - let r = ui.button("Make Comp Size"); + let r = ui.button(tr("Make Comp Size")); auto.add("dialog.solid.makeCompSize", r.rect, "Make Comp Size"); if r.clicked() { size = [comp_size.0, comp_size.1]; } ui.add_space(8.0); ui.horizontal(|ui| { - ui.label("Color:"); + ui.label(tr("Color:")); let r = crate::widgets::srgb_color_button(ui, &mut color); auto.add("dialog.solid.color", r.rect, "Color"); }); @@ -421,15 +422,15 @@ fn solid(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) { .on_disabled_hover_text("Only this layer uses this solid."); auto.add("dialog.solid.affectAll", r.rect, "Affect all layers that use this solid"); if shared && !affect_all { - ui.label(egui::RichText::new("This change will create a new solid for this layer.").color(t.text_dim).size(11.0)); + ui.label(egui::RichText::new(tr("This change will create a new solid for this layer.")).color(t.text_dim).size(11.0)); } } ui.add_space(14.0); ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { - let r = ui.add(egui::Button::new(egui::RichText::new(" OK ").color(Color32::WHITE)).fill(t.accent)); + let r = ui.add(egui::Button::new(egui::RichText::new(tr(" OK ")).color(Color32::WHITE)).fill(t.accent)); auto.add("dialog.solid.ok", r.rect, "OK"); ok = r.clicked(); - let r = ui.button("Cancel"); + let r = ui.button(tr("Cancel")); auto.add("dialog.solid.cancel", r.rect, "Cancel"); close = r.clicked(); }); @@ -493,7 +494,7 @@ fn palette(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) { .take(14) .collect(); modal(ctx, "Command Palette", vec2(560.0, 470.0), t, |ui| { - let r = ui.add(egui::TextEdit::singleline(&mut q).hint_text("Type a command or effect…").desired_width(f32::INFINITY).font(Tokens::ui(14.0))); + let r = ui.add(egui::TextEdit::singleline(&mut q).hint_text(tr("Type a command or effect…")).desired_width(f32::INFINITY).font(Tokens::ui(14.0))); r.request_focus(); ui.add_space(8.0); let sel = app.dialog_state.palette_sel.min(matches.len().saturating_sub(1)); diff --git a/crates/ui-egui/src/panels/dialogs_3d.rs b/crates/ui-egui/src/panels/dialogs_3d.rs index 87fc16610..485e2e8b5 100644 --- a/crates/ui-egui/src/panels/dialogs_3d.rs +++ b/crates/ui-egui/src/panels/dialogs_3d.rs @@ -2,6 +2,7 @@ //! Settings, Layer Settings on a camera or light). OK runs `layer.newCamera` / `layer.newLight` //! or `layer.cameraSettings` / `layer.lightSettings` with the dialog's values. +use crate::i18n::tr; use effectcraft_engine::project::{AutoOrient, LayerSource, LightKind}; use effectcraft_engine::render::three_d::camera::{PRESETS, angle_of_view, default_aperture, focal_for_zoom, zoom_for_focal}; use egui::{Color32, vec2}; @@ -144,12 +145,12 @@ pub fn open_light(app: &mut EffectcraftApp, layer: Option) -> Result<(), St fn buttons(ui: &mut egui::Ui, t: &Tokens, ok: &mut bool, close: &mut bool) -> (egui::Rect, egui::Rect) { let mut r = (egui::Rect::NOTHING, egui::Rect::NOTHING); ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { - let b = ui.add(egui::Button::new(egui::RichText::new(" OK ").color(Color32::WHITE)).fill(t.accent)); + let b = ui.add(egui::Button::new(egui::RichText::new(tr(" OK ")).color(Color32::WHITE)).fill(t.accent)); r.0 = b.rect; if b.clicked() { *ok = true; } - let c = ui.button("Cancel"); + let c = ui.button(tr("Cancel")); r.1 = c.rect; if c.clicked() { *close = true; @@ -165,16 +166,16 @@ pub fn camera(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) { let mut rects = (egui::Rect::NOTHING, egui::Rect::NOTHING); super::dialogs::modal(ctx, "Camera Settings", vec2(520.0, 420.0), t, |ui| { egui::Grid::new("cam-grid").num_columns(2).spacing([14.0, 9.0]).show(ui, |ui| { - ui.label("Type"); + ui.label(tr("Type")); egui::ComboBox::from_id_salt("cam-type").selected_text(if d.two_node { "Two-Node Camera" } else { "One-Node Camera" }).show_ui(ui, |ui| { - ui.selectable_value(&mut d.two_node, true, "Two-Node Camera"); - ui.selectable_value(&mut d.two_node, false, "One-Node Camera"); + ui.selectable_value(&mut d.two_node, true, tr("Two-Node Camera")); + ui.selectable_value(&mut d.two_node, false, tr("One-Node Camera")); }); ui.end_row(); - ui.label("Name"); + ui.label(tr("Name")); ui.add(egui::TextEdit::singleline(&mut d.name).desired_width(240.0)); ui.end_row(); - ui.label("Preset"); + ui.label(tr("Preset")); let focal = focal_for_zoom(w, d.zoom); let cur = PRESETS.iter().find(|(_, f)| (f - focal).abs() < 1e-6).map(|(n, _)| *n).unwrap_or("Custom"); egui::ComboBox::from_id_salt("cam-preset").selected_text(cur).show_ui(ui, |ui| { @@ -185,7 +186,7 @@ pub fn camera(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) { } }); ui.end_row(); - ui.label("Zoom"); + ui.label(tr("Zoom")); ui.horizontal(|ui| { ui.add(egui::DragValue::new(&mut d.zoom).range(1.0..=100000.0).speed(1.0).suffix(" px")); let mut aov = angle_of_view(w, d.zoom); @@ -198,19 +199,19 @@ pub fn camera(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) { } }); ui.end_row(); - ui.label("Depth of Field"); - ui.checkbox(&mut d.dof, "Enable Depth of Field"); + ui.label(tr("Depth of Field")); + ui.checkbox(&mut d.dof, tr("Enable Depth of Field")); ui.end_row(); - ui.label("Focus Distance"); + ui.label(tr("Focus Distance")); ui.horizontal(|ui| { ui.add_enabled(!d.lock_to_zoom, egui::DragValue::new(&mut d.focus).range(0.0..=100000.0).suffix(" px")); - ui.checkbox(&mut d.lock_to_zoom, "Lock to Zoom"); + ui.checkbox(&mut d.lock_to_zoom, tr("Lock to Zoom")); }); ui.end_row(); - ui.label("Aperture"); + ui.label(tr("Aperture")); ui.add(egui::DragValue::new(&mut d.aperture).range(0.0..=10000.0).speed(0.1).suffix(" px")); ui.end_row(); - ui.label("Blur Level"); + ui.label(tr("Blur Level")); ui.add(egui::DragValue::new(&mut d.blur).range(0.0..=10000.0).suffix(" %")); ui.end_row(); }); @@ -256,33 +257,33 @@ pub fn light(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) { let mut rects = (egui::Rect::NOTHING, egui::Rect::NOTHING); super::dialogs::modal(ctx, "Light Settings", vec2(480.0, 470.0), t, |ui| { egui::Grid::new("light-grid").num_columns(2).spacing([14.0, 9.0]).show(ui, |ui| { - ui.label("Name"); + ui.label(tr("Name")); ui.add(egui::TextEdit::singleline(&mut d.name).desired_width(220.0)); ui.end_row(); - ui.label("Light Type"); + ui.label(tr("Light Type")); egui::ComboBox::from_id_salt("light-type").selected_text(KINDS[d.kind.min(3)].label()).show_ui(ui, |ui| { for (i, k) in KINDS.iter().enumerate() { ui.selectable_value(&mut d.kind, i, k.label()); } }); ui.end_row(); - ui.label("Color"); + ui.label(tr("Color")); ui.color_edit_button_rgb(&mut d.color); ui.end_row(); - ui.label("Intensity"); + ui.label(tr("Intensity")); ui.add(egui::DragValue::new(&mut d.intensity).range(-10000.0..=10000.0).suffix(" %")); ui.end_row(); let kind = KINDS[d.kind.min(3)]; if kind == LightKind::Spot { - ui.label("Cone Angle"); + ui.label(tr("Cone Angle")); ui.add(egui::DragValue::new(&mut d.cone_angle).range(0.0..=180.0).suffix("°")); ui.end_row(); - ui.label("Cone Feather"); + ui.label(tr("Cone Feather")); ui.add(egui::DragValue::new(&mut d.cone_feather).range(0.0..=100.0).suffix(" %")); ui.end_row(); } if kind != LightKind::Ambient { - ui.label("Falloff"); + ui.label(tr("Falloff")); egui::ComboBox::from_id_salt("light-falloff").selected_text(FALLOFFS[d.falloff.min(2)]).show_ui(ui, |ui| { for (i, f) in FALLOFFS.iter().enumerate() { ui.selectable_value(&mut d.falloff, i, *f); @@ -290,26 +291,26 @@ pub fn light(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) { }); ui.end_row(); if d.falloff > 0 { - ui.label("Radius"); + ui.label(tr("Radius")); ui.add(egui::DragValue::new(&mut d.radius).range(0.0..=100000.0).suffix(" px")); ui.end_row(); - ui.label("Falloff Distance"); + ui.label(tr("Falloff Distance")); ui.add(egui::DragValue::new(&mut d.falloff_distance).range(0.0..=100000.0).suffix(" px")); ui.end_row(); } - ui.label("Shadows"); - ui.checkbox(&mut d.casts_shadows, "Casts Shadows"); + ui.label(tr("Shadows")); + ui.checkbox(&mut d.casts_shadows, tr("Casts Shadows")); ui.end_row(); - ui.label("Shadow Darkness"); + ui.label(tr("Shadow Darkness")); ui.add_enabled(d.casts_shadows, egui::DragValue::new(&mut d.shadow_darkness).range(0.0..=100.0).suffix(" %")); ui.end_row(); - ui.label("Shadow Diffusion"); + ui.label(tr("Shadow Diffusion")); ui.add_enabled(d.casts_shadows, egui::DragValue::new(&mut d.shadow_diffusion).range(0.0..=1000.0).suffix(" px")); ui.end_row(); } }); ui.add_space(14.0); - ui.label(egui::RichText::new("Note: Shadows are only cast from layers with Cast Shadows enabled.").font(Tokens::ui(11.0)).color(t.text_dim)); + ui.label(egui::RichText::new(tr("Note: Shadows are only cast from layers with Cast Shadows enabled.")).font(Tokens::ui(11.0)).color(t.text_dim)); ui.add_space(8.0); rects = buttons(ui, t, &mut ok, &mut close); }); diff --git a/crates/ui-egui/src/panels/forms.rs b/crates/ui-egui/src/panels/forms.rs index f4de5f218..12f2c2f92 100644 --- a/crates/ui-egui/src/panels/forms.rs +++ b/crates/ui-egui/src/panels/forms.rs @@ -4,6 +4,7 @@ //! Keyboard Shortcuts: `shortcut_editor`). Every form runs an engine command with the collected parameters, so whatever a //! dialog does an agent can do with one `engine.execute`. +use crate::i18n::tr; use egui::{Color32, vec2}; use serde_json::{Map, Value, json}; @@ -684,7 +685,7 @@ pub fn show_form(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) { FieldKind::Path { value, .. } => { ui.horizontal(|ui| { let r = ui.add(egui::TextEdit::singleline(value).desired_width(150.0)).rect; - let b = ui.button("Browse…"); + let b = ui.button(tr("Browse…")); regs.push((format!("form.field.{}.browse", fl.key), b.rect, "Browse…".into())); if b.clicked() { browse = Some(i); @@ -696,7 +697,7 @@ pub fn show_form(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) { FieldKind::SavePath(value) => { ui.horizontal(|ui| { let r = ui.add(egui::TextEdit::singleline(value).desired_width(150.0)).rect; - let b = ui.button("Browse…"); + let b = ui.button(tr("Browse…")); regs.push((format!("form.field.{}.browse", fl.key), b.rect, "Browse…".into())); if b.clicked() { browse = Some(i); @@ -727,12 +728,12 @@ pub fn show_form(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) { }); ui.add_space(14.0); ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { - let b = ui.add(egui::Button::new(egui::RichText::new(" OK ").color(Color32::WHITE)).fill(t.accent)); + let b = ui.add(egui::Button::new(egui::RichText::new(tr(" OK ")).color(Color32::WHITE)).fill(t.accent)); regs.push(("form.ok".into(), b.rect, "OK".into())); if b.clicked() { ok = true; } - let c = ui.button("Cancel"); + let c = ui.button(tr("Cancel")); regs.push(("form.cancel".into(), c.rect, "Cancel".into())); if c.clicked() { close = true; @@ -786,7 +787,7 @@ pub fn show_info(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) { ui.label(body); ui.add_space(16.0); ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { - if ui.add(egui::Button::new(egui::RichText::new(" OK ").color(Color32::WHITE)).fill(t.accent)).clicked() { + if ui.add(egui::Button::new(egui::RichText::new(tr(" OK ")).color(Color32::WHITE)).fill(t.accent)).clicked() { close = true; } }); @@ -800,16 +801,16 @@ pub fn show_view_options(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Toke let mut close = false; super::dialogs::modal(ctx, "View Options", vec2(360.0, 330.0), t, |ui| { let v = &mut app.ui.viewer; - ui.checkbox(&mut v.show_layer_controls, "Layer controls"); - ui.checkbox(&mut v.show_masks, "Masks"); - ui.checkbox(&mut v.rulers, "Rulers"); - ui.checkbox(&mut v.guides, "Guides"); - ui.checkbox(&mut v.grid, "Grid"); - ui.checkbox(&mut v.safe_margins, "Title/action safe"); - ui.checkbox(&mut v.transparency_grid, "Transparency grid"); + ui.checkbox(&mut v.show_layer_controls, tr("Layer controls")); + ui.checkbox(&mut v.show_masks, tr("Masks")); + ui.checkbox(&mut v.rulers, tr("Rulers")); + ui.checkbox(&mut v.guides, tr("Guides")); + ui.checkbox(&mut v.grid, tr("Grid")); + ui.checkbox(&mut v.safe_margins, tr("Title/action safe")); + ui.checkbox(&mut v.transparency_grid, tr("Transparency grid")); ui.add_space(12.0); ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { - if ui.add(egui::Button::new(egui::RichText::new(" OK ").color(Color32::WHITE)).fill(t.accent)).clicked() { + if ui.add(egui::Button::new(egui::RichText::new(tr(" OK ")).color(Color32::WHITE)).fill(t.accent)).clicked() { close = true; } }); diff --git a/crates/ui-egui/src/panels/home.rs b/crates/ui-egui/src/panels/home.rs index 904e5f2f3..600bc551a 100644 --- a/crates/ui-egui/src/panels/home.rs +++ b/crates/ui-egui/src/panels/home.rs @@ -281,7 +281,7 @@ fn rail_ui(app: &mut EffectcraftApp, ui: &mut egui::Ui, p: &egui::Painter, rail: let foot_h = 22.0 + links.len() as f32 * 30.0 + 22.0 + sib_rows * 26.0; let mut cy = (rail.max.y - 16.0 - foot_h).max(y + 16.0); p.line_segment([pos2(x0, cy - 10.0), pos2(x0 + w, cy - 10.0)], Stroke::new(1.0, t.separator)); - p.text(pos2(x0, cy + 6.0), Align2::LEFT_CENTER, "Community", Tokens::semibold(12.0), t.text_dim); + p.text(pos2(x0, cy + 6.0), Align2::LEFT_CENTER, crate::i18n::tr("Community"), Tokens::semibold(12.0), t.text_dim); cy += 22.0; for (icon, label, cmd) in links { let r = Rect::from_min_size(pos2(x0, cy), vec2(w, 26.0)); @@ -301,7 +301,7 @@ fn rail_ui(app: &mut EffectcraftApp, ui: &mut egui::Ui, p: &egui::Painter, rail: } cy += 30.0; } - p.text(pos2(x0, cy + 8.0), Align2::LEFT_CENTER, "More ArtCraft apps", Tokens::ui(11.0), t.text_faint); + p.text(pos2(x0, cy + 8.0), Align2::LEFT_CENTER, crate::i18n::tr("More ArtCraft apps"), Tokens::ui(11.0), t.text_faint); cy += 20.0; let sw = (w - 6.0) / 2.0; for (i, (name, slug)) in sib.iter().enumerate() { @@ -328,7 +328,7 @@ fn home_page( let t = app.tokens; let (x0, w) = (area.min.x, area.width()); let mut y = area.min.y; - p.text(pos2(x0, y + 14.0), Align2::LEFT_CENTER, "Welcome to EffectCraft", Tokens::semibold(26.0), t.tab_text_active); + p.text(pos2(x0, y + 14.0), Align2::LEFT_CENTER, crate::i18n::tr("Welcome to EffectCraft"), Tokens::semibold(26.0), t.tab_text_active); y += 40.0; p.text( pos2(x0, y + 8.0), @@ -369,7 +369,7 @@ fn home_page( y += 64.0 + 36.0; // Recent: heading, filter, clear. - p.text(pos2(x0, y + 12.0), Align2::LEFT_CENTER, "Recent", Tokens::semibold(17.0), t.tab_text_active); + p.text(pos2(x0, y + 12.0), Align2::LEFT_CENTER, crate::i18n::tr("Recent"), Tokens::semibold(17.0), t.tab_text_active); let entries = recent_entries(&app.session.prefs); let fid = egui::Id::new("home-filter"); let mut filter: String = ctx.data(|d| d.get_temp(fid)).unwrap_or_default(); @@ -380,7 +380,7 @@ fn home_page( if !entries.is_empty() { let cr = Rect::from_min_size(pos2(fr.min.x - 96.0, y + 3.0), vec2(84.0, 20.0)); let resp = ui.interact(cr, egui::Id::new("home-clear-recent"), Sense::click()); - p.text(cr.right_center(), Align2::RIGHT_CENTER, "Clear list", Tokens::ui(11.5), if resp.hovered() { t.text } else { t.text_faint }); + p.text(cr.right_center(), Align2::RIGHT_CENTER, crate::i18n::tr("Clear list"), Tokens::ui(11.5), if resp.hovered() { t.text } else { t.text_faint }); app.auto.add("home.clearRecent", cr, "Clear Recent Projects"); if resp.clicked() { actions.push(("file.clearRecent", json!({}))); diff --git a/crates/ui-egui/src/panels/key_dialogs.rs b/crates/ui-egui/src/panels/key_dialogs.rs index 52190f0dd..3e170c12c 100644 --- a/crates/ui-egui/src/panels/key_dialogs.rs +++ b/crates/ui-egui/src/panels/key_dialogs.rs @@ -1,6 +1,7 @@ //! Animation dialogs: Keyframe Velocity, Keyframe Interpolation and Time Stretch. Each one is //! pre-filled from the engine (`keys.info`, the layer) and commits through one engine command. +use crate::i18n::tr; use egui::{Color32, vec2}; use serde_json::{Value, json}; @@ -114,10 +115,10 @@ fn buttons(ui: &mut egui::Ui, app: &mut EffectcraftApp, t: &Tokens, prefix: &str let (mut ok, mut cancel) = (false, false); ui.add_space(14.0); ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { - let r = ui.add(egui::Button::new(egui::RichText::new(" OK ").color(Color32::WHITE)).fill(t.accent)); + let r = ui.add(egui::Button::new(egui::RichText::new(tr(" OK ")).color(Color32::WHITE)).fill(t.accent)); app.auto.add(&format!("{prefix}.ok"), r.rect, "OK"); ok = r.clicked(); - let r = ui.button("Cancel"); + let r = ui.button(tr("Cancel")); app.auto.add(&format!("{prefix}.cancel"), r.rect, "Cancel"); cancel = r.clicked(); }); @@ -144,13 +145,13 @@ pub fn velocity(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) { ui.label(egui::RichText::new(tag).color(t.text_dim)); let side = if out { "out" } else { "in" }; ui.horizontal(|ui| { - ui.label("Speed:"); + ui.label(tr("Speed:")); let v = if out { &mut d.out_speed[dim] } else { &mut d.in_speed[dim] }; drag(ui, app, &format!("dialog.velocity.{side}Speed.{dim}"), v, 1.0, -1e7..=1e7, "", enabled); ui.label(egui::RichText::new(&d.units).color(t.text_dim)); }); ui.horizontal(|ui| { - ui.label("Influence:"); + ui.label(tr("Influence:")); let v = if out { &mut d.out_inf[dim] } else { &mut d.in_inf[dim] }; drag(ui, app, &format!("dialog.velocity.{side}Influence.{dim}"), v, 0.2, 0.1..=100.0, " %", enabled); }); @@ -159,7 +160,7 @@ pub fn velocity(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) { }); ui.add_space(8.0); } - let r = ui.checkbox(&mut d.continuous, "Continuous (Lock Outgoing to Incoming)"); + let r = ui.checkbox(&mut d.continuous, tr("Continuous (Lock Outgoing to Incoming)")); app.auto.add("dialog.velocity.continuous", r.rect, "Continuous"); if d.continuous { d.out_speed = d.in_speed.clone(); @@ -203,13 +204,13 @@ pub fn interpolation(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) let (mut ok, mut cancel) = (false, false); super::dialogs::modal(ctx, "Keyframe Interpolation", vec2(440.0, 250.0), t, |ui| { egui::Grid::new("interp-grid").num_columns(2).spacing([14.0, 10.0]).show(ui, |ui| { - ui.label("Temporal Interpolation:"); + ui.label(tr("Temporal Interpolation:")); combo(ui, app, "dialog.interpolation.temporal", &mut d.temporal, &TEMPORAL.map(|x| x.0), true); ui.end_row(); - ui.label("Spatial Interpolation:"); + ui.label(tr("Spatial Interpolation:")); combo(ui, app, "dialog.interpolation.spatial", &mut d.spatial, &SPATIAL.map(|x| x.0), d.has_spatial); ui.end_row(); - ui.label("Roving:"); + ui.label(tr("Roving:")); combo(ui, app, "dialog.interpolation.roving", &mut d.roving, &ROVING, d.has_spatial); ui.end_row(); }); @@ -245,19 +246,19 @@ pub fn time_stretch(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) { let mut d = app.dialog_state.stretch.clone(); let (mut ok, mut cancel) = (false, false); super::dialogs::modal(ctx, "Time Stretch", vec2(420.0, 300.0), t, |ui| { - ui.label(egui::RichText::new("Stretch").font(Tokens::medium(12.5))); + ui.label(egui::RichText::new(tr("Stretch")).font(Tokens::medium(12.5))); egui::Grid::new("stretch-grid").num_columns(2).spacing([14.0, 8.0]).show(ui, |ui| { - ui.label("Original Duration:"); + ui.label(tr("Original Duration:")); ui.label(format!("{:.2} s", d.orig_duration)); ui.end_row(); - ui.label("Stretch Factor:"); + ui.label(tr("Stretch Factor:")); let before = d.percent; drag(ui, app, "dialog.timeStretch.percent", &mut d.percent, 0.5, -10000.0..=10000.0, " %", true); if (before - d.percent).abs() > 1e-9 && d.orig_percent.abs() > 1e-9 { d.duration = d.orig_duration * d.percent.abs() / d.orig_percent.abs(); } ui.end_row(); - ui.label("New Duration:"); + ui.label(tr("New Duration:")); let before = d.duration; drag(ui, app, "dialog.timeStretch.duration", &mut d.duration, 0.05, 0.01..=100000.0, " s", true); if (before - d.duration).abs() > 1e-9 && d.orig_duration > 1e-9 { @@ -266,7 +267,7 @@ pub fn time_stretch(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) { ui.end_row(); }); ui.add_space(10.0); - ui.label(egui::RichText::new("Hold In Place").font(Tokens::medium(12.5))); + ui.label(egui::RichText::new(tr("Hold In Place")).font(Tokens::medium(12.5))); for (i, l) in ["Layer In-point", "Current Frame", "Layer Out-point"].into_iter().enumerate() { let r = ui.radio_value(&mut d.hold, i, l); app.auto.add(&format!("dialog.timeStretch.hold.{i}"), r.rect, l); diff --git a/crates/ui-egui/src/panels/layer_styles_dialog.rs b/crates/ui-egui/src/panels/layer_styles_dialog.rs index b84cd8ffe..7738fbea7 100644 --- a/crates/ui-egui/src/panels/layer_styles_dialog.rs +++ b/crates/ui-egui/src/panels/layer_styles_dialog.rs @@ -4,6 +4,7 @@ //! `layerStyles/