Source for file Numbers.php
Documentation is available at Numbers.php
* ----------------------------------------------------------------------
* Copyright (c) 2006-2016 Khaled Al-Sham'aa.
* ----------------------------------------------------------------------
* This program is open source product; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License (LGPL)
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/lgpl.txt>.
* ----------------------------------------------------------------------
* Class Name: Spell numbers in the Arabic idiom
* Original Author(s): Khaled Al-Sham'aa <khaled@ar-php.org>
* Purpose: Spell numbers in the Arabic idiom
* ----------------------------------------------------------------------
* Spell numbers in the Arabic idiom
* PHP class to spell numbers in the Arabic idiom. This function is very
* useful for financial applications in Arabic for example.
* If you ever have to create an Arabic PHP application built around invoicing or
* accounting, you might find this class useful. Its sole reason for existence is
* to help you translate integers into their spoken-word equivalents in Arabic
* How is this useful? Well, consider the typical invoice: In addition to a
* description of the work done, the date, and the hourly or project cost, it always
* includes a total cost at the end, the amount that the customer is expected
* To avoid any misinterpretation of the total amount, many organizations (mine
* included) put the amount in both words and figures; for example, $1,200 becomes
* "one thousand and two hundred dollars." You probably do the same thing every time
* Now take this scenario to a Web-based invoicing system. The actual data used to
* generate the invoice will be stored in a database as integers, both to save space
* and to simplify calculations. So when a printable invoice is generated, your Web
* application will need to convert those integers into words, this is more clarity
* This class will accept almost any numeric value and convert it into an equivalent
* string of words in written Arabic language (using Windows-1256 character set).
* The value can be any positive number up to 999,999,999 (users should not use
* commas). It will take care of feminine and Arabic grammar rules.
* include('./I18N/Arabic.php');
* $obj = new I18N_Arabic('Numbers');
* $text = $obj->int2str($integer);
* echo "<p align=\"right\"><b class=hilight>$integer</b><br />$text</p>";
* $text = $obj->int2str($integer);
* echo "<p align=\"right\"><b class=hilight>$integer</b><br />$text</p>";
* @author Khaled Al-Sham'aa <khaled@ar-php.org>
* @copyright 2006-2016 Khaled Al-Sham'aa
* @license LGPL <http://www.gnu.org/licenses/lgpl.txt>
* @link http://www.ar-php.org
* This PHP class spell numbers in the Arabic idiom
* @author Khaled Al-Sham'aa <khaled@ar-php.org>
* @copyright 2006-2016 Khaled Al-Sham'aa
* @license LGPL <http://www.gnu.org/licenses/lgpl.txt>
* @link http://www.ar-php.org
private $_individual = array();
private $_complications = array();
private $_arabicIndic = array();
private $_ordering = array();
private $_currency = array();
private $_spell = array();
* Loads initialize values
public function __construct()
foreach ($xml->xpath("//individual/number[@gender='male']") as $num) {
if (isset ($num['grammar'])) {
$grammar = $num['grammar'];
$this->_individual["{ $num['value']}"][1]["$grammar"] = (string) $num;
$this->_individual["{ $num['value']}"][1] = (string) $num;
foreach ($xml->xpath("//individual/number[@gender='female']") as $num) {
if (isset ($num['grammar'])) {
$grammar = $num['grammar'];
$this->_individual["{ $num['value']}"][2]["$grammar"] = (string) $num;
$this->_individual["{ $num['value']}"][2] = (string) $num;
foreach ($xml->xpath("//individual/number[@value>19]") as $num) {
if (isset ($num['grammar'])) {
$grammar = $num['grammar'];
$this->_individual["{ $num['value']}"]["$grammar"] = (string) $num;
$this->_individual["{ $num['value']}"] = (string) $num;
foreach ($xml->complications->number as $num) {
$format = $num['format'];
$this->_complications["$scale"]["$format"] = (string) $num;
foreach ($xml->arabicIndic->number as $html) {
$this->_arabicIndic["$value"] = $html;
foreach ($xml->xpath("//order/number[@gender='male']") as $num) {
$this->_ordering["{ $num['value']}"][1] = (string) $num;
foreach ($xml->xpath("//order/number[@gender='female']") as $num) {
$this->_ordering["{ $num['value']}"][2] = (string) $num;
$expression = "//individual/number[@value<11 or @value>19]";
foreach ($xml->xpath($expression) as $num) {
$str = str_replace(array('أ','إ','آ'), 'ا', (string) $num);
$this->_spell[$str] = (integer) $num['value'];
foreach ($xml->xpath("//currency") as $info) {
$money_ar = $info->money->arabic;
$money_en = $info->money->english;
$this->_currency["{ $info->iso}"]['ar']['basic'] = $money_ar->basic;
$this->_currency["{ $info->iso}"]['ar']['fraction'] = $money_ar->fraction;
$this->_currency["{ $info->iso}"]['en']['basic'] = $money_en->basic;
$this->_currency["{ $info->iso}"]['en']['fraction'] = $money_en->fraction;
$this->_currency["{ $info->iso}"]['decimals'] = $info->money->decimals;
* Set feminine flag of the counted object
* @param integer $value Counted object feminine
* (1 for masculine & 2 for feminine)
* @return object $this to build a fluent interface
* @author Khaled Al-Sham'aa <khaled@ar-php.org>
if ($value == 1 || $value == 2) {
$this->_feminine = $value;
* Set the grammar position flag of the counted object
* @param integer $value Grammar position of counted object
* (1 if Marfoua & 2 if Mansoub or Majrour)
* @return object $this to build a fluent interface
* @author Khaled Al-Sham'aa <khaled@ar-php.org>
if ($value == 1 || $value == 2) {
* Set the ordering flag, is it normal number or ordering number
* @param integer $value Is it an ordering number? default is 1
* (use 1 if no and 2 if yes)
* @return object $this to build a fluent interface
* @author Khaled Al-Sham'aa <khaled@ar-php.org>
if ($value == 1 || $value == 2) {
* Get the feminine flag of counted object
* @return integer return current setting of counted object feminine flag
* @author Khaled Al-Sham'aa <khaled@ar-php.org>
* Get the grammer position flag of counted object
* @return integer return current setting of counted object grammer
* @author Khaled Al-Sham'aa <khaled@ar-php.org>
* Get the ordering flag value
* @return integer return current setting of ordering flag value
* @author Khaled Al-Sham'aa <khaled@ar-php.org>
* Spell integer number in Arabic idiom
* @param integer $number The number you want to spell in Arabic idiom
* @return string The Arabic idiom that spells inserted number
* @author Khaled Al-Sham'aa <khaled@ar-php.org>
if ($number == 1 && $this->_order == 2) {
if ($this->_feminine == 1) {
$number = (string) - 1 * $number;
$string .= ' فاصلة ' . $dec;
* Spell number in Arabic idiom as money
* @param integer $number The number you want to spell in Arabic idiom as money
* @param string $iso The three-letter Arabic country code defined in
* @param string $lang The two-letter language code in ISO 639-1 standard
* @return string The Arabic idiom that spells inserted number as money
* @author Khaled Al-Sham'aa <khaled@ar-php.org>
public function money2str($number, $iso= 'SYP', $lang= 'ar')
$number = sprintf("%01.{$this->_currency[$iso]['decimals']}f ", $number);
$string .= ' ' . $this->_currency[$iso][$lang]['basic'];
if (!empty($temp[1]) && $temp[1] != 0) {
$string .= ' ' . $this->_currency[$iso][$lang]['fraction'];
* Convert Arabic idiom number string into Integer
* @param string $str The Arabic idiom that spells input number
* @return integer The number you spell it in the Arabic idiom
* @author Khaled Al-Sham'aa <khaled@ar-php.org>
$ptr = array('ـ', 'َ','ً','ُ','ٌ','ِ','ٍ','ْ','ّ');
$ptr = array('اثنا','اثني','اثنتا', 'اثنتي');
if (strpos($str, 'ناقص') === false
&& strpos($str, 'سالب') === false
$max = count($this->_complications);
for ($scale= $max; $scale> 0; $scale-- ) {
$key = pow(1000, $scale);
$pattern = array('أ','إ','آ');
$format1 = str_replace($pattern, 'ا', $this->_complications[$scale][1]);
$format2 = str_replace($pattern, 'ا', $this->_complications[$scale][2]);
$format3 = str_replace($pattern, 'ا', $this->_complications[$scale][3]);
$format4 = str_replace($pattern, 'ا', $this->_complications[$scale][4]);
if (strpos($str, $format1) !== false) {
list ($temp, $str) = explode($format1, $str);
$segment[$key] = 'اثنان';
} elseif (strpos($str, $format2) !== false) {
list ($temp, $str) = explode($format2, $str);
$segment[$key] = 'اثنان';
} elseif (strpos($str, $format3) !== false) {
list ($segment[$key], $str) = explode($format3, $str);
} elseif (strpos($str, $format4) !== false) {
list ($segment[$key], $str) = explode($format4, $str);
if ($segment[$key] == '') {
if ($segment[$key] != '') {
$segment[$key] = trim($segment[$key]);
$segment[1] = trim($str);
foreach ($segment as $scale => $str) {
foreach ($this->_spell as $word => $value) {
if (strpos($str, "$word ") !== false) {
$total += $subTotal * $scale;
* Spell integer number in Arabic idiom
* @param integer $number The number you want to spell in Arabic idiom
* @param logical $zero Present leading zero if true [default is true]
* @return string The Arabic idiom that spells inserted number
* @author Khaled Al-Sham'aa <khaled@ar-php.org>
protected function subInt2str($number, $zero = true)
$number = ($zero != false) ? trim($number) : trim((float) $number);
//--- by Jnom: handle left zero
while (($fulnum[0]) == '0') {
$blocks_num = count($blocks) - 1;
for ($i = $blocks_num; $i >= 0; $i-- ) {
$number = floor($blocks[$i]);
if ($number == 1 && $i != 0) {
$text = $this->_complications[$i][4];
if ($this->_order == 2) {
} elseif ($number == 2 && $i != 0) {
$text = $this->_complications[$i][$this->_format];
if ($this->_order == 2) {
} elseif ($number > 2 && $number < 11 && $i != 0) {
$text .= ' ' . $this->_complications[$i][3];
if ($this->_order == 2) {
$text .= ' ' . $this->_complications[$i][4];
if ($this->_order == 2) {
//--- by Jnom: handle left zero
if ($text != '' && $zeros != '' && $zero != false) {
$text = $zeros. ' '. $text;
* Spell sub block number of three digits max in Arabic idiom
* @param integer $number Sub block number of three digits max you want to
* @return string The Arabic idiom that spells inserted sub block
* @author Khaled Al-Sham'aa <khaled@ar-php.org>
$hundred = floor($number / 100) * 100;
if ($this->_order == 2) {
$pre. $this->_individual[$hundred][$this->_format]
array_push($items, $pre. $this->_individual[$hundred]);
if ($this->_order == 2) {
array_push($items, $this->_ordering[$number][$this->_feminine]);
} elseif ($number < 20) {
$item = 'ال' . $this->_ordering[$number][$this->_feminine];
if ($this->_feminine == 1) {
$tens = floor($number / 10) * 10;
'ال' . $this->_ordering[$ones][$this->_feminine]
'ال' . $this->_individual[$tens][$this->_format]
if ($number == 2 || $number == 12) {
$this->_individual[$number][$this->_feminine][$this->_format]
} elseif ($number < 20) {
$this->_individual[$number][$this->_feminine]
$tens = floor($number / 10) * 10;
$this->_individual[2][$this->_feminine][$this->_format]
$this->_individual[$ones][$this->_feminine]
array_push($items, $this->_individual[$tens][$this->_format]);
* Represent integer number in Arabic-Indic digits using HTML entities
* @param integer $number The number you want to present in Arabic-Indic digits
* @return string The Arabic-Indic digits represent inserted integer number
* @author Khaled Al-Sham'aa <khaled@ar-php.org>
$str = strtr("$number", $this->_arabicIndic);
|