From 14001aee651090932fe6a6e23898f38b819f8c61 Mon Sep 17 00:00:00 2001 From: Federico Olivares <119693761+FedeOlivares@users.noreply.github.com> Date: Fri, 23 Oct 2026 21:07:34 -0300 Subject: [PATCH] Proofing options reach the Czech, Croatian, Indonesian, Italian, Polish and Portuguese checks (#3966) Each of these languages accepted every word in capitals of up to six letters and every word with a digit on its own, so turning off Ignore words in UPPERCASE or Ignore words that contain numbers had no effect on them for short words. The check now takes the options, as the Spanish one does; the direct is_correct/is_correct_with keep the defaults. Co-authored-by: Federico Olivares Co-authored-by: aiongg <67543387+aiongg@users.noreply.github.com> --- crates/proof/src/cs/mod.rs | 34 ++++++++++++++++++++++++++++++++-- crates/proof/src/hr/mod.rs | 34 ++++++++++++++++++++++++++++++++-- crates/proof/src/id/mod.rs | 38 +++++++++++++++++++++++++++++++++++--- crates/proof/src/it/mod.rs | 34 ++++++++++++++++++++++++++++++++-- crates/proof/src/lib.rs | 12 ++++++------ crates/proof/src/pl/mod.rs | 34 ++++++++++++++++++++++++++++++++-- crates/proof/src/pt/mod.rs | 34 ++++++++++++++++++++++++++++++++-- 7 files changed, 201 insertions(+), 19 deletions(-) diff --git a/crates/proof/src/cs/mod.rs b/crates/proof/src/cs/mod.rs index c2ec00c9aa..7a910c72d7 100644 --- a/crates/proof/src/cs/mod.rs +++ b/crates/proof/src/cs/mod.rs @@ -98,18 +98,30 @@ pub fn is_correct(word: &str) -> bool { is_correct_with(&dictionary(), word) } +/// [`is_correct`] with the File › Options › Proofing choices: with Ignore words in UPPERCASE off, +/// short words in capitals are checked against the dictionary too, and with Ignore words that +/// contain numbers off, so are words with digits. +pub fn is_correct_with_options(word: &str, opts: &crate::Options) -> bool { + check(&dictionary(), word, opts) +} + /// [`is_correct`] against a given dictionary (an empty one accepts everything). pub fn is_correct_with(d: &AffixDict, word: &str) -> bool { + check(d, word, &crate::Options::default()) +} + +/// [`is_correct_with_options`] against a given dictionary. +fn check(d: &AffixDict, word: &str, opts: &crate::Options) -> bool { if d.is_empty() { return true; // no Czech dictionary installed: no spelling marks } let w = word.trim_matches(|c: char| !c.is_alphanumeric() && c != '\'' && c != '’'); let w = w.trim_matches(['\'', '’']); let n = w.chars().count(); - if n <= 1 || w.chars().any(|c| c.is_ascii_digit()) || !w.chars().any(char::is_alphabetic) { + if n <= 1 || (opts.ignore_numbers && w.chars().any(|c| c.is_ascii_digit())) || !w.chars().any(char::is_alphabetic) { return true; } - if w.chars().all(|c| !c.is_lowercase()) && n <= 6 { + if opts.ignore_uppercase && w.chars().all(|c| !c.is_lowercase()) && n <= 6 { return true; } if !w.chars().any(is_latin_letter) { @@ -233,4 +245,22 @@ mod tests { let opts = crate::Options { ignore_uppercase: false, ..Default::default() }; assert!(!crate::is_correct_in_with("ZQZAKÁZANÉ", crate::ProofLang::Cs, &opts)); } + + /// With Ignore words in UPPERCASE or Ignore words that contain numbers off, Czech checks + /// those words against the dictionary instead of accepting them. + #[test] + fn proofing_options_reach_the_czech_check() { + test_dict::install(); + let lang = crate::ProofLang::Cs; + let defaults = crate::Options::default(); + let check_caps = crate::Options { ignore_uppercase: false, ..defaults }; + let check_digits = crate::Options { ignore_numbers: false, ..defaults }; + assert!(crate::is_correct_in_with("ŠKULA", lang, &defaults), "short capitals are acronyms by default"); + assert!(!crate::is_correct_in_with("ŠKULA", lang, &check_caps)); + assert!(crate::is_correct_in_with("ŠKOLA", lang, &check_caps)); + assert!(crate::is_correct_in_with("škula2", lang, &defaults)); + assert!(!crate::is_correct_in_with("škula2", lang, &check_digits)); + assert!(crate::is_correct_in_with("2026", lang, &check_digits), "numbers alone stay correct"); + assert!(is_correct("ŠKULA"), "direct checks keep the defaults"); + } } diff --git a/crates/proof/src/hr/mod.rs b/crates/proof/src/hr/mod.rs index 23df9e9355..1c89b4fac8 100644 --- a/crates/proof/src/hr/mod.rs +++ b/crates/proof/src/hr/mod.rs @@ -85,18 +85,30 @@ pub fn is_correct(word: &str) -> bool { is_correct_with(&dictionary(), word) } +/// [`is_correct`] with the File › Options › Proofing choices: with Ignore words in UPPERCASE off, +/// short words in capitals are checked against the dictionary too, and with Ignore words that +/// contain numbers off, so are words with digits. +pub fn is_correct_with_options(word: &str, opts: &crate::Options) -> bool { + check(&dictionary(), word, opts) +} + /// [`is_correct`] against a given dictionary (an empty one accepts everything). pub fn is_correct_with(d: &AffixDict, word: &str) -> bool { + check(d, word, &crate::Options::default()) +} + +/// [`is_correct_with_options`] against a given dictionary. +fn check(d: &AffixDict, word: &str, opts: &crate::Options) -> bool { if d.is_empty() { return true; // no Croatian dictionary installed: no spelling marks } let w = word.trim_matches(|c: char| !c.is_alphanumeric() && c != '\'' && c != '’'); let w = w.trim_matches(['\'', '’']); let n = w.chars().count(); - if n <= 1 || w.chars().any(|c| c.is_ascii_digit()) || !w.chars().any(char::is_alphabetic) { + if n <= 1 || (opts.ignore_numbers && w.chars().any(|c| c.is_ascii_digit())) || !w.chars().any(char::is_alphabetic) { return true; } - if w.chars().all(|c| !c.is_lowercase()) && n <= 6 { + if opts.ignore_uppercase && w.chars().all(|c| !c.is_lowercase()) && n <= 6 { return true; } if !w.chars().any(is_latin_letter) { @@ -147,4 +159,22 @@ mod tests { fn missing_dictionary_is_silent() { assert!(is_correct_with(&AffixDict::default(), "nepostojećariječ")); } + + /// With Ignore words in UPPERCASE or Ignore words that contain numbers off, Croatian checks + /// those words against the dictionary instead of accepting them. + #[test] + fn proofing_options_reach_the_croatian_check() { + test_dict::install(); + let lang = crate::ProofLang::Hr; + let defaults = crate::Options::default(); + let check_caps = crate::Options { ignore_uppercase: false, ..defaults }; + let check_digits = crate::Options { ignore_numbers: false, ..defaults }; + assert!(crate::is_correct_in_with("ŠKOLLA", lang, &defaults), "short capitals are acronyms by default"); + assert!(!crate::is_correct_in_with("ŠKOLLA", lang, &check_caps)); + assert!(crate::is_correct_in_with("ŠKOLA", lang, &check_caps)); + assert!(crate::is_correct_in_with("školla2", lang, &defaults)); + assert!(!crate::is_correct_in_with("školla2", lang, &check_digits)); + assert!(crate::is_correct_in_with("2026", lang, &check_digits), "numbers alone stay correct"); + assert!(is_correct("ŠKOLLA"), "direct checks keep the defaults"); + } } diff --git a/crates/proof/src/id/mod.rs b/crates/proof/src/id/mod.rs index 5b9be698d0..09771e8520 100644 --- a/crates/proof/src/id/mod.rs +++ b/crates/proof/src/id/mod.rs @@ -91,18 +91,30 @@ pub fn is_correct(word: &str) -> bool { is_correct_with(&dictionary(), word) } +/// [`is_correct`] with the File › Options › Proofing choices: with Ignore words in UPPERCASE off, +/// short words in capitals are checked against the dictionary too, and with Ignore words that +/// contain numbers off, so are words with digits. +pub fn is_correct_with_options(word: &str, opts: &crate::Options) -> bool { + check(&dictionary(), word, opts) +} + /// [`is_correct`] against a given dictionary (an empty one accepts everything). pub fn is_correct_with(d: &AffixDict, word: &str) -> bool { + check(d, word, &crate::Options::default()) +} + +/// [`is_correct_with_options`] against a given dictionary. +fn check(d: &AffixDict, word: &str, opts: &crate::Options) -> bool { if d.is_empty() { return true; // no Indonesian dictionary installed: no spelling marks } let w = word.trim_matches(|c: char| !c.is_alphanumeric() && c != '\'' && c != '’'); let w = w.trim_matches(['\'', '’']); let n = w.chars().count(); - if n <= 1 || w.chars().any(|c| c.is_ascii_digit()) || !w.chars().any(char::is_alphabetic) { + if n <= 1 || (opts.ignore_numbers && w.chars().any(|c| c.is_ascii_digit())) || !w.chars().any(char::is_alphabetic) { return true; } - if w.chars().all(|c| !c.is_lowercase()) && n <= 6 { + if opts.ignore_uppercase && w.chars().all(|c| !c.is_lowercase()) && n <= 6 { return true; } if !w.chars().any(is_latin_letter) { @@ -113,7 +125,7 @@ pub fn is_correct_with(d: &AffixDict, word: &str) -> bool { || w.split('-').all(|p| { p.chars().count() <= 1 || CLITICS.iter().any(|c| p.eq_ignore_ascii_case(c)) - || (p.chars().all(|c| !c.is_lowercase()) && p.chars().count() <= 6) + || (opts.ignore_uppercase && p.chars().all(|c| !c.is_lowercase()) && p.chars().count() <= 6) || known(d, p) }); } @@ -224,4 +236,24 @@ mod tests { let opts = crate::Options { ignore_uppercase: false, ..Default::default() }; assert!(!crate::is_correct_in_with("ZQTERLARANG", crate::ProofLang::Id, &opts)); } + + /// With Ignore words in UPPERCASE or Ignore words that contain numbers off, Indonesian checks + /// those words against the dictionary instead of accepting them. + #[test] + fn proofing_options_reach_the_indonesian_check() { + test_dict::install(); + let lang = crate::ProofLang::Id; + let defaults = crate::Options::default(); + let check_caps = crate::Options { ignore_uppercase: false, ..defaults }; + let check_digits = crate::Options { ignore_numbers: false, ..defaults }; + assert!(crate::is_correct_in_with("RUMHA", lang, &defaults), "short capitals are acronyms by default"); + assert!(!crate::is_correct_in_with("RUMHA", lang, &check_caps)); + assert!(crate::is_correct_in_with("RUMAH", lang, &check_caps)); + assert!(crate::is_correct_in_with("RUMHA-nya", lang, &defaults)); + assert!(!crate::is_correct_in_with("RUMHA-nya", lang, &check_caps), "a part in capitals is checked too"); + assert!(crate::is_correct_in_with("rumha2", lang, &defaults)); + assert!(!crate::is_correct_in_with("rumha2", lang, &check_digits)); + assert!(crate::is_correct_in_with("2026", lang, &check_digits), "numbers alone stay correct"); + assert!(is_correct("RUMHA"), "direct checks keep the defaults"); + } } diff --git a/crates/proof/src/it/mod.rs b/crates/proof/src/it/mod.rs index 7728478436..0e11ccaae2 100644 --- a/crates/proof/src/it/mod.rs +++ b/crates/proof/src/it/mod.rs @@ -111,18 +111,30 @@ pub fn is_correct(word: &str) -> bool { is_correct_with(&dictionary(), word) } +/// [`is_correct`] with the File › Options › Proofing choices: with Ignore words in UPPERCASE off, +/// short words in capitals are checked against the dictionary too, and with Ignore words that +/// contain numbers off, so are words with digits. +pub fn is_correct_with_options(word: &str, opts: &crate::Options) -> bool { + check(&dictionary(), word, opts) +} + /// [`is_correct`] against a given dictionary (an empty one accepts everything). pub fn is_correct_with(d: &AffixDict, word: &str) -> bool { + check(d, word, &crate::Options::default()) +} + +/// [`is_correct_with_options`] against a given dictionary. +fn check(d: &AffixDict, word: &str, opts: &crate::Options) -> bool { if d.is_empty() { return true; // no Italian dictionary installed: no spelling marks } let w = word.trim_matches(|c: char| !c.is_alphanumeric() && c != '\'' && c != '’'); let w = w.trim_matches(['\'', '’']); let n = w.chars().count(); - if n <= 1 || w.chars().any(|c| c.is_ascii_digit()) || !w.chars().any(char::is_alphabetic) { + if n <= 1 || (opts.ignore_numbers && w.chars().any(|c| c.is_ascii_digit())) || !w.chars().any(char::is_alphabetic) { return true; } - if w.chars().all(|c| !c.is_lowercase()) && n <= 6 { + if opts.ignore_uppercase && w.chars().all(|c| !c.is_lowercase()) && n <= 6 { return true; } if !w.chars().any(is_latin_letter) { @@ -229,4 +241,22 @@ mod tests { crate::add_word("Zxqitalianaparola"); assert!(is_correct("zxqitalianaparola")); } + + /// With Ignore words in UPPERCASE or Ignore words that contain numbers off, Italian checks + /// those words against the dictionary instead of accepting them. + #[test] + fn proofing_options_reach_the_italian_check() { + test_dict::install(); + let lang = crate::ProofLang::It; + let defaults = crate::Options::default(); + let check_caps = crate::Options { ignore_uppercase: false, ..defaults }; + let check_digits = crate::Options { ignore_numbers: false, ..defaults }; + assert!(crate::is_correct_in_with("LIBRRO", lang, &defaults), "short capitals are acronyms by default"); + assert!(!crate::is_correct_in_with("LIBRRO", lang, &check_caps)); + assert!(crate::is_correct_in_with("LIBRO", lang, &check_caps)); + assert!(crate::is_correct_in_with("librro2", lang, &defaults)); + assert!(!crate::is_correct_in_with("librro2", lang, &check_digits)); + assert!(crate::is_correct_in_with("2026", lang, &check_digits), "numbers alone stay correct"); + assert!(is_correct("LIBRRO"), "direct checks keep the defaults"); + } } diff --git a/crates/proof/src/lib.rs b/crates/proof/src/lib.rs index 71f96827bd..0ab16529d6 100644 --- a/crates/proof/src/lib.rs +++ b/crates/proof/src/lib.rs @@ -729,14 +729,14 @@ pub fn is_correct_in(word: &str, lang: ProofLang) -> bool { pub fn is_correct_in_with(word: &str, lang: ProofLang, opts: &Options) -> bool { match lang { ProofLang::En | ProofLang::EnGb | ProofLang::EnCa => is_correct_english(word, lang, opts), - ProofLang::Pl => accepted_by_options(word, opts) || pl::is_correct(word), - ProofLang::Hr => accepted_by_options(word, opts) || hr::is_correct(word), - ProofLang::Pt => accepted_by_options(word, opts) || pt::is_correct(word), - ProofLang::It => accepted_by_options(word, opts) || it::is_correct(word), + ProofLang::Pl => accepted_by_options(word, opts) || pl::is_correct_with_options(word, opts), + ProofLang::Hr => accepted_by_options(word, opts) || hr::is_correct_with_options(word, opts), + ProofLang::Pt => accepted_by_options(word, opts) || pt::is_correct_with_options(word, opts), + ProofLang::It => accepted_by_options(word, opts) || it::is_correct_with_options(word, opts), ProofLang::Tr => accepted_by_options(word, opts) || tr::is_correct(word), ProofLang::Ru => accepted_by_options(word, opts) || ru::is_correct(word), - ProofLang::Cs => accepted_by_options(word, opts) || cs::is_correct(word), - ProofLang::Id => accepted_by_options(word, opts) || id::is_correct(word), + ProofLang::Cs => accepted_by_options(word, opts) || cs::is_correct_with_options(word, opts), + ProofLang::Id => accepted_by_options(word, opts) || id::is_correct_with_options(word, opts), ProofLang::Bg | ProofLang::De | ProofLang::Other => true, } } diff --git a/crates/proof/src/pl/mod.rs b/crates/proof/src/pl/mod.rs index 5330dd7224..76fa455ece 100644 --- a/crates/proof/src/pl/mod.rs +++ b/crates/proof/src/pl/mod.rs @@ -96,18 +96,30 @@ pub fn is_correct(word: &str) -> bool { is_correct_with(&dictionary(), word) } +/// [`is_correct`] with the File › Options › Proofing choices: with Ignore words in UPPERCASE off, +/// short words in capitals are checked against the dictionary too, and with Ignore words that +/// contain numbers off, so are words with digits. +pub fn is_correct_with_options(word: &str, opts: &crate::Options) -> bool { + check(&dictionary(), word, opts) +} + /// [`is_correct`] against a given dictionary (an empty one accepts everything). pub fn is_correct_with(d: &AffixDict, word: &str) -> bool { + check(d, word, &crate::Options::default()) +} + +/// [`is_correct_with_options`] against a given dictionary. +fn check(d: &AffixDict, word: &str, opts: &crate::Options) -> bool { if d.is_empty() { return true; // no Polish dictionary installed: no spelling marks } let w = word.trim_matches(|c: char| !c.is_alphanumeric() && c != '\'' && c != '’'); let w = w.trim_matches(['\'', '’']); let n = w.chars().count(); - if n <= 1 || w.chars().any(|c| c.is_ascii_digit()) || !w.chars().any(char::is_alphabetic) { + if n <= 1 || (opts.ignore_numbers && w.chars().any(|c| c.is_ascii_digit())) || !w.chars().any(char::is_alphabetic) { return true; } - if w.chars().all(|c| !c.is_lowercase()) && n <= 6 { + if opts.ignore_uppercase && w.chars().all(|c| !c.is_lowercase()) && n <= 6 { return true; } if !w.chars().any(is_latin_letter) { @@ -209,4 +221,22 @@ mod tests { crate::add_word("Zxqpolskisłowo"); assert!(is_correct("zxqpolskisłowo")); } + + /// With Ignore words in UPPERCASE or Ignore words that contain numbers off, Polish checks + /// those words against the dictionary instead of accepting them. + #[test] + fn proofing_options_reach_the_polish_check() { + test_dict::install(); + let lang = crate::ProofLang::Pl; + let defaults = crate::Options::default(); + let check_caps = crate::Options { ignore_uppercase: false, ..defaults }; + let check_digits = crate::Options { ignore_numbers: false, ..defaults }; + assert!(crate::is_correct_in_with("ŻEKA", lang, &defaults), "short capitals are acronyms by default"); + assert!(!crate::is_correct_in_with("ŻEKA", lang, &check_caps)); + assert!(crate::is_correct_in_with("RZEKA", lang, &check_caps)); + assert!(crate::is_correct_in_with("żeka2", lang, &defaults)); + assert!(!crate::is_correct_in_with("żeka2", lang, &check_digits)); + assert!(crate::is_correct_in_with("2026", lang, &check_digits), "numbers alone stay correct"); + assert!(is_correct("ŻEKA"), "direct checks keep the defaults"); + } } diff --git a/crates/proof/src/pt/mod.rs b/crates/proof/src/pt/mod.rs index e0f62e52bb..3a8b059fcb 100644 --- a/crates/proof/src/pt/mod.rs +++ b/crates/proof/src/pt/mod.rs @@ -91,18 +91,30 @@ pub fn is_correct(word: &str) -> bool { is_correct_with(&dictionary(), word) } +/// [`is_correct`] with the File › Options › Proofing choices: with Ignore words in UPPERCASE off, +/// short words in capitals are checked against the dictionary too, and with Ignore words that +/// contain numbers off, so are words with digits. +pub fn is_correct_with_options(word: &str, opts: &crate::Options) -> bool { + check(&dictionary(), word, opts) +} + /// [`is_correct`] against a given dictionary (an empty one accepts everything). pub fn is_correct_with(d: &AffixDict, word: &str) -> bool { + check(d, word, &crate::Options::default()) +} + +/// [`is_correct_with_options`] against a given dictionary. +fn check(d: &AffixDict, word: &str, opts: &crate::Options) -> bool { if d.is_empty() { return true; // no Portuguese dictionary installed: no spelling marks } let w = word.trim_matches(|c: char| !c.is_alphanumeric() && c != '\'' && c != '’'); let w = w.trim_matches(['\'', '’']); let n = w.chars().count(); - if n <= 1 || w.chars().any(|c| c.is_ascii_digit()) || !w.chars().any(char::is_alphabetic) { + if n <= 1 || (opts.ignore_numbers && w.chars().any(|c| c.is_ascii_digit())) || !w.chars().any(char::is_alphabetic) { return true; } - if w.chars().all(|c| !c.is_lowercase()) && n <= 6 { + if opts.ignore_uppercase && w.chars().all(|c| !c.is_lowercase()) && n <= 6 { return true; } if !w.chars().any(is_latin_letter) { @@ -217,4 +229,22 @@ mod tests { crate::add_word("Unigranrio-RJ"); assert!(crate::check_spelling_in(text, crate::ProofLang::Pt).is_empty()); } + + /// With Ignore words in UPPERCASE or Ignore words that contain numbers off, Portuguese checks + /// those words against the dictionary instead of accepting them. + #[test] + fn proofing_options_reach_the_portuguese_check() { + test_dict::install(); + let lang = crate::ProofLang::Pt; + let defaults = crate::Options::default(); + let check_caps = crate::Options { ignore_uppercase: false, ..defaults }; + let check_digits = crate::Options { ignore_numbers: false, ..defaults }; + assert!(crate::is_correct_in_with("PRAYA", lang, &defaults), "short capitals are acronyms by default"); + assert!(!crate::is_correct_in_with("PRAYA", lang, &check_caps)); + assert!(crate::is_correct_in_with("PRAIA", lang, &check_caps)); + assert!(crate::is_correct_in_with("praya2", lang, &defaults)); + assert!(!crate::is_correct_in_with("praya2", lang, &check_digits)); + assert!(crate::is_correct_in_with("2026", lang, &check_digits), "numbers alone stay correct"); + assert!(is_correct("PRAYA"), "direct checks keep the defaults"); + } }