From 27760812de29a56ea34c16423c57ef872f9cca56 Mon Sep 17 00:00:00 2001 From: bmh129 <11478682+bmh129@users.noreply.github.com> Date: Mon, 19 Oct 2026 21:38:08 -0500 Subject: [PATCH] i18n: test that translations keep the shape of their English MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The French catalog once shipped with over a hundred translations sitting on their neighbours' keys ("Close" read "Société"); main has since fixed them. A catalog shifted that way still covers every key, so the existing coverage tests don't catch it. A new test checks every language's translations keep the shape of their English: the same {placeholders}, and no line break in a one-line label (the two big ribbon buttons that wrap are exempt). Plus spot checks of French labels that were once misplaced. Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/i18n.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/crates/ui-egui/src/i18n.rs b/crates/ui-egui/src/i18n.rs index 98919f8a4..a631c351c 100644 --- a/crates/ui-egui/src/i18n.rs +++ b/crates/ui-egui/src/i18n.rs @@ -647,6 +647,38 @@ mod tests { assert_eq!(Language::parse("xx"), None); } + /// A catalog whose translations slip onto their neighbours' keys still covers every key, so + /// check the shape of each translation against its English: placeholders survive, and a + /// one-line label (a menu item, a tooltip) doesn't turn into a two-line ribbon label. French + /// once shipped with over a hundred translations shifted this way ("Close" read "Société"). + #[test] + fn translations_keep_the_shape_of_their_english() { + let placeholders = + |s: &str| s.split('{').skip(1).filter_map(|rest| rest.split_once('}').map(|(name, _)| name.to_string())).collect::>(); + for language in Language::ALL { + for (english, _) in JAPANESE { + let label = english.rsplit_once('|').map_or(*english, |(_, label)| label); + let text = language.tr(english); + assert_eq!(placeholders(text), placeholders(label), "{language:?}: {english:?} => {text:?}"); + // Big ribbon buttons whose English fits on one line may still need two. + if !label.contains('\n') && !["AutoSum", "PivotTable"].contains(english) { + assert!(!text.contains('\n'), "{language:?}: one-line {english:?} => {text:?}"); + } + } + } + for (english, french) in [ + ("Close", "Fermer"), + ("File", "Fichier"), + ("Save", "Enregistrer"), + ("Animations", "Animations"), + ("Company", "Société"), + ("Show Detail", "Afficher le détail"), + ("Solver", "Solveur"), + ] { + assert_eq!(Language::Fr.tr(english), french); + } + } + #[test] fn german_cell_and_header_context_menus_are_fully_translated() { for label in [