Location: PHPKode > scripts > Piragibe > piragibe/PGBNlsAgent.php
<?php
/**
 * An agent for National Language Support implementation.
 * 
 * This agent allows for rendering text and other resources in any number of national
 * languages, based on a simple array of resource codes and values for each language.
 * It uses a metaphor similar to the one used by classical NLS implementations, such as
 * the one implemented by JAVA environments, for instance.
 * @package Piragibe
 * @author Francisco Piragibe
 * @version 1.00
 * @copyright Copyright &copy; 2006, Francisco Piragibe
 * This file is part of The PIRAGIBE Framework.
 *
 * The PIRAGIBE Framework is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * The PIRAGIBE Framework 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with The PIRAGIBE Framework; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
class PGBNlsAgent {
    
    /**
     * These are included from the excelent num2words PHP class, by Luis Pinto. This, in turn,
     * is a derived work itself, built from baa2words by Barry A Andrew. Copyright notices
     * included below:
     * Class:        num2words (adapted form baa2words)
     * Date:         June 2006
     * Author:       Luis Pinto
     * Original:
     * Version:      1.0
     * Date:         June 2003
     * Author:       Barry A Andrew
     * Copyright:    © BA Andrew 2003
     */
   var $magind;
   var $units;
   var $teens;
   var $tens;
   var $cens;
   var $mag;
   var $mags;
   var $conj;
   
   var $extenso = array(
    'units' => array(
        'pt' => array('zero','um','dois','tres','quatro','cinco','seis','sete','oito','nove')
        ,'en' => array('zero','one','two','three','four','five','six','seven','eight','nine')
    ),
    'teens' => array(
        'pt' => array('dez','onze','doze','treze','quatorze','quinze','dezesseis','dezessete','dezoito','dezenove')
        ,'en' => array('ten','eleven','twelve','thirteen','fourteen','fifteen','sixteen','seventeen','eighteen','nineteen') 
    ),
    'tens' => array(
        'pt' => array('','','vinte','trinta','quarenta','cinquenta','sessenta','setenta','oitenta','noventa')
        ,'en' => array('','','twenty','thirty','fourty','fifty','sixty','seventy','eighty','ninety') 
    ),
    'cens' => array(
        'pt' => array('cem','cento','duzentos','trezentos','quatrocentos','quinhentos','seiscentos','setecentos','oitocentos','novecentos')
        ,'en' => array('a hundred','one hundred','two hundreds','three hundreds','four hundreds','five hundreds','six hundreds','seven hundreds','eight hundreds','nine hundreds') 
    ),
    'mag' => array(
        'pt' => array('','mil','milhão','bilhão','trilhão')
        ,'en' => array('','thousand','million','billion','trillion')
    ),
    'mags' => array(
        'pt' => array('','mil','milhões','bilhões','trilhões')
        ,'en' => array('','thousands','millions','billions','trillions')
    ),
    'conj' => array(
        'pt' => array( ' e ' )
        ,'en' => array( ' and ' )
    )
   );
    /**
     * The resource array.
     * 
     * This is the agent's backbone. It's an array of 'resource code' => 'resource values'
     * elements. Each resource value, in turn, is another array having 'language code' =>
     * 'resource value' elements. Four special resource codes may be defined:
     * - __CURRENCY_SIGN -> the currency symbol (defaults to a dollar sign)
     * - __CURRENCY_NAME -> the currency full name (no default)
     * - __CENTS_NAME    -> the cents name (centimes, centavos, etc) (no default)
     * - __THOUSANDS     -> the thousands separator (defaults to "." for non-english languages and "," for english)
     * - __DECIMAL       -> the decimal point (defaults to "," for non-english languages and "." for english)
     * - __DATES         -> the date format (detaults do DD/MM/YYYY for non-english languages and MM/DD/YYYY for english)
     * @var array the resource definition array
     */
    var $m_mens;    //array com mensagens em diversas línguas
    //  [<cod mensagem>] -> array com variantes da mensagem
    //  [<sigla lingua>] -> texto
    /**
     * @var string the default language identifier
     */
    var $m_stdl;    // sigla da lingua padrão (se língua desconhecida, usa esta)
    //  as siglas devem estar EM MINÚSCULAS!!
    
    /**
     * Standard constructor.
     * @param string $pStd the default language code (defaults, in turn, to "pt")
     * @param array $pMA the message array to use
     */
    function PGBNlsAgent( $pStd = 'pt', $pMA = array() ) {
        $this->m_stdl   = strtolower( $pStd );
        $this->m_mens   = array();
        $this->setMessageArray( $pMA );
        // inicializa arrays de extenso de quantia
        $ls  = $this->getCurrentLanguages();
        $m   = NULL;
        $lm  = NULL;
        while( list( $k, $l ) = each( $ls ) ) {
            //print 'Tentando: ' . $l . '<br>';
            $lm = $l;
            $m  = $this->extenso['conj'][$lm];
            if ( !is_null( $m ) ) {
                break;
            }
        }
        if ( !is_null( $m ) ) {
            $this->units   = $this->extenso['units'][$lm];
            $this->teens   = $this->extenso['teens'][$lm];
            $this->tens    = $this->extenso['tens'][$lm];
            $this->cens    = $this->extenso['cens'][$lm];
            $this->mag     = $this->extenso['mag'][$lm];
            $this->mags    = $this->extenso['mags'][$lm];
            $this->conj    = $this->extenso['conj'][$lm];
        }
        else {
            // nao conseguindo, tenta achar em cada codigo generico de linguagem (en, fr, pt, etc)
            reset( $ls );
            while( list( $k, $l ) = each( $ls ) ) {
                $l = substr( $l, 0, 2 );
                //print 'Tentando: ' . $l . '<br>';
                $lm = $l;
                $m  = $this->extenso['conj'][$lm];
                if ( !is_null( $m ) ) {
                    break;
                }
            }
            if ( !is_null( $m ) ) {
                $this->units   = $this->extenso['units'][$lm];
                $this->teens   = $this->extenso['teens'][$lm];
                $this->tens    = $this->extenso['tens'][$lm];
                $this->cens    = $this->extenso['cens'][$lm];
                $this->mag     = $this->extenso['mag'][$lm];
                $this->mags    = $this->extenso['mags'][$lm];
                $this->conj    = $this->extenso['conj'][$lm];
            }
            else {
                // nao conseguindo, tenta achar na lingua default
                $lm = $this->m_stdl;
                $this->units   = $this->extenso['units'][$lm];
                $this->teens   = $this->extenso['teens'][$lm];
                $this->tens    = $this->extenso['tens'][$lm];
                $this->cens    = $this->extenso['cens'][$lm];
                $this->mag     = $this->extenso['mag'][$lm];
                $this->mags    = $this->extenso['mags'][$lm];
                $this->conj    = $this->extenso['conj'][$lm];
            }
        }
    }
    
    /**
     * Getter for the language's currency name.
     * @return string the currency full name
     */
    function getCurrencyName() {
        $m   = $this->getMessage( '__CURRENCY_NAME' );
        if ( is_null( $m ) ) {
            $m   = '';
        }
        return $m;
    }

    /**
     * Getter for the language's currency cents name.
     * @return string the currency cents name
     */
    function getCentsName() {
        $m   = $this->getMessage( '__CENTS_NAME' );
        if ( is_null( $m ) ) {
            $m   = '$';
        }
        return $m;
    }

    /**
     * Getter for the language's currency symbol.
     * @return string the currency symbol
     */
    function getCurrencySign() {
        $m   = $this->getMessage( '__CURRENCY_SIGN' );
        if ( is_null( $m ) ) {
            $m   = '$';
        }
        return $m;
    }

    /**
     * Getter for the thousands separator character.
     * @return string the thousands separator
     */
    function getThousandsSeparator() {
        $m   = $this->getMessage( '__THOUSANDS' );
        if ( is_null( $m ) ) {
            $lss = split( ';', $_SERVER[ 'HTTP_ACCEPT_LANGUAGE' ] );
            $ls  = split( ',', $lss[0] );
            $l   = strtolower(substr( $ls[0], 0, 2 ));
            if ( $l == 'en') {
              $m = ',';
            }
            else {
              $m = '.';
            }
        }
        return $m;
    }

    /**
     * Getter for the decimal point character.
     * @return string the decimal point
     */
    function getDecimalsSeparator() {
        $m   = $this->getMessage( '__DECIMAL' );
        if ( is_null( $m ) ) {
            $lss = split( ';', $_SERVER[ 'HTTP_ACCEPT_LANGUAGE' ] );
            $ls  = split( ',', $lss[0] );
            $l   = strtolower(substr( $ls[0], 0, 2 ));
            if ( $l == 'en') {
              $m = '.';
            }
            else {
              $m = ',';
            }
        }
        return $m;
    }
    
    /**
     * Getter for the language date format.
     * @return string the date format
     */
    function getDateFormat() {
        $m   = $this->getMessage( '__DATES' );
        if ( is_null( $m ) ) {
            $lss = split( ';', $_SERVER[ 'HTTP_ACCEPT_LANGUAGE' ] );
            $ls  = split( ',', $lss[0] );
            $l   = strtolower(substr( $ls[0], 0, 2 ));
            if ( $l == 'en') {
              $m = 'MM/DD/YYYY';
            }
            else {
              $m = 'DD/MM/YYYY';
            }
        }
        return $m;
    }
    
    /**
     * Writes numbers in words, with optional currency markers.
     * 
     * Numbers are spelled in the language preferred by the browser.
     * Included from the excelent num2words PHP class, by Luis Pinto, which is, in turn,
     * a derived work itself, built from baa2words by Barry A Andrew. Copyright notices
     * included below:
     * Class:        num2words (adapted form baa2words)
     * Date:         June 2006
     * Author:       Luis Pinto
     * Original:
     * Version:      1.0
     * Date:         June 2003
     * Author:       Barry A Andrew
     * Copyright:    © BA Andrew 2003
     * @return string the number in words
     * @param double $amount the number to be written
     */
    function spellNumber( $amount ) {
         $words = '';
         $major = $this->getCurrencyName();
         $minor = $this->getCentsName();
         $number = number_format($amount,2);
         $pounds = NULL;
         $pence  = NULL;
         list($pounds,$pence) = explode('.',$number);
         if ($pence==0){
            $words = "$major";
         }
         else {
            $groups = explode(',',$pence);
            $groups = array_reverse($groups);   
            if ( $pounds > 0 ) {
                $words = "$major $pence $minor";
            }
            else {
                $words = "$pence $minor";
            }
            for ($this->magind=0; $this->magind<count($groups); $this->magind++) {
                //print "$words\n";
                if (($this->magind==1)&&(strpos($words,$this->cens[0]) === false)&&($groups[0]!='000'))
                    $words = $this->conj[0] . $words;
                if ( $pounds > 0 ) {
                    $words = $major . $this->conj[0] . $this->_build($groups[$this->magind]) . $minor;
                }
                else {
                    $words = $this->_build($groups[$this->magind]) . $minor;
                }
            }
         }
    
         if ($pounds>0) {
             $groups = explode(',',$pounds);
             $groups = array_reverse($groups);
             for ($this->magind=0; $this->magind<count($groups); $this->magind++) {
                  //print "$words\n";
                  if (($this->magind==1)&&(strpos($words,$this->cens[0]) === false)&&($groups[0]!='000'))
                       $words = $this->conj[0] . $words;
                  $words = $this->_build($groups[$this->magind]) . ($words{0}==' '?$words:(' ' . $words));
             }
         }
         return $words;
    }
    
    /**
     * Auxiliary method used to write numbers.
     * 
     * DO NOT use it directly, unless you know what you're doing.
     * Included from the excelent num2words PHP class, by Luis Pinto, which is, in turn,
     * a derived work itself, built from baa2words by Barry A Andrew. Copyright notices
     * included below:
     * Class:        num2words (adapted form baa2words)
     * Date:         June 2006
     * Author:       Luis Pinto
     * Original:
     * Version:      1.0
     * Date:         June 2003
     * Author:       Barry A Andrew
     * Copyright:    © BA Andrew 2003
     * @param integer $n the part to be spelled
     */
    function _build($n) {
         $res = '';
         $na = str_pad("$n",3,"0",STR_PAD_LEFT);
         if ($na == '000') return '';
         if ($na == '100') return '' . $this->cens[0] . '';
         if ($na{0} != 0 && $na != '100') {
             $res = '' . $this->cens[$na{0}];
         }
         if (($na{1}=='0')&&($na{2}=='0'))
              return $res . ' ' . ($na<=1?$this->mag[$this->magind]:$this->mags[$this->magind]);
         $res .= $res==''? '' : $this->conj[0];
         $t = (int)$na{1}; $u = (int)$na{2};
         switch ($t) {
                 case 0: $res .= '' . $this->units[$u]; break;
                 case 1: $res .= '' . $this->teens[$u]; break;
                 default:
                     if ( $u > 0 ) {
                         $res .= '' . $this->tens[$t] . $this->conj[0] . $this->units[$u];
                     }
                     else {
                         $res .= '' . $this->tens[$t];
                     }
                     break;
         }
         $res .= ' ' . ($na<=1?$this->mag[$this->magind]:$this->mags[$this->magind]);
         return $res;
    }
    
    /**
     * A secondary message getter, that returns a message given a message code and a language code.
     * @return mixed the message corresponding to the code in the given language (or NULL, if it can't be found)
     * @param string $pCod the message code
     * @param string $pLin the language code
     */
    function getLocalizedMessage( $pCod, $pLin ) {
        $l = strtolower( $pLin );
        if ( isset( $this->m_mens[$pCod][$l] ) ) {
            return $this->m_mens[$pCod][$l];
        }
        else {
            return NULL;
        }
    }
    
    /**
     * Getter for the current languages accepted by the browser.
     * @return array with the current acceptable language symbols
     */
    function getCurrentLanguages() {
        if ( isset( $_SERVER[ 'HTTP_ACCEPT_LANGUAGE' ] ) ) {
            $lss = split( ';', $_SERVER[ 'HTTP_ACCEPT_LANGUAGE' ] );
            $ls  = split( ',', $lss[0] );
            return $ls;
        }
        else {
        	return array( $this->getStandardLanguage() );
        }
    }
    
    /**
     * Main message getter, given the resource (message) code.
     * 
     * This is a four step process:
     * - First, if the code can't be found, the code itself is returned, unless it's a "__" code (meant for internal use only); in this case, it returns a NULL.
     * - Second, it tries to find a message in any language acceptable to the HTTP server, in the given order.
     * - Failing to do so, it'll return a message using the default language.
     * - Finally, if it's at all impossible to find a suitable message, it'll return the message code itself, unchanged.
     * @return mixed the desired message or the message code (if a message cannot be retrieved)
     * @param string $pCod the message code
     */
    function getMessage( $pCod ) {
        // se o codigo nao esta setado no array, retorna o proprio codigo, a menos que
        // ele comece por duplo "underscore": neste caso, retorna NULL
        if ( !isset( $this->m_mens[$pCod] ) ) {
            if ( substr( $pCod, 0, 2 ) == '__' ) {
                return NULL;
            }
            else {
                return $pCod;
            }
        }
        // obtem uma mensagem na lingua preferencial
        $ls  = $this->getCurrentLanguages();
        $m   = NULL;
        // tenta achar mensagem na variacao correta de cada lingua
        //print '<pre>' . $pCod;
        while( list( $k, $l ) = each( $ls ) ) {
            //print 'Tentando: ' . $l . '<br>';
            $m = $this->getLocalizedMessage( $pCod, $l );
            if ( !is_null( $m ) ) {
                break;
            }
        }
        if ( !is_null( $m ) ) {
            return $m;
        }
        // nao conseguindo, tenta achar em cada codigo generico de linguagem (en, fr, pt, etc)
        reset( $ls );
        while( list( $k, $l ) = each( $ls ) ) {
            $l = substr( $l, 0, 2 );
            //print 'Tentando: ' . $l . '<br>';
            $m = $this->getLocalizedMessage( $pCod, $l );
            if ( !is_null( $m ) ) {
                break;
            }
        }
        if ( !is_null( $m ) ) {
            return $m;
        }
        // nao conseguindo, tenta achar na lingua default
        if ( is_null( $m ) ) {
            $m = $this->getLocalizedMessage( $pCod, $this->m_stdl );
        }
        if ( !is_null( $m ) ) {
            return $m;
        }
        // finalmente, retorna o proprio codigo
        $m = $pCod ;
        //print '</pre>';
        return $m;
    }
    
    /**
     * Getter for the standard language code.
     * @return string the language code
     */
    function getStandardLanguage() {
    // retorna a lingua padrao das mensagens
        return $this->m_stdl;
    }
    
    /**
     * Setter for the base array of messages.
     * 
     * It's the implementor's responsibility making sure the array adheres to the stated
     * standard. Note that this method <b>merges</b> the given array into the existing
     * one. If you want to <b>replace</b> the message array, use the {@link replaceMessageArray}
     * method.
     * @param array $pMA the message array to merge.
     */
    function setMessageArray( $pMA ) {
    // seta o array de mensagens
        if ( is_array( $pMA ) ) {
            $this->m_mens = array_merge( $this->m_mens, $pMA );
        }
    }
    
    /**
     * Clears the existing message array.
     */
    function clearMessageArray() {
        $this->m_mens   = array();
    }
    
    /**
     * Replaces the existing message array by the given one.
     * @param array $pMA the new message array.
     */
    function replaceMessageArray( $pMA ) { 
        $this->clearMessageArray();
        $this->setMessageArray( $pMA );
    }
        
}
?>
Return current item: Piragibe