// Copyright (c) 2026 Storm Dragon // SPDX-License-Identifier: GPL-3.0-or-later package spellcheck import ( "os" "strings" "unicode" "unicode/utf8" ) // Language uses the effective session message locale, with no English fallback. // LANGUAGE is a translation fallback list, not the session's regional locale. func Language() string { for _, name := range []string{"LC_ALL", "LC_MESSAGES", "LANG"} { if value := os.Getenv(name); value != "" { return language_tag(value) } } return "" } func language_tag(value string) string { value, _, _ = strings.Cut(strings.TrimSpace(value), ".") value, _, _ = strings.Cut(value, "@") if value == "C" || value == "POSIX" { return "" } value = strings.ReplaceAll(value, "-", "_") for _, char := range value { if !unicode.IsLetter(char) && char != '_' { return "" } } return value } // Word strips surrounding prose punctuation but leaves structured terminal // tokens intact so they can be excluded rather than checked as fragments. func Word(value string) string { value = strings.TrimSpace(value) if strings.HasPrefix(value, "-") || strings.HasPrefix(value, "#") || strings.HasPrefix(value, "@") || strings.ContainsAny(value, "/\\@_=0123456789") || strings.Contains(value, "://") { return "" } value = strings.TrimFunc(value, func(char rune) bool { return !unicode.IsLetter(char) && !unicode.IsMark(char) }) if !valid_word(value) { return "" } return value } func valid_word(value string) bool { if !utf8.ValidString(value) || utf8.RuneCountInString(value) > 128 || value == "" { return false } letters := false for _, char := range value { if unicode.IsLetter(char) { letters = true } else if !unicode.IsMark(char) && char != '\'' && char != '’' && char != '-' && char != '‐' { return false } } return letters } func Separator(char rune) bool { return unicode.IsSpace(char) || unicode.IsPunct(char) && !strings.ContainsRune("'.:/@\\_=-’‐#", char) }