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-2013 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-2013 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. // New in PHP V5.3: Namespaces
  105. // namespace I18N\Arabic;
  106. // 
  107. // $obj = new I18N\Arabic\Numbers();
  108. // 
  109. // use I18N\Arabic;
  110. // $obj = new Arabic\Numbers();
  111. //
  112. // use I18N\Arabic\Numbers as Numbers;
  113. // $obj = new Numbers();
  114.  
  115. /**
  116.  * This PHP class spell numbers in the Arabic idiom
  117.  *  
  118.  * @category  I18N
  119.  * @package   I18N_Arabic
  120.  * @author    Khaled Al-Sham'aa <khaled@ar-php.org>
  121.  * @copyright 2006-2013 Khaled Al-Sham'aa
  122.  *    
  123.  * @license   LGPL <http://www.gnu.org/licenses/lgpl.txt>
  124.  * @link      http://www.ar-php.org
  125.  */ 
  126. {
  127.     private $_individual    array();
  128.     private $_complications array();
  129.     private $_arabicIndic   array();
  130.     private $_ordering      array();
  131.     private $_currency      array();
  132.     private $_spell         array();
  133.     private $_feminine      1;
  134.     private $_format        1;
  135.     private $_order         1;
  136.  
  137.     /**
  138.      * Loads initialize values
  139.      *
  140.      * @ignore
  141.      */         
  142.     public function __construct()
  143.     {
  144.         $xml simplexml_load_file(dirname(__FILE__).'/data/ArNumbers.xml');
  145.  
  146.         foreach ($xml->xpath("//individual/number[@gender='male']"as $num{
  147.             if (isset($num['grammar'])) {
  148.                 $grammar $num['grammar'];
  149.                 
  150.                 $this->_individual["{$num['value']}"][1]["$grammar"= (string)$num;
  151.             else {
  152.                 $this->_individual["{$num['value']}"][1= (string)$num;
  153.             }
  154.         
  155.         
  156.         foreach ($xml->xpath("//individual/number[@gender='female']"as $num{
  157.             if (isset($num['grammar'])) {
  158.                 $grammar $num['grammar'];
  159.                 
  160.                 $this->_individual["{$num['value']}"][2]["$grammar"= (string)$num;
  161.             else {
  162.                 $this->_individual["{$num['value']}"][2= (string)$num;
  163.             }
  164.         
  165.         
  166.         foreach ($xml->xpath("//individual/number[@value>19]"as $num{
  167.             if (isset($num['grammar'])) {
  168.                 $grammar $num['grammar'];
  169.                 
  170.                 $this->_individual["{$num['value']}"]["$grammar"= (string)$num;
  171.             else {
  172.                 $this->_individual["{$num['value']}"= (string)$num;
  173.             }
  174.         
  175.         
  176.         foreach ($xml->complications->number as $num{
  177.             $scale  $num['scale'];
  178.             $format $num['format'];
  179.             
  180.             $this->_complications["$scale"]["$format"= (string)$num;
  181.         
  182.         
  183.         foreach ($xml->arabicIndic->number as $html{
  184.             $value  $html['value'];
  185.             
  186.             $this->_arabicIndic["$value"$html;
  187.         
  188.  
  189.         foreach ($xml->xpath("//order/number[@gender='male']"as $num{
  190.             $this->_ordering["{$num['value']}"][1= (string)$num;
  191.         
  192.  
  193.         foreach ($xml->xpath("//order/number[@gender='female']"as $num{
  194.             $this->_ordering["{$num['value']}"][2= (string)$num;
  195.         }
  196.         
  197.         foreach ($xml->xpath("//individual/number[@value<11 or @value>19]"as $num{
  198.             $this->_spell[(string)$num= (integer)$num['value'];
  199.         
  200.         
  201.         $xml simplexml_load_file(dirname(__FILE__).'/data/arab_countries.xml');
  202.         
  203.         foreach ($xml->xpath("//currency"as $info{
  204.             $this->_currency["{$info->iso}"]['ar']['basic']    $info->money->arabic->basic;
  205.             $this->_currency["{$info->iso}"]['ar']['fraction'$info->money->arabic->fraction;
  206.             $this->_currency["{$info->iso}"]['en']['basic']    $info->money->english->basic;
  207.             $this->_currency["{$info->iso}"]['en']['fraction'$info->money->english->fraction;
  208.             $this->_currency["{$info->iso}"]['decimals']       $info->money->decimals;
  209.         }
  210.     }
  211.     
  212.     /**
  213.      * Set feminine flag of the counted object
  214.      *      
  215.      * @param integer $value Counted object feminine
  216.      *                       (1 for masculine & 2 for feminine)
  217.      *      
  218.      * @return object $this to build a fluent interface
  219.      * @author Khaled Al-Sham'aa <khaled@ar-php.org>
  220.      */
  221.     public function setFeminine($value)
  222.     {
  223.         if ($value == || $value == 2{
  224.             $this->_feminine $value;
  225.         }
  226.         
  227.         return $this;
  228.     }
  229.     
  230.     /**
  231.      * Set the grammar position flag of the counted object
  232.      *      
  233.      * @param integer $value Grammar position of counted object
  234.      *                        (1 if Marfoua & 2 if Mansoub or Majrour)
  235.      *                            
  236.      * @return object $this to build a fluent interface
  237.      * @author Khaled Al-Sham'aa <khaled@ar-php.org>
  238.      */
  239.     public function setFormat($value)
  240.     {
  241.         if ($value == || $value == 2{
  242.             $this->_format $value;
  243.         }
  244.         
  245.         return $this;
  246.     }
  247.     
  248.     /**
  249.      * Set the ordering flag, is it normal number or ordering number
  250.      *      
  251.      * @param integer $value Is it an ordering number? default is 1
  252.      *                        (use 1 if no and 2 if yes)
  253.      *                            
  254.      * @return object $this to build a fluent interface
  255.      * @author Khaled Al-Sham'aa <khaled@ar-php.org>
  256.      */
  257.     public function setOrder($value)
  258.     {
  259.         if ($value == || $value == 2{
  260.             $this->_order $value;
  261.         }
  262.         
  263.         return $this;
  264.     }
  265.     
  266.     /**
  267.      * Get the feminine flag of counted object
  268.      *      
  269.      * @return integer return current setting of counted object feminine flag
  270.      * @author Khaled Al-Sham'aa <khaled@ar-php.org>
  271.      */
  272.     public function getFeminine()
  273.     {
  274.         return $this->_feminine;
  275.     }
  276.     
  277.     /**
  278.      * Get the grammer position flag of counted object
  279.      *      
  280.      * @return integer return current setting of counted object grammer
  281.      *                  position flag
  282.      * @author Khaled Al-Sham'aa <khaled@ar-php.org>
  283.      */
  284.     public function getFormat()
  285.     {
  286.         return $this->_format;
  287.     }
  288.     
  289.     /**
  290.      * Get the ordering flag value
  291.      *      
  292.      * @return integer return current setting of ordering flag value
  293.      * @author Khaled Al-Sham'aa <khaled@ar-php.org>
  294.      */
  295.     public function getOrder()
  296.     {
  297.         return $this->_format;
  298.     }
  299.     
  300.     /**
  301.      * Spell integer number in Arabic idiom
  302.      *      
  303.      * @param integer $number The number you want to spell in Arabic idiom
  304.      *                    
  305.      * @return string The Arabic idiom that spells inserted number
  306.      * @author Khaled Al-Sham'aa <khaled@ar-php.org>
  307.      */
  308.     public function int2str($number)
  309.     {
  310.         if ($number == && $this->_order == 2{
  311.             if ($this->_feminine == 1{
  312.                 $string 'الأول';
  313.             else {
  314.                 $string 'الأولى';
  315.             }
  316.         else {
  317.             if ($number 0{
  318.                 $string 'سالب ';
  319.                 $number = (string) -$number;
  320.             else {
  321.                 $string '';
  322.             }
  323.             
  324.             $temp explode('.'$number);
  325.  
  326.             $string .= $this->subInt2str($temp[0]);
  327.  
  328.             if (!empty($temp[1])) {
  329.                 $dec     $this->subInt2str($temp[1]);
  330.                 $string .= ' فاصلة ' $dec
  331.             }
  332.         }
  333.         
  334.         return $string;
  335.     }
  336.     
  337.     /**
  338.      * Spell number in Arabic idiom as money
  339.      *      
  340.      * @param integer $number The number you want to spell in Arabic idiom as money
  341.      * @param string  $iso    The three-letter Arabic country code defined in ISO 3166 standard
  342.      * @param string  $lang   The two-letter language code in ISO 639-1 standard [ar|en]
  343.      *                    
  344.      * @return string The Arabic idiom that spells inserted number as money
  345.      * @author Khaled Al-Sham'aa <khaled@ar-php.org>
  346.      */
  347.     public function money2str($number$iso='SYP'$lang='ar')
  348.     {
  349.         $iso  strtoupper($iso);
  350.         $lang strtolower($lang);
  351.         
  352.         $number sprintf("%01.{$this->_currency[$iso]['decimals']}f"$number);
  353.         $temp   explode('.'$number);
  354.  
  355.         $string  $this->subInt2str($temp[0]);
  356.         $string .= ' ' $this->_currency[$iso][$lang]['basic'];
  357.  
  358.         if (!empty($temp[1])) {
  359.             if ($lang == 'ar'{
  360.                 $string .= ' و ';
  361.             else {
  362.                 $string .= ' and ';
  363.             }
  364.             
  365.             $string .= $this->subInt2str((int)$temp[1])
  366.             $string .= ' ' $this->_currency[$iso][$lang]['fraction'];
  367.         }
  368.         
  369.         return $string;
  370.     }
  371.     
  372.     /**
  373.      * Convert Arabic idiom number string into Integer
  374.      *      
  375.      * @param string $str The Arabic idiom that spells input number
  376.      *                    
  377.      * @return integer The number you spell it in the Arabic idiom
  378.      * @author Khaled Al-Sham'aa <khaled@ar-php.org>
  379.      */
  380.     public function str2int ($str
  381.     {
  382.         // Normalization phase
  383.         $str str_replace(array('أ','إ','آ')'ا'$str);
  384.         $str str_replace('ه''ة'$str);
  385.         $str preg_replace('/\s+/'' '$str);
  386.         $str str_replace(array('ـ''َ','ً','ُ','ٌ','ِ','ٍ','ْ','ّ')''$str);
  387.         $str str_replace('مائة''مئة'$str);
  388.         $str str_replace(array('احدى','احد')'واحد'$str);
  389.         $str str_replace(array('اثنا','اثني','اثنتا''اثنتي')'اثنان'$str);
  390.         $str trim($str);
  391.         
  392.         if (strpos($str'ناقص'=== false && strpos($str'سالب'=== false{
  393.             $negative false;
  394.         else {
  395.             $negative true;
  396.         }
  397.         
  398.         // Complications process
  399.         $segment array();
  400.         $max     count($this->_complications);
  401.         
  402.         for ($scale=$max$scale>0$scale--{
  403.             $key pow(1000$scale);
  404.             
  405.             $format1 str_replace(array('أ','إ','آ')'ا'$this->_complications[$scale][1]);
  406.             $format2 str_replace(array('أ','إ','آ')'ا'$this->_complications[$scale][2]);
  407.             $format3 str_replace(array('أ','إ','آ')'ا'$this->_complications[$scale][3]);
  408.             $format4 str_replace(array('أ','إ','آ')'ا'$this->_complications[$scale][4]);
  409.             
  410.             if (strpos($str$format1!== false{
  411.                 list($temp$strexplode($format1$str);
  412.                 $segment[$key]    'اثنان';
  413.             elseif (strpos($str$format2!== false{
  414.                 list($temp$strexplode($format2$str);
  415.                 $segment[$key]    'اثنان';
  416.             elseif (strpos($str$format3!== false{
  417.                 list($segment[$key]$strexplode($format3$str);
  418.             elseif (strpos($str$format4!== false{
  419.                 list($segment[$key]$strexplode($format4$str);
  420.                 if ($segment[$key== ''{
  421.                     $segment[$key'واحد';
  422.                 }
  423.             }
  424.             
  425.             if ($segment[$key!= ''{
  426.                 $segment[$keytrim($segment[$key]);
  427.             }
  428.         }
  429.         
  430.         $segment[1trim($str);
  431.         
  432.         // Individual process
  433.         $total    0;
  434.         $subTotal 0;
  435.         
  436.         foreach ($segment as $scale => $str{
  437.             $str " $str ";
  438.             foreach ($this->_spell as $word => $value{
  439.                 if (strpos($str" $word "!== false{
  440.                     $str str_replace(" $word "' '$str);
  441.                     $subTotal += $value;
  442.                 }
  443.             }
  444.             
  445.             $total   += $subTotal $scale;
  446.             $subTotal 0;
  447.         }
  448.         
  449.         if ($negative{
  450.             $total = -$total;
  451.         }
  452.         
  453.         return $total;
  454.     }
  455.     
  456.     /**
  457.      * Spell integer number in Arabic idiom
  458.      *      
  459.      * @param integer $number The number you want to spell in Arabic idiom
  460.      * @param logical $zero   Present leading zero if true [default is true]
  461.      *      
  462.      * @return string The Arabic idiom that spells inserted number
  463.      * @author Khaled Al-Sham'aa <khaled@ar-php.org>
  464.      */
  465.     protected function subInt2str($number$zero true)
  466.     {
  467.         $blocks array();
  468.         $items  array();
  469.         $zeros  '';
  470.         $string '';
  471.         $number ($zero != falsetrim($numbertrim((float)$number);
  472.         
  473.         if ($number 0{
  474.         
  475.             //--- by Jnom: handle left zero
  476.             // http://www.itkane.com
  477.             // jnom23@gmail.com
  478.             if ($zero != false{
  479.                 $fulnum $number;
  480.                 while (($fulnum[0]== '0'{
  481.                     $zeros 'صفر '.$zeros;
  482.                     $fulnum substr($fulnum1strlen($fulnum));
  483.                 };
  484.             };
  485.             //---/
  486.  
  487.             while (strlen($number3{
  488.                 array_push($blockssubstr($number-3));
  489.                 $number substr($number0strlen($number3);
  490.             }
  491.             array_push($blocks$number);
  492.             
  493.             $blocks_num count($blocks1;
  494.   
  495.             for ($i $blocks_num$i >= 0$i--{
  496.                 $number floor($blocks[$i]);
  497.   
  498.                 $text $this->writtenBlock($number);
  499.                 if ($text{
  500.                     if ($number == && $i != 0{
  501.                         $text $this->_complications[$i][4];
  502.                         if ($this->_order == 2{
  503.                             $text 'ال' $text;
  504.                         }
  505.                     elseif ($number == && $i != 0{
  506.                         $text $this->_complications[$i][$this->_format];
  507.                         if ($this->_order == 2{
  508.                             $text 'ال' $text;
  509.                         }
  510.                     elseif ($number && $number 11 && $i != 0{
  511.                         $text .= ' ' $this->_complications[$i][3];
  512.                         if ($this->_order == 2{
  513.                             $text 'ال' $text;
  514.                         }
  515.                     elseif ($i != 0{
  516.                         $text .= ' ' $this->_complications[$i][4];
  517.                         if ($this->_order == 2{
  518.                             $text 'ال' $text;
  519.                         }
  520.                     }
  521.                     
  522.                     //--- by Jnom: handle left zero
  523.                     if ($text != '' && $zeros != '' && $zero != false{
  524.                         $text  $zeros.' '.$text;
  525.                         $zeros '';
  526.                     };
  527.                     //---/
  528.  
  529.                     array_push($items$text);
  530.                 }
  531.             }
  532.             
  533.             $string implode(' و '$items);
  534.         else {
  535.             $string 'صفر';
  536.         }
  537.         return $string;
  538.     }
  539.     
  540.     /**
  541.      * Spell sub block number of three digits max in Arabic idiom
  542.      *      
  543.      * @param integer $number Sub block number of three digits max you want to
  544.      *                         spell in Arabic idiom
  545.      *                      
  546.      * @return string The Arabic idiom that spells inserted sub block
  547.      * @author Khaled Al-Sham'aa <khaled@ar-php.org>
  548.      */
  549.     protected function writtenBlock($number)
  550.     {
  551.         $items  array();
  552.         $string '';
  553.         
  554.         if ($number 99{
  555.             $hundred floor($number 100100;
  556.             $number  $number 100;
  557.             
  558.             if ($this->_order == 2{
  559.                 $pre 'ال';
  560.             else {
  561.                 $pre '';
  562.             }
  563.             
  564.             if ($hundred == 200{
  565.                 array_push(
  566.                     $items
  567.                     $pre.$this->_individual[$hundred][$this->_format]
  568.                 );
  569.             else {
  570.                 array_push($items$pre.$this->_individual[$hundred]);
  571.             }
  572.         }
  573.         
  574.         if ($number != 0{
  575.             if ($this->_order == 2{
  576.                 if ($number <= 10{
  577.                     array_push($items$this->_ordering[$number][$this->_feminine]);
  578.                 elseif ($number 20{
  579.                     $number -= 10;
  580.                     $item    'ال' $this->_ordering[$number][$this->_feminine];
  581.  
  582.                     if ($this->_feminine == 1{
  583.                         $item .= ' عشر';
  584.                     else {
  585.                         $item .= ' عشرة';
  586.                     }
  587.  
  588.                     array_push($items$item);
  589.                 else {
  590.                     $ones $number 10;
  591.                     $tens floor($number 1010;
  592.  
  593.                     array_push(
  594.                         $items
  595.                         'ال' $this->_ordering[$ones][$this->_feminine]
  596.                     );
  597.                     array_push(
  598.                         $items
  599.                         'ال' $this->_individual[$tens][$this->_format]
  600.                     );
  601.                 }
  602.             else {
  603.                 if ($number == || $number == 12{
  604.                     array_push(
  605.                         $items
  606.                         $this->_individual[$number][$this->_feminine][$this->_format]
  607.                     );
  608.                 elseif ($number 20{
  609.                     array_push(
  610.                         $items
  611.                         $this->_individual[$number][$this->_feminine]
  612.                     );
  613.                 else {
  614.                     $ones $number 10;
  615.                     $tens floor($number 1010;
  616.                     
  617.                     if ($ones == 2{
  618.                         array_push(
  619.                             $items
  620.                             $this->_individual[$ones][$this->_feminine][$this->_format]
  621.                         );
  622.                     elseif ($ones 0{
  623.                         array_push(
  624.                             $items
  625.                             $this->_individual[$ones][$this->_feminine]
  626.                         );
  627.                     }
  628.                     
  629.                     array_push($items$this->_individual[$tens][$this->_format]);
  630.                 }
  631.             }
  632.         }
  633.         
  634.         $items  array_diff($itemsarray(''));
  635.         $string implode(' و '$items);
  636.         
  637.         return $string;
  638.     }
  639.  
  640.     
  641.     /**
  642.      * Represent integer number in Arabic-Indic digits using HTML entities
  643.      *      
  644.      * @param integer $number The number you want to present in Arabic-Indic digits
  645.      *                         using HTML entities
  646.      *                    
  647.      * @return string The Arabic-Indic digits represent inserted integer number
  648.      *                 using HTML entities
  649.      * @author Khaled Al-Sham'aa <khaled@ar-php.org>
  650.      */
  651.     public function int2indic($number)
  652.     {
  653.         $str strtr("$number"$this->_arabicIndic);
  654.  
  655.         return $str;
  656.     }
  657. }

Documentation generated on Mon, 14 Jan 2013 17:49:01 +0100 by phpDocumentor 1.4.0