textmachine/backend/internal/terminology/script.go

46 lines
1.9 KiB
Go

package terminology
import "unicode"
// script.go: the answer-language guarantee. Measured on live replies: with no signed rows to show the
// model, the role answers in a foreign script — and every existing guard admits it, because a foreign
// script is legitimate for foreign proper names, the answer is not an echo of the source, and an empty
// canon anchor has nothing to contradict. Input-side fixes (the anchor, the drafts evidence, a format
// example in the prompt) lower the rate; a guarantee may not depend on the content of a request, so it
// lives on the output side.
// ScriptByName resolves a Unicode script NAME ("Cyrillic", "Latin", "Han", …) to its range table. The
// table set is the standard library's, so the accepted spellings are Unicode's — this engine keeps no
// list of languages to fall out of date. config validates a declared name against the same map.
func ScriptByName(name string) (*unicode.RangeTable, bool) {
t, ok := unicode.Scripts[name]
return t, ok
}
// OffLanguage reports whether dst carries letters yet not one of them belongs to the target script.
//
// The weakest rule that catches the measured failure: one target-script letter is enough to pass, so a
// mixed rendering is untouched, and a rendering with no letters at all is not judged here.
//
// Direction of error, declared: it can refuse a legitimate rendering a pair wants kept in foreign letters.
// That costs one proposal the owner can still sign by hand; the opposite mistake puts a foreign word in a
// book's canon.
//
// target == nil → inert, mirroring a nil ScoreOpts.Conformance. The loudness for that case belongs to
// config and to the runner, not here.
func OffLanguage(dst string, target *unicode.RangeTable) bool {
if target == nil {
return false
}
letters := false
for _, r := range dst {
if !unicode.IsLetter(r) {
continue
}
if unicode.Is(target, r) {
return false
}
letters = true
}
return letters
}