!pip install PyICU
import icu
def transliterator_from_rules(name, rules):
    fromrules = icu.Transliterator.createFromRules(name, rules)
    icu.Transliterator.registerInstance(fromrules)
    return icu.Transliterator.createInstance(name)
rules = r"""
::NFC;

$wordBoundary = [^[:L:][:M:][:N:]];
$upperVowels  = [АЕЁЭИОУЫЮЯ];
$lowerVowels  = [аеёэиоуыюя];
$vowels       = [$upperVowels $lowerVowels];
$upper        = [:Uppercase:];
$lower        = [:Lowercase:];
$ZSTC         = [ЗзСсТтЦц];
$CHSH         = [ЧчШшЩщЖж];
$IJ           = [ИиЙй];
$Front        = [ЕеИиЫыЭэ];
$FrontUpper   = [ЕИЫЭ];
$FrontLower   = [еиыэ];

# --- Г: gu before e/i/ы; else g ---
Г } $FrontLower → Gu;
Г } $FrontUpper → GU;
г } $FrontLower → gu;
г } $FrontUpper → gu;

# --- Е (E) ---
# after consonant → e
[$upper-$upperVowels] { Е } → E;
[$lower-$lowerVowels] { е } → e;

# after И/Й → e
$IJ { Е } → E;
$IJ { е } → e;

# after soft/hard sign → ie
Ь { Е } $lower → ie;
Ь { Е } [^$lower] → IE;
ь { е } → ie;
Ъ { Е } $lower → ie;
Ъ { Е } [^$lower] → IE;
ъ { е } → ie;

# word-initial default (Wikipedia): Ie
$wordBoundary { Е } $lower → Ie;
$wordBoundary { Е } [^$lower] → IE;
$wordBoundary { е } → ie;

# after vowel (≠ и/й) → ïe
[$vowels-[Ии]] { Е } $lower → ïe;
[$vowels-[Ии]] { Е } [^$lower] → ÏE;
[$vowels-[Ии]] { е } → ïe;

# --- Ё (YO) ---
# default io with right-case
Ё } $lower → io;
Ё } [^$lower] → IO;
ё → io;
# convention admitted: after sibilants map to e (e.g., Gorbatchev)
$CHSH { Ё } → e;
$CHSH { ё } → e;

# --- И (I) ---
# after vowel (≠ и) → ï
[$vowels-[Ии]] { И } → Ï;
[$vowels-[Ии]] { и } → ï;

# --- Й (JOT) ---
# finals
ий $ → i;
Ий $ → i;
ый $ → y;
Ый $ → y;
# suppress in ...ьев / ...иев cluster
[ИиЬь] { Й } [Ее] → ;
# otherwise ï
Й → Ï;
й → ï;

# --- Ю (YU) ---
# after И/Й → ou
$IJ { Ю } $lower → ou;
$IJ { Ю } [^$lower] → OU;
$IJ { ю } → ou;
# after other vowel:
# front vowels (ЕЭИЯЮ) → ïou ; back vowels (АОУЫ) → you
[ЕЭИЯЮеэияю] { Ю } $lower → ïou;
[ЕЭИЯЮеэияю] { Ю } [^$lower] → ÏOU;
[ЕЭИЯЮеэияю] { ю } → ïou;
[АОУЫаоуы] { Ю } $lower → you;
[АОУЫаоуы] { Ю } [^$lower] → YOU;
[АОУЫаоуы] { ю } → you;
# elsewhere (initial/after consonant) → iou
Ю } $lower → iou;
Ю } [^$lower] → IOU;
ю → iou;

# --- Я (YA) ---
# after И/Й → a
$IJ { Я } $lower → a;
$IJ { Я } [^$lower] → A;
$IJ { я } → a;
# after other vowel → ïa
[$vowels-$IJ] { Я } $lower → ïa;
[$vowels-$IJ] { Я } [^$lower] → ÏA;
[$vowels-$IJ] { я } → ïa;
# word-initial convention admitted: Ya
$wordBoundary { Я } $lower → Ya;
$wordBoundary { Я } [^$lower] → YA;
$wordBoundary { я } → ya;
# elsewhere (after consonant) → ia
Я } $lower → ia;
Я } [^$lower] → IA;
я → ia;

# --- С between vowels → ss ---
$vowels { С } $vowels → ss;
$vowels { с } $vowels → ss;

# --- Н final after и/ы → ne (lowercase words only) ---
[иы] { н } $ → ne;
[иы] { н } $wordBoundary → ne;

# --- Final -ев (Tourgueniev, Prokofiev) → -iev ---
ЕВ $ → iev;
ев $ → iev;

А → A;
а → a;
Б → B;
б → b;
В → V;
в → v;
Г → G;
г → g;
Д → D;
д → d;
Е → E;
е → e;
Ж → J;
ж → j;
З → Z;
з → z;
И → I;
и → i;
К → K;
к → k;
Л → L;
л → l;
М → M;
м → m;
Н → N;
н → n;
О → O;
о → o;
П → P;
п → p;
Р → R;
р → r;
С → S;
с → s;
Т → T;
т → t;

У } $lower → ou;
У } [^$lower] → OU;
у → ou;

Х } $lower → kh;
Х } [^$lower] → KH;
х → kh;

Ц } $lower → ts;
Ц } [^$lower] → TS;
ц → ts;

Ч } $lower → tch;
Ч } [^$lower] → TCH;
ч → tch;

Ш } $lower → ch;
Ш } [^$lower] → CH;
ш → ch;

Щ } $lower → chtch;
Щ } [^$lower] → CHTCH;
щ → chtch;

Ъ → ;
ъ → ;
Ы → y;
ы → y;
Ь → ;
ь → ;
Э } $lower → e;
Э } [^$lower] → E;
э → e;

::NFC;
"""
rusv = transliterator_from_rules("ru-sv", rules)

tests = [
    ("Москва", "Moskva"),
    ("Чайковский", "Tjajkovskij"),
    ("Щука", "Sjtjuka"),
    ("Жириновский", "Zjirinovskij"),
    ("Юрий", "Jurij"),
    ("Яковлев", "Jakovlev"),
    ("Хрущёв", "Chrusjtjov"),
    ("Циолковский", "Tsiolkovskij")
]

# --- Run test ---
for w in tests:
    assert w[1] == rusv.transliterate(w[0])
    print(f"{w[0]:15s}{rusv.transliterate(w[0])}")
Москва          → Moskva
Чайковский      → Tjajkovskij
Щука            → Sjtjuka
Жириновский     → Zjirinovskij
Юрий            → Jurij
Яковлев         → Jakovlev
Хрущёв          → Chrusjtjov
Циолковский     → Tsiolkovskij
fr_rules = r"""
::[АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщ];
::NFC;
$upper = [:Uppercase:];
$vU = [АЕЁИОУЫЭЮЯ];
$vL = [аеёиоуыэюя];
$v  = [АЕЁИОУЫЭЮЯаеёиоуыэюя];
$WB = [^[:L:][:M:][:N:]];

А > A;
а > a;
Б > B;
б > b;
В > V;
в > v;

ГЕ > GUE;
Ги > Gui;
ГИ > GUI;
Ге > Gue;
ГЫ > GY;
Гы > Gy;
ге > gue;
ги > gui;
гы > gy;

Г > G;
г > g;

Д > D;
д > d;

$WB { Е } $upper > IE;
$WB { Е > Ie;
$WB { е > ie;
[ЬЪ] { Е > IE;
[ьъ] { Е > Ie;
[АЕЁОУЫЭЮЯ] { Е > ÏE;
[аеёоуыэюя] { е > ïe;
Е > E;
е > e;

Ё } $upper > IO;
$upper { Ё > IO;
Ё > Io;
ё > io;

Ж > J;
ж > j;
З > Z;
з > z;

ИЙ } $WB > I;
ий } $WB > i;
ЫЙ } $WB > Y;
ый } $WB > y;

[АаЕеЁёОоУуЫыЭэЮюЯя] { И > Ï;
[АаЕеЁёОоУуЫыЭэЮюЯя] { и > ï;
И > I;
и > i;

Й > Ï;
й > ï;

К > K;
к > k;
Л > L;
л > l;
М > M;
м > m;

[ИиЫы] { н } $WB > ne;
[ИиЫы] { Н } $WB > NE;
Н > N;
н > n;

О > O;
о > o;
П > P;
п > p;
Р > R;
р > r;

$v { С } $v > ss;
$v { с } $v > ss;
С > S;
с > s;

Т > T;
т > t;

У } $upper > OU;
$upper { У > OU;
У > Ou;
у > ou;

Ф > F;
ф > f;

Х } $upper > KH;
$upper { Х > KH;
Х > Kh;
х > kh;

Ц } $upper > TS;
$upper { Ц > TS;
Ц > Ts;
ц > ts;

Ч } $upper > TCH;
$upper { Ч  > TCH;
Ч > Tch;
ч > tch;

Ш } $upper > CH;
$upper { Ш > CH;
Ш > Ch;
ш > ch;

Щ } $upper > CHTCH;
$upper { Щ  > CHTCH;
Щ > Chtch;
щ > chtch;

Ъ > ;
ъ > ;
Ь > ;
ь > ;

Ы > Y;
ы > y;

Э > E;
э > e;

[ИиЙй] { Ю } $upper > OU;
[ИиЙй] { Ю > Ou;
[ИиЙй] { ю > ou;
[АаЕеЁёОоУуЫыЭэЮюЯя] { Ю } $upper > ÏOU;
[АаЕеЁёОоУуЫыЭэЮюЯя] { Ю > Ïou;
[АаЕеЁёОоУуЫыЭэЮюЯя] { ю > ïou;
Ю } $upper > IOU;
Ю > Iou;
ю > iou;

[ИиЙй] { Я } $upper > A;
[ИиЙй] { Я > A;
[ИиЙй] { я > a;
[АаЕеЁёОоУуЫыЭэЮюЯя] { Я } $upper > ÏA;
[АаЕеЁёОоУуЫыЭэЮюЯя] { Я > Ïa;
[АаЕеЁёОоУуЫыЭэЮюЯя] { я > ïa;
Я } $upper > IA;
Я > Ia;
я > ia;

::NFC;

"""
fr_examples = """
Владимир → Vladimir
Борис → Boris
Смирнов → Smirnov
Сергей → Sergueï
Георгий → Gueorgui
Новгород → Novgorod
Менделеев → Mendeleïev
Чехов → Tchekhov
Дмитриев → Dmitriev
Дудаев → Doudaïev
Екатеринбург → Iekaterinbourg
Васильев → Vassiliev
Тургенев → Tourgueniev
Пётр → Piotr
Королёв → Koroliov
Нижний → Nijni
Казимир → Kazimir
Михаил → Mikhaïl
Мир → Mir
Достоевский → Dostoïevski
Грозный → Grozny
Алексей → Alekseï
Андрей → Andreï
Александр → Aleksandr
Калининград → Kaliningrad
Малевич → Malevitch
Дума → Douma
Гагарин → Gagarine
Солженицын → Soljenitsyne
Магадан → Magadan
Байконур → Baïkonour
Волга → Volga
Спутник → Spoutnik
Самара → Samara
Новосибирск → Novossibirsk
Курск → Koursk
Владивосток → Vladivostok
Ульянов → Oulianov
Туполев → Tupolev
Прокофьев → Prokofiev
Михаил → Mikhaïl
Хабаровск → Khabarovsk
Цветаева → Tsvetaïeva
Черненко → Tchernenko
Пушкин → Pouchkine
Щедрин → Chtchedrine
Черномырдин → Tchernomyrdine
Область → Oblast
Элиста → Elista
Биюлин → Biouline
Нефтеюганск → Nefteïougansk
Юрий → Iouri
Союз → Soyouz
Мария → Maria
Майя → Maïa
Маяковский → Maïakovski
Ярославль → Iaroslavl
Ялта → Yalta
"""
tests = []
for line in fr_examples.split("\n"):
    line = line.strip()
    if line == "":
        continue
    parts = line.split(" → ")
    tests.append(parts)
rufr = transliterator_from_rules("ru-fr", fr_rules)

for w in tests:
    # assert w[1] == rufr.transliterate(w[0]), f"{w[0]:15s} → {rufr.transliterate(w[0])}"
    if w[1] != rufr.transliterate(w[0]):
        print("Error", f"{w[0]:15s}{rufr.transliterate(w[0])} ({w[1]})")
    if w[1].upper() != rufr.transliterate(w[0].upper()):
        print("Uppercase error", f"{w[0].upper():15s}{rufr.transliterate(w[0].upper())} ({w[1].upper()})")
    # print(f"{w[0]:15s} → {rufr.transliterate(w[0])}")
Error Георгий         → Gueorguiï (Gueorgui)
Uppercase error ГЕОРГИЙ         → GUEORGUIÏ (GUEORGUI)
Error Менделеев       → Mendeleev (Mendeleïev)
Uppercase error МЕНДЕЛЕЕВ       → MENDELEEV (MENDELEÏEV)
Error Дудаев          → Doudaev (Doudaïev)
Uppercase error ДУДАЕВ          → DOUDAEV (DOUDAÏEV)
Error Васильев        → Vasilьev (Vassiliev)
Uppercase error ВАСИЛЬЕВ        → VASILEV (VASSILIEV)
Error Тургенев        → Tourguenev (Tourgueniev)
Uppercase error ТУРГЕНЕВ        → TOURGUENEV (TOURGUENIEV)
Error Михаил          → Mikhail (Mikhaïl)
Uppercase error МИХАИЛ          → MIKHAIL (MIKHAÏL)
Error Достоевский     → Dostoevski (Dostoïevski)
Uppercase error ДОСТОЕВСКИЙ     → DOSTOEVSKI (DOSTOÏEVSKI)
Error Грозный         → Groznыï (Grozny)
Error Гагарин         → Gagarin (Gagarine)
Uppercase error ГАГАРИН         → GAGARIN (GAGARINE)
Error Солженицын      → Soljenitsыne (Soljenitsyne)
Uppercase error СОЛЖЕНИЦЫН      → SOLJENITSYN (SOLJENITSYNE)
Error Новосибирск     → Novosibirsk (Novossibirsk)
Uppercase error НОВОСИБИРСК     → NOVOSIBIRSK (NOVOSSIBIRSK)
Error Ульянов         → Oulьяnov (Oulianov)
Error Туполев         → Toupolev (Tupolev)
Uppercase error ТУПОЛЕВ         → TOUPOLEV (TUPOLEV)
Error Прокофьев       → Prokofьev (Prokofiev)
Uppercase error ПРОКОФЬЕВ       → PROKOFEV (PROKOFIEV)
Error Михаил          → Mikhail (Mikhaïl)
Uppercase error МИХАИЛ          → MIKHAIL (MIKHAÏL)
Error Цветаева        → Tsvetaeva (Tsvetaïeva)
Uppercase error ЦВЕТАЕВА        → TSVETAEVA (TSVETAÏEVA)
Error Пушкин          → Pouchkin (Pouchkine)
Uppercase error ПУШКИН          → POUCHKIN (POUCHKINE)
Error Щедрин          → Chtchedrin (Chtchedrine)
Uppercase error ЩЕДРИН          → CHTCHEDRIN (CHTCHEDRINE)
Error Черномырдин     → Tchernomыrdin (Tchernomyrdine)
Uppercase error ЧЕРНОМЫРДИН     → TCHERNOMYRDIN (TCHERNOMYRDINE)
Error Область         → Oblastь (Oblast)
Error Биюлин          → Biюlin (Biouline)
Uppercase error БИЮЛИН          → BIIOULIN (BIOULINE)
Error Нефтеюганск     → Nefteюgansk (Nefteïougansk)
Uppercase error НЕФТЕЮГАНСК     → NEFTEIOUGANSK (NEFTEÏOUGANSK)
Error Союз            → Soюz (Soyouz)
Uppercase error СОЮЗ            → SOIOUZ (SOYOUZ)
Error Мария           → Mariя (Maria)
Uppercase error МАРИЯ           → MARIIa (MARIA)
Error Майя            → Maïя (Maïa)
Uppercase error МАЙЯ            → MAÏIa (MAÏA)
Error Маяковский      → Maяkovski (Maïakovski)
Uppercase error МАЯКОВСКИЙ      → MAIAKOVSKI (MAÏAKOVSKI)
Error Ярославль       → Iaroslavlь (Iaroslavl)
Error Ялта            → Ialta (Yalta)
Uppercase error ЯЛТА            → IALTA (YALTA)