diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d4ba52..e795683 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/). +## [Unreleased] + +## [1.8.1] - 2020-07-04 +### Fixed + - Fix `onlyCategories` option not applied correctly #135 (bjornhij) + +## [1.8.0] - 2019-08-25 + +### Added + - Add `onlyCategories` option to Module settings #128 + +### Changed + - Improve TranslateBehavior saving #99 + The attribute is now saved as translation when the language of the application is different from the source language. + ## [1.7.3] - 2018-04-04 ### Fixed - Fix invalid column name in findOne() condition #118 @@ -80,11 +95,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - Import/export feature #31 ### Changed - - Autocofus translation textarea in frontend translation dialog #33 + - Autofocus translation textarea in frontend translation dialog #33 ### Fixed - Round error in translation statistic +[Unreleased]: https://github.com/lajax/yii2-translate-manager/compare/1.8.1...HEAD +[1.8.1]: https://github.com/lajax/yii2-translate-manager/compare/1.8.0...1.8.1 +[1.8.0]: https://github.com/lajax/yii2-translate-manager/compare/1.7.3...1.8.0 [1.7.3]: https://github.com/lajax/yii2-translate-manager/compare/1.7.2...1.7.3 [1.7.2]: https://github.com/lajax/yii2-translate-manager/compare/1.7.1...1.7.2 [1.7.1]: https://github.com/lajax/yii2-translate-manager/compare/1.7.0...1.7.1 diff --git a/Module.php b/Module.php index 01dd20e..2138b49 100644 --- a/Module.php +++ b/Module.php @@ -3,6 +3,7 @@ namespace lajax\translatemanager; use Yii; +use yii\base\InvalidConfigException; use yii\web\ForbiddenHttpException; use yii\web\Response; @@ -121,10 +122,15 @@ class Module extends \yii\base\Module public $roles = []; /** - * @var array list of the categories being ignored. + * @var string[] List of the categories being ignored. */ public $ignoredCategories = []; + /** + * @var string[] List of the categories to be scanned. If empty, all categories will be scanned. + */ + public $onlyCategories = []; + /** * @var array directories/files being ignored. */ @@ -286,6 +292,18 @@ class Module extends \yii\base\Module */ public $scanners = []; + /** + * @throws InvalidConfigException + */ + public function init() + { + parent::init(); + + if ($this->onlyCategories && $this->ignoredCategories) { + throw new InvalidConfigException("Please configure either 'ignoredCategories', or 'onlyCategories'!"); + } + } + /** * @inheritdoc */ diff --git a/README.md b/README.md index e68283a..c889899 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,7 @@ A more complex example including database table with multilingual support is bel 'jsTranslators' => ['lajax.t'], // list of the js function for translating messages. 'patterns' => ['*.js', '*.php'],// list of file extensions that contain language elements. 'ignoredCategories' => ['yii'], // these categories won't be included in the language database. + 'onlyCategories' => ['yii'], // only these categories will be included in the language database (cannot be used together with "ignoredCategories"). 'ignoredItems' => ['config'], // these files will not be processed. 'scanTimeLimit' => null, // increase to prevent "Maximum execution time" errors, if null the default max_execution_time will be used 'searchEmptyCommand' => '!', // the search string to enter in the 'Translation' search field to find not yet translated items, set to null to disable this feature @@ -420,7 +421,17 @@ class Category extends \yii\db\ActiveRecord { * With behavior (since 1.5.3): - **Note:** This will replace the model's original attribute values! + This behavior does the following: + - Replaces the specified attributes with translations after the model is loaded. + - Saves the attribute values as: + 1. Source messages, if the current language is the source language. + 2. Translations, if the current language is different from the source language. + This way the value stored in database is not overwritten with the translation. + + **Note**: If the model should be saved as translation, but the source message does not exist yet in the database + then the message is saved as the source message whether the current language is the source language or not. + To avoid this scan the database for existing messages when using the behavior first, and only save new records + when the current language is the source language. ```php namespace common\models; diff --git a/behaviors/TranslateBehavior.php b/behaviors/TranslateBehavior.php index 0e298a7..7b21310 100644 --- a/behaviors/TranslateBehavior.php +++ b/behaviors/TranslateBehavior.php @@ -6,9 +6,23 @@ use yii\db\BaseActiveRecord; use yii\behaviors\AttributeBehavior; use lajax\translatemanager\helpers\Language; +use lajax\translatemanager\models\LanguageSource; +use lajax\translatemanager\models\LanguageTranslate; /** - * TranslateManager Database translate behavior. + * Behavior that translates the model attributes, and saves the changes into database. + * + * This behavior does the following: + * - Replaces the specified attributes with translations after the model is loaded. + * - Saves the attribute values as: + * 1. Source messages, if the current language is the source language. + * 2. Translations, if the current language is different from the source language. + * This way the value stored in database is not overwritten with the translation. + * + * **Note**: If the model should be saved as translation, but the source message does not exist yet in the database + * then the message is saved as the source message whether the current language is the source language or not. + * To avoid this scan the database for existing messages when using the behavior first, and only save new records + * when the current language is the source language. * * Installation: * @@ -45,6 +59,11 @@ class TranslateBehavior extends AttributeBehavior */ public $category = 'database'; + /** + * @var BaseActiveRecord the owner model of this behavior + */ + public $owner; + /** * @inheritdoc */ @@ -68,31 +87,88 @@ public function events() } /** - * Translates a message to the specified language. + * Translates the attributes to the current language. * * @param \yii\base\Event $event */ public function translateAttributes($event) { - /* @var $owner BaseActiveRecord */ - $owner = $this->owner; foreach ($this->translateAttributes as $attribute) { - $owner->{$attribute} = Yii::t($this->category, $owner->attributes[$attribute]); + $this->owner->{$attribute} = Yii::t($this->category, $this->owner->attributes[$attribute]); } } /** - * Saveing new language element by category. + * Saves new language element by category. * * @param \yii\base\Event $event */ public function saveAttributes($event) { - /* @var $owner BaseActiveRecord */ - $owner = $this->owner; + $isAppInSourceLanguage = Yii::$app->sourceLanguage === Yii::$app->language; + foreach ($this->translateAttributes as $attribute) { - if ($owner->isAttributeChanged($attribute)) { - Language::saveMessage($owner->attributes[$attribute], $this->category); + if (!$this->owner->isAttributeChanged($attribute)) { + continue; + } + + if ($isAppInSourceLanguage || !$this->saveAttributeValueAsTranslation($attribute)) { + Language::saveMessage($this->owner->attributes[$attribute], $this->category); + } + } + } + + /** + * @param string $attribute The name of the attribute. + * + * @return bool Whether the translation is saved. + */ + private function saveAttributeValueAsTranslation($attribute) + { + $sourceMessage = $this->owner->getOldAttribute($attribute); + $translatedMessage = $this->owner->attributes[$attribute]; + + // Restore the original value, so it won't be replaced with the translation in the database. + $this->owner->{$attribute} = $sourceMessage; + + $translateSource = $this->findSourceMessage($sourceMessage); + if (!$translateSource) { + return false; // The source does not exist, the message cannot be saved as translation. + } + + $translation = new LanguageTranslate(); + foreach ($translateSource->languageTranslates as $tmpTranslate) { + if ($tmpTranslate->language === Yii::$app->language) { + $translation = $tmpTranslate; + break; + } + } + + if ($translation->isNewRecord) { + $translation->id = $translateSource->id; + $translation->language = Yii::$app->language; + } + + $translation->translation = $translatedMessage; + $translation->save(); + + return true; + } + + /** + * Finds the source record with case sensitive match. + * + * @param string $message + * + * @return LanguageSource|null Null if the source is not found. + */ + private function findSourceMessage($message) + { + $sourceMessages = LanguageSource::findAll(['message' => $message, 'category' => $this->category]); + + foreach ($sourceMessages as $source) { + if ($source->message === $message) { + return $source; } } } diff --git a/composer.json b/composer.json index 94a0684..a37bf68 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "lajax/yii2-translate-manager", - "description": "Online Translate", + "description": "Translation management extension for Yii 2", "type": "yii2-extension", "keywords": ["yii2", "extension", "translate", "language", "module"], "license": "MIT", diff --git a/services/scanners/ScannerFile.php b/services/scanners/ScannerFile.php index 83e4867..083657e 100644 --- a/services/scanners/ScannerFile.php +++ b/services/scanners/ScannerFile.php @@ -269,6 +269,14 @@ private function _getRoots() */ protected function isValidCategory($category) { - return !in_array($category, $this->module->ignoredCategories); + if ($this->module->onlyCategories) { + return in_array($category, $this->module->onlyCategories); + } + + if ($this->module->ignoredCategories) { + return !in_array($category, $this->module->ignoredCategories); + } + + return true; } }