60 lines
2.1 KiB
Python
60 lines
2.1 KiB
Python
"""RUAccent stresses for the text of the voice.
|
||
|
||
plus_marks(): '+' before the stressed vowel, carried onto the ORIGINAL text word by word. RUAccent is
|
||
fed lowercase (it is worse on Title-case) and drops spaces before "(", so a char-by-char walk drifts and
|
||
garbles the tail ("в годод", "))"); here only the n-th Cyrillic word of its output gives its '+' (and ё)
|
||
to the n-th word of the source, punctuation and spacing stay the source's. If the word counts differ,
|
||
the text is accented clause by clause.
|
||
acute_marks(): '+' before a vowel -> the vowel followed by U+0301, the input of the ru-stress model.
|
||
"""
|
||
import re
|
||
|
||
VOWELS = "аеёиоуыэюяАЕЁИОУЫЭЮЯ"
|
||
W = re.compile(r"[А-Яа-яЁё]+(?:-[А-Яа-яЁё]+)*")
|
||
WP = re.compile(r"[А-Яа-яЁё+]+(?:-[А-Яа-яЁё+]+)*")
|
||
_acc = None
|
||
|
||
|
||
def accentizer():
|
||
global _acc
|
||
if _acc is None:
|
||
from ruaccent import RUAccent
|
||
_acc = RUAccent()
|
||
_acc.load(omograph_model_size="turbo3.1", use_dictionary=True, tiny_mode=False)
|
||
return _acc
|
||
|
||
|
||
def _transfer(orig, acc):
|
||
out, i = [], 0
|
||
for ch in acc:
|
||
if ch == "+":
|
||
out.append("+"); continue
|
||
o = orig[i]
|
||
out.append(("Ё" if o == "Е" else "ё") if ch.lower() == "ё" and o in "еЕ" else o)
|
||
i += 1
|
||
return "".join(out)
|
||
|
||
|
||
def _plus(text):
|
||
acc = accentizer().process_all(text.lower())
|
||
src, dst = list(W.finditer(text)), WP.findall(acc)
|
||
if len(src) != len(dst) or any(len(a.group(0)) != len(b.replace("+", "")) for a, b in zip(src, dst)):
|
||
return None
|
||
out, last = [], 0
|
||
for a, b in zip(src, dst):
|
||
out += [text[last:a.start()], _transfer(a.group(0), b)]
|
||
last = a.end()
|
||
return "".join(out) + text[last:]
|
||
|
||
|
||
def plus_marks(text: str) -> str:
|
||
r = _plus(text)
|
||
if r is not None:
|
||
return r
|
||
parts = re.split(r"([,.;:!?()«»…]+\s*)", text)
|
||
return "".join(p if i % 2 or not p.strip() else (_plus(p) or p) for i, p in enumerate(parts))
|
||
|
||
|
||
def acute_marks(plus_text: str) -> str:
|
||
return re.sub(r"\+([" + VOWELS + "])", lambda m: m.group(1) + "́", plus_text).replace("+", "")
|