Number to words

Spell integers out in Persian (up to 10^21) and Arabic (up to 10^27), and read Persian number words back into numbers with fromWords().

On this page
  1. Converting numbers to words
    1. Accepted input
    2. Errors
  2. Arabic
  3. From words back to a number
    1. How words are read

Converting numbers to words#

NumberToWords::convert() spells an integer out as words. Persian (fa) is the default and covers every integer below 1021.

<?php
require 'vendor/autoload.php';

use RtlyKit\Number\NumberToWords;
use function RtlyKit\number_to_words;

echo NumberToWords::convert(0), "\n";        // صفر
echo NumberToWords::convert(21), "\n";       // بیست و یک
echo NumberToWords::convert(115), "\n";      // صد و پانزده
echo NumberToWords::convert(1001), "\n";     // یک هزار و یک
echo NumberToWords::convert(12345), "\n";    // دوازده هزار و سیصد و چهل و پنج
echo NumberToWords::convert(-45), "\n";      // منفی چهل و پنج
echo number_to_words(2000000000), "\n";      // دو میلیارد
echo NumberToWords::convert(1234567), "\n";
// یک میلیون و دویست و سی و چهار هزار و پانصد و شصت و هفت

Words are joined with « و » (and) between all parts. Each scale word follows its group: «هزار» (103), «میلیون» (106), «میلیارد» (109), «تریلیون» (1012), «کوادریلیون» (1015) and «کوینتیلیون» (1018). A group equal to 1 keeps its «یک», so 1000 is «یک هزار». To read a bare «هزار», use fromWords() below. Zero groups are skipped.

Accepted input#

The parameter is typed int|float|string:

  • int: the whole PHP range works, including PHP_INT_MAX: «نه کوینتیلیون و دویست و بیست و سه کوادریلیون ...».
  • float: must be finite and whole. 3.0 gives «سه». 1.5 throws and is not silently cut.
  • string: an optional sign and digits in any digit set. Commas, ٬ and spaces are removed, so a number copied from a form works. Strings can be longer than a PHP int, up to 21 digits after leading zeros are dropped.
echo NumberToWords::convert('۱۲۳٬۴۵۶'), "\n";        // صد و بیست و سه هزار و چهارصد و پنجاه و شش
echo NumberToWords::convert('1,000,000'), "\n";      // یک میلیون
echo NumberToWords::convert('+7'), "\n";             // هفت
echo NumberToWords::convert('-0'), "\n";             // صفر
echo NumberToWords::convert('999999999999999999999'), "\n";
// نهصد و نود و نه کوینتیلیون و نهصد و نود و نه کوادریلیون ... و نهصد و نود و نه

Errors#

Invalid input throws InvalidNumberException, a subclass of RtlyKitException. Read the stable code with getErrorCode():

InputError code
1.5, '12a', ''invalid_number
NAN, INFnon_finite_number
A string longer than 4096 bytesinput_too_long
1e21, '1000000000000000000000' (22 digits)number_too_large
try {
    NumberToWords::convert('1000000000000000000000');
} catch (\RtlyKit\Exceptions\InvalidNumberException $e) {
    echo $e->getMessage(), ' [', $e->getErrorCode()->value, "]\n";
    // Number is too large (limit is 10^21 - 1). [number_too_large]
}

See Error handling to catch every library exception with one type.

Arabic#

Pass 'ar' as the second argument for Modern Standard Arabic. The locale is matched on its first two letters, ignoring case. So 'ar_SA' and 'AR' work, and 'fa_IR' works for Persian.

echo NumberToWords::convert(11, 'ar'), "\n";        // أحد عشر
echo NumberToWords::convert(21, 'ar'), "\n";        // واحد وعشرون
echo NumberToWords::convert(200, 'ar'), "\n";       // مئتان
echo NumberToWords::convert(3000, 'ar'), "\n";      // ثلاثة آلاف
echo NumberToWords::convert(2000000, 'ar'), "\n";   // مليونان
echo NumberToWords::convert(1002003, 'ar'), "\n";   // مليون وألفان وثلاثة
echo NumberToWords::convert(-7, 'ar'), "\n";        // سالب سبعة

Without options, the Arabic output is the bare counting form (masculine, nominative, no vowel marks). The range is every integer below 1027, and negative numbers start with «سالب». A larger number throws number_too_large.

When a counted noun follows the number, you can ask for gender, grammatical case, vowel marks, other spellings of «مئة» and more. The new ArabicOptions and the Arabic ordinals (first to ninety-ninth) have their own page: Arabic number words.

An unsupported locale throws UnsupportedLocaleException with the code unsupported_locale:

try {
    NumberToWords::convert(5, 'en');
} catch (\RtlyKit\Exceptions\UnsupportedLocaleException $e) {
    echo $e->getMessage(), "\n";   // Unsupported locale 'en' (use 'fa' or 'ar').
}

From words back to a number#

NumberToWords::fromWords() is the reverse of the Persian conversion for the forms convert() produces. It returns an int when the value fits in a PHP int, and a digit string otherwise.

var_dump(NumberToWords::fromWords('بیست و سه'));                 // int(23)
var_dump(NumberToWords::fromWords('منفی چهل و دو'));             // int(-42)
var_dump(NumberToWords::fromWords('هزار و یک'));                 // int(1001)
var_dump(NumberToWords::fromWords('دو میلیون و سیصد هزار و پنج')); // int(2300005)

// Round trip across the whole range:
var_dump(NumberToWords::fromWords(NumberToWords::convert(PHP_INT_MAX)));
// int(9223372036854775807)
var_dump(NumberToWords::fromWords(NumberToWords::convert('999999999999999999999')));
// string(21) "999999999999999999999"

How words are read#

  • The input is cleaned first. Arabic letter variants become Persian («ك» to «ک», «ي» to «ی»), a ZWNJ counts as a space, and extra whitespace is ignored.
  • A leading «منفی» makes the result negative. «منفی صفر» is 0.
  • A scale word with no number before it means one, so «هزار» is 1000.
  • Scales must go from large to small. «هزار هزار» and «هزار میلیون» throw.
  • Inside a group the order is hundreds, then tens, then units. A single word from «ده» to «نوزده» can stand in for tens and units. «دو صد», «پنج و بیست» and «یازده و یک» throw.
  • The word «و» is required between every two number words. The one pair written without it is a multiplier and its scale word, as in «دو هزار». A «و» at the start, at the end, doubled or next to a scale word is rejected.
  • «صفر» is accepted only on its own, or after «منفی».
try {
    NumberToWords::fromWords('یکصد');
} catch (\RtlyKit\Exceptions\InvalidNumberException $e) {
    echo $e->getMessage(), ' [', $e->getErrorCode()->value, "]\n";
    // Unknown number word 'یکصد'. [invalid_number_words]
}

Errors carry the code invalid_number_words (unknown word, wrong sequence, scales out of order, empty text) or input_too_long for text over 4096 bytes.

Good to know. fromWords() reads only the canonical word order. A phrase like «دو صد» (which a lenient reader would turn into 102), «بیست یک» or a stray «و» at the end throws InvalidNumberException with the code invalid_number_words. Every string that convert() produces is read back unchanged, and a bare scale word at the start («هزار و یک») is allowed. Only the Persian form is read. Arabic words from convert($n, 'ar'), ordinals such as «سی‌ام» and the compact spelling «یکصد» are not understood and throw.