PageRenderTime 381ms CodeModel.GetById 13ms app.highlight 39ms RepoModel.GetById 1ms app.codeStats 1ms

/library/Zend/Translate/Adapter.php

https://bitbucket.org/nosen/jelly2
PHP | 982 lines | 587 code | 112 blank | 283 comment | 167 complexity | 11574ad0f06222f40e9275be061f92a7 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-2011 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 23775 2011-03-01 17:25:24Z ralph $
 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-2011 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        $originate = null;
217        if (!empty($options['locale'])) {
218            $originate = (string) $options['locale'];
219        }
220
221        if ((array_key_exists('log', $options)) && !($options['log'] instanceof Zend_Log)) {
222            require_once 'Zend/Translate/Exception.php';
223            throw new Zend_Translate_Exception('Instance of Zend_Log expected for option log');
224        }
225
226        try {
227            if (!($options['content'] instanceof Zend_Translate) && !($options['content'] instanceof Zend_Translate_Adapter)) {
228                if (empty($options['locale'])) {
229                    $options['locale'] = null;
230                }
231
232                $options['locale'] = Zend_Locale::findLocale($options['locale']);
233            }
234        } catch (Zend_Locale_Exception $e) {
235            require_once 'Zend/Translate/Exception.php';
236            throw new Zend_Translate_Exception("The given Language '{$options['locale']}' does not exist", 0, $e);
237        }
238
239        $options  = $options + $this->_options;
240        if (is_string($options['content']) and is_dir($options['content'])) {
241            $options['content'] = realpath($options['content']);
242            $prev = '';
243            foreach (new RecursiveIteratorIterator(
244                     new RecursiveDirectoryIterator($options['content'], RecursiveDirectoryIterator::KEY_AS_PATHNAME),
245                     RecursiveIteratorIterator::SELF_FIRST) as $directory => $info) {
246                $file = $info->getFilename();
247                if (is_array($options['ignore'])) {
248                    foreach ($options['ignore'] as $key => $ignore) {
249                        if (strpos($key, 'regex') !== false) {
250                            if (preg_match($ignore, $directory)) {
251                                // ignore files matching the given regex from option 'ignore' and all files below
252                                continue 2;
253                            }
254                        } else if (strpos($directory, DIRECTORY_SEPARATOR . $ignore) !== false) {
255                            // ignore files matching first characters from option 'ignore' and all files below
256                            continue 2;
257                        }
258                    }
259                } else {
260                    if (strpos($directory, DIRECTORY_SEPARATOR . $options['ignore']) !== false) {
261                        // ignore files matching first characters from option 'ignore' and all files below
262                        continue;
263                    }
264                }
265
266                if ($info->isDir()) {
267                    // pathname as locale
268                    if (($options['scan'] === self::LOCALE_DIRECTORY) and (Zend_Locale::isLocale($file, true, false))) {
269                        $options['locale'] = $file;
270                        $prev              = (string) $options['locale'];
271                    }
272                } else if ($info->isFile()) {
273                    // filename as locale
274                    if ($options['scan'] === self::LOCALE_FILENAME) {
275                        $filename = explode('.', $file);
276                        array_pop($filename);
277                        $filename = implode('.', $filename);
278                        if (Zend_Locale::isLocale((string) $filename, true, false)) {
279                            $options['locale'] = (string) $filename;
280                        } else {
281                            $parts  = explode('.', $file);
282                            $parts2 = array();
283                            foreach($parts as $token) {
284                                $parts2 += explode('_', $token);
285                            }
286                            $parts  = array_merge($parts, $parts2);
287                            $parts2 = array();
288                            foreach($parts as $token) {
289                                $parts2 += explode('-', $token);
290                            }
291                            $parts = array_merge($parts, $parts2);
292                            $parts = array_unique($parts);
293                            $prev  = '';
294                            foreach($parts as $token) {
295                                if (Zend_Locale::isLocale($token, true, false)) {
296                                    if (strlen($prev) <= strlen($token)) {
297                                        $options['locale'] = $token;
298                                        $prev              = $token;
299                                    }
300                                }
301                            }
302                        }
303                    }
304
305                    try {
306                        $options['content'] = $info->getPathname();
307                        $this->_addTranslationData($options);
308                    } catch (Zend_Translate_Exception $e) {
309                        // ignore failed sources while scanning
310                    }
311                }
312            }
313        } else {
314            $this->_addTranslationData($options);
315        }
316
317        if ((isset($this->_translate[$originate]) === true) and (count($this->_translate[$originate]) > 0)) {
318            $this->setLocale($originate);
319        }
320
321        return $this;
322    }
323
324    /**
325     * Sets new adapter options
326     *
327     * @param  array $options Adapter options
328     * @throws Zend_Translate_Exception
329     * @return Zend_Translate_Adapter Provides fluent interface
330     */
331    public function setOptions(array $options = array())
332    {
333        $change = false;
334        $locale = null;
335        foreach ($options as $key => $option) {
336            if ($key == 'locale') {
337                $locale = $option;
338            } else if ((isset($this->_options[$key]) and ($this->_options[$key] != $option)) or
339                    !isset($this->_options[$key])) {
340                if (($key == 'log') && !($option instanceof Zend_Log)) {
341                    require_once 'Zend/Translate/Exception.php';
342                    throw new Zend_Translate_Exception('Instance of Zend_Log expected for option log');
343                }
344
345                if ($key == 'cache') {
346                    self::setCache($option);
347                    continue;
348                }
349
350                $this->_options[$key] = $option;
351                $change = true;
352            }
353        }
354
355        if ($locale !== null) {
356            $this->setLocale($locale);
357        }
358
359        if (isset(self::$_cache) and ($change == true)) {
360            $id = 'Zend_Translate_' . $this->toString() . '_Options';
361            if (self::$_cacheTags) {
362                self::$_cache->save($this->_options, $id, array($this->_options['tag']));
363            } else {
364                self::$_cache->save($this->_options, $id);
365            }
366        }
367
368        return $this;
369    }
370
371    /**
372     * Returns the adapters name and it's options
373     *
374     * @param  string|null $optionKey String returns this option
375     *                                null returns all options
376     * @return integer|string|array|null
377     */
378    public function getOptions($optionKey = null)
379    {
380        if ($optionKey === null) {
381            return $this->_options;
382        }
383
384        if (isset($this->_options[$optionKey]) === true) {
385            return $this->_options[$optionKey];
386        }
387
388        return null;
389    }
390
391    /**
392     * Gets locale
393     *
394     * @return Zend_Locale|string|null
395     */
396    public function getLocale()
397    {
398        return $this->_options['locale'];
399    }
400
401    /**
402     * Sets locale
403     *
404     * @param  string|Zend_Locale $locale Locale to set
405     * @throws Zend_Translate_Exception
406     * @return Zend_Translate_Adapter Provides fluent interface
407     */
408    public function setLocale($locale)
409    {
410        if (($locale === "auto") or ($locale === null)) {
411            $this->_automatic = true;
412        } else {
413            $this->_automatic = false;
414        }
415
416        try {
417            $locale = Zend_Locale::findLocale($locale);
418        } catch (Zend_Locale_Exception $e) {
419            require_once 'Zend/Translate/Exception.php';
420            throw new Zend_Translate_Exception("The given Language ({$locale}) does not exist", 0, $e);
421        }
422
423        if (!isset($this->_translate[$locale])) {
424            $temp = explode('_', $locale);
425            if (!isset($this->_translate[$temp[0]]) and !isset($this->_translate[$locale])) {
426                if (!$this->_options['disableNotices']) {
427                    if ($this->_options['log']) {
428                        $this->_options['log']->log("The language '{$locale}' has to be added before it can be used.", $this->_options['logPriority']);
429                    } else {
430                        trigger_error("The language '{$locale}' has to be added before it can be used.", E_USER_NOTICE);
431                    }
432                }
433            }
434
435            $locale = $temp[0];
436        }
437
438        if (empty($this->_translate[$locale])) {
439            if (!$this->_options['disableNotices']) {
440                if ($this->_options['log']) {
441                    $this->_options['log']->log("No translation for the language '{$locale}' available.", $this->_options['logPriority']);
442                } else {
443                    trigger_error("No translation for the language '{$locale}' available.", E_USER_NOTICE);
444                }
445            }
446        }
447
448        if ($this->_options['locale'] != $locale) {
449            $this->_options['locale'] = $locale;
450
451            if (isset(self::$_cache)) {
452                $id = 'Zend_Translate_' . $this->toString() . '_Options';
453                if (self::$_cacheTags) {
454                    self::$_cache->save($this->_options, $id, array($this->_options['tag']));
455                } else {
456                    self::$_cache->save($this->_options, $id);
457                }
458            }
459        }
460
461        return $this;
462    }
463
464    /**
465     * Returns the available languages from this adapter
466     *
467     * @return array|null
468     */
469    public function getList()
470    {
471        $list = array_keys($this->_translate);
472        $result = null;
473        foreach($list as $value) {
474            if (!empty($this->_translate[$value])) {
475                $result[$value] = $value;
476            }
477        }
478        return $result;
479    }
480
481    /**
482     * Returns the message id for a given translation
483     * If no locale is given, the actual language will be used
484     *
485     * @param  string             $message Message to get the key for
486     * @param  string|Zend_Locale $locale (optional) Language to return the message ids from
487     * @return string|array|false
488     */
489    public function getMessageId($message, $locale = null)
490    {
491        if (empty($locale) or !$this->isAvailable($locale)) {
492            $locale = $this->_options['locale'];
493        }
494
495        return array_search($message, $this->_translate[(string) $locale]);
496    }
497
498    /**
499     * Returns all available message ids from this adapter
500     * If no locale is given, the actual language will be used
501     *
502     * @param  string|Zend_Locale $locale (optional) Language to return the message ids from
503     * @return array
504     */
505    public function getMessageIds($locale = null)
506    {
507        if (empty($locale) or !$this->isAvailable($locale)) {
508            $locale = $this->_options['locale'];
509        }
510
511        return array_keys($this->_translate[(string) $locale]);
512    }
513
514    /**
515     * Returns all available translations from this adapter
516     * If no locale is given, the actual language will be used
517     * If 'all' is given the complete translation dictionary will be returned
518     *
519     * @param  string|Zend_Locale $locale (optional) Language to return the messages from
520     * @return array
521     */
522    public function getMessages($locale = null)
523    {
524        if ($locale === 'all') {
525            return $this->_translate;
526        }
527
528        if ((empty($locale) === true) or ($this->isAvailable($locale) === false)) {
529            $locale = $this->_options['locale'];
530        }
531
532        return $this->_translate[(string) $locale];
533    }
534
535    /**
536     * Is the wished language available ?
537     *
538     * @see    Zend_Locale
539     * @param  string|Zend_Locale $locale Language to search for, identical with locale identifier,
540     *                                    @see Zend_Locale for more information
541     * @return boolean
542     */
543    public function isAvailable($locale)
544    {
545        $return = isset($this->_translate[(string) $locale]);
546        return $return;
547    }
548
549    /**
550     * Load translation data
551     *
552     * @param  mixed              $data
553     * @param  string|Zend_Locale $locale
554     * @param  array              $options (optional)
555     * @return array
556     */
557    abstract protected function _loadTranslationData($data, $locale, array $options = array());
558
559    /**
560     * Internal function for adding translation data
561     *
562     * This may be a new language or additional data for an existing language
563     * If the options 'clear' is true, then the translation data for the specified
564     * language is replaced and added otherwise
565     *
566     * @see    Zend_Locale
567     * @param  array|Zend_Config $content Translation data to add
568     * @throws Zend_Translate_Exception
569     * @return Zend_Translate_Adapter Provides fluent interface
570     */
571    private function _addTranslationData($options = array())
572    {
573        if ($options instanceof Zend_Config) {
574            $options = $options->toArray();
575        } else if (func_num_args() > 1) {
576            $args = func_get_args();
577            $options['content'] = array_shift($args);
578
579            if (!empty($args)) {
580                $options['locale'] = array_shift($args);
581            }
582
583            if (!empty($args)) {
584                $options += array_shift($args);
585            }
586        }
587
588        if (($options['content'] instanceof Zend_Translate) || ($options['content'] instanceof Zend_Translate_Adapter)) {
589            $options['usetranslateadapter'] = true;
590            if (!empty($options['locale']) && ($options['locale'] !== 'auto')) {
591                $options['content'] = $options['content']->getMessages($options['locale']);
592            } else {
593                $content = $options['content'];
594                $locales = $content->getList();
595                foreach ($locales as $locale) {
596                    $options['locale']  = $locale;
597                    $options['content'] = $content->getMessages($locale);
598                    $this->_addTranslationData($options);
599                }
600
601                return $this;
602            }
603        }
604
605        try {
606            $options['locale'] = Zend_Locale::findLocale($options['locale']);
607        } catch (Zend_Locale_Exception $e) {
608            require_once 'Zend/Translate/Exception.php';
609            throw new Zend_Translate_Exception("The given Language '{$options['locale']}' does not exist", 0, $e);
610        }
611
612        if ($options['clear'] || !isset($this->_translate[$options['locale']])) {
613            $this->_translate[$options['locale']] = array();
614        }
615
616        $read = true;
617        if (isset(self::$_cache)) {
618            $id = 'Zend_Translate_' . md5(serialize($options['content'])) . '_' . $this->toString();
619            $temp = self::$_cache->load($id);
620            if ($temp) {
621                $read = false;
622            }
623        }
624
625        if ($options['reload']) {
626            $read = true;
627        }
628
629        if ($read) {
630            if (!empty($options['usetranslateadapter'])) {
631                $temp = array($options['locale'] => $options['content']);
632            } else {
633                $temp = $this->_loadTranslationData($options['content'], $options['locale'], $options);
634            }
635        }
636
637        if (empty($temp)) {
638            $temp = array();
639        }
640
641        $keys = array_keys($temp);
642        foreach($keys as $key) {
643            if (!isset($this->_translate[$key])) {
644                $this->_translate[$key] = array();
645            }
646
647            if (array_key_exists($key, $temp) && is_array($temp[$key])) {
648                $this->_translate[$key] = $temp[$key] + $this->_translate[$key];
649            }
650        }
651
652        if ($this->_automatic === true) {
653            $find = new Zend_Locale($options['locale']);
654            $browser = $find->getEnvironment() + $find->getBrowser();
655            arsort($browser);
656            foreach($browser as $language => $quality) {
657                if (isset($this->_translate[$language])) {
658                    $this->_options['locale'] = $language;
659                    break;
660                }
661            }
662        }
663
664        if (($read) and (isset(self::$_cache))) {
665            $id = 'Zend_Translate_' . md5(serialize($options['content'])) . '_' . $this->toString();
666            if (self::$_cacheTags) {
667                self::$_cache->save($temp, $id, array($this->_options['tag']));
668            } else {
669                self::$_cache->save($temp, $id);
670            }
671        }
672
673        return $this;
674    }
675
676    /**
677     * Translates the given string
678     * returns the translation
679     *
680     * @see Zend_Locale
681     * @param  string|array       $messageId Translation string, or Array for plural translations
682     * @param  string|Zend_Locale $locale    (optional) Locale/Language to use, identical with
683     *                                       locale identifier, @see Zend_Locale for more information
684     * @return string
685     */
686    public function translate($messageId, $locale = null)
687    {
688        if ($locale === null) {
689            $locale = $this->_options['locale'];
690        }
691
692        $plural = null;
693        if (is_array($messageId)) {
694            if (count($messageId) > 2) {
695                $number = array_pop($messageId);
696                if (!is_numeric($number)) {
697                    $plocale = $number;
698                    $number  = array_pop($messageId);
699                } else {
700                    $plocale = 'en';
701                }
702
703                $plural    = $messageId;
704                $messageId = $messageId[0];
705            } else {
706                $messageId = $messageId[0];
707            }
708        }
709
710        if (!Zend_Locale::isLocale($locale, true, false)) {
711            if (!Zend_Locale::isLocale($locale, false, false)) {
712                // language does not exist, return original string
713                $this->_log($messageId, $locale);
714                // use rerouting when enabled
715                if (!empty($this->_options['route'])) {
716                    if (array_key_exists($locale, $this->_options['route']) &&
717                        !array_key_exists($locale, $this->_routed)) {
718                        $this->_routed[$locale] = true;
719                        return $this->translate($messageId, $this->_options['route'][$locale]);
720                    }
721                }
722
723                $this->_routed = array();
724                if ($plural === null) {
725                    return $messageId;
726                }
727
728                $rule = Zend_Translate_Plural::getPlural($number, $plocale);
729                if (!isset($plural[$rule])) {
730                    $rule = 0;
731                }
732
733                return $plural[$rule];
734            }
735
736            $locale = new Zend_Locale($locale);
737        }
738
739        $locale = (string) $locale;
740        if ((is_string($messageId) || is_int($messageId)) && isset($this->_translate[$locale][$messageId])) {
741            // return original translation
742            if ($plural === null) {
743                $this->_routed = array();
744                return $this->_translate[$locale][$messageId];
745            }
746
747            $rule = Zend_Translate_Plural::getPlural($number, $locale);
748            if (isset($this->_translate[$locale][$plural[0]][$rule])) {
749                $this->_routed = array();
750                return $this->_translate[$locale][$plural[0]][$rule];
751            }
752        } else if (strlen($locale) != 2) {
753            // faster than creating a new locale and separate the leading part
754            $locale = substr($locale, 0, -strlen(strrchr($locale, '_')));
755
756            if ((is_string($messageId) || is_int($messageId)) && isset($this->_translate[$locale][$messageId])) {
757                // return regionless translation (en_US -> en)
758                if ($plural === null) {
759                    $this->_routed = array();
760                    return $this->_translate[$locale][$messageId];
761                }
762
763                $rule = Zend_Translate_Plural::getPlural($number, $locale);
764                if (isset($this->_translate[$locale][$plural[0]][$rule])) {
765                    $this->_routed = array();
766                    return $this->_translate[$locale][$plural[0]][$rule];
767                }
768            }
769        }
770
771        $this->_log($messageId, $locale);
772        // use rerouting when enabled
773        if (!empty($this->_options['route'])) {
774            if (array_key_exists($locale, $this->_options['route']) &&
775                !array_key_exists($locale, $this->_routed)) {
776                $this->_routed[$locale] = true;
777                return $this->translate($messageId, $this->_options['route'][$locale]);
778            }
779        }
780
781        $this->_routed = array();
782        if ($plural === null) {
783            return $messageId;
784        }
785
786        $rule = Zend_Translate_Plural::getPlural($number, $plocale);
787        if (!isset($plural[$rule])) {
788            $rule = 0;
789        }
790
791        return $plural[$rule];
792    }
793
794    /**
795     * Translates the given string using plural notations
796     * Returns the translated string
797     *
798     * @see Zend_Locale
799     * @param  string             $singular Singular translation string
800     * @param  string             $plural   Plural translation string
801     * @param  integer            $number   Number for detecting the correct plural
802     * @param  string|Zend_Locale $locale   (Optional) Locale/Language to use, identical with
803     *                                      locale identifier, @see Zend_Locale for more information
804     * @return string
805     */
806    public function plural($singular, $plural, $number, $locale = null)
807    {
808        return $this->translate(array($singular, $plural, $number), $locale);
809    }
810
811    /**
812     * Logs a message when the log option is set
813     *
814     * @param string $message Message to log
815     * @param String $locale  Locale to log
816     */
817    protected function _log($message, $locale) {
818        if ($this->_options['logUntranslated']) {
819            $message = str_replace('%message%', $message, $this->_options['logMessage']);
820            $message = str_replace('%locale%', $locale, $message);
821            if ($this->_options['log']) {
822                $this->_options['log']->log($message, $this->_options['logPriority']);
823            } else {
824                trigger_error($message, E_USER_NOTICE);
825            }
826        }
827    }
828
829    /**
830     * Translates the given string
831     * returns the translation
832     *
833     * @param  string             $messageId Translation string
834     * @param  string|Zend_Locale $locale    (optional) Locale/Language to use, identical with locale
835     *                                       identifier, @see Zend_Locale for more information
836     * @return string
837     */
838    public function _($messageId, $locale = null)
839    {
840        return $this->translate($messageId, $locale);
841    }
842
843    /**
844     * Checks if a string is translated within the source or not
845     * returns boolean
846     *
847     * @param  string             $messageId Translation string
848     * @param  boolean            $original  (optional) Allow translation only for original language
849     *                                       when true, a translation for 'en_US' would give false when it can
850     *                                       be translated with 'en' only
851     * @param  string|Zend_Locale $locale    (optional) Locale/Language to use, identical with locale identifier,
852     *                                       see Zend_Locale for more information
853     * @return boolean
854     */
855    public function isTranslated($messageId, $original = false, $locale = null)
856    {
857        if (($original !== false) and ($original !== true)) {
858            $locale   = $original;
859            $original = false;
860        }
861
862        if ($locale === null) {
863            $locale = $this->_options['locale'];
864        }
865
866        if (!Zend_Locale::isLocale($locale, true, false)) {
867            if (!Zend_Locale::isLocale($locale, false, false)) {
868                // language does not exist, return original string
869                return false;
870            }
871
872            $locale = new Zend_Locale($locale);
873        }
874
875        $locale = (string) $locale;
876        if ((is_string($messageId) || is_int($messageId)) && isset($this->_translate[$locale][$messageId])) {
877            // return original translation
878            return true;
879        } else if ((strlen($locale) != 2) and ($original === false)) {
880            // faster than creating a new locale and separate the leading part
881            $locale = substr($locale, 0, -strlen(strrchr($locale, '_')));
882
883            if ((is_string($messageId) || is_int($messageId)) && isset($this->_translate[$locale][$messageId])) {
884                // return regionless translation (en_US -> en)
885                return true;
886            }
887        }
888
889        // No translation found, return original
890        return false;
891    }
892
893    /**
894     * Returns the set cache
895     *
896     * @return Zend_Cache_Core The set cache
897     */
898    public static function getCache()
899    {
900        return self::$_cache;
901    }
902
903    /**
904     * Sets a cache for all Zend_Translate_Adapters
905     *
906     * @param Zend_Cache_Core $cache Cache to store to
907     */
908    public static function setCache(Zend_Cache_Core $cache)
909    {
910        self::$_cache = $cache;
911        self::_getTagSupportForCache();
912    }
913
914    /**
915     * Returns true when a cache is set
916     *
917     * @return boolean
918     */
919    public static function hasCache()
920    {
921        if (self::$_cache !== null) {
922            return true;
923        }
924
925        return false;
926    }
927
928    /**
929     * Removes any set cache
930     *
931     * @return void
932     */
933    public static function removeCache()
934    {
935        self::$_cache = null;
936    }
937
938    /**
939     * Clears all set cache data
940     *
941     * @param string $tag Tag to clear when the default tag name is not used
942     * @return void
943     */
944    public static function clearCache($tag = null)
945    {
946        require_once 'Zend/Cache.php';
947        if (self::$_cacheTags) {
948            if ($tag == null) {
949                $tag = 'Zend_Translate';
950            }
951
952            self::$_cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array($tag));
953        } else {
954            self::$_cache->clean(Zend_Cache::CLEANING_MODE_ALL);
955        }
956    }
957
958    /**
959     * Returns the adapter name
960     *
961     * @return string
962     */
963    abstract public function toString();
964
965    /**
966     * Internal method to check if the given cache supports tags
967     *
968     * @param Zend_Cache $cache
969     */
970    private static function _getTagSupportForCache()
971    {
972        $backend = self::$_cache->getBackend();
973        if ($backend instanceof Zend_Cache_Backend_ExtendedInterface) {
974            $cacheOptions = $backend->getCapabilities();
975            self::$_cacheTags = $cacheOptions['tags'];
976        } else {
977            self::$_cacheTags = false;
978        }
979
980        return self::$_cacheTags;
981    }
982}