diff --git a/crates/proof/README.md b/crates/proof/README.md index b1672faa..5d38164a 100644 --- a/crates/proof/README.md +++ b/crates/proof/README.md @@ -74,8 +74,8 @@ English spelling uses the first of these that is there; `english_source(lang)` s dictionary knows it (or `en-extra.txt` does), never because another English's does (`colourblind` is wrong in en-US, `colorblind` in en-GB); suggestions come from the same dictionary. SCOWL's size-60 lists keep one spelling per dialect, and Word accepts more, so - British English also takes the `-ize` spellings of its `-ise` words (`organize`, - `realization`), and `en-variants.txt` flags the other dialect's spellings and accepts this + British English also takes the `-ize` spellings of its `-ise` words that American or Canadian + English spell so (`organize`, `realization`; never `wize` or `advize`), and `en-variants.txt` flags the other dialect's spellings and accepts this dialect's other spellings of known words (`judgement` in en-US). English with no dialect (`is_correct`) takes a word any of the three knows. Suggestions offer a missing space only between two lower-case words of three letters or more, after every one-word candidate, and no diff --git a/crates/proof/src/english.rs b/crates/proof/src/english.rs index 17a9c0c0..a18f060f 100644 --- a/crates/proof/src/english.rs +++ b/crates/proof/src/english.rs @@ -290,7 +290,7 @@ fn generic(s: Speller) -> Speller { match s { Speller::Scowl(..) => { let all: Vec<_> = [ProofLang::En, ProofLang::EnGb, ProofLang::EnCa].into_iter().filter_map(crate::scowl::dict).collect(); - Speller::Scowl(all, true) + Speller::Scowl(all, false) } other => other, } @@ -301,7 +301,12 @@ impl Speller { fn knows(&self, w: &str) -> bool { match self { Speller::Hunspell(d, _) => affix_correct(&[d.as_ref()], w, false), - Speller::Scowl(ds, ise) => affix_correct(ds, w, true) || (*ise && ise_spelling(w).is_some_and(|x| affix_correct(ds, &x, true))), + Speller::Scowl(ds, ise) => { + affix_correct(ds, w, true) + || (*ise + && ise_spelling(w).is_some_and(|x| affix_correct(ds, &x, true)) + && ize_elsewhere(w, |d, p| affix_correct(&[d], p, false))) + } Speller::List => list_correct(w), } } @@ -310,7 +315,9 @@ impl Speller { fn knows_lower(&self, p: &str) -> bool { match self { Speller::Hunspell(d, _) => d.check(p), - Speller::Scowl(ds, ise) => ds.iter().any(|d| d.check(p) || (*ise && ise_spelling(p).is_some_and(|x| d.check(&x)))), + Speller::Scowl(ds, ise) => { + ds.iter().any(|d| d.check(p) || (*ise && ise_spelling(p).is_some_and(|x| d.check(&x)) && ize_elsewhere(p, |d, p| d.check(p)))) + } Speller::List => crate::known_in_word_list(p), } } @@ -339,7 +346,8 @@ pub(crate) fn is_correct(word: &str, lang: ProofLang, opts: &Options, policy: bo /// `w` with its `-ize`/`-yze` endings (and their forms: `-ization`, `-izing`…) spelled `-ise`/ /// `-yse`, when it has any: British English takes both (Word accepts `organize` and `organise` in /// en-GB), and SCOWL's en-GB dictionary has only `-ise`. A `z` between `i`/`y` and a vowel is -/// swapped (`citizen` → `citisen`, which no dictionary knows, so that does no harm). +/// swapped; the `-ize` form counts only when American or Canadian English spells the word so +/// ([`ize_elsewhere`]), or `wize` would pass as `wise`. pub(crate) fn ise_spelling(w: &str) -> Option { let b = w.as_bytes(); let mut out = b.to_vec(); @@ -361,6 +369,12 @@ pub(crate) fn ise_spelling(w: &str) -> Option { if changed { String::from_utf8(out).ok() } else { None } } +/// Is `w` a word in American or Canadian English's SCOWL dictionary (`check` asks one): the +/// `-ize` spellings British English takes are theirs (`organize`, never `advize` or `wize`). +fn ize_elsewhere(w: &str, check: impl Fn(&AffixDict, &str) -> bool) -> bool { + [ProofLang::En, ProofLang::EnCa].into_iter().filter_map(crate::scowl::dict).any(|d| check(d, w)) +} + /// Against affix dictionaries (any of them): the word, or each part of a hyphenated one; with /// `supplement`, our supplement's words count too. fn affix_correct(ds: &[&AffixDict], w: &str, supplement: bool) -> bool { diff --git a/crates/proof/src/lib.rs b/crates/proof/src/lib.rs index 421c3db5..ff384792 100644 --- a/crates/proof/src/lib.rs +++ b/crates/proof/src/lib.rs @@ -1781,6 +1781,26 @@ mod tests { } } + /// craft-libs#483 re-review: British English takes the `-ize` spelling of an `-ise` word only + /// when American or Canadian English spells it so; a misspelling whose `-s-` form is a word + /// (`wize`, `advize`) stays wrong, as in Hunspell's en-GB. + #[cfg_attr(not(craft_proof_data), ignore = "needs the Language data (CRAFT_I18N_DIR)")] + #[test] + fn british_ize_needs_a_real_ize_word() { + let wrong = [ + "wize", "rize", "noize", "dizease", "advize", "rizing", "praize", "raize", "poize", "wizer", "surprize", "arize", "revize", "devize", + "mizer", "concize", "precize", "exercize", "chaize", + ]; + for l in [ProofLang::EnGb, ProofLang::from_tag(Some("en-AU"))] { + for w in wrong.iter().chain(&["colorize"]) { + assert!(!is_correct_in(w, l), "{w} in {l:?}"); + } + for w in ["organize", "realization", "colourize", "organizing", "realizes"] { + assert!(is_correct_in(w, l), "{w} in {l:?}"); + } + } + } + #[cfg_attr(not(craft_proof_data), ignore = "needs the Language data (CRAFT_I18N_DIR)")] #[test] fn user_dictionary_accepts_the_other_dialects_spelling() { diff --git a/crates/proof/src/scowl.rs b/crates/proof/src/scowl.rs index 8d79c1d1..9a71188f 100644 --- a/crates/proof/src/scowl.rs +++ b/crates/proof/src/scowl.rs @@ -7,9 +7,10 @@ //! come from its own dictionary, and a word is right when its own dictionary knows it: the other //! dialects' dictionaries are never asked (en-US would take `colourblind` from en-GB's). SCOWL //! picks one spelling per dialect (en-GB `-ise` has no `organize`, en-US no `judgement`), and Word -//! accepts more, so British English also takes the `-ize` spellings of its `-ise` words, and -//! `en-variants.txt` widens each dialect by its pairs (`crate::english`). An English dictionary in the dictionaries folder comes first; a SCOWL dictionary that isn't -//! embedded, or fails to load, leaves that English to the next source (`crate::english`). +//! accepts more, so British English also takes the `-ize` spellings of its `-ise` words that +//! en-US or en-CA spells so, and `en-variants.txt` widens each dialect by its pairs +//! (`crate::english`). An English dictionary in the dictionaries folder comes first; a SCOWL +//! dictionary that isn't embedded, or fails to load, leaves that English to the next source (`crate::english`). //! //! SCOWL is under its own permissive licence and the affix files under Ispell's BSD licence; //! [`notice`] is the upstream text, for an About window.