n2w
v1.2.1
Published
Type-safe number-to-words for JavaScript and TypeScript, with first-class Hebrew and English support
Maintainers
Readme
n2w
Type-safe number-to-words for JavaScript and TypeScript, with first-class Hebrew and English support.
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 n2wQuick 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 formHebrew 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
BigIntfor numbers beyondNumber.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
): stringParameters:
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
): ConversionResultOptions
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 supportedYou 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 optionsExamples
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
LanguageConverterinterface, 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: trueand 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_INTEGERnatively
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: falseconfiguration
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:
- TypeScript
- tsup for bundling
- Vitest for testing
- Biome for linting and formatting
