8c36e7ef84
A clock time read aloud now inflects its nouns and drops its leading zeros.
The old rewrite said "часов" for every hour and "минут" for every minute, so
21:00 came out as "21 часов" and 14:00 as "14 часов 00 минут". Russian
inflects a noun after a numeral and internal/say already owns that rule, so
spokenTime calls say.CountWord for both halves and omits the minutes when
there are none. 21:00 is "21 час", 22:02 is "22 часа 2 минуты", 14:00 is
"14 часов".
internal/persona held its own copies of the twelve months and the seven
weekdays. Both are closed classes and both already live in internal/lexicon,
which is where cmd/mavend/ruwords.go sent its copy. The block now reads
lexicon.Weekday and lexicon.MonthGenitive and carries no word list of its own.
LoadSummaries asserted that stall_sitting keeps its two counts and not the two
count words beside them. A variant dropping {word} or {dayword} would have
loaded and spoken a bare number. Both are required now.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
26 lines
970 B
Go
26 lines
970 B
Go
package ttsnorm
|
|
|
|
import "testing"
|
|
|
|
func TestSpeakable(t *testing.T) {
|
|
cases := []struct{ in, want string }{
|
|
{"напомню 10.07.2026", "напомню 10 июля 2026"},
|
|
{"срок 01.01", "срок 1 января"},
|
|
{"встреча в 14:00", "встреча в 14 часов"},
|
|
{"в 9:05 подъём", "в 9 часов 5 минут подъём"},
|
|
{"в 21:00 отбой", "в 21 час отбой"},
|
|
{"в 22:02 отбой", "в 22 часа 2 минуты отбой"},
|
|
{"в 1:01 проснулся", "в 1 час 1 минута проснулся"},
|
|
{"это 3.2.1 версия", "это 3 точка 2 точка 1 версия"},
|
|
{"без чисел", "без чисел"},
|
|
}
|
|
for _, c := range cases {
|
|
if got := Speakable(c.in); got != c.want {
|
|
t.Errorf("Speakable(%q) = %q, want %q", c.in, got, c.want)
|
|
}
|
|
if got := Speakable(Speakable(c.in)); got != Speakable(c.in) {
|
|
t.Errorf("not idempotent for %q: %q", c.in, got)
|
|
}
|
|
}
|
|
}
|