Files
Shaman 82c306ff69 init
2026-10-01 07:38:01 +03:00

132 lines
3.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)