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 🙏

© 2025 – Pkg Stats / Ryan Hefner

n2w

v1.2.1

Published

Type-safe number-to-words for JavaScript and TypeScript, with first-class Hebrew and English support

Readme

n2w

Type-safe number-to-words for JavaScript and TypeScript, with first-class Hebrew and English support.

npm version License: MIT

Convert numbers to their word representation in English and Hebrew. Supports cardinals, ordinals, decimals, percentages, and currencies with complete grammatical support for Hebrew gender and construct forms.

Why this library?

  • Native Hebrew Support - First-class Hebrew with correct gender agreement, dual forms, and construct states (smichut)
  • English with Standards - British and American conventions, proper ordinals, currency formatting
  • Zero Dependencies - Lightweight, self-contained, no external runtime overhead
  • Strict TypeScript - Fully typed with excellent IntelliSense and editor support
  • Framework Agnostic - Works anywhere JavaScript runs (Node, browser, edge)

Installation

npm install n2w

Quick Start

Try this minimal example - copy and run it:

import { toWords } from 'n2w';

console.log(toWords(42));
// "forty-two"

More examples:

import { toWords } from 'n2w';

// English
toWords(1500);                    // "one thousand five hundred"
toWords(21, { mode: 'ordinal' }); // "twenty-first"

// Hebrew
toWords(1500, { lang: 'he' });    // "אלף וחמש מאות"

// Currency
toWords(2.50, { mode: 'currency', currency: 'USD' });
// "two dollars and fifty cents"

For CommonJS:

const { toWords } = require('n2w');

console.log(toWords(42));
// "forty-two"

More Examples

// Hebrew with gender
toWords(3, { lang: 'he', gender: 'masculine' }); // "שלושה"
toWords(3, { lang: 'he', gender: 'feminine' });  // "שלוש"

// Percentages
toWords(50, { mode: 'percentage' });             // "fifty percent"
toWords(2, { mode: 'percentage', lang: 'he' });  // "שני אחוזים"

// Decimals
toWords(3.14);                                   // "three point one four"

Fluent API

The library also provides a fluent builder API for more readable code:

import { builder } from 'n2w';

builder(42).in('en').asOrdinal().toString();
// "forty-second"

builder(1.5).asCurrency('USD').toString();
// "one dollar and fifty cents"

builder(123).in('he').feminine().toString();
// "מאה עשרים ושלוש"

builder(42).in('he').masculine().asOrdinal().toString();
// "ארבעים ושניים"

Concepts

Number Modes

The library supports four conversion modes:

  • Cardinal (default): Standard counting numbers - "one", "two", "three"
  • Ordinal: Position numbers - "first", "second", "third"
  • Currency: Money amounts with proper singular/plural - "one dollar", "two dollars"
  • Percentage: Percent values - "fifty percent", "2.5 percent"

Hebrew Gender Agreement

Hebrew numbers 1-10 change form based on grammatical gender:

// Feminine (default for counting)
toWords(1, { lang: 'he' }); // "אחת" (achat)
toWords(2, { lang: 'he' }); // "שתיים" (shtayim)

// Masculine
toWords(1, { lang: 'he', gender: 'masculine' }); // "אחד" (echad)
toWords(2, { lang: 'he', gender: 'masculine' }); // "שניים" (shnayim)

Default behavior: Feminine is the default, as it's standard for counting in Hebrew.

Hebrew Construct Forms (Smichut)

The number 2 uses a special construct form when followed by a noun:

  • Standalone: שתיים (shtayim) / שניים (shnayim)
  • Before nouns: שתי (shtei) / שני (shnei)

This is automatically handled in currency and percentage modes:

toWords(2, { mode: 'currency', currency: 'ILS', lang: 'he' });
// "שני שקלים" (shnei shkalim) - uses construct form

Hebrew Special Forms

  • 200: מאתיים (matayim) - dual form, not "שתיים מאות"
  • 2000: אלפיים (alpayim) - dual form, not "שתיים אלפים"
  • Conjunction: Numbers are joined with ו (vav), meaning "and"

Range and BigInt

  • Supported range: Up to quintillion (10¹⁸)
  • Use BigInt for numbers beyond Number.MAX_SAFE_INTEGER (2⁵³-1)
  • Negative numbers supported with "minus" prefix
toWords(9007199254740991n); // Works with BigInt
toWords(-42);               // "minus forty-two"

API Reference

Direct Function API

toWords(input, options?)

Convert a number to words.

function toWords(
  input: NumberInput,
  options?: ConversionOptions
): string

Parameters:

  • input: Number to convert (number, bigint, or string)
  • options: Conversion options (see below)

Returns: Word representation as a string

convert(input, options?)

Get full conversion result with metadata.

function convert(
  input: NumberInput,
  options?: ConversionOptions
): ConversionResult

Options

interface ConversionOptions {
  lang?: 'en' | 'he';              // Language (default: 'en')
  mode?: 'cardinal' | 'ordinal' | 'currency' | 'percentage';  // Mode (default: 'cardinal')
  gender?: 'masculine' | 'feminine';  // Gender for Hebrew (default: 'feminine')
  currency?: string;               // Currency code for currency mode (e.g., 'USD', 'ILS')
  style?: 'formal' | 'informal';   // Language style (default: 'informal')
  hyphenate?: boolean;             // Use hyphens in compound numbers (default: true)
  includeAnd?: 'always' | 'british' | 'never';  // "and" usage in English (default: 'british')
  output?: 'string' | 'tokens';    // Output format (default: 'string')
  useCache?: boolean;              // Enable memoization (default: true)
}

Fluent Builder API (Advanced)

For more complex use cases, the library provides a fluent builder pattern:

import { builder } from 'n2w';

builder(42).in('en').asOrdinal().toString();     // "forty-second"
builder(1.5).asCurrency('USD').toString();       // "one dollar and fifty cents"
builder(123).in('he').feminine().toString();     // "מאה עשרים ושלוש"

Currency Support

Built-in support for major currencies:

// USD
toWords(1.50, { mode: 'currency', currency: 'USD' });
// "one dollar and fifty cents"

// ILS (Israeli Shekel)
toWords(2.50, { mode: 'currency', currency: 'ILS', lang: 'he' });
// "שני שקלים וחמישים אגורות"

// EUR, GBP, JPY, CAD, AUD also supported

You can also register custom currencies:

import { currencyRegistry } from 'n2w';

currencyRegistry.register({
  code: 'BTC',
  decimals: 8,
  major: {
    en: { singular: 'bitcoin', plural: 'bitcoins' },
    he: { singular: 'ביטקוין', plural: 'ביטקוין', gender: 'masculine' }
  },
  minor: {
    en: { singular: 'satoshi', plural: 'satoshis' },
    he: { singular: 'סטושי', plural: 'סטושי', gender: 'masculine' }
  }
});

CLI

The package includes a command-line tool:

# Basic usage
n2w 123                          # one hundred twenty-three
n2w 123 --lang he                # מאה עשרים ושלוש

# Ordinals
n2w 42 --ordinal                 # forty-second
n2w 42 -o --lang he --gender m   # ארבעים ושניים

# Currency
n2w 1.50 --currency USD          # one dollar and fifty cents
n2w 2 -c ILS --lang he           # שני שקלים

# Percentages
n2w 50 --percent                 # fifty percent

# Options
n2w 21 --no-hyphen               # twenty one (instead of twenty-one)
n2w --help                       # Show all options

Examples

English Examples

// Cardinals
toWords(0);        // "zero"
toWords(21);       // "twenty-one"
toWords(100);      // "one hundred"
toWords(1234);     // "one thousand two hundred and thirty-four"
toWords(1000000);  // "one million"

// Ordinals
toWords(1, { mode: 'ordinal' });   // "first"
toWords(21, { mode: 'ordinal' });  // "twenty-first"
toWords(100, { mode: 'ordinal' }); // "one hundredth"

// Decimals
toWords(3.14);     // "three point one four"
toWords(0.5);      // "zero point five"

// Negative numbers
toWords(-42);      // "minus forty-two"

Hebrew Examples

const he = { lang: 'he' as const };
const heM = { lang: 'he' as const, gender: 'masculine' as const };

// Cardinals (feminine - default for counting)
toWords(0, he);     // "אפס"
toWords(1, he);     // "אחת"
toWords(21, he);    // "עשרים ואחת"
toWords(100, he);   // "מאה"
toWords(200, he);   // "מאתיים" (special dual form)
toWords(1234, he);  // "אלף מאתיים שלושים וארבע"

// Cardinals (masculine)
toWords(3, heM);    // "שלושה"
toWords(42, heM);   // "ארבעים ושניים"

// Ordinals (1-10 have special forms)
toWords(1, { mode: 'ordinal', ...he });  // "ראשונה" (feminine)
toWords(1, { mode: 'ordinal', ...heM }); // "ראשון" (masculine)
toWords(11, { mode: 'ordinal', ...he }); // "אחד עשר" (uses cardinal)

Browser Usage

For direct browser use without a bundler:

<script src="https://unpkg.com/n2w"></script>
<script>
  const { toWords } = N2W;
  console.log(toWords(42)); // "forty-two"
</script>

Tree-Shaking

The library is fully tree-shakeable. Import only what you need:

// Import only English converter
import { englishConverter } from 'n2w/english';

// Import only Hebrew converter
import { hebrewConverter } from 'n2w/hebrew';

Performance

  • Memoization: Results are cached for improved performance on repeated conversions
  • Lookup tables: Common numbers (0-100) use pre-computed lookups
  • Zero dependencies: No external runtime overhead

TypeScript

Full TypeScript support with comprehensive type definitions:

import type { ConversionOptions, ConversionResult } from 'n2w';

const options: ConversionOptions = {
  lang: 'he',
  mode: 'ordinal',
  gender: 'feminine'
};

const result: ConversionResult = convert(42, options);

Error Handling

The library includes specific error types for better debugging:

import {
  InvalidInputError,
  NumberRangeError,
  UnsupportedCurrencyError
} from 'n2w';

try {
  toWords(NaN);
} catch (error) {
  if (error instanceof InvalidInputError) {
    console.log('Invalid input:', error.input);
  }
}

Design Notes

The library is architected for extensibility, type safety, and performance:

Architecture:

  • Language converters: Each language (English, Hebrew) implements a common LanguageConverter interface, making it easy to add new languages
  • Mode-based conversion: Cardinal, ordinal, currency, and percentage modes share the same entry point with clear option-based routing
  • Zero dependencies: Self-contained implementation with no external runtime dependencies

Type Safety:

  • Strict TypeScript: Built with strict: true and comprehensive type definitions
  • Union literals: All options use string literal types for excellent autocomplete and compile-time validation
  • BigInt support: Handles numbers beyond Number.MAX_SAFE_INTEGER natively

Performance:

  • Lookup tables: Numbers 0-100 use pre-computed arrays for instant access
  • Simple caching: Optional LRU cache with configurable size and TTL
  • Tree-shakeable: ES modules with proper sideEffects: false configuration

Hebrew Implementation:

  • Full gender agreement for numbers 1-10 (masculine/feminine)
  • Special dual forms for 200 (מאתיים) and 2000 (אלפיים)
  • Construct forms (smichut) for number 2 when used with nouns
  • RTL-aware spacing and optional maqaf (־) support

See CONTRIBUTING.md for development workflow and coding standards.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT License

Credits

This library was built with: