Rashad
Rashad

Reputation: 1344

Yii 2 customize localization database (Message, SourceMessage)

By Default Yii 2 has databases for localization which calls TranslationMessage and TranslationSourceMessage. For some important reasons I decided to write my custom translation class, that should get some localization from cached data (Redis).

I customized my db structure. By default, Yii 2 provides columns in TranslationMessage (id, language).

I customized this DB, instead of id I prefered using message_id.

So my new TranslationMessage structure is (id, message_id, translation).

message_id is a foreign key of TranslationSourceMessage (id)

However, sometimes I need to use

Yii::t($message, $category)

But in this case, this doesn't retrieve suitable row from db, just because I changed relation of two tables.

From debugbar I see, Yii::t retrieves a query:

SELECT `t1`.`message` AS `message`, `t2`.`translation` AS `translation` FROM `TranslationSourceMessage` `t1`, `TranslationMessage` `t2` WHERE (`t1`.`id`=`t2`.`id`)...

Instead of:

`t1`.`id`=`t2`.`id`

it should be:

`t1`.`id`=`t2`.`message_id`

I tried to set my custom hasOne method on model, but wasn't any result.

Upvotes: 1

Views: 288

Answers (1)

Jap Mul
Jap Mul

Reputation: 18789

Create your own version of the DbMessageSource class and change your i18n config to use this version.

So in your config change:

'class' => 'yii\i18n\DbMessageSource',

to

'class' => 'your\namespace\path\CustomDbMessageSource',

and add the following class:

<?php

namespace your\namespace\path;

use yii\helpers\ArrayHelper;
use yii\db\Query;
use yii\db\Expression;

class CustomDbMessageSource extends \yii\i18n\DbMessageSource {

    protected function loadMessagesFromDb($category, $language) {
        $mainQuery = (new Query())
            ->select([
                'message' => 't1.message',
                'translation' => 't2.translation',
            ])
            ->from([
                't1' => $this->sourceMessageTable,
                't2' => $this->messageTable,
            ])
            ->where([
                't1.id' => new Expression('[[t2.message_id]]'), // <-- this is the change to your field.
                't1.category' => $category,
                't2.language' => $language,
            ]);

        $fallbackLanguage = substr($language, 0, 2);
        $fallbackSourceLanguage = substr($this->sourceLanguage, 0, 2);
        if ($fallbackLanguage !== $language) {
            $mainQuery->union($this->createFallbackQuery($category, $language, $fallbackLanguage), true);
        } elseif ($language === $fallbackSourceLanguage) {
            $mainQuery->union($this->createFallbackQuery($category, $language, $fallbackSourceLanguage), true);
        }
        $messages = $mainQuery->createCommand($this->db)->queryAll();
        return ArrayHelper::map($messages, 'message', 'translation');
    }

    protected function createFallbackQuery($category, $language, $fallbackLanguage) {
        return (new Query())
            ->select([
                'message' => 't1.message',
                'translation' => 't2.translation',
            ])
            ->from([
                't1' => $this->sourceMessageTable,
                't2' => $this->messageTable,
            ])
            ->where([
                't1.id' => new Expression('[[t2.message_id]]'), // <-- also changed it here
                't1.category' => $category,
                't2.language' => $fallbackLanguage,
            ])->andWhere([
                'NOT IN',
                't2.message_id', // <-- also changed it here
                (new Query())->select('[[id]]')->from($this->messageTable)->where(['language' => $language]),
            ]);
    }

}

Upvotes: 2

Related Questions