73 lines
3.5 KiB
Swift
73 lines
3.5 KiB
Swift
import AppKit
|
|
import Foundation
|
|
import NaturalLanguage
|
|
|
|
public protocol WordKnowledge {
|
|
/// A common word in Dutch or English (case-insensitive). Rare words, names and product names are not.
|
|
func isWord(_ word: String) -> Bool
|
|
/// A very frequent function word ("in", "de", "the", "of").
|
|
func isFunctionWord(_ word: String) -> Bool
|
|
}
|
|
|
|
/// Splitting and normalising the left-hand side of an entry, shared by the engine and the warnings.
|
|
enum EntryKey {
|
|
static func parts(_ source: String) -> [String] {
|
|
source.split(whereSeparator: { $0.isWhitespace || "-‐‑–".contains($0) }).map(String.init)
|
|
}
|
|
|
|
/// Spacing, hyphens and case do not matter: "Cloud-Code", "cloud code" and "cloudcode" are the same source.
|
|
static func key(_ source: String) -> String {
|
|
parts(source).joined().lowercased()
|
|
}
|
|
}
|
|
|
|
/// The most frequent Dutch and English function words.
|
|
enum CommonWords {
|
|
static let english: Set<String> = [
|
|
"a", "about", "after", "all", "also", "an", "and", "any", "are", "as", "at", "be", "because", "been", "but",
|
|
"by", "can", "could", "did", "do", "does", "for", "from", "had", "has", "have", "he", "her", "here", "him",
|
|
"his", "how", "i", "if", "in", "into", "is", "it", "its", "just", "me", "more", "most", "my", "no", "not",
|
|
"now", "of", "on", "one", "only", "or", "other", "our", "out", "over", "she", "so", "some", "such", "than",
|
|
"that", "the", "their", "them", "then", "there", "these", "they", "this", "those", "to", "up", "us", "very",
|
|
"was", "we", "were", "what", "when", "which", "who", "will", "with", "would", "you", "your",
|
|
]
|
|
static let dutch: Set<String> = [
|
|
"aan", "al", "als", "ben", "bij", "dan", "dat", "de", "den", "der", "deze", "die", "dit", "doen", "door",
|
|
"dus", "een", "en", "er", "ga", "had", "heb", "heeft", "hem", "het", "hier", "hij", "hoe", "ik", "in", "is",
|
|
"ja", "je", "jij", "kan", "kon", "maar", "me", "mij", "mijn", "moet", "na", "naar", "niet", "nog", "nu",
|
|
"of", "om", "ons", "onze", "ook", "op", "over", "te", "ten", "ter", "toch", "toen", "tot", "u", "uit", "van",
|
|
"veel", "voor", "was", "wat", "we", "wel", "werd", "wij", "wil", "worden", "wordt", "zal", "ze", "zei",
|
|
"zich", "zij", "zijn", "zo", "zou",
|
|
]
|
|
static let all: Set<String> = english.union(dutch)
|
|
}
|
|
|
|
/// English: the on-device word list of `NaturalLanguage` (common words only, so "cloud" and "reef" are in, "anthropic" and "vercel" are not).
|
|
/// Dutch has no such list, so the spell checker decides. Function words come from the built-in list.
|
|
public final class SystemWordKnowledge: WordKnowledge, @unchecked Sendable {
|
|
private let englishWords = NLEmbedding.wordEmbedding(for: .english)
|
|
|
|
public init() {}
|
|
|
|
public func isWord(_ word: String) -> Bool {
|
|
let lowered = word.lowercased()
|
|
guard !lowered.isEmpty else { return false }
|
|
if let englishWords {
|
|
if englishWords.contains(lowered) { return true }
|
|
} else if spellChecks(lowered, language: "en") {
|
|
return true
|
|
}
|
|
return spellChecks(lowered, language: "nl")
|
|
}
|
|
|
|
public func isFunctionWord(_ word: String) -> Bool {
|
|
CommonWords.all.contains(word.lowercased())
|
|
}
|
|
|
|
private func spellChecks(_ word: String, language: String) -> Bool {
|
|
NSSpellChecker.shared.checkSpelling(
|
|
of: word, startingAt: 0, language: language, wrap: false,
|
|
inSpellDocumentWithTag: 0, wordCount: nil).location == NSNotFound
|
|
}
|
|
}
|