<?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 © 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 );
}
}
?>