Skip to the guide
All guides

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().

app RTLY-Kit 0.2.0checked reading 6 minutes

On this page

Converting numbers to words

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

PHP
<?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.
PHP
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
PHP
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.

PHP
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:

PHP
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.

PHP
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 «منفی».
PHP
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.