better stress in some russian words

This commit is contained in:
sergeych committed 2026-05-23 22:24:47 +03:00
1 parent ed66ea0d55
commit b0f45aaf1b
4 files changed
+141 -4

No files matched your search

@@ -31,11 +31,17 @@ data class ReadAloudVoiceOption(
val networkRequired: Boolean,
)
data class ReadAloudTextReplacement(
val from: String,
val to: String,
)
data class ReadAloudSettingsState(
val engines: List<ReadAloudEngineOption> = emptyList(),
val selectedEngineId: String? = null,
val voices: List<ReadAloudVoiceOption> = emptyList(),
val selectedVoiceId: String? = null,
val textReplacements: List<ReadAloudTextReplacement> = emptyList(),
val loading: Boolean = false,
val message: String? = null,
)
@@ -799,10 +799,37 @@ private fun String.isReadAloudPauseBreak(): Boolean {
}
private fun String.toReadAloudSpokenText(): String =
replace(Regex("\\.{2,}"), ".")
applyReadAloudTextReplacements(ReadAloudHardcodedTextReplacements)
.withReadAloudStressMarkers()
.replace(Regex("\\.{2,}"), ".")
.replace(Regex("([!?])\\.+"), "$1")
.replace('…', '.')
.trim()
private fun String.applyReadAloudTextReplacements(replacements: List<ReadAloudTextReplacement>): String =
replacements.fold(this) { text, replacement ->
text.replace(replacement.from, replacement.to)
}
private fun String.withReadAloudStressMarkers(): String = buildString(length) {
var index = 0
while (index < this@withReadAloudStressMarkers.length) {
val current = this@withReadAloudStressMarkers[index]
val next = this@withReadAloudStressMarkers.getOrNull(index + 1)
if ((current == '/' || current == '\'') && next != null && next.isReadAloudStressableLetter()) {
append(next)
append(CombiningAcuteAccent)
index += 2
} else {
append(current)
index += 1
}
}
}
private fun Char.isReadAloudStressableLetter(): Boolean =
this in ReadAloudStressableLetters
private fun String.sentenceRanges(): List<ReaderSentenceRange> {
val ranges = mutableListOf<ReaderSentenceRange>()
var start = 0
@@ -838,9 +865,10 @@ private fun String.sentenceRanges(): List<ReaderSentenceRange> {
}
private fun String.sentenceTerminatorEnd(index: Int): Int {
if (this[index] != '.') return index + 1
var end = index + 1
while (end < length && this[end] == '.') end += 1
if (this[index] == '.' || this[index] == '!' || this[index] == '?') {
while (end < length && this[end] == '.') end += 1
}
return end
}
@@ -854,6 +882,14 @@ private const val HeadingPauseBeforeMillis = 1_000L
private const val HeadingPauseAfterMillis = 600L
private const val StarBreakPauseMillis = 1_200L
private const val EllipsisPauseAfterMillis = 350L
private const val CombiningAcuteAccent = '\u0301'
private const val ReadAloudStressableLetters = "аеёиоуыэюяАЕЁИОУЫЭЮЯaeiouyAEIOUY"
private val ReadAloudHardcodedTextReplacements = listOf(
ReadAloudTextReplacement("Господа,", "Господ/а,"),
ReadAloudTextReplacement("господа", "господ/а"),
ReadAloudTextReplacement("прошуршала", "прошурш/ала"),
)
private fun List<Fb2Section>.flattenSections(depth: Int = 0): List<ChapterEntry> =
flatMapIndexed { index, section ->