I18N_Arabic
[ class tree: I18N_Arabic ] [ index: I18N_Arabic ] [ all elements ]

Source for file Numbers.php

Documentation is available at Numbers.php

  1. <?php
  2. /**
  3.  * ----------------------------------------------------------------------
  4.  *  
  5.  * Copyright (c) 2006-2016 Khaled Al-Sham'aa.
  6.  *  
  7.  * http://www.ar-php.org
  8.  *  
  9.  * PHP Version 5
  10.  *  
  11.  * ----------------------------------------------------------------------
  12.  *  
  13.  * LICENSE
  14.  *
  15.  * This program is open source product; you can redistribute it and/or
  16.  * modify it under the terms of the GNU Lesser General Public License (LGPL)
  17.  * as published by the Free Software Foundation; either version 3
  18.  * of the License, or (at your option) any later version.
  19.  *  
  20.  * This program is distributed in the hope that it will be useful,
  21.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  22.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  23.  * GNU Lesser General Public License for more details.
  24.  *  
  25.  * You should have received a copy of the GNU Lesser General Public License
  26.  * along with this program.  If not, see <http://www.gnu.org/licenses/lgpl.txt>.
  27.  *  
  28.  * ----------------------------------------------------------------------
  29.  *  
  30.  * Class Name: Spell numbers in the Arabic idiom
  31.  *  
  32.  * Filename:   Numbers.php
  33.  *  
  34.  * Original    Author(s): Khaled Al-Sham'aa <khaled@ar-php.org>
  35.  *  
  36.  * Purpose:    Spell numbers in the Arabic idiom
  37.  *  
  38.  * ----------------------------------------------------------------------
  39.  *  
  40.  * Spell numbers in the Arabic idiom
  41.  *
  42.  * PHP class to spell numbers in the Arabic idiom. This function is very
  43.  * useful for financial applications in Arabic for example.
  44.  *    
  45.  * If you ever have to create an Arabic PHP application built around invoicing or
  46.  * accounting, you might find this class useful. Its sole reason for existence is
  47.  * to help you translate integers into their spoken-word equivalents in Arabic
  48.  * language.
  49.  * 
  50.  * How is this useful? Well, consider the typical invoice: In addition to a
  51.  * description of the work done, the date, and the hourly or project cost, it always
  52.  * includes a total cost at the end, the amount that the customer is expected
  53.  * to pay.
  54.  *   
  55.  * To avoid any misinterpretation of the total amount, many organizations (mine
  56.  * included) put the amount in both words and figures; for example, $1,200 becomes
  57.  * "one thousand and two hundred dollars." You probably do the same thing every time
  58.  * you write a check.
  59.  * 
  60.  * Now take this scenario to a Web-based invoicing system. The actual data used to
  61.  * generate the invoice will be stored in a database as integers, both to save space
  62.  * and to simplify calculations. So when a printable invoice is generated, your Web
  63.  * application will need to convert those integers into words, this is more clarity
  64.  * and more personality.
  65.  * 
  66.  * This class will accept almost any numeric value and convert it into an equivalent
  67.  * string of words in written Arabic language (using Windows-1256 character set).
  68.  * The value can be any positive number up to 999,999,999 (users should not use
  69.  * commas). It will take care of feminine and Arabic grammar rules.
  70.  *
  71.  * Example:
  72.  * <code>
  73.  *     include('./I18N/Arabic.php');
  74.  *     $obj = new I18N_Arabic('Numbers');
  75.  *     
  76.  *     $obj->setFeminine(1);
  77.  *     $obj->setFormat(1);
  78.  *     
  79.  *     $integer = 2147483647;
  80.  *     
  81.  *     $text = $obj->int2str($integer);
  82.  *     
  83.  *     echo "<p align=\"right\"><b class=hilight>$integer</b><br />$text</p>";
  84.  * 
  85.  *     $obj->setFeminine(2);
  86.  *     $obj->setFormat(2);
  87.  *     
  88.  *     $integer = 2147483647;
  89.  *     
  90.  *     $text = $obj->int2str($integer);
  91.  *     
  92.  *     echo "<p align=\"right\"><b class=hilight>$integer</b><br />$text</p>";
  93.  * </code>
  94.  *             
  95.  * @category  I18N
  96.  * @package   I18N_Arabic
  97.  * @author    Khaled Al-Sham'aa <khaled@ar-php.org>
  98.  * @copyright 2006-2016 Khaled Al-Sham'aa
  99.  *    
  100.  * @license   LGPL <http://www.gnu.org/licenses/lgpl.txt>
  101.  * @link      http://www.ar-php.org
  102.  */
  103.  
  104. /**
  105.  * This PHP class spell numbers in the Arabic idiom
  106.  *  
  107.  * @category  I18N
  108.  * @package   I18N_Arabic
  109.  * @author    Khaled Al-Sham'aa <khaled@ar-php.org>
  110.  * @copyright 2006-2016 Khaled Al-Sham'aa
  111.  *    
  112.  * @license   LGPL <http://www.gnu.org/licenses/lgpl.txt>
  113.  * @link      http://www.ar-php.org
  114.  */ 
  115. {
  116.     private $_individual    array();
  117.     private $_complications array();
  118.     private $_arabicIndic   array();
  119.     private $_ordering      array();
  120.     private $_currency      array();
  121.     private $_spell         array();
  122.     private $_feminine      1;
  123.     private $_format        1;
  124.     private $_order         1;
  125.  
  126.     /**
  127.      * Loads initialize values
  128.      *
  129.      * @ignore
  130.      */         
  131.     public function __construct()
  132.     {
  133.         $xml simplexml_load_file(dirname(__FILE__).'/data/ArNumbers.xml');
  134.  
  135.         foreach ($xml->xpath("//individual/number[@gender='male']"as $num{
  136.             if (isset($num['grammar'])) {
  137.                 $grammar $num['grammar'];
  138.                 
  139.                 $this->_individual["{$num['value']}"][1]["$grammar"= (string)$num;
  140.             else {
  141.                 $this->_individual["{$num['value']}"][1= (string)$num;
  142.             }
  143.         
  144.         
  145.         foreach ($xml->xpath("//individual/number[@gender='female']"as $num{
  146.             if (isset($num['grammar'])) {
  147.                 $grammar $num['grammar'];
  148.                 
  149.                 $this->_individual["{$num['value']}"][2]["$grammar"= (string)$num;
  150.             else {
  151.                 $this->_individual["{$num['value']}"][2= (string)$num;
  152.             }
  153.         
  154.         
  155.         foreach ($xml->xpath("//individual/number[@value>19]"as $num{
  156.             if (isset($num['grammar'])) {
  157.                 $grammar $num['grammar'];
  158.                 
  159.                 $this->_individual["{$num['value']}"]["$grammar"= (string)$num;
  160.             else {
  161.                 $this->_individual["{$num['value']}"= (string)$num;
  162.             }
  163.         
  164.         
  165.         foreach ($xml->complications->number as $num{
  166.             $scale  $num['scale'];
  167.             $format $num['format'];
  168.             
  169.             $this->_complications["$scale"]["$format"= (string)$num;
  170.         
  171.         
  172.         foreach ($xml->arabicIndic->number as $html{
  173.             $value  $html['value'];
  174.             
  175.             $this->_arabicIndic["$value"$html;
  176.         
  177.  
  178.         foreach ($xml->xpath("//order/number[@gender='male']"as $num{
  179.             $this->_ordering["{$num['value']}"][1= (string)$num;
  180.         
  181.  
  182.         foreach ($xml->xpath("//order/number[@gender='female']"as $num{
  183.             $this->_ordering["{$num['value']}"][2= (string)$num;
  184.         }
  185.         
  186.         $expression "//individual/number[@value<11 or @value>19]";
  187.         foreach ($xml->xpath($expressionas $num{
  188.             $str str_replace(array('أ','إ','آ')'ا'(string)$num);
  189.             $this->_spell[$str= (integer)$num['value'];
  190.         
  191.         
  192.         $xml simplexml_load_file(dirname(__FILE__).'/data/arab_countries.xml');
  193.         
  194.         foreach ($xml->xpath("//currency"as $info{
  195.             $money_ar $info->money->arabic;
  196.             $money_en $info->money->english;
  197.             
  198.             $this->_currency["{$info->iso}"]['ar']['basic']    $money_ar->basic;
  199.             $this->_currency["{$info->iso}"]['ar']['fraction'$money_ar->fraction;
  200.             $this->_currency["{$info->iso}"]['en']['basic']    $money_en->basic;
  201.             $this->_currency["{$info->iso}"]['en']['fraction'$money_en->fraction;
  202.  
  203.             $this->_currency["{$info->iso}"]['decimals'$info->money->decimals;
  204.         }
  205.     }
  206.     
  207.     /**
  208.      * Set feminine flag of the counted object
  209.      *      
  210.      * @param integer $value Counted object feminine
  211.      *                       (1 for masculine & 2 for feminine)
  212.      *      
  213.      * @return object $this to build a fluent interface
  214.      * @author Khaled Al-Sham'aa <khaled@ar-php.org>
  215.      */
  216.     public function setFeminine($value)
  217.     {
  218.         if ($value == || $value == 2{
  219.             $this->_feminine $value;
  220.         }
  221.         
  222.         return $this;
  223.     }
  224.     
  225.     /**
  226.      * Set the grammar position flag of the counted object
  227.      *      
  228.      * @param integer $value Grammar position of counted object
  229.      *                        (1 if Marfoua & 2 if Mansoub or Majrour)
  230.      *                            
  231.      * @return object $this to build a fluent interface
  232.      * @author Khaled Al-Sham'aa <khaled@ar-php.org>
  233.      */
  234.     public function setFormat($value)
  235.     {
  236.         if ($value == || $value == 2{
  237.             $this->_format $value;
  238.         }
  239.         
  240.         return $this;
  241.     }
  242.     
  243.     /**
  244.      * Set the ordering flag, is it normal number or ordering number
  245.      *      
  246.      * @param integer $value Is it an ordering number? default is 1
  247.      *                        (use 1 if no and 2 if yes)
  248.      *                            
  249.      * @return object $this to build a fluent interface
  250.      * @author Khaled Al-Sham'aa <khaled@ar-php.org>
  251.      */
  252.     public function setOrder($value)
  253.     {
  254.         if ($value == || $value == 2{
  255.             $this->_order $value;
  256.         }
  257.         
  258.         return $this;
  259.     }
  260.     
  261.     /**
  262.      * Get the feminine flag of counted object
  263.      *      
  264.      * @return integer return current setting of counted object feminine flag
  265.      * @author Khaled Al-Sham'aa <khaled@ar-php.org>
  266.      */
  267.     public function getFeminine()
  268.     {
  269.         return $this->_feminine;
  270.     }
  271.     
  272.     /**
  273.      * Get the grammer position flag of counted object
  274.      *      
  275.      * @return integer return current setting of counted object grammer
  276.      *                  position flag
  277.      * @author Khaled Al-Sham'aa <khaled@ar-php.org>
  278.      */
  279.     public function getFormat()
  280.     {
  281.         return $this->_format;
  282.     }
  283.     
  284.     /**
  285.      * Get the ordering flag value
  286.      *      
  287.      * @return integer return current setting of ordering flag value
  288.      * @author Khaled Al-Sham'aa <khaled@ar-php.org>
  289.      */
  290.     public function getOrder()
  291.     {
  292.         return $this->_format;
  293.     }
  294.     
  295.     /**
  296.      * Spell integer number in Arabic idiom
  297.      *      
  298.      * @param integer $number The number you want to spell in Arabic idiom
  299.      *                    
  300.      * @return string The Arabic idiom that spells inserted number
  301.      * @author Khaled Al-Sham'aa <khaled@ar-php.org>
  302.      */
  303.     public function int2str($number)
  304.     {
  305.         if ($number == && $this->_order == 2{
  306.             if ($this->_feminine == 1{
  307.                 $string 'الأول';
  308.             else {
  309.                 $string 'الأولى';
  310.             }
  311.         else {
  312.             if ($number 0{
  313.                 $string 'سالب ';
  314.                 $number = (string) -$number;
  315.             else {
  316.                 $string '';
  317.             }
  318.             
  319.             $temp explode('.'$number);
  320.  
  321.             $string .= $this->subInt2str($temp[0]);
  322.  
  323.             if (!empty($temp[1])) {
  324.                 $dec     $this->subInt2str($temp[1]);
  325.                 $string .= ' فاصلة ' $dec
  326.             }
  327.         }
  328.         
  329.         return $string;
  330.     }
  331.     
  332.     /**
  333.      * Spell number in Arabic idiom as money
  334.      *      
  335.      * @param integer $number The number you want to spell in Arabic idiom as money
  336.      * @param string  $iso    The three-letter Arabic country code defined in
  337.      *                         ISO 3166 standard
  338.      * @param string  $lang   The two-letter language code in ISO 639-1 standard
  339.      *                         [ar|en]
  340.      *                    
  341.      * @return string The Arabic idiom that spells inserted number as money
  342.      * @author Khaled Al-Sham'aa <khaled@ar-php.org>
  343.      */
  344.     public function money2str($number$iso='SYP'$lang='ar')
  345.     {
  346.         $iso  strtoupper($iso);
  347.         $lang strtolower($lang);
  348.         
  349.         $number sprintf("%01.{$this->_currency[$iso]['decimals']}f"$number);
  350.         $temp   explode('.'$number);
  351.         $string '';
  352.  
  353.         if ($temp[0!= 0{
  354.             $string .= $this->subInt2str($temp[0]);
  355.             $string .= ' ' $this->_currency[$iso][$lang]['basic'];
  356.         }
  357.  
  358.         if (!empty($temp[1]&& $temp[1!= 0{
  359.             if ($string != ''{
  360.                 if ($lang == 'ar'{
  361.                     $string .= ' و ';
  362.                 else {
  363.                     $string .= ' and ';
  364.                 }
  365.             }
  366.             
  367.             $string .= $this->subInt2str((int)$temp[1])
  368.             $string .= ' ' $this->_currency[$iso][$lang]['fraction'];
  369.         }
  370.         
  371.         return $string;
  372.     }
  373.     
  374.     /**
  375.      * Convert Arabic idiom number string into Integer
  376.      *      
  377.      * @param string $str The Arabic idiom that spells input number
  378.      *                    
  379.      * @return integer The number you spell it in the Arabic idiom
  380.      * @author Khaled Al-Sham'aa <khaled@ar-php.org>
  381.      */
  382.     public function str2int ($str
  383.     {
  384.         // Normalization phase
  385.         $str str_replace(array('أ','إ','آ')'ا'$str);
  386.         $str str_replace('ه''ة'$str);
  387.         $str preg_replace('/\s+/'' '$str);
  388.         $ptr array('ـ''َ','ً','ُ','ٌ','ِ','ٍ','ْ','ّ');
  389.         $str str_replace($ptr''$str);
  390.         $str str_replace('مائة''مئة'$str);
  391.         $str str_replace(array('احدى','احد')'واحد'$str);
  392.         $ptr array('اثنا','اثني','اثنتا''اثنتي');
  393.         $str str_replace($ptr'اثنان'$str);
  394.         $str trim($str);
  395.         
  396.         if (strpos($str'ناقص'=== false
  397.             && strpos($str'سالب'=== false
  398.         {
  399.             $negative false;
  400.         else {
  401.             $negative true;
  402.         }
  403.         
  404.         // Complications process
  405.         $segment array();
  406.         $max     count($this->_complications);
  407.         
  408.         for ($scale=$max$scale>0$scale--{
  409.             $key pow(1000$scale);
  410.             
  411.             $pattern array('أ','إ','آ');
  412.             $format1 str_replace($pattern'ا'$this->_complications[$scale][1]);
  413.             $format2 str_replace($pattern'ا'$this->_complications[$scale][2]);
  414.             $format3 str_replace($pattern'ا'$this->_complications[$scale][3]);
  415.             $format4 str_replace($pattern'ا'$this->_complications[$scale][4]);
  416.             
  417.             if (strpos($str$format1!== false{
  418.                 list($temp$strexplode($format1$str);
  419.                 $segment[$key]    'اثنان';
  420.             elseif (strpos($str$format2!== false{
  421.                 list($temp$strexplode($format2$str);
  422.                 $segment[$key]    'اثنان';
  423.             elseif (strpos($str$format3!== false{
  424.                 list($segment[$key]$strexplode($format3$str);
  425.             elseif (strpos($str$format4!== false{
  426.                 list($segment[$key]$strexplode($format4$str);
  427.                 if ($segment[$key== ''{
  428.                     $segment[$key'واحد';
  429.                 }
  430.             }
  431.             
  432.             if ($segment[$key!= ''{
  433.                 $segment[$keytrim($segment[$key]);
  434.             }
  435.         }
  436.         
  437.         $segment[1trim($str);
  438.  
  439.         // Individual process
  440.         $total    0;
  441.         $subTotal 0;
  442.         
  443.         foreach ($segment as $scale => $str{
  444.             $str " $str ";
  445.             foreach ($this->_spell as $word => $value{
  446.                 if (strpos($str"$word "!== false{
  447.                     $str str_replace("$word "' '$str);
  448.                     $subTotal += $value;
  449.                 }
  450.             }
  451.  
  452.             $total   += $subTotal $scale;
  453.             $subTotal 0;
  454.         }
  455.         
  456.         if ($negative{
  457.             $total = -$total;
  458.         }
  459.         
  460.         return $total;
  461.     }
  462.     
  463.     /**
  464.      * Spell integer number in Arabic idiom
  465.      *      
  466.      * @param integer $number The number you want to spell in Arabic idiom
  467.      * @param logical $zero   Present leading zero if true [default is true]
  468.      *      
  469.      * @return string The Arabic idiom that spells inserted number
  470.      * @author Khaled Al-Sham'aa <khaled@ar-php.org>
  471.      */
  472.     protected function subInt2str($number$zero true)
  473.     {
  474.         $blocks array();
  475.         $items  array();
  476.         $zeros  '';
  477.         $string '';
  478.         $number ($zero != falsetrim($numbertrim((float)$number);
  479.         
  480.         if ($number 0{
  481.         
  482.             //--- by Jnom: handle left zero
  483.             // http://www.itkane.com
  484.             // jnom23@gmail.com
  485.             if ($zero != false{
  486.                 $fulnum $number;
  487.                 while (($fulnum[0]== '0'{
  488.                     $zeros 'صفر '.$zeros;
  489.                     $fulnum substr($fulnum1strlen($fulnum));
  490.                 };
  491.             };
  492.             //---/
  493.  
  494.             while (strlen($number3{
  495.                 array_push($blockssubstr($number-3));
  496.                 $number substr($number0strlen($number3);
  497.             }
  498.             array_push($blocks$number);
  499.             
  500.             $blocks_num count($blocks1;
  501.   
  502.             for ($i $blocks_num$i >= 0$i--{
  503.                 $number floor($blocks[$i]);
  504.   
  505.                 $text $this->writtenBlock($number);
  506.                 if ($text{
  507.                     if ($number == && $i != 0{
  508.                         $text $this->_complications[$i][4];
  509.                         if ($this->_order == 2{
  510.                             $text 'ال' $text;
  511.                         }
  512.                     elseif ($number == && $i != 0{
  513.                         $text $this->_complications[$i][$this->_format];
  514.                         if ($this->_order == 2{
  515.                             $text 'ال' $text;
  516.                         }
  517.                     elseif ($number && $number 11 && $i != 0{
  518.                         $text .= ' ' $this->_complications[$i][3];
  519.                         if ($this->_order == 2{
  520.                             $text 'ال' $text;
  521.                         }
  522.                     elseif ($i != 0{
  523.                         $text .= ' ' $this->_complications[$i][4];
  524.                         if ($this->_order == 2{
  525.                             $text 'ال' $text;
  526.                         }
  527.                     }
  528.                     
  529.                     //--- by Jnom: handle left zero
  530.                     if ($text != '' && $zeros != '' && $zero != false{
  531.                         $text  $zeros.' '.$text;
  532.                         $zeros '';
  533.                     };
  534.                     //---/
  535.  
  536.                     array_push($items$text);
  537.                 }
  538.             }
  539.             
  540.             $string implode(' و '$items);
  541.         else {
  542.             $string 'صفر';
  543.         }
  544.         return $string;
  545.     }
  546.     
  547.     /**
  548.      * Spell sub block number of three digits max in Arabic idiom
  549.      *      
  550.      * @param integer $number Sub block number of three digits max you want to
  551.      *                         spell in Arabic idiom
  552.      *                      
  553.      * @return string The Arabic idiom that spells inserted sub block
  554.      * @author Khaled Al-Sham'aa <khaled@ar-php.org>
  555.      */
  556.     protected function writtenBlock($number)
  557.     {
  558.         $items  array();
  559.         $string '';
  560.         
  561.         if ($number 99{
  562.             $hundred floor($number 100100;
  563.             $number  $number 100;
  564.             
  565.             if ($this->_order == 2{
  566.                 $pre 'ال';
  567.             else {
  568.                 $pre '';
  569.             }
  570.             
  571.             if ($hundred == 200{
  572.                 array_push(
  573.                     $items
  574.                     $pre.$this->_individual[$hundred][$this->_format]
  575.                 );
  576.             else {
  577.                 array_push($items$pre.$this->_individual[$hundred]);
  578.             }
  579.         }
  580.         
  581.         if ($number != 0{
  582.             if ($this->_order == 2{
  583.                 if ($number <= 10{
  584.                     array_push($items$this->_ordering[$number][$this->_feminine]);
  585.                 elseif ($number 20{
  586.                     $number -= 10;
  587.                     $item    'ال' $this->_ordering[$number][$this->_feminine];
  588.  
  589.                     if ($this->_feminine == 1{
  590.                         $item .= ' عشر';
  591.                     else {
  592.                         $item .= ' عشرة';
  593.                     }
  594.  
  595.                     array_push($items$item);
  596.                 else {
  597.                     $ones $number 10;
  598.                     $tens floor($number 1010;
  599.  
  600.                     array_push(
  601.                         $items
  602.                         'ال' $this->_ordering[$ones][$this->_feminine]
  603.                     );
  604.                     array_push(
  605.                         $items
  606.                         'ال' $this->_individual[$tens][$this->_format]
  607.                     );
  608.                 }
  609.             else {
  610.                 if ($number == || $number == 12{
  611.                     array_push(
  612.                         $items
  613.                         $this->_individual[$number][$this->_feminine][$this->_format]
  614.                     );
  615.                 elseif ($number 20{
  616.                     array_push(
  617.                         $items
  618.                         $this->_individual[$number][$this->_feminine]
  619.                     );
  620.                 else {
  621.                     $ones $number 10;
  622.                     $tens floor($number 1010;
  623.                     
  624.                     if ($ones == 2{
  625.                         array_push(
  626.                             $items
  627.                             $this->_individual[2][$this->_feminine][$this->_format]
  628.                         );
  629.                     elseif ($ones 0{
  630.                         array_push(
  631.                             $items
  632.                             $this->_individual[$ones][$this->_feminine]
  633.                         );
  634.                     }
  635.                     
  636.                     array_push($items$this->_individual[$tens][$this->_format]);
  637.                 }
  638.             }
  639.         }
  640.         
  641.         $items  array_diff($itemsarray(''));
  642.         $string implode(' و '$items);
  643.         
  644.         return $string;
  645.     }
  646.  
  647.     
  648.     /**
  649.      * Represent integer number in Arabic-Indic digits using HTML entities
  650.      *      
  651.      * @param integer $number The number you want to present in Arabic-Indic digits
  652.      *                         using HTML entities
  653.      *                    
  654.      * @return string The Arabic-Indic digits represent inserted integer number
  655.      *                 using HTML entities
  656.      * @author Khaled Al-Sham'aa <khaled@ar-php.org>
  657.      */
  658.     public function int2indic($number)
  659.     {
  660.         $str strtr("$number"$this->_arabicIndic);
  661.  
  662.         return $str;
  663.     }
  664. }

Documentation generated on Fri, 01 Jan 2016 10:26:16 +0200 by phpDocumentor 1.4.0