mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-12 21:42:13 +00:00
CP4.2: review — German and Bulgarian are spelled from their pack; pack languages are checked in any script
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FypgCX8LsYYE8fsj4byBm2
This commit is contained in:
1 parent
afbdede88e
commit
2495fad496
4 files changed
+62
-12
No files matched your search
@@ -193,18 +193,31 @@ impl ProofLang {
|
||||
}
|
||||
}
|
||||
|
||||
/// The locale whose Hunspell dictionary (dictionaries folder, Language pack, system) spells
|
||||
/// this language through the shared affix engine: a [`ProofLang::Pack`] language's own, and
|
||||
/// German's and Bulgarian's, which have hyphenation (and Bulgarian rules) of their own but no
|
||||
/// dictionary slot. `None` for the languages with a slot of their own, English and `Other`.
|
||||
pub(crate) fn spelling_locale(self) -> Option<PackLocale> {
|
||||
match self {
|
||||
ProofLang::Pack(l) => Some(l),
|
||||
ProofLang::De => PackLocale::new("de-DE"),
|
||||
ProofLang::Bg => PackLocale::new("bg-BG"),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// The locale of a [`ProofLang::Pack`] language.
|
||||
pub fn pack_locale(self) -> Option<PackLocale> {
|
||||
if let ProofLang::Pack(l) = self { Some(l) } else { None }
|
||||
}
|
||||
|
||||
/// Does WordCraft check spelling in this language? For a [`ProofLang::Pack`] language: is a
|
||||
/// dictionary for it available now (read on first use, cached until the dictionaries folder or
|
||||
/// Does WordCraft check spelling in this language? For a [`ProofLang::Pack`] language, German
|
||||
/// and Bulgarian: is a dictionary for it available now (read on first use, cached until the dictionaries folder or
|
||||
/// the packs change)?
|
||||
pub fn is_proofed(self) -> bool {
|
||||
match self {
|
||||
ProofLang::Pack(l) => crate::pack_lang::has_dictionary(l),
|
||||
_ => Self::PROOFED.contains(&self),
|
||||
match self.spelling_locale() {
|
||||
Some(l) => crate::pack_lang::has_dictionary(l),
|
||||
None => Self::PROOFED.contains(&self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+36
-5
@@ -322,11 +322,11 @@ pub(crate) fn reload_dictionaries() {
|
||||
DICTIONARY_GENERATION.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// The `.dic` file a [`ProofLang::Pack`] language's spelling is read from now (the dictionaries
|
||||
/// The `.dic` file a [`ProofLang::Pack`] language's (or German's or Bulgarian's) spelling is read from now (the dictionaries
|
||||
/// folder, an installed Language pack or the system's); `None` when it has none (unchecked) or
|
||||
/// `lang` isn't a pack language. Reads the dictionary once (it is cached afterwards).
|
||||
pub fn pack_dictionary_file(lang: ProofLang) -> Option<std::path::PathBuf> {
|
||||
pack_lang::dictionary_file(lang.pack_locale()?)
|
||||
pack_lang::dictionary_file(lang.spelling_locale()?)
|
||||
}
|
||||
|
||||
/// Each language that reads an installed Hunspell dictionary, with the `.dic` file found for it
|
||||
@@ -343,7 +343,7 @@ pub fn dictionary_load_error(lang: ProofLang) -> Option<String> {
|
||||
if matches!(lang, ProofLang::En | ProofLang::EnGb | ProofLang::EnCa) {
|
||||
return english::load_error(lang);
|
||||
}
|
||||
if let ProofLang::Pack(l) = lang {
|
||||
if let Some(l) = lang.spelling_locale() {
|
||||
return pack_lang::load_error(l);
|
||||
}
|
||||
hunspell::load_error(language(lang)?.slot.name()).map(|(_, reason)| reason)
|
||||
@@ -842,7 +842,7 @@ pub fn is_correct_in_with(word: &str, lang: ProofLang, opts: &Options) -> bool {
|
||||
if lang.is_english() {
|
||||
return is_correct_english(word, lang, opts);
|
||||
}
|
||||
if let ProofLang::Pack(l) = lang {
|
||||
if let Some(l) = lang.spelling_locale() {
|
||||
return accepted_by_options(word, opts) || pack_lang::is_correct(word, l, opts);
|
||||
}
|
||||
// A language WordCraft doesn't proof (Bulgarian, German, any other) has no spelling marks.
|
||||
@@ -856,7 +856,7 @@ pub fn suggest_in(word: &str, max: usize, lang: ProofLang) -> Vec<String> {
|
||||
if lang.is_english() {
|
||||
return suggest_english(word, max, lang);
|
||||
}
|
||||
if let ProofLang::Pack(l) = lang {
|
||||
if let Some(l) = lang.spelling_locale() {
|
||||
return pack_lang::suggest(word, max, l);
|
||||
}
|
||||
language(lang).map_or_else(Vec::new, |l| (l.suggest)(word, max, lang))
|
||||
@@ -1496,6 +1496,37 @@ mod tests {
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
/// Review of #485: German and Bulgarian, which have hyphenation of their own but no
|
||||
/// dictionary slot, are spelled from their Language pack (or a Hunspell dictionary) too.
|
||||
#[test]
|
||||
fn german_and_bulgarian_are_spelled_from_their_packs() {
|
||||
let _guard = DICTIONARY_TESTS.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
set_dictionaries_folder(Some(shared_test_folder()));
|
||||
let root = std::env::temp_dir().join(format!("wc-proof-de-bg-packs-{}", std::process::id()));
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
let hyphen_before = hyphen::hyphenate_word_in("Silbentrennung", &hyphen::Limits::default(), ProofLang::De);
|
||||
write_pack(&root, "de", &[("spelling.aff", b"SET UTF-8\n"), ("spelling.dic", "2\nHaus\nund\n".as_bytes())]);
|
||||
write_pack(&root, "bg", &[("spelling.aff", b"SET UTF-8\n"), ("spelling.dic", "1\nкъща\n".as_bytes())]);
|
||||
packs::set_packs_dir(Some(root.clone()));
|
||||
let de = ProofLang::from_tag(Some("de-AT"));
|
||||
assert_eq!(de, ProofLang::De);
|
||||
assert!(is_correct_in("Haus", de) && !is_correct_in("Hauz", de));
|
||||
assert!(suggest_in("Hauz", 5, de).contains(&"Haus".to_string()));
|
||||
assert_eq!(check_text("Haus und Hauz", &[(0..13, de)]).len(), 1);
|
||||
assert!(de.is_proofed() && has_dictionary(Some("de-CH")));
|
||||
assert_eq!(pack_dictionary_file(de), Some(root.join("de").join("spelling.dic")));
|
||||
assert!(is_correct_in("къща", ProofLang::Bg) && !is_correct_in("къшта", ProofLang::Bg), "any script, not only Latin");
|
||||
assert_eq!(
|
||||
hyphen::hyphenate_word_in("Silbentrennung", &hyphen::Limits::default(), ProofLang::De),
|
||||
hyphen_before,
|
||||
"German keeps its own hyphenation"
|
||||
);
|
||||
packs::set_packs_dir(None);
|
||||
assert!(is_correct_in("Hauz", de) || pack_dictionary_file(de).is_some(), "no dictionary: unchecked");
|
||||
set_dictionaries_folder(Some(shared_test_folder()));
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
/// At most [`pack_lang::MAX_LOADED`] pack-language dictionaries stay loaded; others load again
|
||||
/// when used.
|
||||
#[test]
|
||||
|
||||
@@ -144,7 +144,7 @@ pub(crate) fn is_correct(word: &str, locale: PackLocale, opts: &crate::Options)
|
||||
if d.is_empty() {
|
||||
return true;
|
||||
}
|
||||
let Some(w) = crate::spelling::word_to_check(&d, word, opts) else { return true };
|
||||
let Some(w) = crate::spelling::word_to_check_in(&d, word, opts) else { return true };
|
||||
let known = |w: &str| d.check(w) || crate::in_user_dictionary(&w.to_lowercase());
|
||||
if w.contains('-') && !known(w) {
|
||||
return w.split('-').all(|p| p.chars().count() <= 1 || known(p));
|
||||
|
||||
@@ -15,6 +15,12 @@ fn latin(c: char) -> bool {
|
||||
/// letter or has no letters, it has digits or is a short word in capitals (an acronym) and the
|
||||
/// options skip those, or it is in another script.
|
||||
pub(crate) fn word_to_check<'a>(d: &AffixDict, word: &'a str, opts: &Options) -> Option<&'a str> {
|
||||
word_to_check_in(d, word, opts).filter(|w| w.chars().any(latin))
|
||||
}
|
||||
|
||||
/// [`word_to_check`] in any script: the start of a Language pack language's check, whose
|
||||
/// dictionary may be Cyrillic, Greek or any other script.
|
||||
pub(crate) fn word_to_check_in<'a>(d: &AffixDict, word: &'a str, opts: &Options) -> Option<&'a str> {
|
||||
if d.is_empty() {
|
||||
return None;
|
||||
}
|
||||
@@ -27,7 +33,7 @@ pub(crate) fn word_to_check<'a>(d: &AffixDict, word: &'a str, opts: &Options) ->
|
||||
if opts.ignore_uppercase && w.chars().all(|c| !c.is_lowercase()) && n <= 6 {
|
||||
return None;
|
||||
}
|
||||
w.chars().any(latin).then_some(w)
|
||||
Some(w)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
Reference in new issue
Block a user