Skip to content

Repository files navigation

Windows GUI Open in Colab PyPI

ru-normalizr — лучший no-LLM open-source нормализатор русского текста

Приводит числа, даты, время, сокращения, римские цифры, символы и латиницу в русские буквы для использования в TTS и NLP:

"В 1980-е годы денег было 25 млн." → "В тысяча девятьсот восьмидесятые годы денег было двадцать пять миллионов."

Ставит слова в правильную грамматическую форму, а не просто делает замены по словарю.

  • Гораздо быстрее и дешевле, чем нормализаторы на LLM
  • Покрывает больше реальных кейсов, чем dictionary-only решения
  • Малое потребление ресурсов. Не требует GPU

Скачать программу ru-normalizr GUI для Windows
Смотри сравнение с другими нормализаторами здесь
English version of README

Основные возможности и режимы

По умолчанию используется режим safe:

  • приводит к словам числа, даты, время и годы
  • обрабатывает римские цифры
  • раскрывает самые типичные сокращения, где преобразование обычно однозначно
  • нормализует единицы измерения и некоторые символы
  • исправляет пробелы, пунктуацию, переносы строк и часть склеек в тексте

Режим tts нормализует ближе к "как это надо читать вслух" — рекомендуется для TTS. Кроме всего перечисленного выше, этот режим:

  • включает нормализацию CAPS (ГЛАВАГлава)
  • раскрывает инициалы и аббревиатуры в звуки (ГИБДДги бэ дэ дэ)
  • включает перевод латиницы в кириллицу (iPhoneайфоун)
  • читает явные URL вроде https://example.com/a1 ближе к тому, как их произносят вслух
  • может убирать сноски в скобках, например, [1]

ru-normalizr умеет ставить верные ударения ТОЛЬКО при переводе латиницы в кириллицу. Чтоб включить эту функцию, используйте флаг CLI-флаг --with-latin-stress или настройку enable_latinization_stress_marks=True.

Пакет не ставит ударения в русских словах. Для TTS-пайплайна постановку ударений лучше добавлять отдельным этапом, например, через пакет Silero Stress.

Установка

pip install ru-normalizr

IPA-бэкенд латинизации (latinization_backend="ipa") работает из коробки: пакет eng_to_ipa входит в зависимости по умолчанию. Если по какой-то причине он окажется недоступен, запрос IPA-бэкенда один раз выдаёт предупреждение и автоматически переключается на словарный бэкенд латинизации.

На поддерживаемых версиях CPython пакет по умолчанию использует compiled DAWG-backend из pymorphy3[fast]. В CI проверяются CPython 3.10–3.13 на Linux и Windows; для этих интерпретаторов DAWG2 публикует готовые колёса также для macOS, manylinux и musllinux. Активный backend можно проверить так:

from pymorphy3 import dawg

assert dawg.EXTENSION_AVAILABLE

Значение False на поддерживаемом CPython означает проблему установки или конфигурации, а не штатный production-режим. Корректность нормализации от backend не зависит: перед сменой зависимости portable и compiled варианты сравниваются parity-gate по публичным результатам, морфологическим разборам и полному набору тестов. На Windows x86-64 с CPython 3.11.15 короткий прогон 10 000 различных слов дал около 12 тыс. слов/с для portable backend и 64 тыс. слов/с для compiled; это описание методики на одной машине, а не универсальная гарантия производительности.

Для Windows также доступен GUI: Скачать ru-normalizr GUI для Windows

Запуск из cmd или bash (CLI)

Нормализовать строку (по умолчанию в режиме safe):

ru-normalizr "Глава IV. Встреча в 10:07."

Использовать TTS-режим (рекомендуется):

ru-normalizr "Глава IV. Встреча в 10:07." --mode tts

Прочитать текст из файла и сохранить результат в файл:

ru-normalizr --mode tts --file ./sample.txt --output ./sample.normalized.txt

Если команда ru-normalizr не работает, добавьте перед ней python -m, например:

python -m ru_normalizr "Глава IV. Встреча в 10:07." --mode tts

Полезные флаги:

  • --mode safe|tts
  • --with-latin-stress — по возможности ставить верное удар+ение при кириллизации латиницы
  • --keep-links — не удалять [1], (2.5) и прочие сноски
  • --check — вывести результат в stdout и завершиться с кодом 1, если он отличается от входа, иначе 0 (нельзя вместе с --output)
  • --version — вывести установленную версию ru-normalizr и выйти

Использование в Python

Cамый простой способ — normalize():

from ru_normalizr import normalize

# по умолчанию режим safe
print(normalize("Глава IV. Встреча в 10:07."))
# Глава четвёртая. Встреча в десять, ноль семь.

Если вы обрабатываете много текстов с одними и теми же настройками, удобнее создать Normalizer:

from ru_normalizr import NormalizeOptions, Normalizer

# в режиме tts:
normalizer = Normalizer(NormalizeOptions.tts())

print(normalizer.normalize("ГИБДД"))
# ги бэ дэ дэ

print(normalizer.normalize_batch(["Глава IV.", "В 1980-е годы было 25 млн."]))
# "Глава четвёртая.", "В тысяча девятьсот восьмидесятые годы было двадцать пять миллионов."

Для более точной настройки используйте NormalizeOptions:

from ru_normalizr import NormalizeOptions, normalize

options = NormalizeOptions.tts(
    latinization_backend="ipa",
    enable_latinization_stress_marks=True,
    letter_vowel_mode="double",    # "single" для обычных названий гласных
    initials_pause_mode="comma",   # "preserve" без дополнительных пауз
)

print(normalize("YouTube в 2024 г.", options))
# +ютуб в две тысячи двадцать четвёртом году.

Все опции NormalizeOptions

Значение по умолчанию зависит от режима: NormalizeOptions() и NormalizeOptions.safe() используют колонку safe, NormalizeOptions.tts() — колонку tts. Любую опцию можно переопределить явно.

Опция Тип safe tts Описание
enable_caps_normalization bool False True Нормализация ЗАГЛАВНЫХ заголовков (ГЛАВАГлава)
enable_first_word_decap bool False True Приведение первого слова предложения к строчным при нормализации капса
remove_links bool False True Удаление скобочных ссылок-сносок вида [1], (2.5)
enable_url_normalization bool False True Озвучивание явных URL (http://, https://, www.) поразрядно и по словам-разделителям; при False такие URL сохраняются дословно
remove_links_ignore_interval tuple[int, int] (1000, 2200) (1000, 2200) Диапазон чисел в скобках, которые считаются годами и не удаляются как сноски
enable_year_normalization bool True True Нормализация годов, десятилетий и диапазонов лет
enable_roman_normalization bool True True Обработка римских цифр
enable_dates_time_normalization bool True True Нормализация дат и времени
enable_numeral_normalization bool True True Нормализация числительных, порядковых форм, дробей и десятичных
enable_abbreviation_expansion bool True True Раскрытие сокращений (ул., и т. д.)
enable_contextual_abbreviation_expansion bool True True Согласование прилагательных-сокращений по контексту (гос.государственного)
enable_years_ago_expansion bool True True Раскрытие л. н.лет назад в числовом контексте
enable_initials_expansion bool False True Раскрытие инициалов (Ч.чэ)
letter_vowel_mode "single" | "double" "single" "double" При double удваивает отдельно произносимые гласные в инициалах, аббревиатурах и буквенных индексах (Е.ее, SUэс юю)
initials_vowel_mode "single" | "double" алиас алиас Совместимое устаревшее имя letter_vowel_mode; разные значения двух имён одновременно запрещены
initials_pause_mode "preserve" | "comma" "preserve" "comma" При comma добавляет паузы-запятые вокруг инициалов в формате «Фамилия И.»
cleanup_malformed_punctuation bool False True В конце конвейера схлопывать мусорную последовательность ., в точку; можно отключить в TTS или явно включить для грязного ввода
enable_letter_abbreviation_expansion bool False True Побуквенное чтение аббревиатур (ГИБДДги бэ дэ дэ)
enable_dictionary_normalization bool True True Применение словарных правил из пользовательских .dic
enable_latinization bool False True Транслитерация латиницы в кириллицу
latinization_backend "ipa" | "dictionary" "ipa" "ipa" Бэкенд латинизации (IPA работает по умолчанию)
enable_latinization_stress_marks bool False False Сохранять знаки ударения + при IPA-латинизации
latin_dictionary_filename str "latinization_rules.dic" "latinization_rules.dic" Имя .dic в каталоге latinization/, используемого стадией latinization
dictionary_include_files tuple[str, ...] () () Если задано — загружать только эти .dic (иначе все, кроме latinization/)
dictionary_exclude_files tuple[str, ...] () () Какие .dic исключить из загрузки
dictionaries_path Path | None None None Корневой каталог с .dic (по умолчанию встроенный dictionaries/)
mode "safe" | "tts" | None None None Маркер пресета; задаёт значения по умолчанию для остальных опций

Важно: стадии годов и обычных числительных управляются независимо. remove_links_ignore_interval только не даёт удалить скобочное число как ссылку: например, при стандартном интервале [1500] сохраняется как возможный год. После этого enable_year_normalization=True всё равно раскроет его как (одна тысяча пятьсот), даже если enable_numeral_normalization=False. Чтобы сохранить цифры, отключите обе числовые стадии: enable_year_normalization=False и enable_numeral_normalization=False.

Пользовательские словари

Стадия dictionary применяет пользовательские правила замены из .dic-файлов. Важно: по умолчанию она не загружает ни одного встроенного правила — единственный встроенный .dic относится к каталогу latinization/ и используется стадией latinization, а не dictionary. То есть стадия dictionary существует именно для ваших собственных словарей.

Формат .dic показан ниже:

# строки, начинающиеся с #, — комментарии
# одно правило на строку: источник=замена
YouTube=ютуб
OpenAI=оупэнэйай
# `*` — подстановка для последовательности непробельных символов
*Tube=тюб
Open*=оупен

Подключить словари можно двумя способами: положить .dic в каталог dictionaries/ пакета (загрузятся автоматически, кроме latinization/) или указать собственный каталог через dictionaries_path:

from pathlib import Path
from ru_normalizr import NormalizeOptions, normalize

# ./my_dicts/brands.dic:
#   YouTube=ютуб
#   OpenAI=оупэнэйай
options = NormalizeOptions(dictionaries_path=Path("./my_dicts"))

print(normalize("YouTube и OpenAI", options))
# ютуб и оупэнэйай

Из каталога можно загрузить только часть файлов через dictionary_include_files либо исключить отдельные через dictionary_exclude_files (имя файла или путь относительно dictionaries_path).

Модульность

ru-normalizr можно использовать не только как один большой black box, но и как модульный пайплайн.

Доступны отдельные стадии:

  • urls — ранняя нормализация явных URL в TTS-режиме: переводит разделители в слова и читает цифры внутри ссылок поцифрово, оставляя чистую латиницу на более позднюю стадию latinization
  • preprocess — предварительная очистка текста: пробелы, переносы строк, пунктуация, часть OCR-подобных склеек, базовая подготовка перед остальной нормализацией
  • roman — обработка римских цифр
  • dates_time — нормализация дат и времени
  • years — нормализация годов, десятилетий и диапазонов лет
  • numerals — нормализация числительных, порядковых форм, дробей, десятичных и других числовых выражений
  • abbreviations — раскрытие сокращений, инициалов и буквенных аббревиатур в зависимости от выбранного режима
  • dictionary — применение пользовательских словарных правил (встроенные правила по умолчанию не загружаются; см. Пользовательские словари)
  • latinization — перевод латиницы в кириллицу через словарь или IPA-бэкенд
  • finalize — финальная чистка текста после всех преобразований: нормализация пунктуации, регистра и восстановление абзацев

Это удобно для отладки, тестирования и встраивания только нужных частей пайплайна в собственную систему.

Разработка

Окружение управляется uv. Требуется Python 3.10 или новее.

uv sync --frozen

Проверки, которые обязаны проходить перед коммитом (тот же набор выполняет CI):

uv run ruff check .
uv run pyright
uv run pytest -q

Отчёт о покрытии по ветвям:

uv run pytest -q --cov=src/ru_normalizr --cov-branch --cov-report=term-missing

Полный цикл проверки выпуска — очистка, сверка версий, линт, типы, тесты, сборка и twine check:

uv run python scripts/dev.py check

Порядок выпуска на PyPI описан в docs/RELEASING.md, правила версионирования — в VERSIONING.md, соглашения для правок кода — в AGENTS.md и docs/agents/.

Сравнение с другими нормализаторами

Сравнение проводилось на одном компьютере на одних и тех же текстах. Качество можете оценить сами по примерам ниже.

Нормализатор Скорость Качество Время на книгу
ru-normalizr ⚡ очень быстро ✅ высокое ⚡ 2 секунды
RuNorm 🐢 очень медленно ❌ с артефактами 140-700 секунд
Demagog быстро низкое 35 секунд

Также было произведено сравнение с многообещающим russian_text_normalizer. Он показал себя гораздо лучше, чем RuNorm, но он тоже допускает ошибки в нормализации числительных (например, получил приблизительно 2500 голосов.получил приблизительно двести пятьдесят тысяч голосов.), нормализирует не всё и съедает слова из оригинального текста.

Входной текст (пример)

Глава 1
      ПОСЛЕДНИЕ СЛОВА ГЕНЕРАЛА

В 1990–2000 гг компания выпустила 15 моделей iPhone XII и продала 1 234 567 устройств в странах СНГ и ЕС.

АВТОМОБИЛЕСТРОЕНИЕ КОНВЕЙЕРНОЕ организовал американский промышленник Р. Э. Олдс – основатель компании «Олдс Мотор Уоркс» (Olds Motor Works) в городе Детройте.

Компания ABC Ltd. протестировала систему на 3.5 TB данных со скоростью 100 Mb/s.
Размер модели: 2.7 GB, точность: 99.5%.

Результат обработки с ru-normalizr (режим tts)

Глава один.
Последние слова генерала.

В тысяча девятьсот девяностых — двухтысячных годах компания выпустила пятнадцать моделей айфоун двенадцать и продала один миллион двести тридцать четыре тысячи пятьсот шестьдесят семь устройств в странах эс эн гэ и е эс.

Автомобилестроение конвейерное организовал американский промышленник эр ээ Олдс — основатель компании "Олдс Мотор Уоркс" (оулдз моутэр вэркс) в городе Детройте.

Компания эй би си элтиди. Протестировала систему на трёх целых пять десятых терабайта данных со скоростью ста мегабайтов в секунду.
Размер модели: две целых семь десятых гигабайта, точность: девяносто девять целых пять десятых процента.

На нормализацию примера ушло 0.24 секунды. Аббревиатуры и инициалы раскрываются в буквы намерено. Это можно отключить.

Реальную книгу (здесь и далее — Дэвид Бергланд, «ЛИБЕРТАРИАНСТВО ЗА ОДИН УРОК» с flibusta — небольшая книга в 274K символов) ru-normalizer обработал за 2 секунды.

Результат обработки с RuNorm Big

Глава первая пэ о эс эл е дэ эн и е эс эл о вэ а гэ е эн е эр а эл а в тысяча девятьсот девяносто первом – две тысячи втором годах компания выпустила пятнадцать моделей айфон двенадцать и продала один миллион двести двадцать четыре тысячи пятьсот семьдесят семь устройств в странах эс эн гэ и е эс. А вэ тэ о эм о бэ и эл е эс тэ эр о е эн и е ка о эн вэ е ий е эр эн о е, на промышленной основе, организовал американский промышленник р. э. олдс – основатель компании «олдс мотор уоркс » ( олдс мотор уоркс ) в городе детройте. Компания эй би си литд. протестировала систему на три целых и пять десятых терабайта данных со скоростью сто мегабайт /секунд. Размер модели : два целых семь десятых гигабайта, точность : девяносто девять целых и пять десятыхпроцент.

На нормализацию примера ушло 15.76 секунды.

Реальную книгу (274K символов) RuNorm Small обработал за 143 секунды, RuNorm Big — за 723 секунды.

RuNorm значительно медленнее и использует значительно больше ресурсов. Качество нормализации у RuNorm низкое и нестабильное. Ломает форматирование.

Результат обработки словарями Demagog

Глава первая
ПОСЛЕДНИЕ СЛОВА ГЕНЕРАЛА

В одна тысяча девятьсот девяносто–две тысячи компания выпустила пятнадцать моделей ифон двенадцать и продала один двести тридцать четыре пятьсот шестьдесят семь устройств в странах СНГ и ЕС.

АВТОМОБИЛЕСТРОЕНИЕ КОНВЕЙЕРНОЕ организовал американский промышленник Р. Э. Олдс – основатель компании «Олдс Мотор Уоркс» (олдс мото Оркс) в городе Детройте.

Компания эбс лимитэд. протестировала систему на три с половиной тб данных со скоростью сто мегабайтов /с.
Размер модели: два,семь гигабайтов , точность: девяносто девять с половиной%.

Применены популярные Демагог словари 10_REX_числа(chisla).rex, 65_ЛАТИНИЦА@.dic.

На нормализацию примера ушло менее секунды.

Реальную книгу (274K символов) Демагог обработал за 35 секунд.

Качество нормализации хуже, чем у ru-normalizr, особенно в не покрываемых словарём случаях. Функционал ниже.

Упоминания

Упомянута на 4PDA Библиотека ru-normalizr используется каталогом ИИ аудиокниг AIaudiobooks.org

ru-normalizr — the best open-source Russian text normalizer

Main features

ru-normalizr converts numbers, dates, time, abbreviations, Roman numerals, symbols, and Latin text into Russian words for use in TTS and NLP.

It inflects words into the correct grammatical form instead of relying on simple dictionary replacements.

  • Very fast
  • Covers more real-world cases than dictionary-only solutions
  • More stable and higher quality than LLM-based normalizers
  • Lightweight and resource-efficient. No GPU required

See the comparison with other normalizers here

Why ru-normalizr?

Russian text normalization is difficult because:

  • numbers must be inflected
  • abbreviations are ambiguous
  • Latin words appear in modern text
  • dictionary-only solutions fail on unseen cases

ru-normalizr solves this with a rule-based pipeline that produces grammatically correct forms instead of simple replacements.

Modes

By default, safe mode is used:

  • converts numbers, dates, time, and years into words
  • handles Roman numerals
  • expands the most common abbreviations where the transformation is usually unambiguous
  • normalizes measurement units and some symbols
  • fixes spacing, punctuation, line breaks, and some tokenization / merged-text issues

tts mode normalizes text closer to “how it should be read aloud” and is recommended for TTS:

  • includes CAPS normalization (ГЛАВАГлава)
  • expands initials and abbreviations into letter names / sounds (ГИБДДги бэ дэ дэ)
  • includes Latin-to-Cyrillic transliteration (iPhoneайфоун)
  • reads explicit URLs such as https://example.com/a1 closer to how they would be spoken aloud
  • can remove bracketed references such as [1]

ru-normalizr can add correct stress marks ONLY when converting Latin text to Cyrillic.
To enable this feature, use the CLI flag --with-latin-stress or the setting enable_latinization_stress_marks=True.

The package does not add stress marks to Russian words. For a TTS pipeline, it is better to add stress marks as a separate stage, for example with the Silero Stress package.

Installation

pip install ru-normalizr

The IPA latinization backend (latinization_backend="ipa") works out of the box: the eng_to_ipa package is a default dependency. If it ever becomes unavailable, requesting the IPA backend emits a one-time warning and automatically falls back to the dictionary latinization backend.

Running from cmd or bash (CLI)

Normalize a string (uses safe mode by default):

ru-normalizr "Глава IV. Встреча в 10:07."

Use TTS mode (recommended):

ru-normalizr "Глава IV. Встреча в 10:07." --mode tts

Read text from a file and save the result to another file:

ru-normalizr --mode tts --file ./sample.txt --output ./sample.normalized.txt

If the ru-normalizr command does not work, prepend it with python -m, for example:

python -m ru_normalizr "Глава IV. Встреча в 10:07." --mode tts

Useful flags:

  • --mode safe|tts
  • --with-latin-stress — add correct stress marks when possible during Latin-to-Cyrillic conversion
  • --keep-links — do not remove references such as [1], (2.5), etc.
  • --check — print the result to stdout and exit with status 1 if it differs from the input, 0 if unchanged (cannot be combined with --output)
  • --version — print the installed ru-normalizr version and exit

Usage in Python

The simplest way is to use normalize():

from ru_normalizr import normalize

# safe mode by default
print(normalize("Глава IV. Встреча в 10:07."))
# Глава четвёртая. Встреча в десять, ноль семь.

If you process many texts with the same settings, it is more convenient to create a Normalizer:

from ru_normalizr import NormalizeOptions, Normalizer

# tts mode:
normalizer = Normalizer(NormalizeOptions.tts())

print(normalizer.normalize("ГИБДД"))
# ги бэ дэ дэ

print(normalizer.normalize_batch(["Глава IV.", "В 1980-е годы было 25 млн."]))
# "Глава четвёртая.", "В тысяча девятьсот восьмидесятые годы было двадцать пять миллионов."

For finer control, use NormalizeOptions:

from ru_normalizr import NormalizeOptions, normalize

options = NormalizeOptions.tts(
    latinization_backend="ipa",
    enable_latinization_stress_marks=True,
    letter_vowel_mode="double",    # use "single" for ordinary vowel names
    initials_pause_mode="comma",   # use "preserve" for source punctuation
)

print(normalize("YouTube в 2024 г.", options))
# +ютуб в две тысячи двадцать четвёртом году.

All NormalizeOptions fields

The default value depends on the mode: NormalizeOptions() and NormalizeOptions.safe() use the safe column, NormalizeOptions.tts() uses the tts column. Any option can be overridden explicitly.

Option Type safe tts Description
enable_caps_normalization bool False True Normalize ALL-CAPS headings (ГЛАВАГлава)
enable_first_word_decap bool False True Lowercase the first word of a sentence during caps normalization
remove_links bool False True Remove bracketed reference links such as [1], (2.5)
enable_url_normalization bool False True Read explicit URLs (http://, https://, www.) aloud by separators and digits; when False, preserve them verbatim
remove_links_ignore_interval tuple[int, int] (1000, 2200) (1000, 2200) Range of bracketed numbers treated as years and kept, not removed as references
enable_year_normalization bool True True Normalize years, decades, and year ranges
enable_roman_normalization bool True True Roman numeral handling
enable_dates_time_normalization bool True True Normalize dates and time expressions
enable_numeral_normalization bool True True Normalize cardinals, ordinals, fractions, and decimals
enable_abbreviation_expansion bool True True Expand abbreviations (ул., и т. д.)
enable_contextual_abbreviation_expansion bool True True Inflect adjective-like abbreviations from context (гос.государственного)
enable_years_ago_expansion bool True True Expand л. н.лет назад in numeric context
enable_initials_expansion bool False True Expand initials (Ч.чэ)
letter_vowel_mode "single" | "double" "single" "double" With double, doubles independently pronounced vowels in initials, abbreviations, and letter indices (Е.ее, SUэс юю)
initials_vowel_mode "single" | "double" alias alias Backwards-compatible deprecated name for letter_vowel_mode; conflicting simultaneous values are rejected
initials_pause_mode "preserve" | "comma" "preserve" "comma" With comma, adds comma pauses around initials in "Surname I." order
cleanup_malformed_punctuation bool False True At the end of the pipeline, collapse the noisy ., sequence to a period; it can be disabled in TTS or enabled explicitly for dirty input
enable_letter_abbreviation_expansion bool False True Read abbreviations letter by letter (ГИБДДги бэ дэ дэ)
enable_dictionary_normalization bool True True Apply dictionary rewrite rules from user .dic files
enable_latinization bool False True Transliterate Latin words into Cyrillic
latinization_backend "ipa" | "dictionary" "ipa" "ipa" Latinization backend (IPA works by default)
enable_latinization_stress_marks bool False False Keep + stress markers when using IPA latinization
latin_dictionary_filename str "latinization_rules.dic" "latinization_rules.dic" Name of the .dic in latinization/ used by the latinization stage
dictionary_include_files tuple[str, ...] () () If set, load only these .dic files (otherwise all except latinization/)
dictionary_exclude_files tuple[str, ...] () () Which .dic files to skip when loading
dictionaries_path Path | None None None Root directory of .dic files (defaults to the bundled dictionaries/)
mode "safe" | "tts" | None None None Preset marker; sets the defaults for the remaining options

Important: year and general-numeral stages are controlled independently. remove_links_ignore_interval only prevents a bracketed number from being removed as a reference: with the default interval, [1500] is kept as a possible year. Then enable_year_normalization=True still expands it to (одна тысяча пятьсот), even when enable_numeral_normalization=False. To keep the digits, disable both numeric stages: enable_year_normalization=False and enable_numeral_normalization=False.

Custom dictionaries

The dictionary stage applies user-defined rewrite rules from .dic files. Note: by default it loads zero bundled rules — the only bundled .dic belongs to the latinization/ folder and is used by the latinization stage, not dictionary. In other words, the dictionary stage exists purely for your own dictionaries.

The .dic format is shown below:

# lines starting with # are comments
# one rule per line: source=replacement
YouTube=ютуб
OpenAI=оупэнэйай
# `*` is a wildcard for a run of non-space characters
*Tube=тюб
Open*=оупен

There are two ways to load dictionaries: drop .dic files into the package's dictionaries/ folder (loaded automatically, except latinization/), or point dictionaries_path at your own folder:

from pathlib import Path
from ru_normalizr import NormalizeOptions, normalize

# ./my_dicts/brands.dic:
#   YouTube=ютуб
#   OpenAI=оупэнэйай
options = NormalizeOptions(dictionaries_path=Path("./my_dicts"))

print(normalize("YouTube и OpenAI", options))
# ютуб и оупэнэйай

You can load only some files from a folder via dictionary_include_files, or skip individual ones via dictionary_exclude_files (by file name or path relative to dictionaries_path).

Modularity

ru-normalizr can be used not only as one big black box, but also as a modular pipeline.

Individual stages are available:

  • urls — early normalization of explicit URLs in TTS mode: turns separators into spoken words and reads digits inside links digit-by-digit while leaving plain Latin words for the later latinization stage
  • preprocess — preliminary text cleanup: spaces, line breaks, punctuation, some OCR-like glued text, and other preparation before the main normalization stages
  • roman — Roman numeral handling
  • dates_time — normalization of dates and time expressions
  • years — normalization of years, decades, and year ranges
  • numerals — normalization of cardinal numbers, ordinal forms, fractions, decimals, and other numeric expressions
  • abbreviations — expansion of abbreviations, initials, and letter-by-letter abbreviations depending on the selected mode
  • dictionary — application of user-defined dictionary rules (no bundled rules are loaded here by default; see Custom dictionaries)
  • latinization — conversion of Latin words into Cyrillic using either dictionary rules or the IPA backend
  • finalize — final cleanup after all transformations: punctuation normalization, casing fixes, and paragraph restoration

Development

The environment is managed with uv. Python 3.10 or newer is required.

uv sync --frozen

The checks that must pass before a commit (CI runs the same set):

uv run ruff check .
uv run pyright
uv run pytest -q

Branch coverage report:

uv run pytest -q --cov=src/ru_normalizr --cov-branch --cov-report=term-missing

The full release verification cycle — clean, version sync, lint, types, tests, build, and twine check:

uv run python scripts/dev.py check

The PyPI release procedure is described in docs/RELEASING.md, the versioning policy in VERSIONING.md, and the code conventions in AGENTS.md and docs/agents/.

Comparison with other normalizers

The comparison was run on the same machine and the same texts.

Normalizer Speed Quality
ru-normalizr ⚡ very fast ✅ high
RuNorm 🐢 slow ❌ unstable
Demagog ⚡ fast ❌ limited

A comparison was also made with the promising russian_text_normalizer. It performed much better than RuNorm, but it also makes errors in normalizing numerals (for example, получил приблизительно 2500 голосов.получил приблизительно двести пятьдесят тысяч голосов.), does not normalize everything, and omits words from the original text.

Input text (example)

Глава 1
      ПОСЛЕДНИЕ СЛОВА ГЕНЕРАЛА

В 1990–2000 гг компания выпустила 15 моделей iPhone XII и продала 1 234 567 устройств в странах СНГ и ЕС.

АВТОМОБИЛЕСТРОЕНИЕ КОНВЕЙЕРНОЕ организовал американский промышленник Р. Э. Олдс – основатель компании «Олдс Мотор Уоркс» (Olds Motor Works) в городе Детройте.

Компания ABC Ltd. протестировала систему на 3.5 TB данных со скоростью 100 Mb/s.
Размер модели: 2.7 GB, точность: 99.5%.

Output produced by ru-normalizr (tts mode)

Глава один.
Последние слова генерала.

В тысяча девятьсот девяностых — двухтысячных годах компания выпустила пятнадцать моделей айфоун двенадцать и продала один миллион двести тридцать четыре тысячи пятьсот шестьдесят семь устройств в странах эс эн гэ и е эс.

Автомобилестроение конвейерное организовал американский промышленник эр ээ Олдс — основатель компании "Олдс Мотор Уоркс" (оулдз моутэр вэркс) в городе Детройте.

Компания эй би си элтиди. Протестировала систему на трёх целых пять десятых терабайта данных со скоростью ста мегабайтов в секунду.
Размер модели: две целых семь десятых гигабайта, точность: девяносто девять целых пять десятых процента.

Normalizing this example took 0.24 seconds. Abbreviations and initials are intentionally expanded into letter names. This can be disabled.

A real book (here and below: David Bergland, Libertarianism in One Lesson from Flibusta — 274K characters) was processed by ru-normalizr in 2 seconds.

Output produced by RuNorm Big

Глава первая пэ о эс эл е дэ эн и е эс эл о вэ а гэ е эн е эр а эл а в тысяча девятьсот девяносто первом – две тысячи втором годах компания выпустила пятнадцать моделей айфон двенадцать и продала один миллион двести двадцать четыре тысячи пятьсот семьдесят семь устройств в странах эс эн гэ и е эс. А вэ тэ о эм о бэ и эл е эс тэ эр о е эн и е ка о эн вэ е ий е эр эн о е, на промышленной основе, организовал американский промышленник р. э. олдс – основатель компании «олдс мотор уоркс » ( олдс мотор уоркс ) в городе детройте. Компания эй би си литд. протестировала систему на три целых и пять десятых терабайта данных со скоростью сто мегабайт /секунд. Размер модели : два целых семь десятых гигабайта, точность : девяносто девять целых и пять десятыхпроцент.

Normalizing this example took 15.76 seconds.

A real book (274K characters) was processed in 143 seconds by RuNorm Small and in 723 seconds by RuNorm Big.

RuNorm is significantly slower and uses significantly more resources. Its normalization quality is low and unstable. It also breaks formatting.

Output produced with Demagog dictionaries

Глава первая
ПОСЛЕДНИЕ СЛОВА ГЕНЕРАЛА

В одна тысяча девятьсот девяносто–две тысячи компания выпустила пятнадцать моделей ифон двенадцать и продала один двести тридцать четыре пятьсот шестьдесят семь устройств в странах СНГ и ЕС.

АВТОМОБИЛЕСТРОЕНИЕ КОНВЕЙЕРНОЕ организовал американский промышленник Р. Э. Олдс – основатель компании «Олдс Мотор Уоркс» (олдс мото Оркс) в городе Детройте.

Компания эбс лимитэд. протестировала систему на три с половиной тб данных со скоростью сто мегабайтов /с.
Размер модели: два,семь гигабайтов , точность: девяносто девять с половиной%.

Popular Demagog dictionaries 10_REX_числа(chisla).rex and 65_ЛАТИНИЦА@.dic were used.

Normalizing this example took less than one second.

A real book (274K characters) was processed by Demagog in 35 seconds.

Normalization quality is worse than ru-normalizr, especially in cases not covered by the dictionary. Functionality is also more limited.

About

ru-normalizr — лучший нормализатор русского текста без LLM. Приводит числа, даты, время, сокращения, римские цифры, символы и латиницу в русские буквы для использования в TTS и NLP.

Topics

Resources

Stars

23 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages