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

lyric-romanizer

v0.1.1

Published

Script detection and local romanization engine for lyrics — supports Japanese, Chinese, Korean, Cyrillic, Indic scripts, Tamil, Thai, and more

Readme

lyric-romanizer

npm version license

Script detection and local romanization engine for lyrics. Supports 13 scripts across Japanese, Chinese, Korean, Cyrillic, Indic, Tamil, Thai, and Latin — all running locally with zero API calls.

Extracted from Spotify Karaoke. Used by OpenKara.

Installation

npm install lyric-romanizer
yarn add lyric-romanizer
pnpm add lyric-romanizer

Quick Start

import { createRomanizer, detectScript } from 'lyric-romanizer';

const romanizer = createRomanizer();

// Auto-detect script and romanize
const result = await romanizer.romanizeLines(['你好世界', 'こんにちは']);
// { script: 'chinese', lines: ['nǐ hǎo shì jiè', 'こんにちは'] }

// Romanize a single line
const line = await romanizer.romanizeLine('안녕하세요');
// 'annyeonghaseyo'

API

Imports

// Main entry — full romanization engine
import {
  createRomanizer,
  detectScript,
  isLatinScript,
  requiresExternalRomanization,
  UnsupportedRomanizationError,
} from 'lyric-romanizer';

// Detector-only subpath — lightweight, no romanization dependencies
import { detectScript, isLatinScript, NON_LATIN_SCRIPT_RE } from 'lyric-romanizer/detector';

Types

type ScriptType =
  | 'japanese' | 'chinese' | 'korean' | 'cyrillic'
  | 'devanagari' | 'gujarati' | 'gurmukhi' | 'telugu'
  | 'kannada' | 'odia' | 'tamil' | 'malayalam'
  | 'bengali' | 'arabic' | 'hebrew' | 'thai'
  | 'latin' | 'other';

interface Romanizer {
  romanizeLine(line: string, options?: RomanizeOptions): Promise<string>;
  romanizeLines(lines: readonly string[], options?: RomanizeOptions): Promise<RomanizeResult>;
}

type RomanizeOptions = { script?: ScriptType };
type RomanizeResult = { script: ScriptType; lines: string[] };
type RomanizerOptions = { japaneseDictPath?: string };

Functions

createRomanizer(options?)

Factory that returns a Romanizer instance. The Kuroshiro engine (Japanese) is lazily initialized on first use and cached.

const romanizer = createRomanizer();

// Override the Kuromoji dictionary CDN path (e.g. for self-hosting)
const romanizer = createRomanizer({
  japaneseDictPath: 'https://my-cdn.com/kuromoji/dict',
});

detectScript(lines)

Detects the dominant script in the given text lines. Checks for Japanese kana first (definitive), then scores all other scripts by character count.

detectScript(['こんにちは']);          // 'japanese'
detectScript(['你好世界']);            // 'chinese'
detectScript(['Привет']);             // 'cyrillic'
detectScript(['Hello world']);        // 'latin'
detectScript(['123 ???']);            // 'other'

isLatinScript(lines)

Fast check — returns true if the text contains only Latin letters (no CJK, Cyrillic, Indic, etc.). Useful for skipping romanization entirely.

isLatinScript(['Hello world']);  // true
isLatinScript(['안녕하세요']);    // false
isLatinScript(['♪♪♪']);         // false (no letters)

requiresExternalRomanization(script)

Returns true for scripts that cannot be romanized locally and require an external API.

requiresExternalRomanization('chinese');   // false
requiresExternalRomanization('arabic');    // true
requiresExternalRomanization('malayalam'); // true

Romanizer Interface

romanizer.romanizeLine(line, options?)

Romanizes a single line. If script is omitted, it is auto-detected via detectScript. Returns the original line unchanged for Latin text or non-letter content.

Throws UnsupportedRomanizationError for external scripts.

await romanizer.romanizeLine('你好世界');
// 'nǐ hǎo shì jiè'

await romanizer.romanizeLine('Привет мир');
// 'Privet mir'

await romanizer.romanizeLine('Hello world');
// 'Hello world' (no-op)

await romanizer.romanizeLine('مرحبا');
// throws UnsupportedRomanizationError { script: 'arabic' }

romanizer.romanizeLines(lines, options?)

Romanizes multiple lines in parallel. Returns the detected script and romanized lines.

const { script, lines } = await romanizer.romanizeLines([
  'สวัสดี',
  'ชาวโลก',
]);
// { script: 'thai', lines: ['sawatdi', 'chaolok'] }

UnsupportedRomanizationError

Thrown when attempting to romanize a script that requires an external API. Has a script property for programmatic handling.

try {
  await romanizer.romanizeLine('مرحبا');
} catch (err) {
  if (err instanceof UnsupportedRomanizationError) {
    console.log(err.script); // 'arabic'
    // fall back to external API
  }
}

Supported Scripts

Local (fully offline)

| Script | Engine | Example | |--------|--------|---------| | Japanese | kuroshiro + kuromoji | こんにちはkonnichiha | | Chinese | pinyin-pro | 你好nǐ hǎo | | Korean | @romanize/korean | 안녕annyeong | | Cyrillic | cyrillic-to-translit-js | ПриветPrivet | | Devanagari | sanscript | नमस्तेnamaste | | Gujarati | sanscript | નમસ્તેnamaste | | Gurmukhi | sanscript | ਨਮਸਤੇnamaste | | Telugu | sanscript | నమస్తేnamaste | | Kannada | sanscript | ನಮಸ್ತೆnamaste | | Odia | sanscript | ନମସ୍ତେnamaste | | Tamil | tamil-romanizer | வணக்கம்vanakkam | | Thai | @dehoist/romanize-thai | สวัสดีsawatdi | | Latin | (no-op) | HelloHello |

External (requires API)

malayalam, bengali, arabic, hebrew, other — use requiresExternalRomanization() to detect these and branch to your preferred API.

Cyrillic Detection

Cyrillic auto-detects Ukrainian-specific characters (і, ї, є, ґ) and applies the Ukrainian transliteration preset. All other Cyrillic text defaults to Russian.

License

MIT