Harper 的英式英语方言校验机制从 yogurt_british_clean 测试样例到方言感知拼写管线【免费下载链接】harperOffline, privacy-first grammar checker. Fast, open-source, Rust-powered项目地址: https://gitcode.com/GitHub_Trending/har/harperHarper 是一个离线、隐私优先、用 Rust 编写的语法检查器其核心能力之一是方言感知dialect-aware的拼写与语法校验同一篇文档在美式英语与英式英语配置下应得到不同的判定结果。本文以测试样例 yogurt_british_clean.md 为主体完整解析这个英式英语零误报回归测试的验收标准、测试执行管线、Dialect方言模型与词典中方言标记的实现原理。读完本文你将理解 Harper 如何用测试样例固化方言行为并能追溯从词典元数据到SpellCheckLinter 的完整判定链路。一、测试样例本体一篇必须零报错的英式英语文章yogurt_british_clean.md 是一篇关于酸奶yoghurt历史、用途与营养的英文短文。它的第一行就明确了自身的验收标准The following essay should produce no errors when Harper is set to British English.也就是说当 Harper 的方言配置为英式英语Dialect::British时整篇文档必须产生 0 条 lint。文章刻意使用了大量英式拼写来覆盖方言词表例如标题与全文核心词Yoghurt/yoghurt英式若换成美式yogurt本文的验证意图就不存在了形容词flavour、savoury对应美式的flavor、savory动名词fuelling对应美式fueling。这些词正是英美拼写差异最典型的样本。值得注意的一点是第一行说明文字本身也是 Markdown 文档的一部分同样会被解析并参与 lint——因此整份文件含说明行都必须通过英式英语检查这对样例的自洽性提出了隐含要求。该样例的测试注册位于 run_tests.rscreate_test!(yogurt_british_clean.md, 0, Dialect::British);三个参数分别指定文件名、期望的 lint 数量0、方言Dialect::British。在 run_tests.rs 中可以看到Dialect::British还被用于issue_1581.md期望 0、issue_2054.md期望 6、issue_2054_clean.md期望 0、issue_1873.md期望 0、issue_2151.md期望 1等用例说明clean零报错与dirtyN 条报错两类样例共同构成了方言行为的回归测试矩阵本文主角属于 clean 类用于防止英式拼写被误判为拼写错误false positive。二、测试执行管线宏展开后的每一步做了什么create_test!宏定义在 run_tests.rs展开后生成名为lints_yogurt_british_clean_correctly的#[test]函数其内部流程如下let source include_str!(./test_sources/yogurt_british_clean.md); let dict FstDictionary::curated(); let document Document::new_markdown_default(source, dict); let mut linter LintGroup::new_curated(dict, Dialect::British); let lints linter.lint(document); assert_eq!(lints.len(), 0); // 核心断言零 lint // 额外健壮性断言每个 token 的 span 必须映射到真实字符 for token in document.tokens() { assert!(token.span.try_get_content(document.get_source()).is_some()); }关键步骤拆解FstDictionary::curated()fst_dictionary.rs加载 Harper 的精选词典底层是编译期嵌入的 dictionary.dict 词表构建的 FST有限状态转换器词典Document::new_markdown_default用默认 Markdown 解析器把源码切分为 token 序列词、标点、空格等词 token 会携带来自词典的DictWordMetadata词性、方言标记等LintGroup::new_curated(dict, Dialect::British)lint_group/mod.rs装配整套精选 Linter 集合并把Dialect::British注入给所有方言敏感的 Linter断言 lint 数为 0同时校验 token span 与源码偏移一致保证任何 Linter 生成的区间都不会越界或指向不存在的字符。因此这个样例的价值在于它以一篇真实语感完整的文章把英式拼写不应被标记这一行为固化为可重复执行的回归断言。任何人改动词典、拼写 Linter 或方言逻辑后cargo test -p harper-core都会立即暴露英式英语行为是否被破坏。三、Dialect 方言模型比特位编码与判定语义方言枚举定义在 dict_word_metadata.rspub enum Dialect { American 1 0, Canadian 1 1, Australian 1 2, British 1 3, Indian 1 4, }每个方言占用一个独立的比特位以便与DialectFlags位标记bitflags无缝组合DialectFlags 定义。由此支撑三个核心机制3.1 词级的方言可用性判定DialectFlags提供两个语义不同的查询方法is_dialect_enabledL1176-L1178self.is_empty() || self.intersects(...)——若某词没有显式标注任何方言标志为空则视为在所有方言中都可用只有显式标注了方言集合的词才受限制。is_dialect_enabled_strictL1185-L1187只有显式包含该方言才返回true。这一空 通用词的设计意味着绝大多数中性英语词如yoghurt的通用变体、milk等无需标注即可通行全部方言词典只需为真正存在地区差异的词打上标记。3.2 方言缩写与 BCP-47 映射Dialect::try_from_abbr支持US/CA/AU/GB/IN缩写并可直接解析 BCP-47 标签en-GB→Dialect::British为对外接口如语言服务器、插件端配置提供标准入口。3.3 从文档自动推断方言DialectFlags::get_most_used_dialects_from_documentL1210-L1245会遍历文档中每个带元数据的词按方言计数并取最大值再由Dialect::try_guess_from_document收敛为单一方言平票时返回None。单测 guess_british_dialect 验证了 Aluminium was used. 会被推断为英式而 Aluminum was used. 推断为美式——与本文样例中Yoghurt的方言信号属于同一机制。四、词典如何编码方言以 yoghurt 词族为例harper-core/dictionary.dict 是 Harper 的策展词表首行54800为词条总数每行格式为词条/元数据串。酸奶一词族的三个变体分别位于53774: yoghurt/NwgS!_₹ 53777: yogourt/NwgS 53778: yogurt/~NwSg元数据串中的字母N名词、w可小写、g… 等编码词性与形态属性串尾的特殊符号则是方言标记。从源码结构看可以确认以下事实yoghurt英式带!_₹组合标记yogurt美式带~标记——两者互不兼容各自只在对应方言中生效yogourt加拿大式带标记这被单元测试直接印证canadian_words_in_australian_dialect断言 Does your mom like yogourt? 在Dialect::Australian下产生 1 条 lint即加拿大拼写在澳式英语下被视为拼写错误同样的模式贯穿形容词族flavour/NwSgVdGz!_₹与flavor/~NwgSVdGzdictionary.dict以及savoury/savorL43950-L43955、fuelled/fuellingL26217-L26218。本文样例中的flavour、savoury、fuelling、yoghurt之所以能在英式配置下通过检查正是因为它们在词典中的方言标志包含了英式位。五、SpellCheck Linter方言判定发生在哪一行拼写检查器 spell_check.rs 是方言逻辑的落点。SpellCheck在构造时即接收dialect: Dialect参数L25-L31并在两个环节应用方言过滤1. 判对放行合法词——lint 主循环L102-L108if let Some(metadata) word.kind.as_word().unwrap() metadata.dialects.is_dialect_enabled(self.dialect) (self.dictionary.contains_exact_word(word_chars) || self.dictionary.contains_exact_word(word_chars.to_lower())) { continue; // 当前方言下合法跳过 }即一个词只有在词典中存在、且其方言标志对当前方言启用时才算拼写正确。在美式配置下检查本文时flavour的词典元数据不含美式位会落入纠错分支而在Dialect::British下它被continue放行——这正是测试期望 0 lint 的直接原因。单元测试american_color_in_british_dialectDo you like the color? 在英式下产生 1 条 lint验证了同一条规则的反方向。2. 判改过滤纠错建议——uncached_suggest_correct_spelling 从编辑距离为 2 起逐级放宽搜索候选词并用is_dialect_enabled(self.dialect)过滤掉不属于当前方言的候选最多保留 3 条建议配合 LRU 缓存容量 10000避免重复计算。这保证了即使误拼建议给出的也是当前方言下的正确拼法如英式上下文不会建议flavor。除拼写外方言还驱动了一批语法 Linter如fed_up_with.rs英式允许fed up of、in_on_the_cards.rs英式选in the cards、have_take_a_look.rs英美在 have/take a look 上的变体、以及规模较大的regionalisms.rsdialects: [British]等逐条声明地区偏好。这些 Linter 与 SpellCheck 一起被LintGroup::new_curated按方言装配共同保证 yogurt_british_clean.md 这类文档在英式配置下干净通过。六、如何运行与验证在仓库根目录下可直接运行这个集成测试cargo test -p harper-core --test run_tests lints_yogurt_british_clean_correctly或通过 Linter 级单元测试抽查方言行为cargo test -p harper-core --lib spell_check需要注意的适用前提测试通过include_str!在编译期嵌入 test_sources 目录样例文件是仓库源码的一部分仓库为只读参考验证方式仅为查看与运行测试FstDictionary::curated()使用内嵌策展词典行为与 dictionary.dict 及方言标记方案绑定于当前仓库版本本文样例验证的是零误报方向漏报方向的方言行为由同目录下的 dirty 类样例如issue_2054.md期望 6 条 lint补充覆盖。七、小结yogurt_british_clean.md 表面上是一篇酸奶科普短文实质是 Harper 方言体系中英式英语零误报契约的载体。它的成立依赖一条完整的证据链dictionary.dict 中yoghurt/flavour/fuelled等词条的方言尾标 → DialectFlags 的空标志即全方言通用判定语义 → SpellCheck 中is_dialect_enabled(self.dialect)的放行逻辑 → run_tests.rs 中0条 lint 的断言。理解这条链路就理解了 Harper 作为离线语法检查器在英美及加拿大、澳洲、印度多方言支持上的核心设计方言不是后处理开关而是深入词典元数据、建议过滤与语法规则三层的统一维度。【免费下载链接】harperOffline, privacy-first grammar checker. Fast, open-source, Rust-powered项目地址: https://gitcode.com/GitHub_Trending/har/harper创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
