Location: PHPKode > projects > SolarPHP > solar-system-1.1.1/solar/source/solar/Solar/Locale.php
<?php
/**
 * 
 * Manages locale strings for all Solar classes.
 * 
 * @category Solar
 * 
 * @package Solar
 * 
 * @author Paul M. Jones <hide@address.com>
 * 
 * @license http://opensource.org/licenses/bsd-license.php BSD
 * 
 * @version $Id: Locale.php 4533 2010-04-23 16:35:15Z pmjones $
 * 
 */
class Solar_Locale extends Solar_Base
{
    /**
     * 
     * Default configuration values.
     * 
     * @config string code The default locale code to use.
     * 
     * @var array
     * 
     */
    protected $_Solar_Locale = array(
        'code' => 'en_US',
    );
    
    /**
     * 
     * Collected translation strings arranged by class and key.
     * 
     * @var array
     * 
     */
    public $trans = array();
    
    /**
     * 
     * The current locale code.
     * 
     * @var string
     * 
     */
    protected $_code = 'en_US';
    
    /**
     * 
     * Post-construction tasks to complete object construction.
     * 
     * @return void
     * 
     */
    protected function _postConstruct()
    {
        parent::_postConstruct();
        $this->setCode($this->_config['code']);
    }
    
    /**
     * 
     * Sets the locale code and clears out previous translations.
     * 
     * @param string $code A locale code, for example, 'en_US'.
     * 
     * @return void
     * 
     */
    public function setCode($code)
    {
        // set the code
        $this->_code = $code;
        
        // reset the strings
        $this->trans = array();
    }
    
    /**
     * 
     * Returns the current locale code.
     * 
     * @return string The current locale code, for example, 'en_US'.
     * 
     */
    public function getCode()
    {
        return $this->_code;
    }
    
    /**
     * 
     * Returns ISO 3166 country code for current locale code.
     * 
     * This is basically just the last two uppercase letters
     * from the locale code.
     * 
     * @return string
     * 
     */
    public function getCountryCode()
    {
        return substr($this->_code, -2);
    }
    
    /**
     * 
     * Returns RFC 1766 (XHTML) language code for current locale code.
     * 
     * This is the same as the locale code, but using a dash instead of an
     * underscore as a separator.
     * 
     * @return string
     * 
     */
    public function getLanguageCode()
    {
        return str_replace('_', '-', $this->_code);
    }
    
    /**
     * 
     * Returns the translated locale string for a class and key.
     * 
     * Loads translations as needed.
     * 
     * You can also pass an array of replacement values.  If the `$replace`
     * array is sequential, this method will use it with vsprintf(); if the
     * array is associative, this method will replace "{:key}" with the array
     * value.
     * 
     * For example:
     * 
     * {{code: php
     *     
     *     $locale = Solar_Registry('locale');
     *     
     *     $page  = 2;
     *     $pages = 10;
     *     
     *     // given a class of 'Solar_Example' with a locale string
     *     // TEXT_PAGES => 'Page %d of %d', uses vsprintf() internally:
     *     $replace = array($page, $pages);
     *     echo $locale->fetch('Solar_Example', 'TEXT_PAGES', $pages, $replace);
     *     // echo "Page 2 of 10"
     *     
     *     // given a class of 'Solar_Example' with a locale string
     *     // TEXT_PAGES => 'Page {:page} of {:pages}', uses str_replace()
     *     // internally:
     *     $replace = array('page' => $page, 'pages' => $pages);
     *     echo $locale->fetch('Solar_Example', 'TEXT_PAGES', $pages, $replace);
     *     // echo "Page 2 of 10"
     * }}
     * 
     * @param string|object $spec The class name (or object) for the translation.
     * 
     * @param string $key The translation key.
     * 
     * @param mixed $num Helps determine whether to get a singular
     * or plural translation.
     * 
     * @param array $replace An array of replacement values for the string.
     * 
     * @return string A translated locale string.
     * 
     * @see _trans()
     * 
     * @see Solar_Base::locale()
     * 
     */
    public function fetch($spec, $key, $num = 1, $replace = null)
    {
        // is the spec an object?
        if (is_object($spec)) {
            // yes, find its class
            $class = get_class($spec);
        } else {
            // no, assume the spec is a class name
            $class = (string) $spec;
        }
        
        // does the translation key exist for this class?
        // pre-empts the stack check.
        $string = $this->_trans($class, $key, $num, $replace);
        if ($string !== null) {
            return $string;
        }
        
        // find all parents of the class, including the class itself
        $parents = array_reverse(Solar_Class::parents($class, true));
        
        // add the vendor namespace to the stack for vendor-wide strings
        $vendor = Solar_Class::vendor($class);
        $parents[] = $vendor;
        
        // add Solar as the final fallback.
        if ($vendor != 'Solar') {
            $parents[] = 'Solar';
        }
        
        // go through all parents and find the first matching key
        foreach ($parents as $parent) {
            
            // do we need to load locale strings for the class?
            if (! array_key_exists($parent, $this->trans)) {
                $this->_load($parent);
            }
        
            // does the key exist for the parent?
            $string = $this->_trans($parent, $key, $num, $replace);
            if ($string !== null) {
                // save it for the class so we don't need to go through the
                // stack again, and then we're done.
                $this->trans[$class][$key] = $this->trans[$parent][$key];
                return $string;
            }
        }
        
        // never found a translation, return the requested key.
        return $key;
    }
    
    /**
     * 
     * Returns an existing class/key/num string from the translation array.
     * 
     * @param string $class The translation class.
     * 
     * @param string $key The translation key.
     * 
     * @param mixed $num Helps determine if we need a singular or plural
     * translation.
     * 
     * @param array $replace An array of replacement values for the string.
     * 
     * @return string The translation string if it exists, or null if it
     * does not.
     * 
     */
    protected function _trans($class, $key, $num = 1, $replace = null)
    {
        if (! array_key_exists($class, $this->trans) ||
            ! array_key_exists($key, $this->trans[$class])) {
            // class or class-key does not exist
            return null;
        }
        
        // get the translation of the key and force to an array.
        $trans = (array) $this->trans[$class][$key];
        
        // find the number-appropriate version of the
        // translated key, if multiple values exist.
        if ($num != 1 && ! empty($trans[1])) {
            $string = $trans[1];
        } else {
            $string = $trans[0];
        }
        
        // do replacements?
        if ($replace) {
            // force to an array first
            $replace = (array) $replace;
            
            // by vsprintf(), or by str_replace?()
            $key = key($replace);
            if (is_int($key)) {
                // sequential array, use vsprintf()
                $string = vsprintf($string, $replace);
            } else {
                // associative array, use str_replace()
                foreach ($replace as $key => $val) {
                    $string = str_replace("{:$key}", (string) $val, $string);
                }
            }
        }
        
        // done!
        return $string;
    }
    
    /**
     * 
     * Loads the translation array for a given class.
     * 
     * @param string $class The class name to load translations for.
     * 
     * @return void
     * 
     */
    protected function _load($class)
    {
        // build the file name.  note that we use the fixdir()
        // method, which automatically replaces '/' with the
        // correct directory separator.
        $base = str_replace('_', '/', $class);
        $file = Solar_Dir::fix($base . '/Locale/')
              . $this->_code . '.php';
        
        // can we find the file?
        $target = Solar_File::exists($file);
        if ($target) {
            // put the locale values into the shared locale array
            $this->trans[$class] = (array) Solar_File::load($target);
        } else {
            // could not find file.
            // fail silently, as it's often the case that the
            // translation file simply doesn't exist.
            $this->trans[$class] = array();
        }
    }
}
Return current item: SolarPHP