PageRenderTime 102ms CodeModel.GetById 60ms app.highlight 31ms RepoModel.GetById 1ms app.codeStats 1ms

/library/Zend/Translate/Adapter.php

https://bitbucket.org/ksekar/campus
PHP | 995 lines | 597 code | 115 blank | 283 comment | 169 complexity | 509521b537145c40d4a15ba61b8670e7 MD5 | raw file
  1<?php
  2/**
  3 * Zend Framework
  4 *
  5 * LICENSE
  6 *
  7 * This source file is subject to the new BSD license that is bundled
  8 * with this package in the file LICENSE.txt.
  9 * It is also available through the world-wide-web at this URL:
 10 * http://framework.zend.com/license/new-bsd
 11 * If you did not receive a copy of the license and are unable to
 12 * obtain it through the world-wide-web, please send an email
 13 * to license@zend.com so we can send you a copy immediately.
 14 *
 15 * @category   Zend
 16 * @package    Zend_Translate
 17 * @subpackage Zend_Translate_Adapter
 18 * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
 19 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 20 * @version    $Id: Adapter.php 24594 2012-01-05 21:27:01Z matthew $
 21 */
 22
 23/**
 24 * @see Zend_Locale
 25 */
 26require_once 'Zend/Locale.php';
 27
 28/**
 29 * @see Zend_Translate_Plural
 30 */
 31require_once 'Zend/Translate/Plural.php';
 32
 33/**
 34 * Basic adapter class for each translation source adapter
 35 *
 36 * @category   Zend
 37 * @package    Zend_Translate
 38 * @subpackage Zend_Translate_Adapter
 39 * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
 40 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 41 */
 42abstract class Zend_Translate_Adapter {
 43    /**
 44     * Shows if locale detection is in automatic level
 45     * @var boolean
 46     */
 47    private $_automatic = true;
 48
 49    /**
 50     * Internal value to see already routed languages
 51     * @var array()
 52     */
 53    private $_routed = array();
 54
 55    /**
 56     * Internal cache for all adapters
 57     * @var Zend_Cache_Core
 58     */
 59    protected static $_cache     = null;
 60
 61    /**
 62     * Internal value to remember if cache supports tags
 63     *
 64     * @var boolean
 65     */
 66    private static $_cacheTags = false;
 67
 68    /**
 69     * Scans for the locale within the name of the directory
 70     * @constant integer
 71     */
 72    const LOCALE_DIRECTORY = 'directory';
 73
 74    /**
 75     * Scans for the locale within the name of the file
 76     * @constant integer
 77     */
 78    const LOCALE_FILENAME  = 'filename';
 79
 80    /**
 81     * Array with all options, each adapter can have own additional options
 82     *   'clear'           => when true, clears already loaded translations when adding new files
 83     *   'content'         => content to translate or file or directory with content
 84     *   'disableNotices'  => when true, omits notices from being displayed
 85     *   'ignore'          => a prefix for files and directories which are not being added
 86     *   'locale'          => the actual set locale to use
 87     *   'log'             => a instance of Zend_Log where logs are written to
 88     *   'logMessage'      => message to be logged
 89     *   'logPriority'     => priority which is used to write the log message
 90     *   'logUntranslated' => when true, untranslated messages are not logged
 91     *   'reload'          => reloads the cache by reading the content again
 92     *   'scan'            => searches for translation files using the LOCALE constants
 93     *   'tag'             => tag to use for the cache
 94     *
 95     * @var array
 96     */
 97    protected $_options = array(
 98        'clear'           => false,
 99        'content'         => null,
100        'disableNotices'  => false,
101        'ignore'          => '.',
102        'locale'          => 'auto',
103        'log'             => null,
104        'logMessage'      => "Untranslated message within '%locale%': %message%",
105        'logPriority'     => 5,
106        'logUntranslated' => false,
107        'reload'          => false,
108        'route'           => null,
109        'scan'            => null,
110        'tag'             => 'Zend_Translate'
111    );
112
113    /**
114     * Translation table
115     * @var array
116     */
117    protected $_translate = array();
118
119    /**
120     * Generates the adapter
121     *
122     * @param  array|Zend_Config $options Translation options for this adapter
123     * @throws Zend_Translate_Exception
124     * @return void
125     */
126    public function __construct($options = array())
127    {
128        if ($options instanceof Zend_Config) {
129            $options = $options->toArray();
130        } else if (func_num_args() > 1) {
131            $args               = func_get_args();
132            $options            = array();
133            $options['content'] = array_shift($args);
134
135            if (!empty($args)) {
136                $options['locale'] = array_shift($args);
137            }
138
139            if (!empty($args)) {
140                $opt     = array_shift($args);
141                $options = array_merge($opt, $options);
142            }
143        } else if (!is_array($options)) {
144            $options = array('content' => $options);
145        }
146
147        if (array_key_exists('cache', $options)) {
148            self::setCache($options['cache']);
149            unset($options['cache']);
150        }
151
152        if (isset(self::$_cache)) {
153            $id = 'Zend_Translate_' . $this->toString() . '_Options';
154            $result = self::$_cache->load($id);
155            if ($result) {
156                $this->_options = $result;
157            }
158        }
159
160        if (empty($options['locale']) || ($options['locale'] === "auto")) {
161            $this->_automatic = true;
162        } else {
163            $this->_automatic = false;
164        }
165
166        $locale = null;
167        if (!empty($options['locale'])) {
168            $locale = $options['locale'];
169            unset($options['locale']);
170        }
171
172        $this->setOptions($options);
173        $options['locale'] = $locale;
174
175        if (!empty($options['content'])) {
176            $this->addTranslation($options);
177        }
178
179        if ($this->getLocale() !== (string) $options['locale']) {
180            $this->setLocale($options['locale']);
181        }
182    }
183
184    /**
185     * Add translations
186     *
187     * This may be a new language or additional content for an existing language
188     * If the key 'clear' is true, then translations for the specified
189     * language will be replaced and added otherwise
190     *
191     * @param  array|Zend_Config $options Options and translations to be added
192     * @throws Zend_Translate_Exception
193     * @return Zend_Translate_Adapter Provides fluent interface
194     */
195    public function addTranslation($options = array())
196    {
197        if ($options instanceof Zend_Config) {
198            $options = $options->toArray();
199        } else if (func_num_args() > 1) {
200            $args = func_get_args();
201            $options            = array();
202            $options['content'] = array_shift($args);
203
204            if (!empty($args)) {
205                $options['locale'] = array_shift($args);
206            }
207
208            if (!empty($args)) {
209                $opt     = array_shift($args);
210                $options = array_merge($opt, $options);
211            }
212        } else if (!is_array($options)) {
213            $options = array('content' => $options);
214        }
215        
216        if (!isset($options['content']) || empty($options['content'])) {
217            require_once 'Zend/Translate/Exception.php';
218            throw new Zend_Translate_Exception("Required option 'content' is missing");
219        }
220
221        $originate = null;
222        if (!empty($options['locale'])) {
223            $originate = (string) $options['locale'];
224        }
225
226        if ((array_key_exists('log', $options)) && !($options['log'] instanceof Zend_Log)) {
227            require_once 'Zend/Translate/Exception.php';
228            throw new Zend_Translate_Exception('Instance of Zend_Log expected for option log');
229        }
230
231        try {
232            if (!($options['content'] instanceof Zend_Translate) && !($options['content'] instanceof Zend_Translate_Adapter)) {
233                if (empty($options['locale'])) {
234                    $options['locale'] = null;
235                }
236
237                $options['locale'] = Zend_Locale::findLocale($options['locale']);
238            }
239        } catch (Zend_Locale_Exception $e) {
240            require_once 'Zend/Translate/Exception.php';
241            throw new Zend_Translate_Exception("The given Language '{$options['locale']}' does not exist", 0, $e);
242        }
243
244        $options  = $options + $this->_options;
245        if (is_string($options['content']) and is_dir($options['content'])) {
246            $options['content'] = realpath($options['content']);
247            $prev = '';
248            $iterator = new RecursiveIteratorIterator(
249                new RecursiveRegexIterator(
250                    new RecursiveDirectoryIterator($options['content'], RecursiveDirectoryIterator::KEY_AS_PATHNAME),
251                    '/^(?!.*(\.svn|\.cvs)).*$/', RecursiveRegexIterator::MATCH
252                ),
253                RecursiveIteratorIterator::SELF_FIRST
254            );
255            
256            foreach ($iterator as $directory => $info) {
257                $file = $info->getFilename();
258                if (is_array($options['ignore'])) {
259                    foreach ($options['ignore'] as $key => $ignore) {
260                        if (strpos($key, 'regex') !== false) {
261                            if (preg_match($ignore, $directory)) {
262                                // ignore files matching the given regex from option 'ignore' and all files below
263                                continue 2;
264                            }
265                        } else if (strpos($directory, DIRECTORY_SEPARATOR . $ignore) !== false) {
266                            // ignore files matching first characters from option 'ignore' and all files below
267                            continue 2;
268                        }
269                    }
270                } else {
271                    if (strpos($directory, DIRECTORY_SEPARATOR . $options['ignore']) !== false) {
272                        // ignore files matching first characters from option 'ignore' and all files below
273                        continue;
274                    }
275                }
276
277                if ($info->isDir()) {
278                    // pathname as locale
279                    if (($options['scan'] === self::LOCALE_DIRECTORY) and (Zend_Locale::isLocale($file, true, false))) {
280                        $options['locale'] = $file;
281                        $prev              = (string) $options['locale'];
282                    }
283                } else if ($info->isFile()) {
284                    // filename as locale
285                    if ($options['scan'] === self::LOCALE_FILENAME) {
286                        $filename = explode('.', $file);
287                        array_pop($filename);
288                        $filename = implode('.', $filename);
289                        if (Zend_Locale::isLocale((string) $filename, true, false)) {
290                            $options['locale'] = (string) $filename;
291                        } else {
292                            $parts  = explode('.', $file);
293                            $parts2 = array();
294                            foreach($parts as $token) {
295                                $parts2 += explode('_', $token);
296                            }
297                            $parts  = array_merge($parts, $parts2);
298                            $parts2 = array();
299                            foreach($parts as $token) {
300                                $parts2 += explode('-', $token);
301                            }
302                            $parts = array_merge($parts, $parts2);
303                            $parts = array_unique($parts);
304                            $prev  = '';
305                            foreach($parts as $token) {
306                                if (Zend_Locale::isLocale($token, true, false)) {
307                                    if (strlen($prev) <= strlen($token)) {
308                                        $options['locale'] = $token;
309                                        $prev              = $token;
310                                    }
311                                }
312                            }
313                        }
314                    }
315
316                    try {
317                        $options['content'] = $info->getPathname();
318                        $this->_addTranslationData($options);
319                    } catch (Zend_Translate_Exception $e) {
320                        // ignore failed sources while scanning
321                    }
322                }
323            }
324            
325            unset($iterator);
326        } else {
327            $this->_addTranslationData($options);
328        }
329
330        if ((isset($this->_translate[$originate]) === true) and (count($this->_translate[$originate]) > 0)) {
331            $this->setLocale($originate);
332        }
333
334        return $this;
335    }
336
337    /**
338     * Sets new adapter options
339     *
340     * @param  array $options Adapter options
341     * @throws Zend_Translate_Exception
342     * @return Zend_Translate_Adapter Provides fluent interface
343     */
344    public function setOptions(array $options = array())
345    {
346        $change = false;
347        $locale = null;
348        foreach ($options as $key => $option) {
349            if ($key == 'locale') {
350                $locale = $option;
351            } else if ((isset($this->_options[$key]) and ($this->_options[$key] != $option)) or
352                    !isset($this->_options[$key])) {
353                if (($key == 'log') && !($option instanceof Zend_Log)) {
354                    require_once 'Zend/Translate/Exception.php';
355                    throw new Zend_Translate_Exception('Instance of Zend_Log expected for option log');
356                }
357
358                if ($key == 'cache') {
359                    self::setCache($option);
360                    continue;
361                }
362
363                $this->_options[$key] = $option;
364                $change = true;
365            }
366        }
367
368        if ($locale !== null) {
369            $this->setLocale($locale);
370        }
371
372        if (isset(self::$_cache) and ($change == true)) {
373            $id = 'Zend_Translate_' . $this->toString() . '_Options';
374            if (self::$_cacheTags) {
375                self::$_cache->save($this->_options, $id, array($this->_options['tag']));
376            } else {
377                self::$_cache->save($this->_options, $id);
378            }
379        }
380
381        return $this;
382    }
383
384    /**
385     * Returns the adapters name and it's options
386     *
387     * @param  string|null $optionKey String returns this option
388     *                                null returns all options
389     * @return integer|string|array|null
390     */
391    public function getOptions($optionKey = null)
392    {
393        if ($optionKey === null) {
394            return $this->_options;
395        }
396
397        if (isset($this->_options[$optionKey]) === true) {
398            return $this->_options[$optionKey];
399        }
400
401        return null;
402    }
403
404    /**
405     * Gets locale
406     *
407     * @return Zend_Locale|string|null
408     */
409    public function getLocale()
410    {
411        return $this->_options['locale'];
412    }
413
414    /**
415     * Sets locale
416     *
417     * @param  string|Zend_Locale $locale Locale to set
418     * @throws Zend_Translate_Exception
419     * @return Zend_Translate_Adapter Provides fluent interface
420     */
421    public function setLocale($locale)
422    {
423        if (($locale === "auto") or ($locale === null)) {
424            $this->_automatic = true;
425        } else {
426            $this->_automatic = false;
427        }
428
429        try {
430            $locale = Zend_Locale::findLocale($locale);
431        } catch (Zend_Locale_Exception $e) {
432            require_once 'Zend/Translate/Exception.php';
433            throw new Zend_Translate_Exception("The given Language ({$locale}) does not exist", 0, $e);
434        }
435
436        if (!isset($this->_translate[$locale])) {
437            $temp = explode('_', $locale);
438            if (!isset($this->_translate[$temp[0]]) and !isset($this->_translate[$locale])) {
439                if (!$this->_options['disableNotices']) {
440                    if ($this->_options['log']) {
441                        $this->_options['log']->log("The language '{$locale}' has to be added before it can be used.", $this->_options['logPriority']);
442                    } else {
443                        trigger_error("The language '{$locale}' has to be added before it can be used.", E_USER_NOTICE);
444                    }
445                }
446            }
447
448            $locale = $temp[0];
449        }
450
451        if (empty($this->_translate[$locale])) {
452            if (!$this->_options['disableNotices']) {
453                if ($this->_options['log']) {
454                    $this->_options['log']->log("No translation for the language '{$locale}' available.", $this->_options['logPriority']);
455                } else {
456                    trigger_error("No translation for the language '{$locale}' available.", E_USER_NOTICE);
457                }
458            }
459        }
460
461        if ($this->_options['locale'] != $locale) {
462            $this->_options['locale'] = $locale;
463
464            if (isset(self::$_cache)) {
465                $id = 'Zend_Translate_' . $this->toString() . '_Options';
466                if (self::$_cacheTags) {
467                    self::$_cache->save($this->_options, $id, array($this->_options['tag']));
468                } else {
469                    self::$_cache->save($this->_options, $id);
470                }
471            }
472        }
473
474        return $this;
475    }
476
477    /**
478     * Returns the available languages from this adapter
479     *
480     * @return array|null
481     */
482    public function getList()
483    {
484        $list = array_keys($this->_translate);
485        $result = null;
486        foreach($list as $value) {
487            if (!empty($this->_translate[$value])) {
488                $result[$value] = $value;
489            }
490        }
491        return $result;
492    }
493
494    /**
495     * Returns the message id for a given translation
496     * If no locale is given, the actual language will be used
497     *
498     * @param  string             $message Message to get the key for
499     * @param  string|Zend_Locale $locale (optional) Language to return the message ids from
500     * @return string|array|false
501     */
502    public function getMessageId($message, $locale = null)
503    {
504        if (empty($locale) or !$this->isAvailable($locale)) {
505            $locale = $this->_options['locale'];
506        }
507
508        return array_search($message, $this->_translate[(string) $locale]);
509    }
510
511    /**
512     * Returns all available message ids from this adapter
513     * If no locale is given, the actual language will be used
514     *
515     * @param  string|Zend_Locale $locale (optional) Language to return the message ids from
516     * @return array
517     */
518    public function getMessageIds($locale = null)
519    {
520        if (empty($locale) or !$this->isAvailable($locale)) {
521            $locale = $this->_options['locale'];
522        }
523
524        return array_keys($this->_translate[(string) $locale]);
525    }
526
527    /**
528     * Returns all available translations from this adapter
529     * If no locale is given, the actual language will be used
530     * If 'all' is given the complete translation dictionary will be returned
531     *
532     * @param  string|Zend_Locale $locale (optional) Language to return the messages from
533     * @return array
534     */
535    public function getMessages($locale = null)
536    {
537        if ($locale === 'all') {
538            return $this->_translate;
539        }
540
541        if ((empty($locale) === true) or ($this->isAvailable($locale) === false)) {
542            $locale = $this->_options['locale'];
543        }
544
545        return $this->_translate[(string) $locale];
546    }
547
548    /**
549     * Is the wished language available ?
550     *
551     * @see    Zend_Locale
552     * @param  string|Zend_Locale $locale Language to search for, identical with locale identifier,
553     *                                    @see Zend_Locale for more information
554     * @return boolean
555     */
556    public function isAvailable($locale)
557    {
558        $return = isset($this->_translate[(string) $locale]);
559        return $return;
560    }
561
562    /**
563     * Load translation data
564     *
565     * @param  mixed              $data
566     * @param  string|Zend_Locale $locale
567     * @param  array              $options (optional)
568     * @return array
569     */
570    abstract protected function _loadTranslationData($data, $locale, array $options = array());
571
572    /**
573     * Internal function for adding translation data
574     *
575     * This may be a new language or additional data for an existing language
576     * If the options 'clear' is true, then the translation data for the specified
577     * language is replaced and added otherwise
578     *
579     * @see    Zend_Locale
580     * @param  array|Zend_Config $content Translation data to add
581     * @throws Zend_Translate_Exception
582     * @return Zend_Translate_Adapter Provides fluent interface
583     */
584    private function _addTranslationData($options = array())
585    {
586        if ($options instanceof Zend_Config) {
587            $options = $options->toArray();
588        } else if (func_num_args() > 1) {
589            $args = func_get_args();
590            $options['content'] = array_shift($args);
591
592            if (!empty($args)) {
593                $options['locale'] = array_shift($args);
594            }
595
596            if (!empty($args)) {
597                $options += array_shift($args);
598            }
599        }
600
601        if (($options['content'] instanceof Zend_Translate) || ($options['content'] instanceof Zend_Translate_Adapter)) {
602            $options['usetranslateadapter'] = true;
603            if (!empty($options['locale']) && ($options['locale'] !== 'auto')) {
604                $options['content'] = $options['content']->getMessages($options['locale']);
605            } else {
606                $content = $options['content'];
607                $locales = $content->getList();
608                foreach ($locales as $locale) {
609                    $options['locale']  = $locale;
610                    $options['content'] = $content->getMessages($locale);
611                    $this->_addTranslationData($options);
612                }
613
614                return $this;
615            }
616        }
617
618        try {
619            $options['locale'] = Zend_Locale::findLocale($options['locale']);
620        } catch (Zend_Locale_Exception $e) {
621            require_once 'Zend/Translate/Exception.php';
622            throw new Zend_Translate_Exception("The given Language '{$options['locale']}' does not exist", 0, $e);
623        }
624
625        if ($options['clear'] || !isset($this->_translate[$options['locale']])) {
626            $this->_translate[$options['locale']] = array();
627        }
628
629        $read = true;
630        if (isset(self::$_cache)) {
631            $id = 'Zend_Translate_' . md5(serialize($options['content'])) . '_' . $this->toString();
632            $temp = self::$_cache->load($id);
633            if ($temp) {
634                $read = false;
635            }
636        }
637
638        if ($options['reload']) {
639            $read = true;
640        }
641
642        if ($read) {
643            if (!empty($options['usetranslateadapter'])) {
644                $temp = array($options['locale'] => $options['content']);
645            } else {
646                $temp = $this->_loadTranslationData($options['content'], $options['locale'], $options);
647            }
648        }
649
650        if (empty($temp)) {
651            $temp = array();
652        }
653
654        $keys = array_keys($temp);
655        foreach($keys as $key) {
656            if (!isset($this->_translate[$key])) {
657                $this->_translate[$key] = array();
658            }
659
660            if (array_key_exists($key, $temp) && is_array($temp[$key])) {
661                $this->_translate[$key] = $temp[$key] + $this->_translate[$key];
662            }
663        }
664
665        if ($this->_automatic === true) {
666            $find = new Zend_Locale($options['locale']);
667            $browser = $find->getEnvironment() + $find->getBrowser();
668            arsort($browser);
669            foreach($browser as $language => $quality) {
670                if (isset($this->_translate[$language])) {
671                    $this->_options['locale'] = $language;
672                    break;
673                }
674            }
675        }
676
677        if (($read) and (isset(self::$_cache))) {
678            $id = 'Zend_Translate_' . md5(serialize($options['content'])) . '_' . $this->toString();
679            if (self::$_cacheTags) {
680                self::$_cache->save($temp, $id, array($this->_options['tag']));
681            } else {
682                self::$_cache->save($temp, $id);
683            }
684        }
685
686        return $this;
687    }
688
689    /**
690     * Translates the given string
691     * returns the translation
692     *
693     * @see Zend_Locale
694     * @param  string|array       $messageId Translation string, or Array for plural translations
695     * @param  string|Zend_Locale $locale    (optional) Locale/Language to use, identical with
696     *                                       locale identifier, @see Zend_Locale for more information
697     * @return string
698     */
699    public function translate($messageId, $locale = null)
700    {
701        if ($locale === null) {
702            $locale = $this->_options['locale'];
703        }
704
705        $plural = null;
706        if (is_array($messageId)) {
707            if (count($messageId) > 2) {
708                $number = array_pop($messageId);
709                if (!is_numeric($number)) {
710                    $plocale = $number;
711                    $number  = array_pop($messageId);
712                } else {
713                    $plocale = 'en';
714                }
715
716                $plural    = $messageId;
717                $messageId = $messageId[0];
718            } else {
719                $messageId = $messageId[0];
720            }
721        }
722
723        if (!Zend_Locale::isLocale($locale, true, false)) {
724            if (!Zend_Locale::isLocale($locale, false, false)) {
725                // language does not exist, return original string
726                $this->_log($messageId, $locale);
727                // use rerouting when enabled
728                if (!empty($this->_options['route'])) {
729                    if (array_key_exists($locale, $this->_options['route']) &&
730                        !array_key_exists($locale, $this->_routed)) {
731                        $this->_routed[$locale] = true;
732                        return $this->translate($messageId, $this->_options['route'][$locale]);
733                    }
734                }
735
736                $this->_routed = array();
737                if ($plural === null) {
738                    return $messageId;
739                }
740
741                $rule = Zend_Translate_Plural::getPlural($number, $plocale);
742                if (!isset($plural[$rule])) {
743                    $rule = 0;
744                }
745
746                return $plural[$rule];
747            }
748
749            $locale = new Zend_Locale($locale);
750        }
751
752        $locale = (string) $locale;
753        if ((is_string($messageId) || is_int($messageId)) && isset($this->_translate[$locale][$messageId])) {
754            // return original translation
755            if ($plural === null) {
756                $this->_routed = array();
757                return $this->_translate[$locale][$messageId];
758            }
759
760            $rule = Zend_Translate_Plural::getPlural($number, $locale);
761            if (isset($this->_translate[$locale][$plural[0]][$rule])) {
762                $this->_routed = array();
763                return $this->_translate[$locale][$plural[0]][$rule];
764            }
765        } else if (strlen($locale) != 2) {
766            // faster than creating a new locale and separate the leading part
767            $locale = substr($locale, 0, -strlen(strrchr($locale, '_')));
768
769            if ((is_string($messageId) || is_int($messageId)) && isset($this->_translate[$locale][$messageId])) {
770                // return regionless translation (en_US -> en)
771                if ($plural === null) {
772                    $this->_routed = array();
773                    return $this->_translate[$locale][$messageId];
774                }
775
776                $rule = Zend_Translate_Plural::getPlural($number, $locale);
777                if (isset($this->_translate[$locale][$plural[0]][$rule])) {
778                    $this->_routed = array();
779                    return $this->_translate[$locale][$plural[0]][$rule];
780                }
781            }
782        }
783
784        $this->_log($messageId, $locale);
785        // use rerouting when enabled
786        if (!empty($this->_options['route'])) {
787            if (array_key_exists($locale, $this->_options['route']) &&
788                !array_key_exists($locale, $this->_routed)) {
789                $this->_routed[$locale] = true;
790                return $this->translate($messageId, $this->_options['route'][$locale]);
791            }
792        }
793
794        $this->_routed = array();
795        if ($plural === null) {
796            return $messageId;
797        }
798
799        $rule = Zend_Translate_Plural::getPlural($number, $plocale);
800        if (!isset($plural[$rule])) {
801            $rule = 0;
802        }
803
804        return $plural[$rule];
805    }
806
807    /**
808     * Translates the given string using plural notations
809     * Returns the translated string
810     *
811     * @see Zend_Locale
812     * @param  string             $singular Singular translation string
813     * @param  string             $plural   Plural translation string
814     * @param  integer            $number   Number for detecting the correct plural
815     * @param  string|Zend_Locale $locale   (Optional) Locale/Language to use, identical with
816     *                                      locale identifier, @see Zend_Locale for more information
817     * @return string
818     */
819    public function plural($singular, $plural, $number, $locale = null)
820    {
821        return $this->translate(array($singular, $plural, $number), $locale);
822    }
823
824    /**
825     * Logs a message when the log option is set
826     *
827     * @param string $message Message to log
828     * @param String $locale  Locale to log
829     */
830    protected function _log($message, $locale) {
831        if ($this->_options['logUntranslated']) {
832            $message = str_replace('%message%', $message, $this->_options['logMessage']);
833            $message = str_replace('%locale%', $locale, $message);
834            if ($this->_options['log']) {
835                $this->_options['log']->log($message, $this->_options['logPriority']);
836            } else {
837                trigger_error($message, E_USER_NOTICE);
838            }
839        }
840    }
841
842    /**
843     * Translates the given string
844     * returns the translation
845     *
846     * @param  string             $messageId Translation string
847     * @param  string|Zend_Locale $locale    (optional) Locale/Language to use, identical with locale
848     *                                       identifier, @see Zend_Locale for more information
849     * @return string
850     */
851    public function _($messageId, $locale = null)
852    {
853        return $this->translate($messageId, $locale);
854    }
855
856    /**
857     * Checks if a string is translated within the source or not
858     * returns boolean
859     *
860     * @param  string             $messageId Translation string
861     * @param  boolean            $original  (optional) Allow translation only for original language
862     *                                       when true, a translation for 'en_US' would give false when it can
863     *                                       be translated with 'en' only
864     * @param  string|Zend_Locale $locale    (optional) Locale/Language to use, identical with locale identifier,
865     *                                       see Zend_Locale for more information
866     * @return boolean
867     */
868    public function isTranslated($messageId, $original = false, $locale = null)
869    {
870        if (($original !== false) and ($original !== true)) {
871            $locale   = $original;
872            $original = false;
873        }
874
875        if ($locale === null) {
876            $locale = $this->_options['locale'];
877        }
878
879        if (!Zend_Locale::isLocale($locale, true, false)) {
880            if (!Zend_Locale::isLocale($locale, false, false)) {
881                // language does not exist, return original string
882                return false;
883            }
884
885            $locale = new Zend_Locale($locale);
886        }
887
888        $locale = (string) $locale;
889        if ((is_string($messageId) || is_int($messageId)) && isset($this->_translate[$locale][$messageId])) {
890            // return original translation
891            return true;
892        } else if ((strlen($locale) != 2) and ($original === false)) {
893            // faster than creating a new locale and separate the leading part
894            $locale = substr($locale, 0, -strlen(strrchr($locale, '_')));
895
896            if ((is_string($messageId) || is_int($messageId)) && isset($this->_translate[$locale][$messageId])) {
897                // return regionless translation (en_US -> en)
898                return true;
899            }
900        }
901
902        // No translation found, return original
903        return false;
904    }
905
906    /**
907     * Returns the set cache
908     *
909     * @return Zend_Cache_Core The set cache
910     */
911    public static function getCache()
912    {
913        return self::$_cache;
914    }
915
916    /**
917     * Sets a cache for all Zend_Translate_Adapters
918     *
919     * @param Zend_Cache_Core $cache Cache to store to
920     */
921    public static function setCache(Zend_Cache_Core $cache)
922    {
923        self::$_cache = $cache;
924        self::_getTagSupportForCache();
925    }
926
927    /**
928     * Returns true when a cache is set
929     *
930     * @return boolean
931     */
932    public static function hasCache()
933    {
934        if (self::$_cache !== null) {
935            return true;
936        }
937
938        return false;
939    }
940
941    /**
942     * Removes any set cache
943     *
944     * @return void
945     */
946    public static function removeCache()
947    {
948        self::$_cache = null;
949    }
950
951    /**
952     * Clears all set cache data
953     *
954     * @param string $tag Tag to clear when the default tag name is not used
955     * @return void
956     */
957    public static function clearCache($tag = null)
958    {
959        require_once 'Zend/Cache.php';
960        if (self::$_cacheTags) {
961            if ($tag == null) {
962                $tag = 'Zend_Translate';
963            }
964
965            self::$_cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array($tag));
966        } else {
967            self::$_cache->clean(Zend_Cache::CLEANING_MODE_ALL);
968        }
969    }
970
971    /**
972     * Returns the adapter name
973     *
974     * @return string
975     */
976    abstract public function toString();
977
978    /**
979     * Internal method to check if the given cache supports tags
980     *
981     * @param Zend_Cache $cache
982     */
983    private static function _getTagSupportForCache()
984    {
985        $backend = self::$_cache->getBackend();
986        if ($backend instanceof Zend_Cache_Backend_ExtendedInterface) {
987            $cacheOptions = $backend->getCapabilities();
988            self::$_cacheTags = $cacheOptions['tags'];
989        } else {
990            self::$_cacheTags = false;
991        }
992
993        return self::$_cacheTags;
994    }
995}