mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-08 19:42:12 +00:00
CP4.2: review — English never takes another dialect's dictionary or pack: each English reads its own names in the dictionaries folder, an exactly named pack and the system, else the built-in Moby list; the British-spelling test runs unconditionally again
hunspell::candidates_exact (folder → exact-locale pack → system, own names only) for English without SCOWL; packs::exact_spelling_dirs. Other languages keep the region-ordered cross-region tiers. Drops has_british_source(). 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
d905406fe3
commit
ba11ccd855
4 files changed
+53
-34
No files matched your search
@@ -191,13 +191,17 @@ fn key(lang: ProofLang) -> &'static str {
|
||||
names(lang).first().copied().unwrap_or("en_US")
|
||||
}
|
||||
|
||||
/// Read the first Hunspell pair of `lang`'s own that loads, trying each name in each folder
|
||||
/// ([`crate::hunspell::candidates`]; with SCOWL embedded, the dictionaries folder only:
|
||||
/// Read the first Hunspell pair of `lang`'s own that loads, trying each name in each folder and
|
||||
/// an installed pack named exactly for it ([`crate::hunspell::candidates_exact`]: never another
|
||||
/// dialect's file or pack; with SCOWL embedded, the dictionaries folder only:
|
||||
/// [`crate::hunspell::user_candidates`]), with the first failure before it (the file `lang`
|
||||
/// would rather use), for [`note_error`].
|
||||
fn load(lang: ProofLang) -> (Loaded, Option<(PathBuf, String)>) {
|
||||
let candidates =
|
||||
if crate::scowl::dict(lang).is_some() { crate::hunspell::user_candidates(names(lang)) } else { crate::hunspell::candidates(names(lang)) };
|
||||
let candidates = if crate::scowl::dict(lang).is_some() {
|
||||
crate::hunspell::user_candidates(names(lang))
|
||||
} else {
|
||||
crate::hunspell::candidates_exact(names(lang))
|
||||
};
|
||||
if candidates.is_empty() {
|
||||
log::info!("English spelling: no Hunspell {} dictionary to read first; using SCOWL or the built-in word list", key(lang));
|
||||
return (None, None);
|
||||
|
||||
@@ -199,6 +199,15 @@ fn ordered(names: &[&str], dirs: &[PathBuf], region_packs: &[PathBuf], system: &
|
||||
out
|
||||
}
|
||||
|
||||
/// [`candidates`] without another region's or dialect's dictionary: the dictionaries folders,
|
||||
/// then an installed pack named exactly by one of `names` (`en_GB` → `en-gb`), then the system's
|
||||
/// folders, `names` only. English uses it: en-GB never falls back to an `en_US` file or pack.
|
||||
pub fn candidates_exact(names: &[&str]) -> Vec<(PathBuf, PathBuf)> {
|
||||
let lo = names.first().copied().map(lo_ext_dirs);
|
||||
let (dirs, system) = search_tiers(lo.as_deref());
|
||||
ordered(names, &dirs, &crate::packs::exact_spelling_dirs(names), &system, &[])
|
||||
}
|
||||
|
||||
/// [`candidates`] in the folders the user keeps dictionaries in for WordCraft ([`user_dirs`])
|
||||
/// only, not the system locations.
|
||||
pub fn user_candidates(names: &[&str]) -> Vec<(PathBuf, PathBuf)> {
|
||||
|
||||
+17
-30
@@ -1321,6 +1321,10 @@ mod tests {
|
||||
/// installed English Language pack, then the system's Hunspell, then the built-in list.
|
||||
#[test]
|
||||
fn an_english_pack_comes_after_the_dictionaries_folder() {
|
||||
// With SCOWL embedded, English reads only the dictionaries folder before it.
|
||||
if scowl_embedded() {
|
||||
return;
|
||||
}
|
||||
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-en-pack-{}", std::process::id()));
|
||||
@@ -1328,24 +1332,24 @@ mod tests {
|
||||
let (dir, packs) = (root.join("dictionaries"), root.join("language-packs"));
|
||||
let aff: &[u8] = b"SET UTF-8\nSFX S Y 1\nSFX S 0 s .\n";
|
||||
let pack = write_pack(&packs, "en-us", &[("spelling.aff", aff), ("spelling.dic", b"2\nzzpackword\nhouse/S\n")]);
|
||||
let gb_pack = write_pack(&packs, "en-gb", &[("spelling.aff", aff), ("spelling.dic", b"2\nzzgbword\nhouse/S\n")]);
|
||||
packs::set_packs_dir(Some(packs.clone()));
|
||||
let english: &[(&str, &[u8], &[u8])] = &[("en_US", aff, b"2\nzzfolderword\nhouse/S\n")];
|
||||
folder_with_english(&dir, english);
|
||||
if english_data() {
|
||||
assert_eq!(english_source(ProofLang::En), Some(EnglishSource::Embedded));
|
||||
} else {
|
||||
assert_eq!(english_source(ProofLang::En), Some(EnglishSource::Hunspell(dir.join("en_US.dic"))), "the folder first");
|
||||
assert!(is_correct_in("zzfolderword", ProofLang::En) && !is_correct_in("zzpackword", ProofLang::En));
|
||||
}
|
||||
assert_eq!(english_source(ProofLang::En), Some(EnglishSource::Hunspell(dir.join("en_US.dic"))), "the folder first");
|
||||
assert!(is_correct_in("zzfolderword", ProofLang::En) && !is_correct_in("zzpackword", ProofLang::En));
|
||||
// Each English its own: the folder's en_US never stands in for en-GB, which takes its pack.
|
||||
assert_eq!(english_source(ProofLang::EnGb), Some(EnglishSource::Hunspell(gb_pack.join("spelling.dic"))));
|
||||
drop_english_files(&dir, english);
|
||||
if !english_data() {
|
||||
assert_eq!(english_source(ProofLang::En), Some(EnglishSource::Hunspell(pack.join("spelling.dic"))), "then the pack");
|
||||
assert!(is_correct_in("zzpackword", ProofLang::En) && is_correct_in("houses", ProofLang::En));
|
||||
}
|
||||
assert_eq!(english_source(ProofLang::En), Some(EnglishSource::Hunspell(pack.join("spelling.dic"))), "then the pack");
|
||||
assert!(is_correct_in("zzpackword", ProofLang::En) && is_correct_in("houses", ProofLang::En));
|
||||
// Neither English takes the other dialect's pack: without its own, en-CA has none.
|
||||
std::fs::remove_dir_all(&gb_pack).unwrap();
|
||||
packs::reload();
|
||||
assert_ne!(english_source(ProofLang::EnGb), Some(EnglishSource::Hunspell(pack.join("spelling.dic"))), "never en-us for en-GB");
|
||||
assert_ne!(english_source(ProofLang::EnCa), Some(EnglishSource::Hunspell(pack.join("spelling.dic"))), "never en-us for en-CA");
|
||||
packs::set_packs_dir(None);
|
||||
if !english_data() {
|
||||
assert_ne!(english_source(ProofLang::En), Some(EnglishSource::Hunspell(pack.join("spelling.dic"))), "a removed pack is not used");
|
||||
}
|
||||
assert_ne!(english_source(ProofLang::En), Some(EnglishSource::Hunspell(pack.join("spelling.dic"))), "a removed pack is not used");
|
||||
set_dictionaries_folder(Some(shared_test_folder()));
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
@@ -2253,20 +2257,6 @@ mod tests {
|
||||
assert_eq!(suggest_in("programmes", 5, us).first().map(String::as_str), Some("programs"));
|
||||
}
|
||||
|
||||
/// Does en-GB read a list with the British spellings: the Moby list (embedded or built in) or a
|
||||
/// British Hunspell dictionary? Without the Language data, a machine with only a US Hunspell
|
||||
/// dictionary (GitHub's Ubuntu runners) spells en-GB with it, by design, and marks `goitre`.
|
||||
fn has_british_source() -> bool {
|
||||
match english_source(ProofLang::EnGb) {
|
||||
Some(EnglishSource::Embedded | EnglishSource::CoreList) => true,
|
||||
Some(EnglishSource::Hunspell(dic)) => {
|
||||
let stem = dic.file_stem().map(|s| s.to_string_lossy().to_string()).unwrap_or_default();
|
||||
!["en_US", "en-US", "en_CA", "en-CA"].contains(&stem.as_str())
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// More British spellings that must stay right in en-GB and en-AU (#1281): -re, single l, ae,
|
||||
/// with their inflections.
|
||||
#[test]
|
||||
@@ -2275,9 +2265,6 @@ mod tests {
|
||||
let _guard = crate::DICTIONARY_TESTS.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
let gb = ProofLang::EnGb;
|
||||
assert_eq!(ProofLang::from_tag(Some("en-AU")), gb);
|
||||
if !has_british_source() {
|
||||
return;
|
||||
}
|
||||
for w in [
|
||||
"goitre",
|
||||
"goitres",
|
||||
|
||||
@@ -468,6 +468,25 @@ pub(crate) fn spelling_dirs(names: &[&str]) -> (Vec<PathBuf>, Vec<PathBuf>) {
|
||||
all.into_iter().partition(in_region)
|
||||
}
|
||||
|
||||
/// The folders of the installed packs with a spelling dictionary whose locale is exactly one of
|
||||
/// `names` (`en_GB` → `en-gb`), in that order: no other region, no language alone.
|
||||
pub(crate) fn exact_spelling_dirs(names: &[&str]) -> Vec<PathBuf> {
|
||||
if read().dir.is_none() {
|
||||
return Vec::new();
|
||||
}
|
||||
let scan = scan();
|
||||
let mut out: Vec<PathBuf> = Vec::new();
|
||||
for n in names {
|
||||
let l = n.to_ascii_lowercase().replace('_', "-");
|
||||
if let Some((p, _)) = scan.packs.iter().find(|(p, h)| h.spelling && p.locale == l)
|
||||
&& !out.contains(&p.path)
|
||||
{
|
||||
out.push(p.path.clone());
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// The pack locales of `names`' own region: the first name's (`pt_BR` → `pt-br`) and the language
|
||||
/// alone (`pt`) when it is written in the same script.
|
||||
pub(crate) fn region_locales(names: &[&str]) -> Vec<String> {
|
||||
|
||||
Reference in new issue
Block a user