File indexing completed on 2024-05-12 06:03:12

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Translate
0017  * @subpackage Zend_Translate_Adapter
0018  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0019  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0020  * @version    $Id$
0021  */
0022 
0023 /**
0024  * @see Zend_Locale
0025  */
0026 // require_once 'Zend/Locale.php';
0027 
0028 /**
0029  * @see Zend_Translate_Plural
0030  */
0031 // require_once 'Zend/Translate/Plural.php';
0032 
0033 /**
0034  * Basic adapter class for each translation source adapter
0035  *
0036  * @category   Zend
0037  * @package    Zend_Translate
0038  * @subpackage Zend_Translate_Adapter
0039  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0040  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0041  */
0042 abstract class Zend_Translate_Adapter {
0043     /**
0044      * Shows if locale detection is in automatic level
0045      * @var boolean
0046      */
0047     private $_automatic = true;
0048 
0049     /**
0050      * Internal value to see already routed languages
0051      * @var array()
0052      */
0053     private $_routed = array();
0054 
0055     /**
0056      * Internal cache for all adapters
0057      * @var Zend_Cache_Core
0058      */
0059     protected static $_cache     = null;
0060 
0061     /**
0062      * Internal value to remember if cache supports tags
0063      *
0064      * @var boolean
0065      */
0066     private static $_cacheTags = false;
0067 
0068     /**
0069      * Scans for the locale within the name of the directory
0070      * @constant integer
0071      */
0072     const LOCALE_DIRECTORY = 'directory';
0073 
0074     /**
0075      * Scans for the locale within the name of the file
0076      * @constant integer
0077      */
0078     const LOCALE_FILENAME  = 'filename';
0079 
0080     /**
0081      * Array with all options, each adapter can have own additional options
0082      *   'clear'           => when true, clears already loaded translations when adding new files
0083      *   'content'         => content to translate or file or directory with content
0084      *   'disableNotices'  => when true, omits notices from being displayed
0085      *   'ignore'          => a prefix for files and directories which are not being added
0086      *   'locale'          => the actual set locale to use
0087      *   'log'             => a instance of Zend_Log where logs are written to
0088      *   'logMessage'      => message to be logged
0089      *   'logPriority'     => priority which is used to write the log message
0090      *   'logUntranslated' => when true, untranslated messages are not logged
0091      *   'reload'          => reloads the cache by reading the content again
0092      *   'scan'            => searches for translation files using the LOCALE constants
0093      *   'tag'             => tag to use for the cache
0094      *
0095      * @var array
0096      */
0097     protected $_options = array(
0098         'clear'           => false,
0099         'content'         => null,
0100         'disableNotices'  => false,
0101         'ignore'          => '.',
0102         'locale'          => 'auto',
0103         'log'             => null,
0104         'logMessage'      => "Untranslated message within '%locale%': %message%",
0105         'logPriority'     => 5,
0106         'logUntranslated' => false,
0107         'reload'          => false,
0108         'route'           => null,
0109         'scan'            => null,
0110         'tag'             => 'Zend_Translate'
0111     );
0112 
0113     /**
0114      * Translation table
0115      * @var array
0116      */
0117     protected $_translate = array();
0118 
0119     /**
0120      * Generates the adapter
0121      *
0122      * @param  string|array|Zend_Config $options Translation options for this adapter
0123      * @param  string|array [$content]
0124      * @param  string|Zend_Locale [$locale]
0125      * @throws Zend_Translate_Exception
0126      * @return void
0127      */
0128     public function __construct($options = array())
0129     {
0130         if ($options instanceof Zend_Config) {
0131             $options = $options->toArray();
0132         } else if (func_num_args() > 1) {
0133             $args               = func_get_args();
0134             $options            = array();
0135             $options['content'] = array_shift($args);
0136 
0137             if (!empty($args)) {
0138                 $options['locale'] = array_shift($args);
0139             }
0140 
0141             if (!empty($args)) {
0142                 $opt     = array_shift($args);
0143                 $options = array_merge($opt, $options);
0144             }
0145         } else if (!is_array($options)) {
0146             $options = array('content' => $options);
0147         }
0148 
0149         if (array_key_exists('cache', $options)) {
0150             self::setCache($options['cache']);
0151             unset($options['cache']);
0152         }
0153 
0154         if (isset(self::$_cache)) {
0155             $id = 'Zend_Translate_' . $this->toString() . '_Options';
0156             $result = self::$_cache->load($id);
0157             if ($result) {
0158                 $this->_options = $result;
0159             }
0160         }
0161 
0162         if (empty($options['locale']) || ($options['locale'] === "auto")) {
0163             $this->_automatic = true;
0164         } else {
0165             $this->_automatic = false;
0166         }
0167 
0168         $locale = null;
0169         if (!empty($options['locale'])) {
0170             $locale = $options['locale'];
0171             unset($options['locale']);
0172         }
0173 
0174         $this->setOptions($options);
0175         $options['locale'] = $locale;
0176 
0177         if (!empty($options['content'])) {
0178             $this->addTranslation($options);
0179         }
0180 
0181         if ($this->getLocale() !== (string) $options['locale']) {
0182             $this->setLocale($options['locale']);
0183         }
0184     }
0185 
0186     /**
0187      * Add translations
0188      *
0189      * This may be a new language or additional content for an existing language
0190      * If the key 'clear' is true, then translations for the specified
0191      * language will be replaced and added otherwise
0192      *
0193      * @param  array|Zend_Config $options Options and translations to be added
0194      * @throws Zend_Translate_Exception
0195      * @return Zend_Translate_Adapter Provides fluent interface
0196      */
0197     public function addTranslation($options = array())
0198     {
0199         if ($options instanceof Zend_Config) {
0200             $options = $options->toArray();
0201         } else if (func_num_args() > 1) {
0202             $args = func_get_args();
0203             $options            = array();
0204             $options['content'] = array_shift($args);
0205 
0206             if (!empty($args)) {
0207                 $options['locale'] = array_shift($args);
0208             }
0209 
0210             if (!empty($args)) {
0211                 $opt     = array_shift($args);
0212                 $options = array_merge($opt, $options);
0213             }
0214         } else if (!is_array($options)) {
0215             $options = array('content' => $options);
0216         }
0217         
0218         if (!isset($options['content']) || empty($options['content'])) {
0219             // require_once 'Zend/Translate/Exception.php';
0220             throw new Zend_Translate_Exception("Required option 'content' is missing");
0221         }
0222 
0223         $originate = null;
0224         if (!empty($options['locale'])) {
0225             $originate = (string) $options['locale'];
0226         }
0227 
0228         if ((array_key_exists('log', $options)) && !($options['log'] instanceof Zend_Log)) {
0229             // require_once 'Zend/Translate/Exception.php';
0230             throw new Zend_Translate_Exception('Instance of Zend_Log expected for option log');
0231         }
0232 
0233         try {
0234             if (!($options['content'] instanceof Zend_Translate) && !($options['content'] instanceof Zend_Translate_Adapter)) {
0235                 if (empty($options['locale'])) {
0236                     $options['locale'] = null;
0237                 }
0238 
0239                 $options['locale'] = Zend_Locale::findLocale($options['locale']);
0240             }
0241         } catch (Zend_Locale_Exception $e) {
0242             // require_once 'Zend/Translate/Exception.php';
0243             throw new Zend_Translate_Exception("The given Language '{$options['locale']}' does not exist", 0, $e);
0244         }
0245 
0246         $options  = $options + $this->_options;
0247         if (is_string($options['content']) and is_dir($options['content'])) {
0248             $options['content'] = realpath($options['content']);
0249             $prev = '';
0250             $iterator = new RecursiveIteratorIterator(
0251                 new RecursiveRegexIterator(
0252                     new RecursiveDirectoryIterator($options['content'], RecursiveDirectoryIterator::KEY_AS_PATHNAME),
0253                     '/^(?!.*(\.svn|\.cvs)).*$/', RecursiveRegexIterator::MATCH
0254                 ),
0255                 RecursiveIteratorIterator::SELF_FIRST
0256             );
0257             
0258             foreach ($iterator as $directory => $info) {
0259                 $file = $info->getFilename();
0260                 if (is_array($options['ignore'])) {
0261                     foreach ($options['ignore'] as $key => $ignore) {
0262                         if (strpos($key, 'regex') !== false) {
0263                             if (preg_match($ignore, $directory)) {
0264                                 // ignore files matching the given regex from option 'ignore' and all files below
0265                                 continue 2;
0266                             }
0267                         } else if (strpos($directory, DIRECTORY_SEPARATOR . $ignore) !== false) {
0268                             // ignore files matching first characters from option 'ignore' and all files below
0269                             continue 2;
0270                         }
0271                     }
0272                 } else {
0273                     if (strpos($directory, DIRECTORY_SEPARATOR . $options['ignore']) !== false) {
0274                         // ignore files matching first characters from option 'ignore' and all files below
0275                         continue;
0276                     }
0277                 }
0278 
0279                 if ($info->isDir()) {
0280                     // pathname as locale
0281                     if (($options['scan'] === self::LOCALE_DIRECTORY) and (Zend_Locale::isLocale($file, true, false))) {
0282                         $options['locale'] = $file;
0283                         $prev              = (string) $options['locale'];
0284                     }
0285                 } else if ($info->isFile()) {
0286                     // filename as locale
0287                     if ($options['scan'] === self::LOCALE_FILENAME) {
0288                         $filename = explode('.', $file);
0289                         array_pop($filename);
0290                         $filename = implode('.', $filename);
0291                         if (Zend_Locale::isLocale((string) $filename, true, false)) {
0292                             $options['locale'] = (string) $filename;
0293                         } else {
0294                             $parts  = explode('.', $file);
0295                             $parts2 = array();
0296                             foreach($parts as $token) {
0297                                 $parts2 += explode('_', $token);
0298                             }
0299                             $parts  = array_merge($parts, $parts2);
0300                             $parts2 = array();
0301                             foreach($parts as $token) {
0302                                 $parts2 += explode('-', $token);
0303                             }
0304                             $parts = array_merge($parts, $parts2);
0305                             $parts = array_unique($parts);
0306                             $prev  = '';
0307                             foreach($parts as $token) {
0308                                 if (Zend_Locale::isLocale($token, true, false)) {
0309                                     if (strlen($prev) <= strlen($token)) {
0310                                         $options['locale'] = $token;
0311                                         $prev              = $token;
0312                                     }
0313                                 }
0314                             }
0315                         }
0316                     }
0317 
0318                     try {
0319                         $options['content'] = $info->getPathname();
0320                         $this->_addTranslationData($options);
0321                     } catch (Zend_Translate_Exception $e) {
0322                         // ignore failed sources while scanning
0323                     }
0324                 }
0325             }
0326             
0327             unset($iterator);
0328         } else {
0329             $this->_addTranslationData($options);
0330         }
0331 
0332         if ((isset($this->_translate[$originate]) === true) and (count($this->_translate[$originate]) > 0)) {
0333             $this->setLocale($originate);
0334         }
0335 
0336         return $this;
0337     }
0338 
0339     /**
0340      * Sets new adapter options
0341      *
0342      * @param  array $options Adapter options
0343      * @throws Zend_Translate_Exception
0344      * @return Zend_Translate_Adapter Provides fluent interface
0345      */
0346     public function setOptions(array $options = array())
0347     {
0348         $change = false;
0349         $locale = null;
0350         foreach ($options as $key => $option) {
0351             if ($key == 'locale') {
0352                 $locale = $option;
0353             } else if ((isset($this->_options[$key]) and ($this->_options[$key] != $option)) or
0354                     !isset($this->_options[$key])) {
0355                 if (($key == 'log') && !($option instanceof Zend_Log)) {
0356                     // require_once 'Zend/Translate/Exception.php';
0357                     throw new Zend_Translate_Exception('Instance of Zend_Log expected for option log');
0358                 }
0359 
0360                 if ($key == 'cache') {
0361                     self::setCache($option);
0362                     continue;
0363                 }
0364 
0365                 $this->_options[$key] = $option;
0366                 $change = true;
0367             }
0368         }
0369 
0370         if ($locale !== null) {
0371             $this->setLocale($locale);
0372         }
0373 
0374         if (isset(self::$_cache) and ($change == true)) {
0375             $id = 'Zend_Translate_' . $this->toString() . '_Options';
0376             if (self::$_cacheTags) {
0377                 self::$_cache->save($this->_options, $id, array($this->_options['tag']));
0378             } else {
0379                 self::$_cache->save($this->_options, $id);
0380             }
0381         }
0382 
0383         return $this;
0384     }
0385 
0386     /**
0387      * Returns the adapters name and it's options
0388      *
0389      * @param  string|null $optionKey String returns this option
0390      *                                null returns all options
0391      * @return integer|string|array|null
0392      */
0393     public function getOptions($optionKey = null)
0394     {
0395         if ($optionKey === null) {
0396             return $this->_options;
0397         }
0398 
0399         if (isset($this->_options[$optionKey]) === true) {
0400             return $this->_options[$optionKey];
0401         }
0402 
0403         return null;
0404     }
0405 
0406     /**
0407      * Gets locale
0408      *
0409      * @return Zend_Locale|string|null
0410      */
0411     public function getLocale()
0412     {
0413         return $this->_options['locale'];
0414     }
0415 
0416     /**
0417      * Sets locale
0418      *
0419      * @param  string|Zend_Locale $locale Locale to set
0420      * @throws Zend_Translate_Exception
0421      * @return Zend_Translate_Adapter Provides fluent interface
0422      */
0423     public function setLocale($locale)
0424     {
0425         if (($locale === "auto") or ($locale === null)) {
0426             $this->_automatic = true;
0427         } else {
0428             $this->_automatic = false;
0429         }
0430 
0431         try {
0432             $locale = Zend_Locale::findLocale($locale);
0433         } catch (Zend_Locale_Exception $e) {
0434             // require_once 'Zend/Translate/Exception.php';
0435             throw new Zend_Translate_Exception("The given Language ({$locale}) does not exist", 0, $e);
0436         }
0437 
0438         if (!isset($this->_translate[$locale])) {
0439             $temp = explode('_', $locale);
0440             if (!isset($this->_translate[$temp[0]]) and !isset($this->_translate[$locale])) {
0441                 if (!$this->_options['disableNotices']) {
0442                     if ($this->_options['log']) {
0443                         $this->_options['log']->log("The language '{$locale}' has to be added before it can be used.", $this->_options['logPriority']);
0444                     } else {
0445                         trigger_error("The language '{$locale}' has to be added before it can be used.", E_USER_NOTICE);
0446                     }
0447                 }
0448             }
0449 
0450             $locale = $temp[0];
0451         }
0452 
0453         if (empty($this->_translate[$locale])) {
0454             if (!$this->_options['disableNotices']) {
0455                 if ($this->_options['log']) {
0456                     $this->_options['log']->log("No translation for the language '{$locale}' available.", $this->_options['logPriority']);
0457                 } else {
0458                     trigger_error("No translation for the language '{$locale}' available.", E_USER_NOTICE);
0459                 }
0460             }
0461         }
0462 
0463         if ($this->_options['locale'] != $locale) {
0464             $this->_options['locale'] = $locale;
0465 
0466             if (isset(self::$_cache)) {
0467                 $id = 'Zend_Translate_' . $this->toString() . '_Options';
0468                 if (self::$_cacheTags) {
0469                     self::$_cache->save($this->_options, $id, array($this->_options['tag']));
0470                 } else {
0471                     self::$_cache->save($this->_options, $id);
0472                 }
0473             }
0474         }
0475 
0476         return $this;
0477     }
0478 
0479     /**
0480      * Returns the available languages from this adapter
0481      *
0482      * @return array|null
0483      */
0484     public function getList()
0485     {
0486         $list = array_keys($this->_translate);
0487         $result = null;
0488         foreach($list as $value) {
0489             if (!empty($this->_translate[$value])) {
0490                 $result[$value] = $value;
0491             }
0492         }
0493         return $result;
0494     }
0495 
0496     /**
0497      * Returns the message id for a given translation
0498      * If no locale is given, the actual language will be used
0499      *
0500      * @param  string             $message Message to get the key for
0501      * @param  string|Zend_Locale $locale (optional) Language to return the message ids from
0502      * @return string|array|false
0503      */
0504     public function getMessageId($message, $locale = null)
0505     {
0506         if (empty($locale) or !$this->isAvailable($locale)) {
0507             $locale = $this->_options['locale'];
0508         }
0509 
0510         return array_search($message, $this->_translate[(string) $locale]);
0511     }
0512 
0513     /**
0514      * Returns all available message ids from this adapter
0515      * If no locale is given, the actual language will be used
0516      *
0517      * @param  string|Zend_Locale $locale (optional) Language to return the message ids from
0518      * @return array
0519      */
0520     public function getMessageIds($locale = null)
0521     {
0522         if (empty($locale) or !$this->isAvailable($locale)) {
0523             $locale = $this->_options['locale'];
0524         }
0525 
0526         return array_keys($this->_translate[(string) $locale]);
0527     }
0528 
0529     /**
0530      * Returns all available translations from this adapter
0531      * If no locale is given, the actual language will be used
0532      * If 'all' is given the complete translation dictionary will be returned
0533      *
0534      * @param  string|Zend_Locale $locale (optional) Language to return the messages from
0535      * @return array
0536      */
0537     public function getMessages($locale = null)
0538     {
0539         if ($locale === 'all') {
0540             return $this->_translate;
0541         }
0542 
0543         if ((empty($locale) === true) or ($this->isAvailable($locale) === false)) {
0544             $locale = $this->_options['locale'];
0545         }
0546 
0547         return $this->_translate[(string) $locale];
0548     }
0549 
0550     /**
0551      * Is the wished language available ?
0552      *
0553      * @see    Zend_Locale
0554      * @param  string|Zend_Locale $locale Language to search for, identical with locale identifier,
0555      *                                    @see Zend_Locale for more information
0556      * @return boolean
0557      */
0558     public function isAvailable($locale)
0559     {
0560         $return = isset($this->_translate[(string) $locale]);
0561         return $return;
0562     }
0563 
0564     /**
0565      * Load translation data
0566      *
0567      * @param  mixed              $data
0568      * @param  string|Zend_Locale $locale
0569      * @param  array              $options (optional)
0570      * @return array
0571      */
0572     abstract protected function _loadTranslationData($data, $locale, array $options = array());
0573 
0574     /**
0575      * Internal function for adding translation data
0576      *
0577      * This may be a new language or additional data for an existing language
0578      * If the options 'clear' is true, then the translation data for the specified
0579      * language is replaced and added otherwise
0580      *
0581      * @see    Zend_Locale
0582      * @param  array|Zend_Config $content Translation data to add
0583      * @throws Zend_Translate_Exception
0584      * @return Zend_Translate_Adapter Provides fluent interface
0585      */
0586     private function _addTranslationData($options = array())
0587     {
0588         if ($options instanceof Zend_Config) {
0589             $options = $options->toArray();
0590         } else if (func_num_args() > 1) {
0591             $args = func_get_args();
0592             $options['content'] = array_shift($args);
0593 
0594             if (!empty($args)) {
0595                 $options['locale'] = array_shift($args);
0596             }
0597 
0598             if (!empty($args)) {
0599                 $options += array_shift($args);
0600             }
0601         }
0602 
0603         if (($options['content'] instanceof Zend_Translate) || ($options['content'] instanceof Zend_Translate_Adapter)) {
0604             $options['usetranslateadapter'] = true;
0605             if (!empty($options['locale']) && ($options['locale'] !== 'auto')) {
0606                 $options['content'] = $options['content']->getMessages($options['locale']);
0607             } else {
0608                 $content = $options['content'];
0609                 $locales = $content->getList();
0610                 foreach ($locales as $locale) {
0611                     $options['locale']  = $locale;
0612                     $options['content'] = $content->getMessages($locale);
0613                     $this->_addTranslationData($options);
0614                 }
0615 
0616                 return $this;
0617             }
0618         }
0619 
0620         try {
0621             $options['locale'] = Zend_Locale::findLocale($options['locale']);
0622         } catch (Zend_Locale_Exception $e) {
0623             // require_once 'Zend/Translate/Exception.php';
0624             throw new Zend_Translate_Exception("The given Language '{$options['locale']}' does not exist", 0, $e);
0625         }
0626 
0627         if ($options['clear'] || !isset($this->_translate[$options['locale']])) {
0628             $this->_translate[$options['locale']] = array();
0629         }
0630 
0631         $read = true;
0632         if (isset(self::$_cache)) {
0633             $id = 'Zend_Translate_' . md5(serialize($options['content'])) . '_' . $this->toString();
0634             $temp = self::$_cache->load($id);
0635             if ($temp) {
0636                 $read = false;
0637             }
0638         }
0639 
0640         if ($options['reload']) {
0641             $read = true;
0642         }
0643 
0644         if ($read) {
0645             if (!empty($options['usetranslateadapter'])) {
0646                 $temp = array($options['locale'] => $options['content']);
0647             } else {
0648                 $temp = $this->_loadTranslationData($options['content'], $options['locale'], $options);
0649             }
0650         }
0651 
0652         if (empty($temp)) {
0653             $temp = array();
0654         }
0655 
0656         $keys = array_keys($temp);
0657         foreach($keys as $key) {
0658             if (!isset($this->_translate[$key])) {
0659                 $this->_translate[$key] = array();
0660             }
0661 
0662             if (array_key_exists($key, $temp) && is_array($temp[$key])) {
0663                 $this->_translate[$key] = $temp[$key] + $this->_translate[$key];
0664             }
0665         }
0666 
0667         if ($this->_automatic === true) {
0668             $find = new Zend_Locale($options['locale']);
0669             $browser = $find->getEnvironment() + $find->getBrowser();
0670             arsort($browser);
0671             foreach($browser as $language => $quality) {
0672                 if (isset($this->_translate[$language])) {
0673                     $this->_options['locale'] = $language;
0674                     break;
0675                 }
0676             }
0677         }
0678 
0679         if (($read) and (isset(self::$_cache))) {
0680             $id = 'Zend_Translate_' . md5(serialize($options['content'])) . '_' . $this->toString();
0681             if (self::$_cacheTags) {
0682                 self::$_cache->save($temp, $id, array($this->_options['tag']));
0683             } else {
0684                 self::$_cache->save($temp, $id);
0685             }
0686         }
0687 
0688         return $this;
0689     }
0690 
0691     /**
0692      * Translates the given string
0693      * returns the translation
0694      *
0695      * @see Zend_Locale
0696      * @param  string|array       $messageId Translation string, or Array for plural translations
0697      * @param  string|Zend_Locale $locale    (optional) Locale/Language to use, identical with
0698      *                                       locale identifier, @see Zend_Locale for more information
0699      * @return string
0700      */
0701     public function translate($messageId, $locale = null)
0702     {
0703         if ($locale === null) {
0704             $locale = $this->_options['locale'];
0705         }
0706 
0707         $plural = null;
0708         if (is_array($messageId)) {
0709             if (count($messageId) > 2) {
0710                 $number = array_pop($messageId);
0711                 if (!is_numeric($number)) {
0712                     $plocale = $number;
0713                     $number  = array_pop($messageId);
0714                 } else {
0715                     $plocale = 'en';
0716                 }
0717 
0718                 $plural    = $messageId;
0719                 $messageId = $messageId[0];
0720             } else {
0721                 $messageId = $messageId[0];
0722             }
0723         }
0724 
0725         if (!Zend_Locale::isLocale($locale, true, false)) {
0726             if (!Zend_Locale::isLocale($locale, false, false)) {
0727                 // language does not exist, return original string
0728                 $this->_log($messageId, $locale);
0729                 // use rerouting when enabled
0730                 if (!empty($this->_options['route'])) {
0731                     if (array_key_exists($locale, $this->_options['route']) &&
0732                         !array_key_exists($locale, $this->_routed)) {
0733                         $this->_routed[$locale] = true;
0734                         return $this->translate($messageId, $this->_options['route'][$locale]);
0735                     }
0736                 }
0737 
0738                 $this->_routed = array();
0739                 if ($plural === null) {
0740                     return $messageId;
0741                 }
0742 
0743                 $rule = Zend_Translate_Plural::getPlural($number, $plocale);
0744                 if (!isset($plural[$rule])) {
0745                     $rule = 0;
0746                 }
0747 
0748                 return $plural[$rule];
0749             }
0750 
0751             $locale = new Zend_Locale($locale);
0752         }
0753 
0754         $locale = (string) $locale;
0755         if ((is_string($messageId) || is_int($messageId)) && isset($this->_translate[$locale][$messageId])) {
0756             // return original translation
0757             if ($plural === null) {
0758                 $this->_routed = array();
0759                 return $this->_translate[$locale][$messageId];
0760             }
0761 
0762             $rule = Zend_Translate_Plural::getPlural($number, $locale);
0763             if (isset($this->_translate[$locale][$plural[0]][$rule])) {
0764                 $this->_routed = array();
0765                 return $this->_translate[$locale][$plural[0]][$rule];
0766             }
0767         } else if (strlen($locale) != 2) {
0768             // faster than creating a new locale and separate the leading part
0769             $locale = substr($locale, 0, -strlen(strrchr($locale, '_')));
0770 
0771             if ((is_string($messageId) || is_int($messageId)) && isset($this->_translate[$locale][$messageId])) {
0772                 // return regionless translation (en_US -> en)
0773                 if ($plural === null) {
0774                     $this->_routed = array();
0775                     return $this->_translate[$locale][$messageId];
0776                 }
0777 
0778                 $rule = Zend_Translate_Plural::getPlural($number, $locale);
0779                 if (isset($this->_translate[$locale][$plural[0]][$rule])) {
0780                     $this->_routed = array();
0781                     return $this->_translate[$locale][$plural[0]][$rule];
0782                 }
0783             }
0784         }
0785 
0786         $this->_log($messageId, $locale);
0787         // use rerouting when enabled
0788         if (!empty($this->_options['route'])) {
0789             if (array_key_exists($locale, $this->_options['route']) &&
0790                 !array_key_exists($locale, $this->_routed)) {
0791                 $this->_routed[$locale] = true;
0792                 return $this->translate($messageId, $this->_options['route'][$locale]);
0793             }
0794         }
0795 
0796         $this->_routed = array();
0797         if ($plural === null) {
0798             return $messageId;
0799         }
0800 
0801         $rule = Zend_Translate_Plural::getPlural($number, $plocale);
0802         if (!isset($plural[$rule])) {
0803             $rule = 0;
0804         }
0805 
0806         return $plural[$rule];
0807     }
0808 
0809     /**
0810      * Translates the given string using plural notations
0811      * Returns the translated string
0812      *
0813      * @see Zend_Locale
0814      * @param  string             $singular Singular translation string
0815      * @param  string             $plural   Plural translation string
0816      * @param  integer            $number   Number for detecting the correct plural
0817      * @param  string|Zend_Locale $locale   (Optional) Locale/Language to use, identical with
0818      *                                      locale identifier, @see Zend_Locale for more information
0819      * @return string
0820      */
0821     public function plural($singular, $plural, $number, $locale = null)
0822     {
0823         return $this->translate(array($singular, $plural, $number), $locale);
0824     }
0825 
0826     /**
0827      * Logs a message when the log option is set
0828      *
0829      * @param string $message Message to log
0830      * @param String $locale  Locale to log
0831      */
0832     protected function _log($message, $locale) {
0833         if ($this->_options['logUntranslated']) {
0834             $message = str_replace('%message%', $message, $this->_options['logMessage']);
0835             $message = str_replace('%locale%', $locale, $message);
0836             if ($this->_options['log']) {
0837                 $this->_options['log']->log($message, $this->_options['logPriority']);
0838             } else {
0839                 trigger_error($message, E_USER_NOTICE);
0840             }
0841         }
0842     }
0843 
0844     /**
0845      * Translates the given string
0846      * returns the translation
0847      *
0848      * @param  string             $messageId Translation string
0849      * @param  string|Zend_Locale $locale    (optional) Locale/Language to use, identical with locale
0850      *                                       identifier, @see Zend_Locale for more information
0851      * @return string
0852      */
0853     public function _($messageId, $locale = null)
0854     {
0855         return $this->translate($messageId, $locale);
0856     }
0857 
0858     /**
0859      * Checks if a string is translated within the source or not
0860      * returns boolean
0861      *
0862      * @param  string             $messageId Translation string
0863      * @param  boolean            $original  (optional) Allow translation only for original language
0864      *                                       when true, a translation for 'en_US' would give false when it can
0865      *                                       be translated with 'en' only
0866      * @param  string|Zend_Locale $locale    (optional) Locale/Language to use, identical with locale identifier,
0867      *                                       see Zend_Locale for more information
0868      * @return boolean
0869      */
0870     public function isTranslated($messageId, $original = false, $locale = null)
0871     {
0872         if (($original !== false) and ($original !== true)) {
0873             $locale   = $original;
0874             $original = false;
0875         }
0876 
0877         if ($locale === null) {
0878             $locale = $this->_options['locale'];
0879         }
0880 
0881         if (!Zend_Locale::isLocale($locale, true, false)) {
0882             if (!Zend_Locale::isLocale($locale, false, false)) {
0883                 // language does not exist, return original string
0884                 return false;
0885             }
0886 
0887             $locale = new Zend_Locale($locale);
0888         }
0889 
0890         $locale = (string) $locale;
0891         if ((is_string($messageId) || is_int($messageId)) && isset($this->_translate[$locale][$messageId])) {
0892             // return original translation
0893             return true;
0894         } else if ((strlen($locale) != 2) and ($original === false)) {
0895             // faster than creating a new locale and separate the leading part
0896             $locale = substr($locale, 0, -strlen(strrchr($locale, '_')));
0897 
0898             if ((is_string($messageId) || is_int($messageId)) && isset($this->_translate[$locale][$messageId])) {
0899                 // return regionless translation (en_US -> en)
0900                 return true;
0901             }
0902         }
0903 
0904         // No translation found, return original
0905         return false;
0906     }
0907 
0908     /**
0909      * Returns the set cache
0910      *
0911      * @return Zend_Cache_Core The set cache
0912      */
0913     public static function getCache()
0914     {
0915         return self::$_cache;
0916     }
0917 
0918     /**
0919      * Sets a cache for all Zend_Translate_Adapters
0920      *
0921      * @param Zend_Cache_Core $cache Cache to store to
0922      */
0923     public static function setCache(Zend_Cache_Core $cache)
0924     {
0925         self::$_cache = $cache;
0926         self::_getTagSupportForCache();
0927     }
0928 
0929     /**
0930      * Returns true when a cache is set
0931      *
0932      * @return boolean
0933      */
0934     public static function hasCache()
0935     {
0936         if (self::$_cache !== null) {
0937             return true;
0938         }
0939 
0940         return false;
0941     }
0942 
0943     /**
0944      * Removes any set cache
0945      *
0946      * @return void
0947      */
0948     public static function removeCache()
0949     {
0950         self::$_cache = null;
0951     }
0952 
0953     /**
0954      * Clears all set cache data
0955      *
0956      * @param string $tag Tag to clear when the default tag name is not used
0957      * @return void
0958      */
0959     public static function clearCache($tag = null)
0960     {
0961         // require_once 'Zend/Cache.php';
0962         if (self::$_cacheTags) {
0963             if ($tag == null) {
0964                 $tag = 'Zend_Translate';
0965             }
0966 
0967             self::$_cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array($tag));
0968         } else {
0969             self::$_cache->clean(Zend_Cache::CLEANING_MODE_ALL);
0970         }
0971     }
0972 
0973     /**
0974      * Returns the adapter name
0975      *
0976      * @return string
0977      */
0978     abstract public function toString();
0979 
0980     /**
0981      * Internal method to check if the given cache supports tags
0982      *
0983      * @param Zend_Cache $cache
0984      */
0985     private static function _getTagSupportForCache()
0986     {
0987         $backend = self::$_cache->getBackend();
0988         if ($backend instanceof Zend_Cache_Backend_ExtendedInterface) {
0989             $cacheOptions = $backend->getCapabilities();
0990             self::$_cacheTags = $cacheOptions['tags'];
0991         } else {
0992             self::$_cacheTags = false;
0993         }
0994 
0995         return self::$_cacheTags;
0996     }
0997 }