numberToWords — number → Persian words
import { numberToWords } from "@persian-tools/persian-tools";
// CommonJS
const { numberToWords } = require("@persian-tools/persian-tools");
Public exports
numberToWords(
numberValue: number | string,
options?: NumberToWordsOptions,
): string | PersianToolsTypeError
interface NumberToWordsOptions {
ordinal?: boolean; // append ordinal suffix via addOrdinalSuffix(...)
}
Basic usage
import { numberToWords } from "@persian-tools/persian-tools";
numberToWords(0); // "صفر"
numberToWords(7); // "هفت"
numberToWords(123); // "صد و بیست و سه"
numberToWords(1234); // "یک هزار و دویست و سی و چهار"
numberToWords(1_000_000); // "یک میلیون"
numberToWords("12,345"); // "دوازده هزار و سیصد و چهل و پنج"
numberToWords(-100); // "منفی صد"
// Ordinal
numberToWords(3, { ordinal: true }); // "سوم"
numberToWords(21, { ordinal: true }); // appends ordinal suffix via addOrdinalSuffix
Input rules
- Accepts
number | string. - Strings are first run through
removeCommas(...)(so"12,345"→12345). - The numeric value must be a safe integer (
Number.isSafeInteger(...)). Otherwise the function returns (not throws) aPersianToolsTypeErrorinstance. - This means the return type is
string | PersianToolsTypeError. Treat it as a tagged union:
import { numberToWords } from "@persian-tools/persian-tools";
import { PersianToolsTypeError } from "@persian-tools/persian-tools"; // re-exported from helpers
const result = numberToWords(userInput);
if (result instanceof PersianToolsTypeError) {
console.error(result.message);
} else {
console.log(result);
}
Range
- Maximum:
Number.MAX_SAFE_INTEGER(=2^53 - 1≈ 9.007e15). Larger numbers lose precision in JS itself and are rejected with aPersianToolsTypeError. - Minimum:
-Number.MAX_SAFE_INTEGER. Negative integers are accepted; the result is prefixed withمنفی. - Decimals (e.g.
1.5) are rejected — the safe-integer guard catches them.
Ordinal mode
{ ordinal: true } runs the result through addOrdinalSuffix(...). The suffix rules are conservative:
- Words ending in
سه→ strip last 2 chars, appendسوم(سه→سوم,سی و سه→سی و سوم). - Words ending in
ی→ appendاُم(with a leading space). - Anything else → append
م.
This produces machine-correct ordinals but not all idiomatic forms — e.g. 1 → "یک" becomes "یک اُم", which is grammatically valid but uncommon ("یکم" is colloquial). Confirm against your locale style guide before using ordinal mode in user-facing copy.
Common pitfalls
- Return type isn't pure
string. Don't blindly assign to astringvariable without checking. Several existing call sites in this codebase wrapnumberToWordsin a type guard againstPersianToolsTypeError. - Persian-digit string inputs (
"۱۲۳") are NOT auto-normalized.removeCommasonly handles commas. Pass the digit-normalized number first or callautoConvertDigitsToENupstream. - Floats silently get rejected, not rounded. If you intend to round, do it before calling.
- The result has no commas or other separators — it's Persian words separated by
و(and). For an invoice line that needs both numeric and word forms, combine withaddCommasseparately.
Composition with addCommas
import { numberToWords, addCommas } from "@persian-tools/persian-tools";
const formatInvoice = (n: number) =>
`${addCommas(n)} ریال (${numberToWords(n)})`;
formatInvoice(1_234_567);
// "1,234,567 ریال (یک میلیون و دویست و سی و چهار هزار و پانصد و شصت و هفت)"
References
- Tests:
test/NumberToWords.spec.ts - Inverse:
wordsToNumberskill - Ordinal mechanics:
addOrdinalSuffixskill