Advertisement
booradleys

Zend\I18n\Translator\Translator

Jul 9th, 2012
46
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 13.84 KB | None | 0 0
  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_I18n
  17.  * @subpackage Translator
  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.  */
  21.  
  22. namespace Zend\I18n\Translator;
  23.  
  24. use Locale;
  25. use Traversable;
  26. use Zend\Stdlib\ArrayUtils;
  27. use Zend\Cache;
  28. use Zend\Cache\Storage\StorageInterface as CacheStorage;
  29. use Zend\I18n\Exception;
  30.  
  31. /**
  32.  * Translator.
  33.  *
  34.  * @category   Zend
  35.  * @package    Zend_I18n
  36.  * @subpackage Translator
  37.  * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  38.  * @license    http://framework.zend.com/license/new-bsd     New BSD License
  39.  */
  40. class Translator
  41. {
  42.     /**
  43.      * Messages loaded by the translator.
  44.      *
  45.      * @var array
  46.      */
  47.     protected $messages = array();
  48.  
  49.     /**
  50.      * Files used for loading messages.
  51.      *
  52.      * @var array
  53.      */
  54.     protected $files = array();
  55.  
  56.     /**
  57.      * Patterns used for loading messages.
  58.      *
  59.      * @var array
  60.      */
  61.     protected $patterns = array();
  62.  
  63.     /**
  64.      * Default locale.
  65.      *
  66.      * @var string
  67.      */
  68.     protected $locale;
  69.  
  70.     /**
  71.      * Locale to use as fallback if there is no translation.
  72.      *
  73.      * @var string
  74.      */
  75.     protected $fallbackLocale;
  76.  
  77.     /**
  78.      * Translation cache.
  79.      *
  80.      * @var CacheStorage
  81.      */
  82.     protected $cache;
  83.  
  84.     /**
  85.      * Plugin manager for translation loaders.
  86.      *
  87.      * @var LoaderPluginManager
  88.      */
  89.     protected $pluginManager;
  90.  
  91.     /**
  92.      * Instantiate a translator
  93.      *
  94.      * @param  array|Traversable $options
  95.      * @return Translator
  96.      * @throws Exception\InvalidArgumentException
  97.      */
  98.     public static function factory($options)
  99.     {
  100.         if ($options instanceof Traversable) {
  101.             $options = ArrayUtils::iteratorToArray($options);
  102.         } elseif (!is_array($options)) {
  103.             throw new Exception\InvalidArgumentException(sprintf(
  104.                 '%s expects an array or Traversable object; received "%s"',
  105.                 __METHOD__,
  106.                 (is_object($options) ? get_class($options) : gettype($options))
  107.             ));
  108.         }
  109.  
  110.         $translator = new static();
  111.  
  112.         // locales
  113.         if (isset($options['locale'])) {
  114.             $locales = (array) $options['locale'];
  115.             $translator->setLocale(array_shift($locales));
  116.             if (count($locales) > 0) {
  117.                 $translator->setFallbackLocale(array_shift($locales));
  118.             }
  119.         }
  120.  
  121.         // patterns
  122.         if (isset($options['translation_patterns'])) {
  123.             if (!is_array($options['translation_patterns'])) {
  124.                 throw new Exception\InvalidArgumentException(
  125.                     '"translation_patterns" should be an array'
  126.                 );
  127.             }
  128.  
  129.             $requiredKeys = array('type', 'base_dir', 'pattern');
  130.             foreach ($options['translation_patterns'] as $pattern) {
  131.                 foreach ($requiredKeys as $key) {
  132.                     if (!isset($pattern[$key])) {
  133.                         throw new Exception\InvalidArgumentException(
  134.                             "'{$key}' is missing for translation pattern options"
  135.                         );
  136.                     }
  137.                 }
  138.  
  139.                 $translator->addTranslationPattern(
  140.                     $pattern['type'],
  141.                     $pattern['base_dir'],
  142.                     $pattern['pattern'],
  143.                     isset($pattern['text_domain']) ? $pattern['text_domain'] : 'default'
  144.                 );
  145.             }
  146.         }
  147.  
  148.         // files
  149.         if (isset($options['translation_files'])) {
  150.             if (!is_array($options['translation_files'])) {
  151.                 throw new Exception\InvalidArgumentException(
  152.                     '"translation_files" should be an array'
  153.                 );
  154.             }
  155.  
  156.             $requiredKeys = array('type', 'filename');
  157.             foreach ($options['translation_files'] as $file) {
  158.                 foreach ($requiredKeys as $key) {
  159.                     if (!isset($file[$key])) {
  160.                         throw new Exception\InvalidArgumentException(
  161.                             "'{$key}' is missing for translation file options"
  162.                         );
  163.                     }
  164.                 }
  165.  
  166.                 $translator->addTranslationFile(
  167.                     $file['type'],
  168.                     $file['filename'],
  169.                     isset($file['text_domain']) ? $file['text_domain'] : 'default',
  170.                     isset($file['locale']) ? $file['locale'] : null
  171.                 );
  172.             }
  173.         }
  174.  
  175.         // cache
  176.         if (isset($options['cache'])) {
  177.             if ($options['cache'] instanceof CacheStorage) {
  178.                 $translator->setCache($options['cache']);
  179.             } else {
  180.                 $translator->setCache(Cache\StorageFactory::factory($options['cache']));
  181.             }
  182.         }
  183.  
  184.         return $translator;
  185.     }
  186.  
  187.     /**
  188.      * Set the default locale.
  189.      *
  190.      * @param  string $locale
  191.      * @return Translator
  192.      */
  193.     public function setLocale($locale)
  194.     {
  195.         $this->locale = $locale;
  196.         return $this;
  197.     }
  198.  
  199.     /**
  200.      * Get the default locale.
  201.      *
  202.      * @return string
  203.      */
  204.     public function getLocale()
  205.     {
  206.         if ($this->locale === null) {
  207.             $this->locale = Locale::getDefault();
  208.         }
  209.  
  210.         return $this->locale;
  211.     }
  212.  
  213.     /**
  214.      * Set the fallback locale.
  215.      *
  216.      * @param  string $locale
  217.      * @return Translator
  218.      */
  219.     public function setFallbackLocale($locale)
  220.     {
  221.         $this->locale = $locale;
  222.         return $this;
  223.     }
  224.  
  225.     /**
  226.      * Get the fallback locale.
  227.      *
  228.      * @return string
  229.      */
  230.     public function getFallbackLocale()
  231.     {
  232.         if ($this->locale === null) {
  233.             $this->locale = Locale::getDefault();
  234.         }
  235.  
  236.         return $this->locale;
  237.     }
  238.  
  239.     /**
  240.      * Sets a cache
  241.      *
  242.      * @param  CacheStorage $cache
  243.      * @return Translator
  244.      */
  245.     public function setCache(CacheStorage $cache = null)
  246.     {
  247.         $this->cache = $cache;
  248.         return $this;
  249.     }
  250.  
  251.     /**
  252.      * Returns the set cache
  253.      *
  254.      * @return CacheStorage The set cache
  255.      */
  256.     public function getCache()
  257.     {
  258.         return $this->cache;
  259.     }
  260.  
  261.     /**
  262.      * Set the plugin manager for translation loaders
  263.      *
  264.      * @param  LoaderPluginManager $pluginManager
  265.      * @return Translator
  266.      */
  267.     public function setPluginManager(LoaderPluginManager $pluginManager)
  268.     {
  269.         $this->pluginManager = $pluginManager;
  270.         return $this;
  271.     }
  272.  
  273.     /**
  274.      * Retreive the plugin manager for tranlation loaders.
  275.      *
  276.      * Lazy loads an instance if none currently set.
  277.      *
  278.      * @return LoaderPluginManager
  279.      */
  280.     public function getPluginManager()
  281.     {
  282.         if (!$this->pluginManager instanceof LoaderPluginManager) {
  283.             $this->setPluginManager(new LoaderPluginManager());
  284.         }
  285.  
  286.         return $this->pluginManager;
  287.     }
  288.  
  289.     /**
  290.      * Translate a message.
  291.      *
  292.      * @param  string $message
  293.      * @param  string $textDomain
  294.      * @param  string $locale
  295.      * @return string
  296.      */
  297.     public function translate($message, $textDomain = 'default', $locale = null)
  298.     {
  299.         $locale      = ($locale ?: $this->getLocale());
  300.         $translation = $this->getTranslatedMessage($message, $locale, $textDomain);
  301.  
  302.         if ($translation !== null && $translation !== '') {
  303.             return $translation;
  304.         }
  305.        
  306.         if (null !== ($fallbackLocale = $this->getFallbackLocale())
  307.             && $locale !== $fallbackLocale
  308.         ) {
  309.             return $this->translate($message, $textDomain, $fallbackLocale);
  310.         }
  311.  
  312.         return $message;
  313.     }
  314.  
  315.     /**
  316.      * Translate a plural message.
  317.      *
  318.      * @param  string      $singular
  319.      * @param  string      $plural
  320.      * @param  int         $number
  321.      * @param  string      $textDomain
  322.      * @param  string|null $locale
  323.      * @return string
  324.      * @throws Exception\OutOfBoundsException
  325.      */
  326.     public function translatePlural(
  327.         $singular,
  328.         $plural,
  329.         $number,
  330.         $textDomain = 'default',
  331.         $locale = null
  332.     ) {
  333.         $locale      = $locale ?: $this->getLocale();
  334.         $translation = $this->getTranslatedMessage($singular, $locale, $textDomain);
  335.  
  336.         if ($translation === null || $translation === '') {
  337.             if (null !== ($fallbackLocale = $this->getFallbackLocale())
  338.                 && $locale !== $fallbackLocale
  339.             ) {
  340.                 return $this->translatePlural(
  341.                     $singular,
  342.                     $plural,
  343.                     $number,
  344.                     $textDomain,
  345.                     $fallbackLocale
  346.                 );
  347.             }
  348.  
  349.             return ($number != 1 ? $singular : $plural);
  350.         }
  351.  
  352.         $index = $this->messages[$textDomain][$locale]
  353.                       ->getPluralRule()
  354.                       ->evaluate($number);
  355.  
  356.         if (!isset($translation[$index])) {
  357.             throw new Exception\OutOfBoundsException(sprintf(
  358.                 'Provided index %d does not exist in plural array', $index
  359.             ));
  360.         }
  361.  
  362.         return $translation[$index];
  363.     }
  364.  
  365.     /**
  366.      * Get a translated message.
  367.      *
  368.      * @param  string $message
  369.      * @param  string $locale
  370.      * @param  string $textDomain
  371.      * @return string|null
  372.      */
  373.     protected function getTranslatedMessage(
  374.         $message,
  375.         $locale = null,
  376.         $textDomain = 'default'
  377.     ) {
  378.         if ($message === '') {
  379.             return '';
  380.         }
  381.  
  382.         if (!isset($this->messages[$textDomain][$locale])) {
  383.             $this->loadMessages($textDomain, $locale);
  384.         }
  385.         foreach($this->messages[$textDomain][$locale] as $item) {
  386.             if (isset($item[$message])) {
  387.                 return $item[$message];            
  388.             }
  389.         }        
  390.         return null;
  391.     }
  392.  
  393.     /**
  394.      * Add a translation file.
  395.      *
  396.      * @param  string $type
  397.      * @param  string $filename
  398.      * @param  string $textDomain
  399.      * @param  string $locale
  400.      * @return Translator
  401.      */
  402.     public function addTranslationFile(
  403.         $type,
  404.         $filename,
  405.         $textDomain = 'default',
  406.         $locale = null
  407.     ) {
  408.         $locale = $locale ?: '*';
  409.  
  410.         if (!isset($this->files[$textDomain])) {
  411.             $this->files[$textDomain] = array();
  412.         }
  413.  
  414.         $this->files[$textDomain][$locale] = array(
  415.             'type'     => $type,
  416.             'filename' => $filename,
  417.         );
  418.  
  419.         return $this;
  420.     }
  421.  
  422.     /**
  423.      * Add multiple translations with a pattern.
  424.      *
  425.      * @param  string $type
  426.      * @param  string $baseDir
  427.      * @param  string $pattern
  428.      * @param  string $textDomain
  429.      * @return Translator
  430.      */
  431.     public function addTranslationPattern(
  432.         $type,
  433.         $baseDir,
  434.         $pattern,
  435.         $textDomain = 'default'
  436.     ) {
  437.         if (!isset($this->patterns[$textDomain])) {
  438.             $this->patterns[$textDomain] = array();
  439.         }
  440.  
  441.         $this->patterns[$textDomain][] = array(
  442.             'type'    => $type,
  443.             'baseDir' => rtrim($baseDir, '/'),
  444.             'pattern' => $pattern,
  445.         );
  446.  
  447.         return $this;
  448.     }
  449.  
  450.     /**
  451.      * Load messages for a given language and domain.
  452.      *
  453.      * @param  string $textDomain
  454.      * @param  string $locale
  455.      * @return void
  456.      */
  457.     protected function loadMessages($textDomain, $locale)
  458.     {
  459.         if (!isset($this->messages[$textDomain])) {
  460.             $this->messages[$textDomain] = array();
  461.         }
  462.  
  463.         if (null !== ($cache = $this->getCache())) {
  464.             $cacheId = 'Zend_I18n_Translator_Messages_' . md5($textDomain . $locale);
  465.  
  466.             if (false !== ($result = $cache->getItem($cacheId))) {
  467.                 $this->messages[$textDomain][$locale] = $result;
  468.                 return;
  469.             }
  470.         }
  471.  
  472.         // Try to load from pattern
  473.         if (isset($this->patterns[$textDomain])) {
  474.             foreach ($this->patterns[$textDomain] as $pattern) {
  475.                 $filename = $pattern['baseDir']
  476.                           . '/' . sprintf($pattern['pattern'], $locale);
  477.                 if (is_file($filename)) {
  478.                     $this->messages[$textDomain][$locale][] = $this->getPluginManager()
  479.                          ->get($pattern['type'])
  480.                          ->load($filename, $locale);
  481.                 }
  482.             }
  483.         }
  484.         // Load concrete files, may override those loaded from patterns
  485.         foreach (array($locale, '*') as $currentLocale) {
  486.             if (!isset($this->files[$textDomain][$currentLocale])) {
  487.                 continue;
  488.             }
  489.  
  490.             $file = $this->files[$textDomain][$currentLocale];
  491.             $this->messages[$textDomain][$locale] = $this->getPluginManager()
  492.                  ->get($file['type'])
  493.                  ->load($file['filename'], $locale);
  494.  
  495.             unset($this->files[$textDomain][$currentLocale]);
  496.         }
  497.  
  498.         // Cache the loaded text domain
  499.         if ($cache !== null) {
  500.             $cache->setItem($cacheId, $this->messages[$textDomain][$locale]);
  501.         }
  502.     }
  503. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement