diff --git a/framework/i18n/I18N.php b/framework/i18n/I18N.php index 2cf118f..fe09a84 100644 --- a/framework/i18n/I18N.php +++ b/framework/i18n/I18N.php @@ -20,7 +20,7 @@ class I18N extends Component $category = 'app'; } - $message = $this->getSource($category)->translate($category, $message, $language); + $message = $this->getMessageSource($category)->translate($category, $message, $language); if (!is_array($params)) { $params = array($params); @@ -42,7 +42,7 @@ class I18N extends Component } - public function getSource($category) + public function getMessageSource($category) { return $category === 'yii' ? $this->getMessages() : $this->getCoreMessages(); } diff --git a/framework/i18n/MessageSource.php b/framework/i18n/MessageSource.php index 17a5b00..4f6bf94 100644 --- a/framework/i18n/MessageSource.php +++ b/framework/i18n/MessageSource.php @@ -15,29 +15,36 @@ use yii\base\Component; /** * MessageSource is the base class for message translation repository classes. * - * A message source is an application component that provides message internationalization (i18n). - * It stores messages translated in different languages and provides - * these translated versions when requested. + * A message source stores message translations in some persistent storage. * - * A concrete class must implement {@link loadMessages} or override {@link translateMessage}. - * - * @property string $language The language that the source messages are written in. - * Defaults to {@link CApplication::language application language}. + * Child classes should override [[loadMessages()]] to provide translated messages. * * @author Qiang Xue * @since 2.0 */ -abstract class MessageSource extends Component +class MessageSource extends Component { /** + * @event MissingTranslationEvent an event that is triggered when a message translation is not found. + */ + const EVENT_MISSING_TRANSLATION = 'missingTranslation'; + + /** * @var boolean whether to force message translation when the source and target languages are the same. * Defaults to false, meaning translation is only performed when source and target languages are different. */ public $forceTranslation = false; + /** + * @var string the language that the original messages are in. If not set, it will use the value of + * [[\yii\base\Application::sourceLanguage]]. + */ public $sourceLanguage; private $_messages = array(); + /** + * Initializes this component. + */ public function init() { parent::init(); @@ -48,26 +55,30 @@ abstract class MessageSource extends Component /** * Loads the message translation for the specified language and category. + * Child classes should override this method to return the message translations of + * the specified language and category. * @param string $category the message category * @param string $language the target language - * @return array the loaded messages + * @return array the loaded messages. The keys are original messages, and the values + * are translated messages. */ - abstract protected function loadMessages($category, $language); + protected function loadMessages($category, $language) + { + return array(); + } /** * Translates a message to the specified language. * - * Note, if the specified language is the same as - * the {@link getLanguage source message language}, messages will NOT be translated. + * Note that unless [[forceTranslation]] is true, if the target language + * is the same as the [[sourceLanguage|source language]], the message + * will NOT be translated. * - * If the message is not found in the translations, an {@link onMissingTranslation} - * event will be raised. Handlers can mark this message or do some - * default handling. The {@link CMissingTranslationEvent::message} - * property of the event parameter will be returned. + * If a translation is not found, a [[missingTranslation]] event will be triggered. * * @param string $category the message category * @param string $message the message to be translated - * @param string $language the target language. If null (default), the {@link CApplication::getLanguage application language} will be used. + * @param string $language the target language * @return string the translated message (or the original message if translation is not needed) */ public function translate($category, $message, $language) @@ -81,8 +92,8 @@ abstract class MessageSource extends Component /** * Translates the specified message. - * If the message is not found, an {@link onMissingTranslation} - * event will be raised. + * If the message is not found, a [[missingTranslation]] event will be triggered + * and the original message will be returned. * @param string $category the category that the message belongs to * @param string $message the message to be translated * @param string $language the target language @@ -96,63 +107,17 @@ abstract class MessageSource extends Component } if (isset($this->_messages[$key][$message]) && $this->_messages[$key][$message] !== '') { return $this->_messages[$key][$message]; - } elseif ($this->hasEventHandler('onMissingTranslation')) { - $event = new CMissingTranslationEvent($this, $category, $message, $language); - $this->onMissingTranslation($event); - return $event->message; + } elseif ($this->hasEventHandlers('missingTranslation')) { + $event = new MissingTranslationEvent(array( + 'category' => $category, + 'message' => $message, + 'language' => $language, + )); + $this->trigger(self::EVENT_MISSING_TRANSLATION, $event); + return $this->_messages[$key] = $event->message; } else { return $message; } } - - /** - * Raised when a message cannot be translated. - * Handlers may log this message or do some default handling. - * The {@link CMissingTranslationEvent::message} property - * will be returned by {@link translateMessage}. - * @param CMissingTranslationEvent $event the event parameter - */ - public function onMissingTranslation($event) - { - $this->raiseEvent('onMissingTranslation', $event); - } } - -/** - * CMissingTranslationEvent represents the parameter for the {@link MessageSource::onMissingTranslation onMissingTranslation} event. - * - * @author Qiang Xue - * @package system.i18n - * @since 1.0 - */ -class CMissingTranslationEvent extends CEvent -{ - /** - * @var string the message to be translated - */ - public $message; - /** - * @var string the category that the message belongs to - */ - public $category; - /** - * @var string the ID of the language that the message is to be translated to - */ - public $language; - - /** - * Constructor. - * @param mixed $sender sender of this event - * @param string $category the category that the message belongs to - * @param string $message the message to be translated - * @param string $language the ID of the language that the message is to be translated to - */ - public function __construct($sender, $category, $message, $language) - { - parent::__construct($sender); - $this->message = $message; - $this->category = $category; - $this->language = $language; - } -} diff --git a/framework/i18n/MissingTranslationEvent.php b/framework/i18n/MissingTranslationEvent.php new file mode 100644 index 0000000..723b68c --- /dev/null +++ b/framework/i18n/MissingTranslationEvent.php @@ -0,0 +1,35 @@ + + * @since 2.0 + */ +class MissingTranslationEvent extends Event +{ + /** + * @var string the message to be translated. An event handler may overwrite this property + * with a translated version if possible. + */ + public $message; + /** + * @var string the category that the message belongs to + */ + public $category; + /** + * @var string the language ID (e.g. en_US) that the message is to be translated to + */ + public $language; +} diff --git a/framework/i18n/PhpMessageSource.php b/framework/i18n/PhpMessageSource.php index 4f8511f..2fea3b7 100644 --- a/framework/i18n/PhpMessageSource.php +++ b/framework/i18n/PhpMessageSource.php @@ -9,121 +9,35 @@ namespace yii\i18n; +use Yii; + /** * PhpMessageSource represents a message source that stores translated messages in PHP scripts. * - * PhpMessageSource uses PHP files and arrays to keep message translations. - * - * When {@link cachingDuration} is set as a positive number, message translations will be cached. - * - * Messages for an extension class (e.g. a widget, a module) can be specially managed and used. - * In particular, if a message belongs to an extension whose class name is Xyz, then the message category - * can be specified in the format of 'Xyz.categoryName'. And the corresponding message file - * is assumed to be 'BasePath/messages/LanguageID/categoryName.php', where 'BasePath' refers to - * the directory that contains the extension class file. When using Yii::t() to translate an extension message, - * the category name should be set as 'Xyz.categoryName'. + * ~~~ * * @author Qiang Xue * @since 2.0 */ class PhpMessageSource extends MessageSource { - const CACHE_KEY_PREFIX='Yii.CPhpMessageSource.'; - - /** - * @var integer the time in seconds that the messages can remain valid in cache. - * Defaults to 0, meaning the caching is disabled. - */ - public $cachingDuration=0; - /** - * @var string the ID of the cache application component that is used to cache the messages. - * Defaults to 'cache' which refers to the primary cache application component. - * Set this property to false if you want to disable caching the messages. - */ - public $cacheID='cache'; /** * @var string the base path for all translated messages. Defaults to null, meaning * the "messages" subdirectory of the application directory (e.g. "protected/messages"). */ - public $basePath; - /** - * @var array the message paths for extensions that do not have a base class to use as category prefix. - * The format of the array should be: - *
-	 * array(
-	 *     'ExtensionName' => 'ext.ExtensionName.messages',
-	 * )
-	 * 
- * Where the key is the name of the extension and the value is the alias to the path - * of the "messages" subdirectory of the extension. - * When using Yii::t() to translate an extension message, the category name should be - * set as 'ExtensionName.categoryName'. - * Defaults to an empty array, meaning no extensions registered. - * @since 1.1.13 - */ - public $extensionPaths=array(); - - private $_files=array(); - - /** - * Initializes the application component. - * This method overrides the parent implementation by preprocessing - * the user request data. - */ - public function init() - { - parent::init(); - if($this->basePath===null) - $this->basePath=Yii::getPathOfAlias('application.messages'); - } - - /** - * Determines the message file name based on the given category and language. - * If the category name contains a dot, it will be split into the module class name and the category name. - * In this case, the message file will be assumed to be located within the 'messages' subdirectory of - * the directory containing the module class file. - * Otherwise, the message file is assumed to be under the {@link basePath}. - * @param string $category category name - * @param string $language language ID - * @return string the message file path - */ - protected function getMessageFile($category,$language) - { - if(!isset($this->_files[$category][$language])) - { - if(($pos=strpos($category,'.'))!==false) - { - $extensionClass=substr($category,0,$pos); - $extensionCategory=substr($category,$pos+1); - // First check if there's an extension registered for this class. - if(isset($this->extensionPaths[$extensionClass])) - $this->_files[$category][$language]=Yii::getPathOfAlias($this->extensionPaths[$extensionClass]).DIRECTORY_SEPARATOR.$language.DIRECTORY_SEPARATOR.$extensionCategory.'.php'; - else - { - // No extension registered, need to find it. - $class=new ReflectionClass($extensionClass); - $this->_files[$category][$language]=dirname($class->getFileName()).DIRECTORY_SEPARATOR.'messages'.DIRECTORY_SEPARATOR.$language.DIRECTORY_SEPARATOR.$extensionCategory.'.php'; - } - } - else - $this->_files[$category][$language]=$this->basePath.DIRECTORY_SEPARATOR.$language.DIRECTORY_SEPARATOR.$category.'.php'; - } - return $this->_files[$category][$language]; - } + public $basePath = '@app/messages'; /** * Loads the message translation for the specified language and category. @@ -131,30 +45,18 @@ class PhpMessageSource extends MessageSource * @param string $language the target language * @return array the loaded messages */ - protected function loadMessages($category,$language) + protected function loadMessages($category, $language) { - $messageFile=$this->getMessageFile($category,$language); - - if($this->cachingDuration>0 && $this->cacheID!==false && ($cache=Yii::app()->getComponent($this->cacheID))!==null) - { - $key=self::CACHE_KEY_PREFIX . $messageFile; - if(($data=$cache->get($key))!==false) - return unserialize($data); - } - - if(is_file($messageFile)) - { - $messages=include($messageFile); - if(!is_array($messages)) - $messages=array(); - if(isset($cache)) - { - $dependency=new CFileCacheDependency($messageFile); - $cache->set($key,serialize($messages),$this->cachingDuration,$dependency); + $messageFile = Yii::getAlias($this->basePath) . "/$language/$category.php"; + if (is_file($messageFile)) { + $messages = include($messageFile); + if (!is_array($messages)) { + $messages = array(); } return $messages; - } - else + } else { + Yii::error("Message file not found: $messageFile", __CLASS__); return array(); + } } } \ No newline at end of file