init
This commit is contained in:
+131
@@ -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)
|
||||
Reference in New Issue
Block a user