├── messages └── ru-RU │ └── zelenin │ └── modules │ └── i18n.php ├── LICENSE ├── composer.json ├── Bootstrap.php ├── Module.php ├── views └── default │ ├── update.php │ └── index.php ├── models ├── Message.php ├── search │ └── SourceMessageSearch.php ├── query │ └── SourceMessageQuery.php └── SourceMessage.php ├── migrations └── m140609_093837_addI18nTables.php ├── components └── I18N.php ├── controllers └── DefaultController.php ├── readme.md └── console └── controllers └── I18nController.php /messages/ru-RU/zelenin/modules/i18n.php: -------------------------------------------------------------------------------- 1 | 'Категория', 5 | 'ID' => 'ID', 6 | 'Language' => 'Язык', 7 | 'Message' => 'Сообщение', 8 | 'Not translated' => 'Не переведено', 9 | 'Source message' => 'Исходное сообщение', 10 | 'Translated' => 'Переведено', 11 | 'Translation' => 'Перевод', 12 | 'Translation status' => 'Статус перевода', 13 | 'Translations' => 'Переводы', 14 | 'Update' => 'Обновить', 15 | 'Updated' => 'Обновлено', 16 | ]; 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Aleksandr Zelenin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zelenin/yii2-i18n-module", 3 | "description": "Yii2 i18n (internalization) module makes the translation of your application so simple", 4 | "version": "0.1.9", 5 | "type": "yii2-extension", 6 | "keywords": [ 7 | "yii2", 8 | "i18n", 9 | "translation", 10 | "translations" 11 | ], 12 | "homepage": "https://github.com/zelenin/yii2-i18n-module", 13 | "time": "2015-03-07", 14 | "license": "MIT", 15 | "authors": [ 16 | { 17 | "name": "Aleksandr Zelenin", 18 | "email": "aleksandr@zelenin.me", 19 | "homepage": "http://zelenin.me", 20 | "role": "Developer" 21 | } 22 | ], 23 | "support": { 24 | "issues": "https://github.com/zelenin/yii2-i18n-module/issues", 25 | "source": "https://github.com/zelenin/yii2-i18n-module" 26 | }, 27 | "require": { 28 | "yiisoft/yii2": "~2", 29 | "zelenin/yii2-semantic-ui": "*" 30 | }, 31 | "autoload": { 32 | "psr-4": { 33 | "Zelenin\\yii\\modules\\I18n\\": "" 34 | } 35 | }, 36 | "extra": { 37 | "bootstrap": "Zelenin\\yii\\modules\\I18n\\Bootstrap" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Bootstrap.php: -------------------------------------------------------------------------------- 1 | getModule('i18n')) { 18 | $moduleId = $i18nModule->id; 19 | $app->getUrlManager()->addRules([ 20 | 'translations/' => $moduleId . '/default/update', 21 | 'translations/page/' => $moduleId . '/default/index', 22 | 'translations' => $moduleId . '/default/index', 23 | ], false); 24 | 25 | Yii::$container->set(Pagination::className(), [ 26 | 'pageSizeLimit' => [1, 100], 27 | 'defaultPageSize' => $i18nModule->pageSize 28 | ]); 29 | } 30 | if ($app instanceof \yii\console\Application) { 31 | if (!isset($app->controllerMap['i18n'])) { 32 | $app->controllerMap['i18n'] = I18nController::className(); 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Module.php: -------------------------------------------------------------------------------- 1 | getDb()->getDriverName(); 24 | $caseInsensitivePrefix = $driver === 'mysql' ? 'binary' : ''; 25 | $sourceMessage = SourceMessage::find() 26 | ->where('category = :category and message = ' . $caseInsensitivePrefix . ' :message', [ 27 | ':category' => $event->category, 28 | ':message' => $event->message 29 | ]) 30 | ->with('messages') 31 | ->one(); 32 | 33 | if (!$sourceMessage) { 34 | $sourceMessage = new SourceMessage; 35 | $sourceMessage->setAttributes([ 36 | 'category' => $event->category, 37 | 'message' => $event->message 38 | ], false); 39 | $sourceMessage->save(false); 40 | } 41 | $sourceMessage->initMessages(); 42 | $sourceMessage->saveMessages(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /views/default/update.php: -------------------------------------------------------------------------------- 1 | title = Module::t('Update') . ': ' . $model->message; 16 | echo Breadcrumb::widget(['links' => [ 17 | ['label' => Module::t('Translations'), 'url' => ['index']], 18 | ['label' => $this->title] 19 | ]]); 20 | ?> 21 |
22 |
23 | 'top attached']) ?> 24 | message), ['class' => 'bottom attached']) ?> 25 | 26 |
27 |
28 | messages as $language => $message) : ?> 29 |
30 | field($model->messages[$language], '[' . $language . ']translation')->label($language) ?> 31 |
32 | 33 |
34 |
35 | 'ui primary button']) ?> 36 | 37 |
38 |
39 | -------------------------------------------------------------------------------- /models/Message.php: -------------------------------------------------------------------------------- 1 | get(Yii::$app->getI18n()->db); 18 | } 19 | 20 | /** 21 | * @return string 22 | * @throws InvalidConfigException 23 | */ 24 | public static function tableName() 25 | { 26 | $i18n = Yii::$app->getI18n(); 27 | if (!isset($i18n->messageTable)) { 28 | throw new InvalidConfigException('You should configure i18n component'); 29 | } 30 | return $i18n->messageTable; 31 | } 32 | 33 | /** 34 | * @inheritdoc 35 | */ 36 | public function rules() 37 | { 38 | return [ 39 | ['language', 'required'], 40 | ['language', 'string', 'max' => 16], 41 | ['translation', 'string'] 42 | ]; 43 | } 44 | 45 | /** 46 | * @inheritdoc 47 | */ 48 | public function attributeLabels() 49 | { 50 | return [ 51 | 'id' => Module::t('ID'), 52 | 'language' => Module::t('Language'), 53 | 'translation' => Module::t('Translation') 54 | ]; 55 | } 56 | 57 | public function getSourceMessage() 58 | { 59 | return $this->hasOne(SourceMessage::className(), ['id' => 'id']); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /migrations/m140609_093837_addI18nTables.php: -------------------------------------------------------------------------------- 1 | getI18n(); 16 | if (!isset($i18n->sourceMessageTable) || !isset($i18n->messageTable)) { 17 | throw new InvalidConfigException('You should configure i18n component'); 18 | } 19 | $sourceMessageTable = $i18n->sourceMessageTable; 20 | $messageTable = $i18n->messageTable; 21 | 22 | $this->createTable($sourceMessageTable, [ 23 | 'id' => Schema::TYPE_PK, 24 | 'category' => Schema::TYPE_STRING, 25 | 'message' => Schema::TYPE_TEXT 26 | ]); 27 | 28 | $this->createTable($messageTable, [ 29 | 'id' => Schema::TYPE_INTEGER, 30 | 'language' => Schema::TYPE_STRING, 31 | 'translation' => Schema::TYPE_TEXT 32 | ]); 33 | $this->addPrimaryKey('id', $messageTable, ['id', 'language']); 34 | $this->addForeignKey('fk_source_message_message', $messageTable, 'id', $sourceMessageTable, 'id', 'cascade', 'restrict'); 35 | } 36 | 37 | public function safeDown() 38 | { 39 | $i18n = Yii::$app->getI18n(); 40 | if (!isset($i18n->sourceMessageTable) || !isset($i18n->messageTable)) { 41 | throw new InvalidConfigException('You should configure i18n component'); 42 | } 43 | 44 | $this->dropTable($i18n->sourceMessageTable); 45 | $this->dropTable($i18n->messageTable); 46 | 47 | return true; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /components/I18N.php: -------------------------------------------------------------------------------- 1 | languages) { 27 | throw new InvalidConfigException('You should configure i18n component [language]'); 28 | } 29 | 30 | if (!isset($this->translations['*'])) { 31 | $this->translations['*'] = [ 32 | 'class' => DbMessageSource::className(), 33 | 'db' => $this->db, 34 | 'sourceMessageTable' => $this->sourceMessageTable, 35 | 'messageTable' => $this->messageTable, 36 | 'on missingTranslation' => $this->missingTranslationHandler 37 | ]; 38 | } 39 | if (!isset($this->translations['app']) && !isset($this->translations['app*'])) { 40 | $this->translations['app'] = [ 41 | 'class' => DbMessageSource::className(), 42 | 'db' => $this->db, 43 | 'sourceMessageTable' => $this->sourceMessageTable, 44 | 'messageTable' => $this->messageTable, 45 | 'on missingTranslation' => $this->missingTranslationHandler 46 | ]; 47 | } 48 | parent::init(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /models/search/SourceMessageSearch.php: -------------------------------------------------------------------------------- 1 | $query]); 38 | 39 | if (!($this->load($params) && $this->validate())) { 40 | return $dataProvider; 41 | } 42 | 43 | if ($this->status == static::STATUS_TRANSLATED) { 44 | $query->translated(); 45 | } 46 | if ($this->status == static::STATUS_NOT_TRANSLATED) { 47 | $query->notTranslated(); 48 | } 49 | 50 | $query 51 | ->andFilterWhere(['like', 'category', $this->category]) 52 | ->andFilterWhere(['like', 'message', $this->message]); 53 | return $dataProvider; 54 | } 55 | 56 | public static function getStatus($id = null) 57 | { 58 | $statuses = [ 59 | self::STATUS_TRANSLATED => Module::t('Translated'), 60 | self::STATUS_NOT_TRANSLATED => Module::t('Not translated'), 61 | ]; 62 | if ($id !== null) { 63 | return ArrayHelper::getValue($statuses, $id, null); 64 | } 65 | return $statuses; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /models/query/SourceMessageQuery.php: -------------------------------------------------------------------------------- 1 | select($messageTableName . '.id'); 15 | $i = 0; 16 | foreach (Yii::$app->getI18n()->languages as $language) { 17 | if ($i === 0) { 18 | $query->andWhere($messageTableName . '.language = :language and ' . $messageTableName . '.translation is not null', [':language' => $language]); 19 | } else { 20 | $query->innerJoin($messageTableName . ' t' . $i, 't' . $i . '.id = ' . $messageTableName . '.id and t' . $i . '.language = :language and t' . $i . '.translation is not null', [':language' => $language]); 21 | } 22 | $i++; 23 | } 24 | $ids = $query->indexBy('id')->all(); 25 | $this->andWhere(['not in', 'id', array_keys($ids)]); 26 | return $this; 27 | } 28 | 29 | public function translated() 30 | { 31 | $messageTableName = Message::tableName(); 32 | $query = Message::find()->select($messageTableName . '.id'); 33 | $i = 0; 34 | foreach (Yii::$app->getI18n()->languages as $language) { 35 | if ($i === 0) { 36 | $query->andWhere($messageTableName . '.language = :language and ' . $messageTableName . '.translation is not null', [':language' => $language]); 37 | } else { 38 | $query->innerJoin($messageTableName . ' t' . $i, 't' . $i . '.id = ' . $messageTableName . '.id and t' . $i . '.language = :language and t' . $i . '.translation is not null', [':language' => $language]); 39 | } 40 | $i++; 41 | } 42 | $ids = $query->indexBy('id')->all(); 43 | $this->andWhere(['in', 'id', array_keys($ids)]); 44 | return $this; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /controllers/DefaultController.php: -------------------------------------------------------------------------------- 1 | search(Yii::$app->getRequest()->get()); 20 | return $this->render('index', [ 21 | 'searchModel' => $searchModel, 22 | 'dataProvider' => $dataProvider 23 | ]); 24 | } 25 | 26 | /** 27 | * @param integer $id 28 | * @return string|Response 29 | */ 30 | public function actionUpdate($id) 31 | { 32 | /** @var SourceMessage $model */ 33 | $model = $this->findModel($id); 34 | $model->initMessages(); 35 | 36 | if (Model::loadMultiple($model->messages, Yii::$app->getRequest()->post()) && Model::validateMultiple($model->messages)) { 37 | $model->saveMessages(); 38 | Yii::$app->getSession()->setFlash('success', Module::t('Updated')); 39 | return $this->redirect(['update', 'id' => $model->id]); 40 | } else { 41 | return $this->render('update', ['model' => $model]); 42 | } 43 | } 44 | 45 | /** 46 | * @param array|integer $id 47 | * @return SourceMessage|SourceMessage[] 48 | * @throws NotFoundHttpException 49 | */ 50 | protected function findModel($id) 51 | { 52 | $query = SourceMessage::find()->where('id = :id', [':id' => $id]); 53 | $models = is_array($id) 54 | ? $query->all() 55 | : $query->one(); 56 | if (!empty($models)) { 57 | return $models; 58 | } else { 59 | throw new NotFoundHttpException(Module::t('The requested page does not exist')); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /views/default/index.php: -------------------------------------------------------------------------------- 1 | title = Module::t('Translations'); 20 | echo Breadcrumbs::widget(['links' => [ 21 | $this->title 22 | ]]); 23 | ?> 24 |
25 |

title) ?>

26 | $searchModel, 30 | 'dataProvider' => $dataProvider, 31 | 'columns' => [ 32 | [ 33 | 'attribute' => 'id', 34 | 'value' => function ($model, $index, $dataColumn) { 35 | return $model->id; 36 | }, 37 | 'filter' => false 38 | ], 39 | [ 40 | 'attribute' => 'message', 41 | 'format' => 'raw', 42 | 'value' => function ($model, $index, $widget) { 43 | return Html::a($model->message, ['update', 'id' => $model->id], ['data' => ['pjax' => 0]]); 44 | } 45 | ], 46 | [ 47 | 'attribute' => 'category', 48 | 'value' => function ($model, $index, $dataColumn) { 49 | return $model->category; 50 | }, 51 | 'filter' => ArrayHelper::map($searchModel::getCategories(), 'category', 'category') 52 | ], 53 | [ 54 | 'attribute' => 'status', 55 | 'value' => function ($model, $index, $widget) { 56 | /** @var SourceMessage $model */ 57 | return $model->isTranslated() ? 'Translated' : 'Not translated'; 58 | }, 59 | 'filter' => $searchModel->getStatus() 60 | ] 61 | ] 62 | ]); 63 | Pjax::end(); 64 | ?> 65 |
66 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Yii2 I18N module 2 | 3 | [Yii2](http://www.yiiframework.com) i18n (internalization) module makes the translation of your application so simple 4 | 5 | ## Installation 6 | 7 | ### Composer 8 | 9 | The preferred way to install this extension is through [Composer](http://getcomposer.org/). 10 | 11 | Either run 12 | 13 | ``` 14 | php composer.phar require zelenin/yii2-i18n-module "dev-master" 15 | ``` 16 | 17 | or add 18 | 19 | ``` 20 | "zelenin/yii2-i18n-module": "dev-master" 21 | ``` 22 | 23 | to the require section of your ```composer.json``` 24 | 25 | ## Usage 26 | 27 | Configure I18N component in common config: 28 | 29 | ```php 30 | 'i18n' => [ 31 | 'class' => Zelenin\yii\modules\I18n\components\I18N::className(), 32 | 'languages' => ['ru-RU', 'de-DE', 'it-IT'] 33 | ], 34 | ``` 35 | 36 | Configure I18N component in backend config: 37 | 38 | ```php 39 | 'modules' => [ 40 | 'i18n' => Zelenin\yii\modules\I18n\Module::className() 41 | ], 42 | ``` 43 | 44 | Run: 45 | 46 | ``` 47 | php yii migrate --migrationPath=@Zelenin/yii/modules/I18n/migrations 48 | ``` 49 | 50 | Go to ```http://backend.yourdomain.com/translations``` for translating your messages 51 | 52 | ### PHP to DB import 53 | 54 | If you have an old project with PHP-based i18n you may migrate to DbSource via console. 55 | 56 | Run: 57 | 58 | ``` 59 | php yii i18n/import @common/messages 60 | ``` 61 | 62 | where ```@common/messages``` is path for app translations 63 | 64 | ### DB to PHP export 65 | 66 | Run: 67 | 68 | ``` 69 | php yii i18n/export @Zelenin/yii/modules/I18n/messages zelenin/modules/i18n 70 | ``` 71 | 72 | where ```@Zelenin/yii/modules/I18n/messages``` is path for app translations and ```zelenin/modules/i18n``` is translations category in DB 73 | 74 | ### Using ```yii``` category with DB 75 | 76 | Import translations from PHP files 77 | 78 | ``` 79 | php yii i18n/import @yii/messages 80 | ``` 81 | 82 | Configure I18N component: 83 | 84 | ```php 85 | 'i18n' => [ 86 | 'class'=> Zelenin\yii\modules\I18n\components\I18N::className(), 87 | 'languages' => ['ru-RU', 'de-DE', 'it-IT'], 88 | 'translations' => [ 89 | 'yii' => [ 90 | 'class' => yii\i18n\DbMessageSource::className() 91 | ] 92 | ] 93 | ], 94 | ``` 95 | 96 | ## Info 97 | 98 | Component uses yii\i18n\MissingTranslationEvent for auto-add of missing translations to database 99 | 100 | See [Yii2 i18n guide](https://github.com/yiisoft/yii2/blob/master/docs/guide/tutorial-i18n.md) 101 | 102 | ## Author 103 | 104 | [Aleksandr Zelenin](https://github.com/zelenin/), e-mail: [aleksandr@zelenin.me](mailto:aleksandr@zelenin.me) 105 | -------------------------------------------------------------------------------- /models/SourceMessage.php: -------------------------------------------------------------------------------- 1 | get(Yii::$app->getI18n()->db); 19 | } 20 | 21 | /** 22 | * @return string 23 | * @throws InvalidConfigException 24 | */ 25 | public static function tableName() 26 | { 27 | $i18n = Yii::$app->getI18n(); 28 | if (!isset($i18n->sourceMessageTable)) { 29 | throw new InvalidConfigException('You should configure i18n component'); 30 | } 31 | return $i18n->sourceMessageTable; 32 | } 33 | 34 | public static function find() 35 | { 36 | return new SourceMessageQuery(get_called_class()); 37 | } 38 | 39 | /** 40 | * @inheritdoc 41 | */ 42 | public function rules() 43 | { 44 | return [ 45 | ['message', 'string'] 46 | ]; 47 | } 48 | 49 | /** 50 | * @inheritdoc 51 | */ 52 | public function attributeLabels() 53 | { 54 | return [ 55 | 'id' => Module::t('ID'), 56 | 'category' => Module::t('Category'), 57 | 'message' => Module::t('Message'), 58 | 'status' => Module::t('Translation status') 59 | ]; 60 | } 61 | 62 | public function getMessages() 63 | { 64 | return $this->hasMany(Message::className(), ['id' => 'id'])->indexBy('language'); 65 | } 66 | 67 | /** 68 | * @return array|SourceMessage[] 69 | */ 70 | public static function getCategories() 71 | { 72 | return SourceMessage::find()->select('category')->distinct('category')->asArray()->all(); 73 | } 74 | 75 | public function initMessages() 76 | { 77 | $messages = []; 78 | foreach (Yii::$app->getI18n()->languages as $language) { 79 | if (!isset($this->messages[$language])) { 80 | $message = new Message; 81 | $message->language = $language; 82 | $messages[$language] = $message; 83 | } else { 84 | $messages[$language] = $this->messages[$language]; 85 | } 86 | } 87 | $this->populateRelation('messages', $messages); 88 | } 89 | 90 | public function saveMessages() 91 | { 92 | /** @var Message $message */ 93 | foreach ($this->messages as $message) { 94 | $this->link('messages', $message); 95 | $message->save(); 96 | } 97 | } 98 | 99 | public function isTranslated() 100 | { 101 | foreach ($this->messages as $message) { 102 | if (!$message->translation) { 103 | return false; 104 | } 105 | } 106 | return true; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /console/controllers/I18nController.php: -------------------------------------------------------------------------------- 1 | prompt('Enter a source path'); 23 | } 24 | $sourcePath = realpath(Yii::getAlias($sourcePath)); 25 | if (!is_dir($sourcePath)) { 26 | throw new Exception('The source path ' . $sourcePath . ' is not a valid directory.'); 27 | } 28 | 29 | $translationsFiles = FileHelper::findFiles($sourcePath, ['only' => ['*.php']]); 30 | 31 | foreach ($translationsFiles as $translationsFile) { 32 | $relativePath = trim(str_replace([$sourcePath, '.php'], '', $translationsFile), '/,\\'); 33 | $relativePath = FileHelper::normalizePath($relativePath, '/'); 34 | $relativePath = explode('/', $relativePath, 2); 35 | if (count($relativePath) > 1) { 36 | $language = $this->prompt('Enter language.', ['default' => $relativePath[0]]); 37 | $category = $this->prompt('Enter category.', ['default' => $relativePath[1]]); 38 | 39 | $translations = require_once $translationsFile; 40 | if (is_array($translations)) { 41 | foreach ($translations as $sourceMessage => $translation) { 42 | if (!empty($translation)) { 43 | $sourceMessage = $this->getSourceMessage($category, $sourceMessage); 44 | $this->setTranslation($sourceMessage, $language, $translation); 45 | } 46 | } 47 | } 48 | } 49 | } 50 | echo PHP_EOL . 'Done.' . PHP_EOL; 51 | } 52 | 53 | /** 54 | * @param string $messagePath 55 | * @param string $category 56 | * @throws Exception 57 | */ 58 | public function actionExport($messagePath = null, $category = null) 59 | { 60 | if (!$messagePath) { 61 | $messagePath = $this->prompt('Enter a message path'); 62 | } 63 | $messagePath = realpath(Yii::getAlias($messagePath)); 64 | if (!is_dir($messagePath)) { 65 | throw new Exception('The message path ' . $messagePath . ' is not a valid directory.'); 66 | } 67 | 68 | if (!$category) { 69 | $category = $this->prompt('Enter an exporting category'); 70 | } 71 | if (empty($category)) { 72 | throw new Exception('The $category is empty.'); 73 | } 74 | 75 | $sourceMessages = SourceMessage::find() 76 | ->where('category = :category', [':category' => $category]) 77 | ->orderBy('message') 78 | ->all(); 79 | 80 | $messages = []; 81 | 82 | foreach ($sourceMessages as $sourceMessage) { 83 | $translations = $sourceMessage->messages; 84 | foreach (Yii::$app->getI18n()->languages as $language) { 85 | $messages[$language][$sourceMessage['message']] = isset($translations[$language]) && !empty($translations[$language]['translation']) ? $translations[$language]['translation'] : ''; 86 | } 87 | } 88 | 89 | foreach ($messages as $language => $translations) { 90 | $translationsFile = FileHelper::normalizePath($messagePath . DIRECTORY_SEPARATOR . $language . DIRECTORY_SEPARATOR . str_replace('\\', '/', $category) . '.php'); 91 | if (!is_file($translationsFile)) { 92 | $dir = dirname($translationsFile); 93 | if (!FileHelper::createDirectory($dir)) { 94 | throw new Exception('Directory ' . $dir . ' is not created'); 95 | } 96 | } 97 | ksort($translations); 98 | 99 | $array = VarDumper::export($translations); 100 | $content = << $category, 121 | 'message' => $message 122 | ]; 123 | $sourceMessage = SourceMessage::find() 124 | ->where($params) 125 | ->with('messages') 126 | ->one(); 127 | if (!$sourceMessage) { 128 | $sourceMessage = new SourceMessage; 129 | $sourceMessage->setAttributes($params, false); 130 | $sourceMessage->save(false); 131 | } 132 | return $sourceMessage; 133 | } 134 | 135 | /** 136 | * @param SourceMessage $sourceMessage 137 | * @param string $language 138 | * @param string $translation 139 | */ 140 | private function setTranslation($sourceMessage, $language, $translation) 141 | { 142 | /** @var Message[] $messages */ 143 | $messages = $sourceMessage->messages; 144 | if (isset($messages[$language]) && $messages[$language]->translation === null) { 145 | $messages[$language]->translation = $translation; 146 | $messages[$language]->save(false); 147 | } elseif (!isset($messages[$language])) { 148 | $message = new Message; 149 | $message->setAttributes([ 150 | 'language' => $language, 151 | 'translation' => $translation 152 | ], false); 153 | $sourceMessage->link('messages', $message); 154 | } 155 | } 156 | 157 | public function actionFlush() 158 | { 159 | $tableNames = [ 160 | Message::tableName(), 161 | SourceMessage::tableName() 162 | ]; 163 | $db = Yii::$app->getDb(); 164 | foreach ($tableNames as $tableName) { 165 | $db->createCommand() 166 | ->delete($tableName) 167 | ->execute(); 168 | } 169 | echo PHP_EOL . 'Done.' . PHP_EOL; 170 | } 171 | } 172 | --------------------------------------------------------------------------------