Commit 08453431bb1751ff1f12f0e113b24ffda9257406

Authored by Yarik
0 parents

first commit

Module.php 0 → 100755
  1 +++ a/Module.php
  1 +<?php
  2 +
  3 + namespace common\modules;
  4 +
  5 + class Module extends \yii\base\Module
  6 + {
  7 +
  8 + public function init()
  9 + {
  10 + parent::init();
  11 + }
  12 + }
0 13 \ No newline at end of file
... ...
behaviors/LanguageBehavior.php 0 → 100755
  1 +++ a/behaviors/LanguageBehavior.php
  1 +<?php
  2 + namespace artweb\artbox\language\behaviors;
  3 +
  4 + use artweb\artbox\language\models\Language;
  5 + use yii\base\Behavior;
  6 + use yii\base\InvalidConfigException;
  7 + use yii\db\ActiveQuery;
  8 + use yii\db\ActiveRecord;
  9 + use yii\db\Transaction;
  10 + use yii\web\Request;
  11 +
  12 + /**
  13 + * Class LanguageBehavior
  14 + * @property ActiveRecord $owner
  15 + * @property string $ownerKey
  16 + * @property string $langKey
  17 + * @property ActiveRecord[] $langs
  18 + * @property ActiveRecord $lang
  19 + */
  20 + class LanguageBehavior extends Behavior
  21 + {
  22 +
  23 + /**
  24 + * @var ActiveRecord $objectLang Empty language model for $owner
  25 + */
  26 + public $objectLang;
  27 +
  28 + /**
  29 + * @var ActiveRecord[] $modelLangs
  30 + */
  31 + public $modelLangs = [];
  32 +
  33 + private $ownerKey;
  34 +
  35 + private $langKey;
  36 +
  37 + /**
  38 + * @var Transaction $transaction
  39 + */
  40 + private $transaction;
  41 +
  42 + /**
  43 + * @var bool $transactionStatus
  44 + */
  45 + private $transactionStatus = false;
  46 +
  47 + public function events()
  48 + {
  49 + return [
  50 + ActiveRecord::EVENT_BEFORE_INSERT => 'beforeSave',
  51 + ActiveRecord::EVENT_BEFORE_UPDATE => 'beforeSave',
  52 + ActiveRecord::EVENT_AFTER_INSERT => 'afterSave',
  53 + ActiveRecord::EVENT_AFTER_UPDATE => 'afterSave',
  54 + ];
  55 + }
  56 +
  57 + /**
  58 + * Get $owner primary key to link language model
  59 + * @return string
  60 + */
  61 + public function getOwnerKey():string
  62 + {
  63 + if(!empty( $this->ownerKey )) {
  64 + return $this->ownerKey;
  65 + } else {
  66 + return $this->owner->primaryKey()[ 0 ];
  67 + }
  68 + }
  69 +
  70 + /**
  71 + * Set which attribute to use as $owner primary key to link language model
  72 + *
  73 + * @param string $value
  74 + */
  75 + public function setOwnerKey(string $value)
  76 + {
  77 + $this->ownerKey = $value;
  78 + }
  79 +
  80 + /**
  81 + * Get language model attribute that is used as foreign key to $owner
  82 + * @return string
  83 + */
  84 + public function getLangKey():string
  85 + {
  86 + if(!empty( $this->langKey )) {
  87 + return $this->langKey;
  88 + } else {
  89 + $owner = $this->owner;
  90 + return $owner::getTableSchema()->name . '_id';
  91 + }
  92 + }
  93 +
  94 + /**
  95 + * Set which attribute to use as language model foreign key to $owner
  96 + *
  97 + * @param $value
  98 + */
  99 + public function setLangKey(string $value)
  100 + {
  101 + $this->langKey = $value;
  102 + }
  103 +
  104 + /**
  105 + * Additional checks to attach this behavior
  106 + *
  107 + * @param ActiveRecord $owner
  108 + *
  109 + * @throws InvalidConfigException
  110 + */
  111 + public function attach($owner)
  112 + {
  113 + if(empty( $this->objectLang )) {
  114 + $this->objectLang = $owner::className() . 'Lang';
  115 + } elseif(!is_string($this->objectLang)) {
  116 + throw new InvalidConfigException('Object lang must be fully classified namespaced classname');
  117 + }
  118 + try {
  119 + $this->objectLang = \Yii::createObject($this->objectLang);
  120 + } catch(\ReflectionException $exception) {
  121 + throw new InvalidConfigException('Object lang must be fully classified namespaced classname');
  122 + }
  123 + if(( !$owner instanceof ActiveRecord ) || ( !$this->objectLang instanceof ActiveRecord )) {
  124 + throw new InvalidConfigException('Object lang must be fully classified namespaced classname');
  125 + }
  126 + parent::attach($owner);
  127 + }
  128 +
  129 + /**
  130 + * Get query to get all language models for $owner indexed by language_id
  131 + * @return ActiveQuery
  132 + */
  133 + public function getLangs()
  134 + {
  135 + $objectLang = $this->objectLang;
  136 + $owner = $this->owner;
  137 + return $owner->hasMany($objectLang::className(), [ $this->getLangKey() => $this->getOwnerKey() ])
  138 + ->indexBy('language_id');
  139 + }
  140 +
  141 + /**
  142 + * Get query to get language model for $owner for language_id, default to
  143 + * Language::getCurrent()
  144 + *
  145 + * @param int $language_id
  146 + *
  147 + * @return ActiveQuery
  148 + */
  149 + public function getLang(int $language_id = NULL)
  150 + {
  151 + if(empty( $language_id )) {
  152 + $language_id = Language::getCurrent()->id;
  153 + }
  154 + $objectLang = $this->objectLang;
  155 + $table_name = $objectLang::getTableSchema()->name;
  156 + $owner = $this->owner;
  157 + return $owner->hasOne($objectLang::className(), [ $this->getLangKey() => $this->getOwnerKey() ])
  158 + ->where([ $table_name . '.language_id' => $language_id ]);
  159 + }
  160 +
  161 + /**
  162 + * Generate language models for $owner for active languages. If $owner not new and language
  163 + * models already inserted, models will be filled with them.
  164 + * @return void
  165 + */
  166 + public function generateLangs()
  167 + {
  168 + $owner = $this->owner;
  169 + $languages = Language::find()
  170 + ->where([ 'status' => true ])
  171 + ->orderBy([ 'id' => SORT_ASC ])
  172 + ->asArray()
  173 + ->column();
  174 + $objectLang = $this->objectLang;
  175 + $owner_key = $this->getOwnerKey();
  176 + $langs = [];
  177 + if(!$owner->isNewRecord) {
  178 + $langs = $this->getLangs()
  179 + ->andFilterWhere([ 'language_id' => $languages ])
  180 + ->orderBy([ 'language_id' => SORT_ASC ])
  181 + ->all();
  182 + }
  183 + foreach($languages as $language) {
  184 + if(!array_key_exists($language, $langs)) {
  185 + $langs[ $language ] = \Yii::createObject([
  186 + 'class' => $objectLang::className(),
  187 + 'language_id' => $language,
  188 + $this->getLangKey() => ( $owner->isNewRecord ? NULL : $owner->$owner_key ),
  189 + ]);
  190 + }
  191 + }
  192 + $this->modelLangs = $langs;
  193 + }
  194 +
  195 + /**
  196 + * Load language models with post data.
  197 + *
  198 + * @param Request $request
  199 + */
  200 + public function loadLangs(Request $request)
  201 + {
  202 + foreach($request->post($this->objectLang->formName(), []) as $lang => $value) {
  203 + if(!empty( $this->modelLangs[ $lang ] )) {
  204 + $this->modelLangs[ $lang ]->attributes = $value;
  205 + $this->modelLangs[ $lang ]->language_id = $lang;
  206 + }
  207 + }
  208 + }
  209 +
  210 + /**
  211 + * Link language models with $owner by setting language model language key to owner key of
  212 + * owner
  213 + * @return bool If $owner is new record then return false else true
  214 + */
  215 + public function linkLangs()
  216 + {
  217 + $owner = $this->owner;
  218 + // if($owner->isNewRecord) {
  219 + // return false;
  220 + // }
  221 + $lang_key = $this->getLangKey();
  222 + $owner_key = $this->getOwnerKey();
  223 + $modelLangs = $this->modelLangs;
  224 + foreach($modelLangs as $model_lang) {
  225 + $model_lang->$lang_key = $owner->$owner_key;
  226 + }
  227 + return true;
  228 + }
  229 +
  230 + /**
  231 + * Try to save all language models to the db. Validation function is run for all models.
  232 + * @return bool Whether all models are valid
  233 + */
  234 + public function saveLangs()
  235 + {
  236 + $success = true;
  237 + $modelLangs = $this->modelLangs;
  238 + foreach($modelLangs as $model_lang) {
  239 + if($model_lang->save() === false) {
  240 + $success = false;
  241 + }
  242 + }
  243 + return $success;
  244 + }
  245 +
  246 + public function beforeSave($event)
  247 + {
  248 + /**
  249 + * @var ActiveRecord $owner
  250 + */
  251 + $owner = $this->owner;
  252 + $db = $owner::getDb();
  253 + $this->transaction = $db->beginTransaction();
  254 + if($owner->hasAttribute('remote_id') && empty( $owner->remote_id )) {
  255 + $owner->remote_id = strval(microtime(true) * 10000);
  256 + }
  257 + }
  258 +
  259 + public function afterSave($event)
  260 + {
  261 + if(!empty( $this->modelLangs )) {
  262 + if($this->linkLangs() && $this->saveLangs()) {
  263 + $this->transaction->commit();
  264 + $this->transactionStatus = true;
  265 + } else {
  266 + $this->transaction->rollBack();
  267 + $this->transactionStatus = false;
  268 + }
  269 + } else {
  270 + $this->transaction->commit();
  271 + $this->transactionStatus = true;
  272 + }
  273 + }
  274 +
  275 + /**
  276 + * @return bool
  277 + */
  278 + public function getTransactionStatus():bool
  279 + {
  280 + return $this->transactionStatus;
  281 + }
  282 + }
0 283 \ No newline at end of file
... ...
components/LanguageRequest.php 0 → 100755
  1 +++ a/components/LanguageRequest.php
  1 +<?php
  2 +
  3 + namespace artweb\artbox\language\components;
  4 +
  5 + use artweb\artbox\language\models\Language;
  6 + use yii\base\InvalidConfigException;
  7 + use yii\web\Request;
  8 +
  9 + class LanguageRequest extends Request
  10 + {
  11 +
  12 + private $languageUrl;
  13 +
  14 + public function getLanguageUrl()
  15 + {
  16 + if($this->languageUrl === NULL) {
  17 + $this->languageUrl = $this->getUrl();
  18 +
  19 + $url_list = explode('/', $this->languageUrl);
  20 +
  21 + $language_url = isset( $url_list[ 1 ] ) ? $url_list[ 1 ] : NULL;
  22 + Language::setCurrent($language_url);
  23 +
  24 + if($language_url !== NULL && $language_url === Language::getCurrent()->url && strpos($this->languageUrl, Language::getCurrent()->url) === 1) {
  25 + $this->languageUrl = substr($this->languageUrl, strlen(Language::getCurrent()->url) + 1);
  26 + }
  27 + }
  28 +
  29 + return $this->languageUrl;
  30 + }
  31 +
  32 + protected function resolvePathInfo()
  33 + {
  34 + $pathInfo = $this->getLanguageUrl();
  35 +
  36 + if(( $pos = strpos($pathInfo, '?') ) !== false) {
  37 + $pathInfo = substr($pathInfo, 0, $pos);
  38 + }
  39 +
  40 + $pathInfo = urldecode($pathInfo);
  41 +
  42 + if(!preg_match('%^(?:
  43 + [\x09\x0A\x0D\x20-\x7E]
  44 + | [\xC2-\xDF][\x80-\xBF]
  45 + | \xE0[\xA0-\xBF][\x80-\xBF]
  46 + | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}
  47 + | \xED[\x80-\x9F][\x80-\xBF]
  48 + | \xF0[\x90-\xBF][\x80-\xBF]{2}
  49 + | [\xF1-\xF3][\x80-\xBF]{3}
  50 + | \xF4[\x80-\x8F][\x80-\xBF]{2}
  51 + )*$%xs', $pathInfo)
  52 + ) {
  53 + $pathInfo = utf8_encode($pathInfo);
  54 + }
  55 +
  56 + $scriptUrl = $this->getScriptUrl();
  57 + $baseUrl = $this->getBaseUrl();
  58 +
  59 + if(strpos($pathInfo, $scriptUrl) === 0) {
  60 + $pathInfo = substr($pathInfo, strlen($scriptUrl));
  61 + } elseif($baseUrl === '' || strpos($pathInfo, $baseUrl) === 0) {
  62 + $pathInfo = substr($pathInfo, strlen($baseUrl));
  63 + } elseif(isset( $_SERVER[ 'PHP_SELF' ] ) && strpos($_SERVER[ 'PHP_SELF' ], $scriptUrl) === 0) {
  64 + $pathInfo = substr($_SERVER[ 'PHP_SELF' ], strlen($scriptUrl));
  65 + } else {
  66 + throw new InvalidConfigException('Unable to determine the path info of the current request.');
  67 + }
  68 +
  69 + if($pathInfo === '/') {
  70 + $pathInfo = substr($pathInfo, 1);
  71 + }
  72 +
  73 + return (string) $pathInfo;
  74 + }
  75 + }
0 76 \ No newline at end of file
... ...
components/LanguageUrlManager.php 0 → 100755
  1 +++ a/components/LanguageUrlManager.php
  1 +<?php
  2 + namespace artweb\artbox\language\components;
  3 +
  4 + use artweb\artbox\language\models\Language;
  5 + use yii\web\UrlManager;
  6 +
  7 + class LanguageUrlManager extends UrlManager
  8 + {
  9 +
  10 + /**
  11 + * @inheritdoc
  12 + */
  13 + public function createUrl($params)
  14 + {
  15 + if(isset( $params[ 'language_id' ] )) {
  16 +
  17 + $language = Language::findOne($params[ 'language_id' ]);
  18 + if($language === NULL) {
  19 + $language = Language::getDefaultLanguage();
  20 + }
  21 + unset( $params[ 'language_id' ] );
  22 + } else {
  23 +
  24 + $language = Language::getCurrent();
  25 + }
  26 +
  27 + $url = parent::createUrl($params);
  28 +
  29 + if($url == '/') {
  30 + return '/' . $language->url;
  31 + } else {
  32 + return '/' . $language->url . $url;
  33 + }
  34 + }
  35 + }
0 36 \ No newline at end of file
... ...
composer.json 0 → 100644
  1 +++ a/composer.json
  1 +{
  2 + "name": "artweb/artbox-language",
  3 + "description": "Yii2 light-weight CMS",
  4 + "license": "BSD-3-Clause",
  5 + "require": {
  6 + "php": ">=7.0",
  7 + "yiisoft/yii2": "*",
  8 + "developeruz/yii2-db-rbac": "*"
  9 + },
  10 + "autoload": {
  11 + "psr-4": {
  12 + "artweb\\artbox\\language\\": ""
  13 + }
  14 + }
  15 +}
0 16 \ No newline at end of file
... ...
migrations/m160829_104745_create_table_language.php 0 → 100755
  1 +++ a/migrations/m160829_104745_create_table_language.php
  1 +<?php
  2 +
  3 + use yii\db\Migration;
  4 +
  5 + class m160829_104745_create_table_language extends Migration
  6 + {
  7 +
  8 + public function up()
  9 + {
  10 + $this->createTable(
  11 + '{{%language}}',
  12 + [
  13 + 'id' => $this->primaryKey(),
  14 + 'url' => $this->string()
  15 + ->notNull(),
  16 + 'local' => $this->string()
  17 + ->notNull(),
  18 + 'name' => $this->string()
  19 + ->notNull(),
  20 + 'default' => $this->boolean()
  21 + ->notNull()
  22 + ->defaultValue(false),
  23 + 'created_at' => $this->integer()
  24 + ->notNull(),
  25 + 'updated_at' => $this->integer()
  26 + ->notNull(),
  27 + ]
  28 + );
  29 + }
  30 +
  31 + public function down()
  32 + {
  33 + $this->dropTable('{{%language}}');
  34 + }
  35 + }
... ...
migrations/m160829_105345_add_default_languages.php 0 → 100755
  1 +++ a/migrations/m160829_105345_add_default_languages.php
  1 +<?php
  2 +
  3 + use yii\db\Migration;
  4 +
  5 + class m160829_105345_add_default_languages extends Migration
  6 + {
  7 +
  8 + public function up()
  9 + {
  10 + $this->batchInsert(
  11 + '{{%language}}',
  12 + [
  13 + 'id',
  14 + 'url',
  15 + 'local',
  16 + 'name',
  17 + 'default',
  18 + 'created_at',
  19 + 'updated_at',
  20 + ],
  21 + [
  22 + [
  23 + 1,
  24 + 'en',
  25 + 'en-EN',
  26 + 'English',
  27 + 0,
  28 + time(),
  29 + time(),
  30 + ],
  31 + [
  32 + 2,
  33 + 'ru',
  34 + 'ru-RU',
  35 + 'Русский',
  36 + 1,
  37 + time(),
  38 + time(),
  39 + ],
  40 + ]
  41 + );
  42 + }
  43 +
  44 + public function down()
  45 + {
  46 + $this->delete(
  47 + '{{%language}}',
  48 + [
  49 + 'id' => [
  50 + 1,
  51 + 2,
  52 + ],
  53 + ]
  54 + );
  55 + }
  56 + }
... ...
migrations/m160901_140639_add_ukrainian_language.php 0 → 100755
  1 +++ a/migrations/m160901_140639_add_ukrainian_language.php
  1 +<?php
  2 +
  3 + use yii\db\Migration;
  4 +
  5 + class m160901_140639_add_ukrainian_language extends Migration
  6 + {
  7 +
  8 + public function up()
  9 + {
  10 + $this->batchInsert(
  11 + '{{%language}}',
  12 + [
  13 + 'id',
  14 + 'url',
  15 + 'local',
  16 + 'name',
  17 + 'default',
  18 + 'created_at',
  19 + 'updated_at',
  20 + ],
  21 + [
  22 + [
  23 + 3,
  24 + 'ua',
  25 + 'ua-UA',
  26 + 'Українська',
  27 + 0,
  28 + time(),
  29 + time(),
  30 + ],
  31 + ]
  32 + );
  33 + }
  34 +
  35 + public function down()
  36 + {
  37 + $this->delete('{{%language}}', [ 'id' => [ 3 ] ]);
  38 + }
  39 + }
... ...
migrations/m160927_124151_add_status_column.php 0 → 100755
  1 +++ a/migrations/m160927_124151_add_status_column.php
  1 +<?php
  2 +
  3 + use yii\db\Migration;
  4 +
  5 + class m160927_124151_add_status_column extends Migration
  6 + {
  7 +
  8 + public function up()
  9 + {
  10 + $this->addColumn('language', 'status', $this->boolean()
  11 + ->notNull()
  12 + ->defaultValue(false));
  13 + }
  14 +
  15 + public function down()
  16 + {
  17 + $this->dropColumn('language', 'status');
  18 + }
  19 + }
... ...
models/Language.php 0 → 100755
  1 +++ a/models/Language.php
  1 +<?php
  2 +
  3 + namespace artweb\artbox\language\models;
  4 +
  5 + use Yii;
  6 + use yii\db\ActiveRecord;
  7 +
  8 + /**
  9 + * This is the model class for table "language".
  10 + *
  11 + * @property integer $id
  12 + * @property string $url
  13 + * @property string $local
  14 + * @property string $name
  15 + * @property boolean $default
  16 + * @property integer $created_at
  17 + * @property integer $updated_at
  18 + */
  19 + class Language extends ActiveRecord
  20 + {
  21 +
  22 + /**
  23 + * @var null|self
  24 + */
  25 + public static $current = null;
  26 +
  27 + /**
  28 + * @inheritdoc
  29 + */
  30 + public static function tableName()
  31 + {
  32 + return 'language';
  33 + }
  34 +
  35 + /**
  36 + * @inheritdoc
  37 + */
  38 + public function behaviors()
  39 + {
  40 + return [
  41 + 'timestamp' => [
  42 + 'class' => 'yii\behaviors\TimestampBehavior',
  43 + 'attributes' => [
  44 + ActiveRecord::EVENT_BEFORE_INSERT => [
  45 + 'created_at',
  46 + 'updated_at',
  47 + ],
  48 + ActiveRecord::EVENT_BEFORE_UPDATE => [
  49 + 'updated_at',
  50 + ],
  51 + ],
  52 + ],
  53 + ];
  54 + }
  55 +
  56 + /**
  57 + * @inheritdoc
  58 + */
  59 + public function rules()
  60 + {
  61 + return [
  62 + [
  63 + [
  64 + 'url',
  65 + 'local',
  66 + 'name',
  67 + 'created_at',
  68 + 'updated_at',
  69 + ],
  70 + 'required',
  71 + ],
  72 + [
  73 + [ 'default' ],
  74 + 'boolean',
  75 + ],
  76 + [
  77 + [
  78 + 'created_at',
  79 + 'updated_at',
  80 + ],
  81 + 'integer',
  82 + ],
  83 + [
  84 + [
  85 + 'url',
  86 + 'local',
  87 + 'name',
  88 + ],
  89 + 'string',
  90 + 'max' => 255,
  91 + ],
  92 + ];
  93 + }
  94 +
  95 + /**
  96 + * @inheritdoc
  97 + */
  98 + public function attributeLabels()
  99 + {
  100 + return [
  101 + 'id' => Yii::t('app', 'Language ID'),
  102 + 'url' => Yii::t('app', 'Url'),
  103 + 'local' => Yii::t('app', 'Local'),
  104 + 'name' => Yii::t('app', 'Name'),
  105 + 'default' => Yii::t('app', 'Default'),
  106 + 'created_at' => Yii::t('app', 'Date Create'),
  107 + 'updated_at' => Yii::t('app', 'Date Update'),
  108 + ];
  109 + }
  110 +
  111 + /**
  112 + * Get current language
  113 + *
  114 + * @return null|Language
  115 + */
  116 + public static function getCurrent()
  117 + {
  118 + if (self::$current === null) {
  119 + self::$current = self::getDefaultLanguage();
  120 + }
  121 + return self::$current;
  122 + }
  123 +
  124 + /**
  125 + * Set current language by Url param
  126 + *
  127 + * @param null|string $url Language url param
  128 + */
  129 + public static function setCurrent($url = null)
  130 + {
  131 + $language = self::getLanguageByUrl($url);
  132 + self::$current = ( $language === null ) ? self::getDefaultLanguage() : $language;
  133 + Yii::$app->language = self::$current->local;
  134 + }
  135 +
  136 + /**
  137 + * Get default language
  138 + *
  139 + * @return null|Language
  140 + */
  141 + public static function getDefaultLanguage()
  142 + {
  143 + /**
  144 + * @var null|Language $language
  145 + */
  146 + $language = self::find()
  147 + ->where([ 'default' => true ])
  148 + ->one();
  149 + return $language;
  150 + }
  151 +
  152 + /**
  153 + * Get language by Url param
  154 + *
  155 + * @param null|string $url Language url param
  156 + *
  157 + * @return null|Language
  158 + */
  159 + public static function getLanguageByUrl($url = null)
  160 + {
  161 + if ($url === null) {
  162 + return null;
  163 + } else {
  164 + /**
  165 + * @var null|Language $language
  166 + */
  167 + $language = self::find()
  168 + ->where([ 'url' => $url ])
  169 + ->one();
  170 + if ($language === null) {
  171 + return null;
  172 + } else {
  173 + return $language;
  174 + }
  175 + }
  176 + }
  177 + }
... ...
readme.txt 0 → 100755
  1 +++ a/readme.txt
  1 +Как включить мультиязычность на сайте:
  2 +1. Запускаем миграцию: php yii migrate --migrationPath=common/modules/language/migrations
  3 +2. Добавляем в файл конфигурации:
  4 +'urlManager' => [
  5 + 'enablePrettyUrl' => true,
  6 + 'showScriptName' => false,
  7 + 'class'=>'artweb\artbox\language\components\LanguageUrlManager',
  8 + 'rules'=>[
  9 + '/' => 'site/index',
  10 + '<controller:\w+>/<action:\w+>/*'=>'<controller>/<action>',
  11 + ]
  12 +],
  13 +3. Добавляем в файл конфигурации:
  14 +'request' => [
  15 + 'class' => 'artweb\artbox\language\components\LanguageRequest'
  16 +],
  17 +4. Добавляем в файл конфигурации:
  18 +'language'=>'ru-RU',
  19 +'i18n' => [
  20 + 'translations' => [
  21 + '*' => [
  22 + 'class' => 'yii\i18n\PhpMessageSource',
  23 + 'basePath' => '@frontend/messages',
  24 + 'sourceLanguage' => 'en',
  25 + 'fileMap' => [
  26 + ],
  27 + ],
  28 + ],
  29 +],
  30 +5. Переводы писать в файл frontend\messages\{language}\app.php, где {language} - нужный язык, например ru.
  31 +6. Для вывода на странице сообщения с переводом используем функцию: Yii::t('app', {message}, $params = [], $language = null),
  32 + где {message} - нужное сообщение, $params - массив параметров, $language - нужный язык (по умолчанию используется текущий язык).
  33 +7. В наличие также виджет переключения языка: LanguagePicker::widget()
  34 +
  35 +
  36 +Как использовать мультиязычность для Active Record:
  37 +1. Создаем для таблицы {table} таблицу с языками {table_lang}.
  38 +2. Создаем для класса {Table} класс с языками {TableLang}.
  39 +3. Подключаеи для класса {Table} поведение LanguageBehavior:
  40 +public function behaviors() {
  41 + return [
  42 + 'language' => [
  43 + 'class' => LanguageBehavior::className(),
  44 + 'objectLang' => {TableLang}::className() // optional, default to {TableLang}::className()
  45 + 'ownerKey' => {Table}->id //optional, default to {Table}->primaryKey()[0]
  46 + 'langKey' => {TableLang}->table_id //optional, default to {Table}->tableName().'_id'
  47 + ],
  48 + ];
  49 +}
  50 +3.1. PHPDoc для {Table}:
  51 + * * From language behavior *
  52 + * @property {TableLang} $lang
  53 + * @property {TableLang}[] $langs
  54 + * @property {TableLang} $objectLang
  55 + * @property string $ownerKey
  56 + * @property string $langKey
  57 + * @property {TableLang}[] $modelLangs
  58 + * @property bool $transactionStatus
  59 + * @method string getOwnerKey()
  60 + * @method void setOwnerKey(string $value)
  61 + * @method string getLangKey()
  62 + * @method void setLangKey(string $value)
  63 + * @method ActiveQuery getLangs()
  64 + * @method ActiveQuery getLang( integer $language_id )
  65 + * @method {TableLang}[] generateLangs()
  66 + * @method void loadLangs(Request $request)
  67 + * @method bool linkLangs()
  68 + * @method bool saveLangs()
  69 + * @method bool getTransactionStatus()
  70 + * * End language behavior *
  71 +3.2. Убрать language behavior с наследуемых таблиц от {Table} ({TableSearch}...)
  72 +4. Доступные полезные методы:
  73 + {Table}->getLangs() - получить все текущие {TableLang} для {Table} проиндексированные по language_id
  74 + {Table}->getLang($language_id = NULL) - получить {TableLang} для определенного языка (default: текущий язык) для {Table}
  75 + {Table}->generateLangs() - получить массив {TableLang} под каждый язык, включая существующие записи, для {Table}
  76 + {Table}->loadLangs($request) - заполнить массив {TableLang} данными с POST
  77 + {Table}->linkLangs() - связать каждый элемент массива {TableLang} с текущей {Table}
  78 + {Table}->saveLangs() - провалидировать и сохранить каждый элемент массива {TableLang}
  79 +5. Добавить поля в форму (к примеру через Bootstrap Tabs).
  80 + В наличии:
  81 + LanguageForm::widget([
  82 + 'modelLangs' => {TableLang}[],
  83 + 'formView' => string,
  84 + 'form' => ActiveForm,
  85 + ]);
  86 +6. Обрабатывать данные в контроллере.
  87 + 1. После создания/поиска {Table} создаем/находим языковые модели {Table}->generateLangs()
  88 + 2. При POST запросе загружаем данные в языковые модели {Table}->loadLangs(Request $request)
  89 + 3. После сохранения, если транзанкция успешна, то свойство {Table}->transactionStatus будет true, иначе возникла ошибка в какой то модели.
  90 +7. Получать данные на публичной части сайта через {Table}->lang.
... ...
widgets/LanguageForm.php 0 → 100755
  1 +++ a/widgets/LanguageForm.php
  1 +<?php
  2 +
  3 + namespace artweb\artbox\language\widgets;
  4 +
  5 + use artweb\artbox\language\models\Language;
  6 + use yii\base\InvalidConfigException;
  7 + use yii\bootstrap\Widget;
  8 + use yii\db\ActiveRecord;
  9 + use yii\widgets\ActiveForm;
  10 +
  11 + class LanguageForm extends Widget
  12 + {
  13 +
  14 + /**
  15 + * @var ActiveRecord[] $modelLangs
  16 + */
  17 + public $modelLangs = [];
  18 +
  19 + /**
  20 + * @var string $formView
  21 + */
  22 + public $formView;
  23 +
  24 + /**
  25 + * @var string $idPrefix
  26 + */
  27 + public $idPrefix = 'lang';
  28 +
  29 + /**
  30 + * @var ActiveForm $form
  31 + */
  32 + private $form;
  33 +
  34 + /**
  35 + * @var Language[] $languages
  36 + */
  37 + private $languages = [];
  38 +
  39 + public function init()
  40 + {
  41 + parent::init();
  42 + if($this->formView === NULL) {
  43 + throw new InvalidConfigException('Form view must be set');
  44 + }
  45 + if(empty( $this->modelLangs ) || !is_array($this->modelLangs)) {
  46 + throw new InvalidConfigException('Language models must be passed');
  47 + }
  48 + if(empty( $this->getForm() )) {
  49 + throw new InvalidConfigException('Form model must be set');
  50 + }
  51 + $this->languages = Language::find()
  52 + ->where([ 'status' => true ])
  53 + ->orderBy([ 'default' => SORT_DESC ])
  54 + ->indexBy('id')
  55 + ->all();
  56 + }
  57 +
  58 + public function run()
  59 + {
  60 + return $this->render('language_form_frame', [
  61 + 'languages' => $this->languages,
  62 + 'form_view' => $this->formView,
  63 + 'modelLangs' => $this->modelLangs,
  64 + 'form' => $this->getForm(),
  65 + 'idPrefix' => $this->idPrefix,
  66 + ]);
  67 + }
  68 +
  69 + public function getForm(): ActiveForm
  70 + {
  71 + return $this->form;
  72 + }
  73 +
  74 + public function setForm(ActiveForm $value)
  75 + {
  76 + $this->form = $value;
  77 + }
  78 + }
0 79 \ No newline at end of file
... ...
widgets/LanguagePicker.php 0 → 100755
  1 +++ a/widgets/LanguagePicker.php
  1 +<?php
  2 +
  3 + namespace artweb\artbox\language\widgets;
  4 +
  5 + use artweb\artbox\language\models\Language;
  6 + use yii\bootstrap\Widget;
  7 +
  8 + class LanguagePicker extends Widget
  9 + {
  10 +
  11 + public function init()
  12 + {
  13 +
  14 + }
  15 +
  16 + public function run()
  17 + {
  18 + return $this->render('view', [
  19 + 'current' => Language::getCurrent(),
  20 + 'languages' => Language::find()
  21 + ->where([
  22 + '!=',
  23 + 'id',
  24 + Language::getCurrent()->id,
  25 + ])
  26 + ->all(),
  27 + ]);
  28 + }
  29 + }
0 30 \ No newline at end of file
... ...
widgets/views/language_form_frame.php 0 → 100755
  1 +++ a/widgets/views/language_form_frame.php
  1 +<?php
  2 + use artweb\artbox\language\models\Language;
  3 + use yii\db\ActiveRecord;
  4 + use yii\helpers\Html;
  5 + use yii\web\View;
  6 + use yii\widgets\ActiveForm;
  7 +
  8 + /**
  9 + * @var Language[] $languages
  10 + * @var string $form_view
  11 + * @var ActiveRecord $modelLangs
  12 + * @var ActiveForm $form
  13 + * @var View $this
  14 + * @var string $idPrefix
  15 + */
  16 +?>
  17 +<div>
  18 + <?php
  19 + if(count($languages) > 1) {
  20 + ?>
  21 + <ul class="nav nav-tabs text-uppercase">
  22 + <?php
  23 + $first = true;
  24 + foreach($modelLangs as $lang => $model_lang) {
  25 + if(!array_key_exists($lang, $languages)) {
  26 + continue;
  27 + }
  28 + echo Html::tag('li', Html::a($languages[ $lang ]->url, [
  29 + '',
  30 + '#' => $idPrefix . '_' . $lang,
  31 + ], [ 'data-toggle' => 'tab' ]), [
  32 + 'class' => $first ? 'active' : '',
  33 + ]);
  34 + $first = false;
  35 + }
  36 + ?>
  37 + </ul>
  38 + <div class="tab-content">
  39 + <?php
  40 + $first = true;
  41 + foreach($modelLangs as $lang => $model_lang) {
  42 + if(!array_key_exists($lang, $languages)) {
  43 + continue;
  44 + }
  45 + echo Html::tag('div', $this->render($form_view, [
  46 + 'model_lang' => $model_lang,
  47 + 'language' => $languages[ $lang ],
  48 + 'form' => $form,
  49 + ]), [
  50 + 'class' => 'tab-pane' . ( $first ? ' active' : '' ),
  51 + 'id' => $idPrefix . '_' . $lang,
  52 + ]);
  53 + $first = false;
  54 + }
  55 + ?>
  56 + </div>
  57 + <?php
  58 + } else {
  59 + $language = current($languages);
  60 + if(isset( $modelLangs[ $language->id ] )) {
  61 + echo $this->render($form_view, [
  62 + 'model_lang' => $modelLangs[ $language->id ],
  63 + 'language' => $language,
  64 + 'form' => $form,
  65 + ]);
  66 + }
  67 + }
  68 + ?>
  69 +</div>
... ...
widgets/views/view.php 0 → 100755
  1 +++ a/widgets/views/view.php
  1 +<?php
  2 + use artweb\artbox\language\components\LanguageRequest;
  3 + use artweb\artbox\language\models\Language;
  4 + use yii\bootstrap\Html;
  5 +
  6 + /**
  7 + * @var Language $current Current language
  8 + * @var Language[] $languages Available languages
  9 + */
  10 +?>
  11 +<div id="language_picker">
  12 + <span id="current_language">
  13 + <?php
  14 + echo $current->name;
  15 + ?>
  16 + <span class="show-more-language">▼</span>
  17 + </span>
  18 + <ul id="languages">
  19 + <?php
  20 + foreach($languages as $language) {
  21 + ?>
  22 + <li class="item-language">
  23 + <?php
  24 + /**
  25 + * @var LanguageRequest $request
  26 + */
  27 + $request = \Yii::$app->getRequest();
  28 + echo Html::a($language->name, '/' . $language->url . $request->getLanguageUrl());
  29 + ?>
  30 + </li>
  31 + <?php
  32 + }
  33 + ?>
  34 + </ul>
  35 +</div>
... ...