This commit is contained in:
Shaman
2026-10-01 07:38:01 +03:00
commit 82c306ff69
33 changed files with 2629 additions and 0 deletions
+131
View File
@@ -0,0 +1,131 @@
import json
import re
from pathlib import Path
_LATIN_RUN_RE = re.compile(r"[A-Za-z]+")
# Фонетические правила en->ru. Применяются слева направо, длиннейшее
# совпадение выигрывает (триграфы раньше диграфов, диграфы раньше букв).
_RULES: dict[str, str] = {
"tion": "шн",
"sure": "шур",
"ture": "тур",
"ough": "оф",
"tch": "ч",
"sch": "ш",
"igh": "ай",
"ng": "нг",
"nk": "нк",
"qu": "кв",
"th": "т",
"sh": "ш",
"ch": "ч",
"ck": "к",
"ph": "ф",
"wh": "у",
"wr": "р",
"kn": "н",
"ee": "и",
"ea": "и",
"oo": "у",
"ou": "ау",
"ow": "ау",
"ai": "ай",
"ay": "эй",
"oi": "ой",
"oy": "ой",
"au": "о",
"aw": "о",
"ir": "ер",
"ur": "ер",
"er": "ер",
"ar": "ар",
"or": "ор",
"a": "а",
"b": "б",
"c": "к",
"d": "д",
"e": "е",
"f": "ф",
"g": "г",
"h": "х",
"i": "и",
"j": "дж",
"k": "к",
"l": "л",
"m": "м",
"n": "н",
"o": "о",
"p": "п",
"q": "к",
"r": "р",
"s": "с",
"t": "т",
"u": "у",
"v": "в",
"w": "у",
"x": "кс",
"y": "и",
"z": "з",
}
_RULE_KEYS = sorted(_RULES, key=len, reverse=True)
_dict_path = Path("translit_dict.json")
_cache: tuple[dict[str, str], dict[str, str]] | None = None
def set_dict_path(path: str | Path) -> None:
global _dict_path, _cache
_dict_path = Path(path)
_cache = None
def _load_dict() -> tuple[dict[str, str], dict[str, str]]:
global _cache
if _cache is None:
data = json.loads(_dict_path.read_text(encoding="utf-8"))
exact = {k.lower(): v for k, v in data.get("exact", {}).items()}
abbrev = {k.upper(): v for k, v in data.get("abbreviations", {}).items()}
_cache = (exact, abbrev)
return _cache
def _apply_rules(word: str) -> str:
out: list[str] = []
i = 0
n = len(word)
while i < n:
for key in _RULE_KEYS:
if word.startswith(key, i):
out.append(_RULES[key])
i += len(key)
break
else:
out.append(word[i])
i += 1
result = "".join(out)
if word.endswith("e") and len(result) > 2 and result.endswith("е"):
result = result[:-1]
return result
def _match_case(token: str, lower_out: str) -> str:
if token.isupper():
return lower_out.upper()
if token[:1].isupper():
return lower_out.capitalize()
return lower_out
def transliterate_token(token: str) -> str:
exact, abbrev = _load_dict()
if token.isupper() and token in abbrev:
return abbrev[token]
key = token.lower()
if key in exact:
return _match_case(token, exact[key])
return _match_case(token, _apply_rules(key))
def transliterate_latin(text: str) -> str:
return _LATIN_RUN_RE.sub(lambda m: transliterate_token(m.group()), text)