Right-to-left: multi-word Hebrew/Arabic names read backwards; prefs search misses multi-word labels (#10471) (#10562)

* claim: #10471

* Show given names in bidi display order; find RTL prefs labels typed in reading order (#10471)

crate::bidi ran over catalog text only, so a multi-word Hebrew or Arabic
body name showed its words left to right in stored order, and Preferences
search compared display-order labels against reading-order queries.
given_name now returns right-to-left names in display order (browser rows,
tabs, status bar all draw names through it), and text_matches also scores
the query after the same bidi transform.

---------

Co-authored-by: Dahak Builds <dahakbuilds@users.noreply.github.com>
This commit is contained in:
Dahak Builds
2026-11-01 10:09:44 +00:00
committed by GitHub
parent 74ee047d3f
commit bcb5efcaa4
2 changed files with 40 additions and 3 deletions
+17 -2
View File
@@ -242,9 +242,11 @@ impl Locale {
}
/// A name someone gave (a body, sketch, component, …): shown as typed, even when it matches
/// a UI label ("Fillet", "Boss"); only the default names are translated.
/// a UI label ("Fillet", "Boss"); only the default names are translated. A right-to-left name
/// is given in display order like a catalog message (see [`crate::bidi`]), so a multi-word
/// Hebrew or Arabic name reads from the right like the interface's own labels (#10471).
pub fn given_name(self, text: &str) -> String {
self.default_name(text).unwrap_or_else(|| text.to_string())
self.default_name(text).unwrap_or_else(|| crate::bidi::visual(text).into_owned())
}
/// Evaluation errors stay English for scripts; the empty Cut/Intersect hint that names
@@ -461,6 +463,19 @@ mod tests {
}
}
/// A name someone gave is shown in display order, like a catalog message: a multi-word
/// Hebrew name must read from the right, not with its words backwards (#10471).
#[test]
fn given_names_are_shown_in_display_order() {
let _language = scope("he");
assert_eq!(tr_name("לוח עליון קטן"), "קטן עליון לוח");
assert_eq!(tr_name("לוח"), "לוח", "one word is shown as it is");
assert_eq!(tr_name("Top plate"), "Top plate", "Latin is shown as it is");
assert_eq!(tr_name("Body2"), tr("browser.body_name").replace("{number}", "2"), "default names stay translated");
let _arabic = scope("ar");
assert_eq!(tr_name("لوحة علوية صغيرة"), "صغيرة علوية لوحة");
}
#[test]
fn construction_operation_names_exist_in_every_supported_catalog() {
let family = crate::sketch_families::family("sketch.construction_line").unwrap();
+23 -1
View File
@@ -521,7 +521,13 @@ fn text_hit(ui: &Ui, text: &str) -> bool {
}
fn text_matches(query: &str, text: &str) -> bool {
setting_score(query, text) > 0 || english_hit(query, text) || keywords(text).iter().any(|word| setting_score(query, word) > 0)
// A right-to-left label reaches here in display order (see `Catalog::shown`), while the query
// is typed in reading order: compare the query in display order too, so a multi-word Hebrew
// or Arabic label is found as it is typed (#10471; #7532 fixed only the test's half).
setting_score(query, text) > 0
|| setting_score(&crate::bidi::visual(query), text) > 0
|| english_hit(query, text)
|| keywords(text).iter().any(|word| setting_score(query, word) > 0)
}
/// Prefix, the start of a word, or a run of at least three characters. Scattered letters are not
@@ -749,6 +755,22 @@ mod tests {
assert!(!matches_text("dark", &["Units of new designs"]));
}
/// A multi-word right-to-left label reaches the search in display order while the query is
/// typed in reading order: typing the label's words as written must find the row (#10471).
#[test]
fn search_finds_right_to_left_labels_typed_in_reading_order() {
let _language = crate::i18n::scope("he");
let label = crate::i18n::tr("prefs.bodies_folders");
assert!(matches_text("תיקיות גופים", &[label]), "the label as it is typed");
assert!(matches_text("גופים תיקיות", &[label]), "the words swapped still match");
assert!(matches_text("תיקיות", &[label]), "one word still matches");
let _language = crate::i18n::scope("ar");
let label = crate::i18n::tr("prefs.bodies_folders");
assert!(matches_text("مجلدات الأجسام", &[label]), "the label as it is typed");
assert!(matches_text("الأجسام مجلدات", &[label]), "the words swapped still match");
assert!(matches_text("مجلدات", &[label]), "one word still matches");
}
/// The search field and its empty result name the window they search (#8318): "Search
/// settings" under a "Preferences" title read as two names for one window.
#[test]