Files
cthulhu/internal/app/reading_speech.go

100 lines
2.3 KiB
Go

// Copyright (c) 2026 Storm Dragon
// Authors: Storm Dragon, Angus Kola
// SPDX-License-Identifier: GPL-3.0-or-later
package app
import (
"strings"
"unicode"
"git.stormux.org/storm/cthulhu/internal/config"
)
// Apply pronunciation choices to the final text handed to the speech backend.
// Python Cthulhu splits lower-to-upper case boundaries and numeric words.
func readingSpeechText(text string, settings config.ReadingSettings) string {
if settings.SpeakMultiCaseStringsAsWords {
var result strings.Builder
var previous rune
for _, current := range text {
if unicode.IsLower(previous) && unicode.IsUpper(current) {
result.WriteByte(' ')
}
result.WriteRune(current)
previous = current
}
text = result.String()
}
if !settings.SpeakNumbersAsDigits {
return text
}
var result strings.Builder
var word []rune
flush := func() {
if len(word) == 0 {
return
}
allNumbers := true
for _, value := range word {
if !unicode.IsNumber(value) {
allNumbers = false
break
}
}
for index, value := range word {
if allNumbers && index != 0 {
result.WriteByte(' ')
}
result.WriteRune(value)
}
word = word[:0]
}
for _, value := range text {
if unicode.IsLetter(value) || unicode.IsNumber(value) || value == '_' {
word = append(word, value)
} else {
flush()
result.WriteRune(value)
}
}
flush()
return result.String()
}
// Replace complete words once, without feeding replacements back into the
// dictionary. This keeps punctuation and spacing from the accessible text.
func pronunciationSpeechText(text string, entries []config.PronunciationEntry) string {
if len(entries) == 0 {
return text
}
dictionary := make(map[string]string, len(entries))
for _, entry := range entries {
dictionary[strings.ToLower(entry.Word)] = entry.Replacement
}
var output strings.Builder
var word []rune
flush := func() {
if len(word) == 0 {
return
}
original := string(word)
if replacement, ok := dictionary[strings.ToLower(original)]; ok {
output.WriteString(replacement)
} else {
output.WriteString(original)
}
word = word[:0]
}
for _, current := range text {
if unicode.IsLetter(current) || unicode.IsNumber(current) || current == '_' {
word = append(word, current)
} else {
flush()
output.WriteRune(current)
}
}
flush()
return output.String()
}