package common import ( "encoding/json" "sort" "strings" "unicode" "github.com/mozillazg/go-pinyin" "36wisdom/biz/consts" ) // AnnotatePinyin 写时一次性标注:词表最长匹配优先(整体转拼音),未命中逐字转 // (pinyin.Tone 带声调);返回逐字对齐的 JSON 数组字符串,非汉字对应空串, // 下标与原文 rune 一一对应。纯函数,文本未变结果不变(幂等)。 func AnnotatePinyin(text string) string { runes := []rune(text) out := make([]string, len(runes)) if len(runes) == 0 { return "[]" } words := make([]string, 0, len(consts.PinyinOverrides)) for w := range consts.PinyinOverrides { words = append(words, w) } sort.Slice(words, func(i, j int) bool { return len(words[i]) > len(words[j]) }) // 词表最长匹配切分:命中片段整体标注(词表拼音音节数须与字数一致) for i := 0; i < len(runes); { matched := false for _, w := range words { n := len([]rune(w)) if i+n > len(runes) || string(runes[i:i+n]) != w { continue } pys := strings.Fields(consts.PinyinOverrides[w]) if len(pys) != n { continue } copy(out[i:i+n], pys) i += n matched = true break } if !matched { i++ } } // 未命中汉字逐字转拼音:词典缺失字(生僻字等)SinglePinyin 返回空串, // 该字自然保持 "",后续字不错位 args := pinyin.Args{Style: pinyin.Tone} i := 0 for _, r := range text { if out[i] == "" && unicode.Is(unicode.Han, r) { if pys := pinyin.SinglePinyin(r, args); len(pys) > 0 { out[i] = pys[0] } } i++ } b, _ := json.Marshal(out) return string(b) }