text-processing-rs

Library Listed

Inverse Text Normalization (ITN) — convert spoken-form ASR output to written form

Checked · Sources

Downloads
3,608
Dependents
2
Stars
48
Forks
21
Contributors
8
Commits
132
Latest release
0.2.2 ·

About

From the project README ·

A Rust port of NVIDIA NeMo Text Processing supporting both Inverse Text Normalization (ITN) and Text Normalization (TN).

What it does

ITN: Spoken → Written

Converts spoken-form ASR output to written form:

InputOutput
two hundred thirty two232
five dollars and fifty cents$5.50
january fifth twenty twenty fiveJanuary 5, 2025
quarter past two pm02:15 p.m.
one point five billion dollars$1.5 billion
seventy two degrees fahrenheit72 °F
TN: Written → Spoken

Converts written-form text to spoken form (useful for TTS preprocessing):

InputOutput
123one hundred twenty three
$5.50five dollars and fifty cents
January 5, 2025january fifth twenty twenty five
2:30 PMtwo thirty p m
1stfirst
200 km/htwo hundred kilometers per hour

Usage

Rust
use text_processing_rs::{normalize, tn_normalize};

// ITN: spoken → written
let result = normalize("two hundred");
assert_eq!(result, "200");

let result = normalize("five dollars and fifty cents");
assert_eq!(result, "$5.50");

// TN: written → spoken
let result = tn_normalize("$5.50");
assert_eq!(result, "five dollars and fifty cents");

let result = tn_normalize("123");
assert_eq!(result, "one hundred twenty three");
JavaScript (WASM)

Build wasm artifacts:

Read the full README
npm run wasm:build:node
npm run wasm:build:web

Node usage:

import * as wasm from "./pkg-node/text_processing_rs.js";

console.log(wasm.normalize("two hundred")); // "200"
console.log(wasm.tnNormalize("$5.50")); // "five dollars and fifty cents"

wasm.addRule("gee pee tee", "GPT");
console.log(wasm.normalize("gee pee tee")); // "GPT"

The generated npm package name is @fluidinference/text-processing-rs.

Web project usage (Vite / Next.js / webpack):

npm install @fluidinference/text-processing-rs
import init, * as wasm from "@fluidinference/text-processing-rs";

async function run() {
  // Loads and initializes the .wasm module (required once at startup)
  await init();

  const itn = wasm.normalize("two hundred");
  const tn = wasm.tnNormalize("$5.50");

  console.log(itn); // "200"
  console.log(tn); // "five dollars and fifty cents"

  wasm.addRule("gee pee tee", "GPT");
  console.log(wasm.normalize("gee pee tee")); // "GPT"
}

run();

If your framework supports top-level await, you can initialize at module load time:

import init, * as wasm from "@fluidinference/text-processing-rs";
await init();

Sentence-level normalization scans for normalizable spans within a larger sentence:

use text_processing_rs::{normalize_sentence, normalize_sentence_lang, tn_normalize_sentence};

// ITN sentence mode
let result = normalize_sentence("I have twenty one apples");
assert_eq!(result, "I have 21 apples");

// ITN sentence mode, language-aware ("en", "fr", "es", "de", "zh", "hi", "ja")
let result = normalize_sentence_lang("j'ai vingt et un ans", "fr");
assert_eq!(result, "j'ai 21 ans");

// TN sentence mode
let result = tn_normalize_sentence("I paid $5 for 23 items");
assert_eq!(result, "I paid five dollars for twenty three items");
Swift
import NemoTextProcessing

// ITN: spoken → written
let result = NemoTextProcessing.normalize("two hundred")
// "200"

// TN: written → spoken
let spoken = NemoTextProcessing.tnNormalize("$5.50")
// "five dollars and fifty cents"

// Sentence modes
let itn = NemoTextProcessing.normalizeSentence("I have twenty one apples")
// "I have 21 apples"

// Language-aware ITN sentence mode ("en", "fr", "es", "de", "zh", "hi", "ja")
let itnFr = NemoTextProcessing.normalizeSentence("j'ai vingt et un ans", language: "fr")
// "j'ai 21 ans"

let tn = NemoTextProcessing.tnNormalizeSentence("I paid $5 for 23 items")
// "I paid five dollars for twenty three items"
CLI
# ITN
nemo-itn two hundred thirty two        # → 232
nemo-itn -s "I have twenty one apples" # → I have 21 apples

# TN
nemo-tn 123                            # → one hundred twenty three
nemo-tn '$5.50'                        # → five dollars and fifty cents
nemo-tn -s 'I paid $5 for 23 items'    # → I paid five dollars for twenty three items

# Pipe from stdin
echo "2:30 PM" | nemo-tn               # → two thirty p m

Compatibility

ITN (Spoken → Written)

98.6% compatible with NeMo text processing test suite (1200/1217 tests passing).

CategoryStatus
Cardinal numbers100%
Ordinal numbers100%
Decimal numbers100%
Money100%
Measurements100%
Dates100%
Time97%
Electronic (email/URL)96%
Telephone/IP96%
Whitelist terms100%
TN (Written → Spoken)
CategoryExamples
Cardinal numbers123 → one hundred twenty three
Ordinal numbers1st → first, 21st → twenty first
Decimal numbers3.14 → three point one four
Money$5.50 → five dollars and fifty cents
Measurements200 km/h → two hundred kilometers per hour
DatesJanuary 5, 2025 → january fifth twenty twenty five
Time2:30 PM → two thirty p m
Electronic (email/URL)test@gmail.com → t e s t at g m a i l dot c o m
Telephone123-456-7890 → one two three, four five six, seven eight nine zero
Whitelist termsDr. → doctor, Mr. → mister

Features

  • ITN (Inverse Text Normalization): spoken → written form for ASR post-processing
  • TN (Text Normalization): written → spoken form for TTS preprocessing
  • Cardinal and ordinal number conversion (both directions)
  • Decimal numbers with scale words (million, billion)
  • Currency formatting (USD, GBP, EUR, JPY, and more)
  • Measurements including temperature (°C, °F, K) and data rates (gbps)
  • Date parsing (multiple formats) and decade verbalization (1980s → nineteen eighties)
  • Time parsing with AM/PM, 24-hour format, and timezone preservation
  • Email and URL normalization
  • Phone numbers, IP addresses, SSN
  • Case preservation for proper nouns and abbreviations
  • Sentence-level normalization with sliding window span matching
  • Custom rules for domain-specific terms
  • C FFI for integration with Swift, Python, and other languages

Building

Rust
cargo build
cargo test
WASM + JavaScript
# Build + smoke test (Node) + build browser artifact
npm run wasm:ci

# Create a tarball from the browser package
npm run wasm:pack

# Publish browser package to npm (requires npm auth)
npm run wasm:publish
CLI Tools
# Build the Rust library (release, with FFI)
cargo build --release --target aarch64-apple-darwin --features ffi

# Build Swift CLI tools
cd swift-test && swift build

Binaries are at swift-test/.build/debug/nemo-itn and swift-test/.build/debug/nemo-tn.

Swift (XCFramework)
# Install Rust targets
rustup target add aarch64-apple-darwin x86_64-apple-darwin
rustup target add aarch64-apple-ios aarch64-apple-ios-sim

# Build XCFramework
./build-xcframework.sh

Output:

  • output/NemoTextProcessing.xcframework - Add to Xcode project
  • output/NemoTextProcessing.swift - Swift wrapper

License

Apache 2.0

Acknowledgments

This project is a Rust implementation based on the inverse text normalization grammars from NVIDIA NeMo Text Processing. All credit for the original algorithms and test cases goes to the NVIDIA NeMo team.

Runs on
GPU support unknown
What shows it
  • no dependency or feature matches a backend row
Native code
None found
  • No native code found
Python bindings
No Python binding crate found
License
Apache-2.0

Compare with similar projects

Order: strongest tier first, then most downloads, then name, with unmeasured values last. A library counts downloads over 30 days. An app counts binary downloads per release. An app with none counts its crate downloads over 30 days. ML Rust runs this catalogue, so its closest project always gets a row.

text-processing-rs beside the projects that share a topic with it
ProjectDownloadsDependentsStarsLatest releaseRuns on (GPU)Native codeLicense
text-processing-rs This page Listed

Inverse Text Normalization (ITN) — convert spoken-form ASR output to written form

3,608 downloads over 30 days 2 48 0.2.2 GPU support unknown No native code found Apache-2.0

Fast ASR & Speaker Diarization with NVIDIA Parakeet via ONNX

51,716 downloads over 30 days 8 421 0.4.4 CUDA, WebGPU, Metal, Vulkan Binds native code: ort-sys (feature api-24) Apache-2.0 OR MIT
rustfst Listed

Library for constructing, combining, optimizing, and searching weighted finite-state transducers (FSTs).

24,531 downloads over 30 days 10 192 1.3.1 GPU support unknown No native code found Apache-2.0 OR MIT
tts-rs Listed

High-level Text-To-Speech (TTS) interface

21,892 downloads over 30 days 19 143 0.26.3 GPU support unknown Binds native code: speech-dispatcher-sys, tolk-sys (feature tolk) MIT

Japanese text preprocessor for Text-to-Speech application (OpenJTalk rewrite in rust language).

14,491 downloads over 30 days 7 61 0.15.0 GPU support unknown No native code found BSD-3-Clause
voxr New

Audio and speech on boostr: compressed-audio decode, resampling, DSP and grapheme to phoneme, with speech synthesis (Kokoro, VITS), voice cloning (VoxCPM2), transcription with word timings (Whisper), voice activity (Silero) and audio-text scoring (CLAP), on CPU or CUDA.

642 downloads over 30 days 2 2 0.9.1 CUDA No native code found Apache-2.0
sherpa-rs Archived

Rust bindings to https://github.com/k2-fsa/sherpa-onnx

9,888 downloads over 30 days 4 313 0.6.8 CUDA Binds native code: sherpa-rs-sys. Build uses a native toolchain: bindgen, cmake MIT
Why ListedNo archive, New or Featured rule places it, so it is Listed.

What it lacks for Featured

  • It has 2 active dependents and 2 new dependents in 365 days. Featured needs 11 active dependents or 9 new dependents in 365 days. The repository route reads 48 stars, 21 forks and 8 contributors. It needs 4,539 stars, 434 forks and 79 contributors together.

What else the check found

  • The last activity is on 1 Nov 2026, 8 days ago. The rules expect an update within 365 days. Dormancy rule

Exact rules: Listed, Featured library. The check on applied thresholds version 9.

Every rule outcome, as the check records it
  • expected-interval: version 0.2.2 base 300, capped at 3 releases = 150, raised to the 365-day floor: E = 365 days
  • dormancy: last activity 2026-11-01, 8 days ago, E = 365: active
  • featured: not featured: active_users 2 < 11 and new_adopters 2 < 9 and repository route stars 48 (needs 4539), forks 21 (needs 434), contributors 8 (needs 79)
  • listed: listed: no archive, new or featured rule applies

Measurements and sources

Each signal row shows the raw value, its source and the fetch date. The Sources list gathers every distinct source on this page. Movement needs more history.

Sources6 sources
Adoption2 active dependents, 2 new dependents in 365 days1 not measured
Adoption measurements
SignalValueSourceFetchedCounts
Active dependents2crates.io database dumpYes
New dependents (365 days)2crates.io database dumpYes
Peak active dependents2crates.io database dumpYes
Active dependents as a share of the peak1crates.io database dumpYes
Binary downloads per releaseNot measuredYes
MaintenanceLatest human commit: 8 days ago3 not measured
Maintenance measurements
SignalValueSourceFetchedCounts
Latest human commit2026-11-01GitHub repositoryYes
Latest release2026-04-27crates.io database dumpYes
First release2026-03-07crates.io database dumpYes
Latest version0.2.2crates.io database dumpYes
Days the oldest outside issue waits for a reply0GitHub repositoryYes
Maintainer replies over 365 days8GitHub repositoryYes
Repository archivedfalseGitHub repositoryYes
Repository gonefalseGitHub repositoryYes
Release commit or manifest in the repotruecrates.io packageYes
Published docstruecrates.io database dumpYes
Latest release fails cargo checkNot measuredYes
Build check dateNot measuredYes
Build check toolchainNot measuredYes
SecurityUnpatched vulnerabilities: none
Security measurements
SignalValueSourceFetchedCounts
Unpatched vulnerabilities past the grace windownoneRustSec advisory databaseYes
RustSec unmaintained advisorynoneRustSec advisory databaseYes
Deprecation noticenonecrates.io database dumpYes
Deprecated requirementnonecrates.io database dumpYes
Distribution3 releases, 3,608 downloads over 30 days5 not measured
Distribution measurements
SignalValueSourceFetchedCounts
Releases3crates.io database dumpYes
Downloads over 30 days3608crates.io database dumpYes
Binary downloads over the last 3 releasesNot measuredYes
Homebrew installs on request over 365 daysNot measuredYes
Repology repository familiesNot measuredYes
Docker pull growth over 90 daysNot measuredYes
Docker pulls at the growth baselineNot measuredYes
Every version a prereleasefalsecrates.io database dumpYes
Every version 0.0.xfalsecrates.io database dumpYes
Every version yankedfalsecrates.io database dumpYes
Crate deleted from crates.iofalsecrates.io database dumpYes
Repository48 stars, 21 forks, 8 contributors, 132 commits. Stars alone never qualify a project.
Repository measurements
SignalValueSourceFetchedCounts
GitHub stars48GitHub repositoryYes — one part of the repository route
GitHub forks21GitHub repositoryYes — one part of the repository route
Contributors with a GitHub account8GitHub contributorsYes — one part of the repository route
Commits on the default branch132GitHub repositoryNo
Repository's main crate left out of the entrynonecrates.io database dumpYes
Facts that never count
Facts that never count measurements
SignalValueSourceFetchedCounts
LicenseApache-2.0crates.io database dumpNo
Runs on, from listed crates and their direct dependenciesGPU support unknowncrates.io database dumpNo
What shows each backendno dependency or feature matches a backend rowcrates.io database dumpNo
Native code, from listed crates and their dependenciesNo native code foundcrates.io database dumpNo
Python bindings, from direct dependencies of listed cratesNo Python binding crate foundcrates.io database dumpNo
Crates that depend on a listed crate directly2crates.io database dumpNo

Corrections and review history

Report a correction

No correction applies to this entry.

Judgments

A judgment enters only through an approved issue and a merged pull request.

No judgment applies to this entry.