diff --git a/crates/ui-egui/src/i18n/es.rs b/crates/ui-egui/src/i18n/es.rs index 372ef1d..6812cb4 100644 --- a/crates/ui-egui/src/i18n/es.rs +++ b/crates/ui-egui/src/i18n/es.rs @@ -409,6 +409,36 @@ pub(super) fn translate(text: &str) -> Option<&'static str> { "Dark" => "Oscuro", "Sheet Options|View" => "Ver", "Print" => "Imprimir", + "PivotTable Fields" => "Campos de tabla dinámica", + "Choose fields to add to report:" => "Seleccione los campos para agregar al informe:", + "Use a field's menu to move it between areas:" => "Use el menú de un campo para moverlo entre áreas:", + "Filters" => "Filtros", + "Columns" => "Columnas", + "Rows" => "Filas", + "Values" => "Valores", + "Move to Row Labels" => "Mover a rótulos de fila", + "Move to Column Labels" => "Mover a rótulos de columna", + "Move to Report Filter" => "Mover al filtro de informe", + "Move to Values" => "Mover a valores", + "Summarize by Sum" => "Resumir por suma", + "Summarize by Count" => "Resumir por cuenta", + "Summarize by Average" => "Resumir por promedio", + "Summarize by Max" => "Resumir por máximo", + "Summarize by Min" => "Resumir por mínimo", + "Show as No Calculation" => "Mostrar como sin cálculo", + "Show as % of Grand Total" => "Mostrar como % del total general", + "Show as Running Total" => "Mostrar como total acumulado", + "Show as Rank" => "Mostrar como clasificación", + "Group by Years" => "Agrupar por años", + "Group by Quarters" => "Agrupar por trimestres", + "Group by Months" => "Agrupar por meses", + "Remove Field" => "Quitar campo", + "Refresh" => "Actualizar", + "Calculated Field…" => "Campo calculado…", + "Report Layout" => "Diseño de informe", + "Compact" => "Compacto", + "Outline" => "Esquema", + "Tabular" => "Tabular", _ => return None, }) } diff --git a/crates/ui-egui/src/pivot_pane.rs b/crates/ui-egui/src/pivot_pane.rs index 0657026..55917ef 100644 --- a/crates/ui-egui/src/pivot_pane.rs +++ b/crates/ui-egui/src/pivot_pane.rs @@ -6,6 +6,7 @@ use egui::{Color32, Ui, vec2}; use serde_json::{Value as Json, json}; use crate::SheetApp; +use crate::i18n; use crate::theme::{self, Tokens}; /// Whether the active cell is inside a pivot report (so the pane should show). @@ -19,13 +20,14 @@ pub fn active_pivot(app: &SheetApp) -> Option { pub fn show(app: &mut SheetApp, ui: &mut Ui) { let Some(name) = active_pivot(app) else { return }; let t = Tokens::get(ui.ctx()); + let lang = i18n::current(ui.ctx()); let info = match app.session.run("pivot.fields", json!({"pivot": name})) { Ok(v) => v, Err(_) => return, }; egui::Panel::right("pivot_pane").default_size(280.0).resizable(true).frame(egui::Frame::NONE.fill(t.ribbon).inner_margin(10)).show(ui, |ui| { - ui.label(egui::RichText::new("PivotTable Fields").font(theme::ui_bold(15.0))); - ui.label(egui::RichText::new("Choose fields to add to report:").small().color(t.text_dim)); + ui.label(egui::RichText::new(lang.tr("PivotTable Fields")).font(theme::ui_bold(15.0))); + ui.label(egui::RichText::new(lang.tr("Choose fields to add to report:")).small().color(t.text_dim)); ui.add_space(4.0); let fields = info["fields"].as_array().cloned().unwrap_or_default(); egui::ScrollArea::vertical().max_height(220.0).id_salt("pv_fields").show(ui, |ui| { @@ -52,7 +54,7 @@ pub fn show(app: &mut SheetApp, ui: &mut Ui) { } }); ui.separator(); - ui.label(egui::RichText::new("Use a field's menu to move it between areas:").small().color(t.text_dim)); + ui.label(egui::RichText::new(lang.tr("Use a field's menu to move it between areas:")).small().color(t.text_dim)); let area = |key: &str| -> Vec { info["pivot"][key] .as_array() @@ -79,7 +81,7 @@ pub fn show(app: &mut SheetApp, ui: &mut Ui) { ui.columns(2, |cols| { for (i, (key, label)) in [("filters", "Filters"), ("columns", "Columns"), ("rows", "Rows"), ("values", "Values")].iter().enumerate() { let ui = &mut cols[i % 2]; - ui.label(egui::RichText::new(*label).strong()); + ui.label(egui::RichText::new(lang.tr(label)).strong()); egui::Frame::NONE.fill(t.window).corner_radius(4.0).inner_margin(4.0).show(ui, |ui| { ui.set_min_size(vec2(ui.available_width(), 60.0)); for f in area_with_values(key) { @@ -96,7 +98,7 @@ pub fn show(app: &mut SheetApp, ui: &mut Ui) { ("filters", "Move to Report Filter"), ("values", "Move to Values"), ] { - if to != *key && (!is_values || matches!(to, "rows" | "columns")) && ui.button(l).clicked() { + if to != *key && (!is_values || matches!(to, "rows" | "columns")) && ui.button(lang.tr(l)).clicked() { app.run_or_alert("pivot.moveField", json!({"pivot": name, "field": f, "from": key, "to": to})); } } @@ -105,25 +107,31 @@ pub fn show(app: &mut SheetApp, ui: &mut Ui) { } if *key == "values" { ui.separator(); - for (func, l) in [("sum", "Sum"), ("count", "Count"), ("average", "Average"), ("max", "Max"), ("min", "Min")] { - if ui.button(format!("Summarize by {l}")).clicked() { + for (func, l) in [ + ("sum", "Summarize by Sum"), + ("count", "Summarize by Count"), + ("average", "Summarize by Average"), + ("max", "Summarize by Max"), + ("min", "Summarize by Min"), + ] { + if ui.button(lang.tr(l)).clicked() { app.run_or_alert("pivot.valueSettings", json!({"pivot": name, "field": f, "func": func})); } } for (sa, l) in [ - ("normal", "No Calculation"), - ("percentOfGrandTotal", "% of Grand Total"), - ("runningTotal", "Running Total"), - ("rank", "Rank"), + ("normal", "Show as No Calculation"), + ("percentOfGrandTotal", "Show as % of Grand Total"), + ("runningTotal", "Show as Running Total"), + ("rank", "Show as Rank"), ] { - if ui.button(format!("Show as {l}")).clicked() { + if ui.button(lang.tr(l)).clicked() { app.run_or_alert("pivot.valueSettings", json!({"pivot": name, "field": f, "showAs": sa})); } } } else { ui.separator(); for (o, l) in [("asc", "Sort A to Z"), ("desc", "Sort Z to A")] { - if ui.button(l).clicked() { + if ui.button(lang.tr(l)).clicked() { app.run_or_alert("pivot.sort", json!({"pivot": name, "field": f, "order": o})); } } @@ -133,13 +141,13 @@ pub fn show(app: &mut SheetApp, ui: &mut Ui) { ("months", "Group by Months"), ("none", "Ungroup"), ] { - if ui.button(l).clicked() { + if ui.button(lang.tr(l)).clicked() { app.run_or_alert("pivot.group", json!({"pivot": name, "field": f, "by": g})); } } } ui.separator(); - if ui.button("Remove Field").clicked() { + if ui.button(lang.tr("Remove Field")).clicked() { app.run_or_alert("pivot.removeField", json!({"pivot": name, "field": f, "area": key})); } }); @@ -149,15 +157,15 @@ pub fn show(app: &mut SheetApp, ui: &mut Ui) { }); ui.separator(); ui.horizontal(|ui| { - if ui.button("Refresh").clicked() { + if ui.button(lang.tr("Refresh")).clicked() { app.run_or_alert("pivot.refresh", json!({"pivot": name})); } - if ui.button("Calculated Field…").clicked() { + if ui.button(lang.tr("Calculated Field…")).clicked() { app.run_or_alert("pivot.calculatedField", json!({"pivot": name})); } - egui::ComboBox::from_id_salt("pv_layout").selected_text("Report Layout").show_ui(ui, |ui| { + egui::ComboBox::from_id_salt("pv_layout").selected_text(lang.tr("Report Layout")).show_ui(ui, |ui| { for (l, v) in [("Compact", "compact"), ("Outline", "outline"), ("Tabular", "tabular")] { - if ui.button(l).clicked() { + if ui.button(lang.tr(l)).clicked() { app.run_or_alert("pivot.layout", json!({"pivot": name, "layout": v})); } } diff --git a/crates/ui-egui/tests/interaction.rs b/crates/ui-egui/tests/interaction.rs index 0958c8c..d136b46 100644 --- a/crates/ui-egui/tests/interaction.rs +++ b/crates/ui-egui/tests/interaction.rs @@ -383,6 +383,26 @@ fn language_switch_translates_the_ribbon_and_persists() { assert_eq!(h.state().ui.language, Language::En); } +#[test] +fn pivot_fields_pane_follows_the_interface_language() { + // #858: the PivotTable Fields pane and its field menus stayed in English after switching to Spanish. + let mut h = harness(blank()); + h.state_mut().run("range.setValues", json!({"range": "A1:B3", "values": [["Region", "Sales"], ["North", 10], ["South", 20]]})).unwrap(); + h.state_mut().run("insert.pivotTable", json!({"source": "Sheet1!$A$1:$B$3", "rows": ["Region"]})).unwrap(); + h.state_mut().run("app.language.set", json!({"language": "es"})).unwrap(); + h.run_steps(4); + for label in ["Campos de tabla dinámica", "Filas", "Valores", "Actualizar", "Campo calculado…"] { + assert!(h.query_by_label(label).is_some(), "the pane shows `{label}`"); + } + assert!(h.query_by_label("PivotTable Fields").is_none()); + h.get_by_label("Region ▾").click(); + h.run_steps(2); + for label in ["Mover a rótulos de columna", "Agrupar por años", "Quitar campo"] { + assert!(h.query_by_label(label).is_some(), "the field menu shows `{label}`"); + } + assert!(h.query_by_label("Remove Field").is_none()); +} + #[test] fn format_cells_draws_its_labels_in_the_interface_language() { let mut h = harness(blank());