Merge PR #1998: ui: language command docs and error list every language

This commit is contained in:
aiongg
2026-10-22 02:35:18 +00:00
4 changed files with 31 additions and 4 deletions
+17 -1
View File
@@ -175,6 +175,7 @@ pub fn handle(app: &mut SheetApp, ctx: &egui::Context, method: &str, p: &Json) -
}
"engine.commands" => {
let mut v: Vec<Json> = app.session.commands().into_iter().map(|c| serde_json::to_value(c).unwrap_or_default()).collect();
let language_params = format!("{{language: {}}} (alias: code)", crate::i18n::Language::code_list("|"));
for (id, label, params) in [
("ui.ribbonTab", "Ribbon Tab", ""),
("view.collapseRibbon", "Collapse Ribbon", ""),
@@ -195,7 +196,7 @@ pub fn handle(app: &mut SheetApp, ctx: &egui::Context, method: &str, p: &Json) -
"Submit Print Job",
"existing file.print sheet/pagination params → {printing,bytes,pending}; submits without opening a dialog",
),
("app.language.set", "Interface Language", "{language: \"en\"|\"zh\"|\"ja\"|\"ko\"|\"ru\"|\"pt\"|\"de\"|\"cs\"} (alias: code)"),
("app.language.set", "Interface Language", language_params.as_str()),
("app.language.english", "Interface Language: English", ""),
("app.language.japanese", "Interface Language: Japanese", ""),
] {
@@ -530,4 +531,19 @@ mod tests {
assert!(!app.printing.busy(), "discovery must not submit a job");
assert!(app.backstage.is_none(), "discovery must not open settings");
}
#[test]
fn language_command_docs_list_every_language() {
let mut session = gridcraft_engine::Session::new();
session.new_workbook();
let mut app = SheetApp::new(session, crate::Services::default());
let Outcome::Done(reply) = handle(&mut app, &egui::Context::default(), "engine.commands", &json!({"search":"language"})) else {
panic!("command discovery must return immediately");
};
let commands = reply["result"].as_array().unwrap();
let docs = commands.iter().find(|c| c["id"] == "app.language.set").unwrap()["params"].as_str().unwrap();
for l in crate::i18n::Language::ALL {
assert!(docs.contains(&format!("\"{}\"", l.code())), "app.language.set docs miss {:?}: {docs}", l.code());
}
}
}
+10
View File
@@ -78,6 +78,12 @@ impl Language {
Self::from_tag(code)
}
/// Every code `app.language.set` takes, quoted and joined (`"en"|"es"|…`), for command docs
/// and errors.
pub fn code_list(separator: &str) -> String {
Self::ALL.iter().map(|l| format!("\"{}\"", l.code())).collect::<Vec<_>>().join(separator)
}
fn from_tag(tag: &str) -> Option<Self> {
match tag.trim().split(['-', '_']).next()?.to_ascii_lowercase().as_str() {
"en" => Some(Self::En),
@@ -774,6 +780,10 @@ mod tests {
app.run("app.language.set", serde_json::json!({"code": "ja"})).unwrap();
assert_eq!(app.ui.language, Language::Ja, "`code` is an alias for `language`");
assert!(app.run("app.language.set", serde_json::json!({})).is_err(), "no language is an error, not a dialog");
let unknown = app.run("app.language.set", serde_json::json!({"language": "xx"})).unwrap_err().to_string();
for l in Language::ALL {
assert!(unknown.contains(&format!("\"{}\"", l.code())), "the error lists {:?}: {unknown}", l.code());
}
app.run("app.language.english", serde_json::json!({})).unwrap();
assert_eq!(app.ui.language, Language::En);
for l in Language::ALL {
+1 -1
View File
@@ -658,7 +658,7 @@ impl SheetApp {
self.ui.language = l;
Ok(json!({"language": l}))
}
None => Err(format!("unknown language {code:?} (use \"en\", \"es\", \"zh\", \"ja\", \"ko\", \"ru\", \"pt\" or \"cs\")")),
None => Err(format!("unknown language {code:?} (use one of {})", i18n::Language::code_list(", "))),
}
}
"app.language.english" => {