From 57943c142e7277968316369b86abcee5b9f38d30 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 (storytold/wordcraft#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 c2ec00c9..7a910c72 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 23df9e93..1c89b4fa 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 5b9be698..09771e85 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 77284784..0e11ccaa 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 71f96827..0ab16529 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 5330dd72..76fa455e 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 e0f62e52..3a8b059f 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"); + } }