npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ozgurguler/trnorm

v0.1.3

Published

Turkish text normalization for speech, in JavaScript: written -> spoken form for TTS, evaluation normalization for ASR (WER/CER), and spoken -> digits. The same output as the Python trnorm.

Readme

trnorm-js

Turkish text normalization for speech, in JavaScript. Turns written Turkish into the way it is spoken (for text-to-speech), gives a canonical form for scoring speech recognition (WER/CER), and turns spoken numbers back into digits. A port of the Python trnorm with the same output for the same input. No dependencies; runs in browsers and Node 18+.

npm install @ozgurguler/trnorm
import { normalize, normalize_for_eval, inverse_normalize, number_to_words } from '@ozgurguler/trnorm';

normalize("Saat 09:30'da 3. kata gelin, ücret 1.250,50 TL.");
// "Saat dokuz otuzda üçüncü kata gelin, ücret bin iki yüz elli lira elli kuruş."

normalize_for_eval("ABD'de 5 TL mi?");                            // "a be dede beş lira mı"
inverse_normalize('saat on dört otuzda yüzde on beşlik indirim'); // "saat 14.30'da %15'lik indirim"
number_to_words(1923);                                            // "bin dokuz yüz yirmi üç"

What it reads

Numbers, dates, times, money, percentages, units, ordinals, abbreviations, acronyms, phone numbers, IBANs, licence plates, URLs and e-mail addresses, foreign names. Turkish suffixes are re-agreed with the spoken form, which is the hard part a TTS model gets wrong:

| Written | Spoken (normalize) | |---|---| | 15.00'de | on beşte | | %15'lik | yüzde on beşlik | | 4'üncü, 3. kat | dördüncü, üçüncü kat | | 1.250,50 TL | bin iki yüz elli lira elli kuruş | | 5 TL mi? | beş lira mı? | | 14.10.2026 | on dört Ekim iki bin yirmi altı | | 5 km/sa | saatte beş kilometre | | SGK'ye | se ge kaya | | Dr. Yılmaz | doktor Yılmaz | | iPhone 15 | ayfon on beş | | 0555 555 55 55 | sıfır beş yüz elli beş, beş yüz elli beş, elli beş, elli beş | | 34 ABC 123 | otuz dört a be ce yüz yirmi üç |

The rules and the reasons behind each choice (in Turkish): docs/TRNORM.md.

API

| Function | What it does | |---|---| | normalize(text) | Written → spoken form, for TTS input. Case, punctuation and apostrophes on proper nouns are kept (models use them for prosody). | | normalize_for_eval(text) | Canonical form for WER/CER: spoken form, Turkish lowercase, no punctuation, "buçuk" = "virgül beş". Apply it to both the reference and the hypothesis. | | inverse_normalize(text) | Spoken → written with digits, for ASR output. Small numbers standing alone stay words ("üç kişi"), as Turkish style guides recommend. | | number_to_words(n), ordinal_to_words(n) | 4 → "dört", "dördüncü". n is a bigint or an integer; any other number is read as the Python float it is. | | tr_lower(text), tr_upper(text) | Turkish-aware case (I ↔ ı, İ ↔ i). |

TypeScript types are included. Where the Python raises, this throws (for example a ValueError for a number of more than 4300 digits).

Is it really the same as the Python?

That is the point of it. The port reproduces Python's str, re and int behaviour where JavaScript differs (src/py.js): indexing by code point, Python's Unicode \w \d \s \b, $ also matching before a final newline, unbounded integers (BigInt), Unicode 15.0 case tables (src/case_tables.js), characters newer than Python 3.12's Unicode (src/unassigned.js), and lone surrogates.

  • npm test runs 7,566 texts and 2,505 numbers from test/golden.json through every function. tools/golden.py writes that file from the Python trnorm in ahenk-turkish-tts; after a change on the Python side:

    git clone --depth 1 https://github.com/ozguradmin/ahenk-turkish-tts ../ahenk-turkish-tts
    npm run golden   # needs Python 3.12
    npm test
  • A wider parity harness lives in ahenk-cep-pdfreader: test suites, real documents and generated hostile input (huge numbers, invisible characters, combining marks, emoji, mixed scripts, lone surrogates, Unicode 16 characters). Its last run matched Python 3.12 on all 742,716 results. The tests pass in V8 (Node, Chrome) and JavaScriptCore (Safari). This library is the in-browser text front-end of that site, Ahenk Okur, which reads PDFs aloud with the Ahenk Cep voice.

Safari 26.0–26.5: their RegExp engine reads a character in U+F800–U+FBFF (or a surrogate) followed by one in U+FC00–U+FFFF (such as fi5) as a single character. For a text with such a pair, the functions throw an error with name === 'EngineError' rather than give a wrong answer; send that text to a server or to the Python instead.

Requirements: a JavaScript engine with ES2022 and Unicode property escapes (Node 18+, current browsers, Safari 16.4+).

Türkçe

Yazılı Türkçeyi okunuş formuna çeviren trnorm'un JavaScript sürümü: tarayıcıda ve Node'da, bağımlılıksız, Python'la aynı girdiye aynı çıktıyı verir. Sayı, tarih, saat, para, yüzde, birim, kısaltma, telefon, IBAN, plaka, URL okunur; ekler okunuşa uyar (15.00'de → "on beşte", SGK'ye → "se ge kaya").

  • normalize: yazılı metin → okunuş (TTS girdisi); büyük/küçük harf ve noktalama korunur.
  • normalize_for_eval: WER/CER ölçümü için kanonik biçim; referansa ve çıktıya aynen uygulanır.
  • inverse_normalize: okunuş → rakamlı yazı (ASR çıktısı için).
  • number_to_words, ordinal_to_words, tr_lower, tr_upper.

npm test, Python trnorm'un 7.566 metin ve 2.505 sayıdaki çıktısıyla karşılaştırır; daha geniş eşitlik testinde 742.716 sonucun hepsi Python 3.12'yle aynı çıktı. Kurallar ve tercihler: docs/TRNORM.md.

License

Apache-2.0. The Python trnorm, its rules and the Ahenk speech models: ozguradmin/ahenk-turkish-tts.