mirror of
https://github.com/storytold/gridcraft.git
synced 2026-10-24 02:36:48 -06:00
Merge PR #2106: Translate form-dialog field labels, choices and notes
This commit is contained in:
@@ -1419,12 +1419,14 @@ fn take_first_focus(d: &mut Dialog, r: &egui::Response) {
|
||||
|
||||
/// Draws a dialog's `fields`, editing its values.
|
||||
fn form_fields(ui: &mut egui::Ui, d: &mut Dialog) {
|
||||
// Field labels, choices and notes are English source strings, translated as they are drawn.
|
||||
let lang = crate::i18n::current(ui.ctx());
|
||||
egui::Grid::new("form").num_columns(1).spacing(vec2(8.0, 6.0)).show(ui, |ui| {
|
||||
for f in d.fields.clone() {
|
||||
match f {
|
||||
Field::Text { key, label } => {
|
||||
ui.vertical(|ui| {
|
||||
ui.label(label);
|
||||
ui.label(lang.tr(label));
|
||||
let mut s = d
|
||||
.values
|
||||
.get(key)
|
||||
@@ -1445,17 +1447,17 @@ fn form_fields(ui: &mut egui::Ui, d: &mut Dialog) {
|
||||
Field::Password { key, label } => {
|
||||
// Masked as typed (Excel shows dots); "Show password" reveals it on purpose.
|
||||
ui.vertical(|ui| {
|
||||
ui.label(label);
|
||||
ui.label(lang.tr(label));
|
||||
let mut s = d.values.get(key).and_then(Json::as_str).unwrap_or_default().to_string();
|
||||
if ui.add(egui::TextEdit::singleline(&mut s).password(!d.show_password).desired_width(320.0)).changed() {
|
||||
d.values.insert(key.into(), json!(s));
|
||||
}
|
||||
ui.checkbox(&mut d.show_password, "Show password");
|
||||
ui.checkbox(&mut d.show_password, lang.tr("Show password"));
|
||||
});
|
||||
}
|
||||
Field::Number { key, label } => {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(label);
|
||||
ui.label(lang.tr(label));
|
||||
let mut n = d.values.get(key).and_then(Json::as_f64).unwrap_or(0.0);
|
||||
// Typed values count straight away: otherwise Enter or OK in the same frame
|
||||
// confirms the old value, because a number field only commits when it loses focus.
|
||||
@@ -1468,20 +1470,20 @@ fn form_fields(ui: &mut egui::Ui, d: &mut Dialog) {
|
||||
}
|
||||
Field::Choice { key, label, options } => {
|
||||
ui.vertical(|ui| {
|
||||
ui.label(egui::RichText::new(label).strong());
|
||||
ui.label(egui::RichText::new(lang.tr(label)).strong());
|
||||
let cur = d.values.get(key).and_then(Json::as_str).unwrap_or("").to_string();
|
||||
if d.name == "dataValidation" {
|
||||
let selected = options.iter().find(|(val, _)| *val == cur).map(|(_, text)| *text).unwrap_or("");
|
||||
let selected = options.iter().find(|(val, _)| *val == cur).map(|(_, text)| lang.tr(text)).unwrap_or("");
|
||||
egui::ComboBox::from_id_salt(("dataValidation", key)).selected_text(selected).width(300.0).show_ui(ui, |ui| {
|
||||
for (val, text) in options {
|
||||
if ui.selectable_label(cur == val, text).clicked() {
|
||||
if ui.selectable_label(cur == val, lang.tr(text)).clicked() {
|
||||
d.set_value(key, json!(val));
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
for (val, text) in options {
|
||||
if ui.radio(cur == val, text).clicked() {
|
||||
if ui.radio(cur == val, lang.tr(text)).clicked() {
|
||||
d.set_value(key, json!(val));
|
||||
}
|
||||
}
|
||||
@@ -1490,12 +1492,15 @@ fn form_fields(ui: &mut egui::Ui, d: &mut Dialog) {
|
||||
}
|
||||
Field::Check { key, label } => {
|
||||
let mut b = d.values.get(key).and_then(Json::as_bool).unwrap_or(false);
|
||||
if ui.checkbox(&mut b, label).changed() {
|
||||
if ui.checkbox(&mut b, lang.tr(label)).changed() {
|
||||
d.values.insert(key.into(), json!(b));
|
||||
}
|
||||
}
|
||||
Field::Note(t) => {
|
||||
ui.label(egui::RichText::new(t).small());
|
||||
// A note can carry user data (a URL, a file or sheet name); `tr` reads a `|` as a
|
||||
// context separator, so such a note is shown as it is.
|
||||
let text = if t.contains('|') { t.as_str() } else { lang.tr(&t) };
|
||||
ui.label(egui::RichText::new(text).small());
|
||||
}
|
||||
}
|
||||
ui.end_row();
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
//! Form dialogs draw their field labels and choices in the interface language, as their titles
|
||||
//! and OK/Cancel buttons already were (Page Setup showed "Left margin (in):" in every language).
|
||||
|
||||
use egui_kittest::kittest::Queryable as _;
|
||||
use gridcraft_engine::Session;
|
||||
use gridcraft_ui_egui::SheetApp;
|
||||
use serde_json::json;
|
||||
|
||||
fn harness() -> egui_kittest::Harness<'static, SheetApp> {
|
||||
let mut session = Session::new();
|
||||
session.new_workbook();
|
||||
let mut h = egui_kittest::Harness::builder().with_size(egui::vec2(1200.0, 800.0)).build_ui_state(
|
||||
|ui, app: &mut SheetApp| {
|
||||
let ctx = ui.ctx().clone();
|
||||
app.logic(&ctx);
|
||||
app.ui(ui);
|
||||
},
|
||||
SheetApp::new(session, Default::default()),
|
||||
);
|
||||
h.run_steps(4);
|
||||
h
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn form_fields_are_translated() {
|
||||
for (dialog, translated, english) in [
|
||||
("pageSetup", ["Margem esquerda (pol.):", "Rodapé (pol.):"], ["Left margin (in):", "Footer (in):"]),
|
||||
("insertPaste", ["Deslocar células para a direita", "Deslocar células para baixo"], ["Shift cells right", "Shift cells down"]),
|
||||
] {
|
||||
let mut h = harness();
|
||||
h.state_mut().run("app.language.set", json!({"language": "pt"})).unwrap();
|
||||
h.state_mut().open_dialog(dialog, json!({}));
|
||||
h.run_steps(3);
|
||||
for label in translated {
|
||||
assert!(h.query_by_label_contains(label).is_some(), "{dialog}: no {label}");
|
||||
}
|
||||
for label in english {
|
||||
assert!(h.query_by_label_contains(label).is_none(), "{dialog}: {label} is untranslated");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn notes_with_a_pipe_show_in_full() {
|
||||
// `tr` reads `|` as a context separator; the Open Hyperlink note carries the URL, which must
|
||||
// be shown whole so the user sees where the link really goes.
|
||||
let url = "https://example.com/report|evil.example";
|
||||
for language in ["en", "pt"] {
|
||||
let mut h = harness();
|
||||
h.state_mut().run("app.language.set", json!({"language": language})).unwrap();
|
||||
h.state_mut().open_dialog("openLink", json!({"url": url}));
|
||||
h.run_steps(3);
|
||||
assert!(h.query_by_label_contains(url).is_some(), "{language}: the link note lost part of {url}");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user