Guide
Getting started
npm install @devix-labs/number-words
import { toWords, toMoney, toCheque, toOrdinal, counted } from '@devix-labs/number-words';
Numbers
toWords(1234); // one thousand two hundred thirty-four
toWords(1234, { language: 'en-GB' }); // one thousand two hundred and thirty-four
toWords(1234, { language: 'ar' }); // ألف ومئتان وأربعة وثلاثون
toWords(1234, { language: 'ur' }); // ایک ہزار دو سو چونتیس
toWords(12345678, { system: 'south-asian' }); // one crore twenty-three lakh forty-five thousand…
toWords(-3.14); // minus three point one four
Urdu counts in lakh and crore by default; English does so when you ask.
Money, in any language
This is the part other libraries will not do: the currency and the language are independent, so a Dubai invoice written in English can be priced in dirhams.
toMoney(1234.5, { currency: 'AED' }); // one thousand two hundred thirty-four dirhams and fifty fils
toMoney(1234.5, { currency: 'AED', language: 'ar' }); // ألف ومئتان وأربعة وثلاثون درهمًا وخمسون فلسًا
toMoney(1234.5, { currency: 'PKR', language: 'ur' }); // ایک ہزار دو سو چونتیس روپے پچاس پیسے
toMoney(1.5, { currency: 'KWD' }); // one dinar and five hundred fils
Thirteen currencies are built in — AED, SAR, QAR, KWD, BHD, JOD, OMR, EGP, PKR, INR, USD, EUR, GBP — each with words in all three languages. The four that divide into a thousand keep all three digits.
The cheque line
toCheque(12345.5, { currency: 'AED' });
// AED twelve thousand three hundred forty-five dirhams and fifty fils only
toCheque(12345.5, { currency: 'PKR', language: 'ur' });
// PKR بارہ ہزار تین سو پینتالیس روپے پچاس پیسے صرف
A cheque names both halves even when one is zero — toCheque(0.3, …) says "zero dollars and thirty
cents only", where toMoney reads it as a person would: "thirty cents".
Arabic, properly
Three rules, and they are why a spellout that only knows the words gets invoices wrong:
// 1. Three to nine take the opposite gender of what they count.
toWords(3, { language: 'ar', gender: 'masculine' }); // ثلاثة
toWords(3, { language: 'ar', gender: 'feminine' }); // ثلاث
// 2. The counted noun changes form.
toMoney(2, { currency: 'AED', language: 'ar' }); // درهمان ← a dual
toMoney(11, { currency: 'AED', language: 'ar' }); // أحد عشر درهمًا ← the tamyīz
toMoney(100, { currency: 'AED', language: 'ar' }); // مئة درهم ← genitive singular
toMoney(200, { currency: 'AED', language: 'ar' }); // مئتا درهم ← the construct state
// 3. The last number governs.
toMoney(1234, { currency: 'AED', language: 'ar' }); // …وأربعة وثلاثون درهمًا
hundred: 'مائة' switches to the other spelling throughout.
Counting your own nouns
Give Arabic the four forms once, and every number agrees:
const invoices = { singular: 'فاتورة', dual: 'فاتورتان', plural: 'فواتير', counted: 'فاتورةً', gender: 'feminine' };
counted(1, invoices, { language: 'ar' }); // فاتورة
counted(2, invoices, { language: 'ar' }); // فاتورتان
counted(3, invoices, { language: 'ar' }); // ثلاث فواتير
counted(11, invoices, { language: 'ar' }); // إحدى عشرة فاتورةً
counted(2000, invoices, { language: 'ar' }); // ألفا فاتورة
English and Urdu need two forms and use them the obvious way.
Ordinals
toOrdinal(21); // twenty-first
toOrdinal(21, { language: 'ar' }); // الحادي والعشرون
toOrdinal(21, { language: 'ar', gender: 'feminine' }); // الحادية والعشرون
toOrdinal(7, { language: 'ur' }); // ساتواں
Money is never a float
Amounts are split and rounded on the digits:
toMoney(0.1 + 0.2, { currency: 'USD' }); // thirty cents
toMoney('1234.005', { currency: 'USD' }); // …and one cent
toMoney('9007199254740993.75', { currency: 'USD' }); // exact, past what a double holds
Pass a string for amounts that came from a database — '1234.50' is exact, 1234.50 is a float.
Your own currency, your own language
import { registerCurrency, registerLanguage } from '@devix-labs/number-words';
registerCurrency({
code: 'BTC',
subunits: 100_000_000,
unit: { en: { singular: 'bitcoin', plural: 'bitcoin' } },
fraction: { en: { singular: 'satoshi', plural: 'satoshis' } },
});
registerLanguage() takes anything implementing the Language interface — cardinal, ordinal, noun,
money and a few words.
On the server
devix-labs/laravel-number-words is the same rules in PHP, with Blade directives and a facade. 796 shared
vectors assert the two agree, so an invoice rendered server-side matches the one in the browser.