angularyii2yii2-advanced-appangular11yii2-api

What is the best approach to implement multi language app using angular and yii2


I'm using yii2 restful api for the backend, and angular for the frontend.

I want to ask about the best approach to implement multi-language feature that can manages both frontend an backend in the same place (db table, file).


Solution

  • The theory and guides:

    1. To implement|enable multi-language feature in a Yii2 application you should use i18n component https://www.yiiframework.com/doc/guide/2.0/en/tutorial-i18n.

    2. Next is to detect which language is requested|required|selected by frontend|backend|api. This can be done in several ways: headers, cookies, session, get, post or even by host name.

    3. Before handling an request you need to detect selected language, by some kind of priority, where is the language config stored and set it to application.

    The code:

    app/common/config/main.php

    return [
    // ...
    'language' => 'en', // base language
    // ...
    'components' => [
    //...
        'i18n' => [
            'translations' => [
                '*' => [
                    'class' => 'yii\i18n\PhpMessageSource',
                    'basePath' => '@common/messages',
                    'sourceLanguage' => 'en',
                ],
            ],
        ],    
    // ...
    ],
    // ...
    
    // handle request and detect language    
    'as beforeRequest' => [
        'class' => app\common\behaviours\LanguageHandler::class,
    ],
    

    app/common/behaviours/LanguageHandler.php

    namespace common\behaviours;
    
    use common\models\User;
    use Yii;
    use yii\base\Behavior;
    use yii\base\Event;
    use yii\helpers\Url;
    use yii\web\Application;
    use yii\web\Cookie;
    
    /**
     * Class LanguageHandler
     * @package common\behaviours
     */
    class LanguageHandler extends Behavior
    {
        const LANG_KEY = 'language';
        public $domainOnly = false;
    
        /**
         * @return array
         */
        public function events()
        {
            return [
                Application::EVENT_BEFORE_REQUEST => 'handleBeginRequest',
            ];
        }
    
        /**
         * Handle app selected language
         *
         * Priority of detecting language
         * 1) Detect from host name
         * 2) Detect from post
         * 3) Detect from headers
         * 4) Detect from cookies
         * 5) Detect from user config
         * 6) Use default app language
         *
         * @param Event $event
         */
        public function handleBeginRequest(Event $event)
        {
            $hostName = $request->getHostName();
            $request = Yii::$app->getRequest();
            $language = $request->post(self::LANG_KEY, null);
            $query = $request->getQueryParams();
            $cookies = $request->getCookies();
            $headers = $request->getHeaders();
    
            /**
             * Try first by domain and hostname
             */
            if ($this->domainOnly) {
                /**
                 * Try first by domain and hostname
                 */
                if (substr($hostName, -3) == '.ru') {
                    $language = 'ru';
                } else {
                    $language = 'en';
                }
            } else {
                if (substr($hostName, -3) == '.ru') {
                    $language = 'ru';
                }
            }
    
            /**
             * Use detected language or try to get language from query params
             */
            $language = $language ?? ($query[self::LANG_KEY] ?? null);
    
            /**
             *  Use detected language or try to get language from request headers
             */
            $language = $language ?? (
                $headers->has(self::LANG_KEY)
                    ? (
                        array_key_exists($headers->get(self::LANG_KEY), Yii::$app->params['languages'])
                            ? $headers->get(self::LANG_KEY)
                            : null
                    )
                    : null
                );
    
            /**
             *  Use detected language or try to get language from cookies headers
             */
            $language = $language ?? (
                $cookies->has(self::LANG_KEY)
                    ? (
                        array_key_exists($cookies->getValue(self::LANG_KEY), Yii::$app->params['languages'])
                            ? $cookies->getValue(self::LANG_KEY)
                            : null
                    )
                    : null
                );
    
            /**
             *  Use detected language or try to get language from user profile
             */
            if (empty($language) && !Yii::$app->user->isGuest) {
                /** @var User $user */
                $user = Yii::$app->user->identity;
                if ($user instanceof User && isset($user->language) && !empty($user->language)) {
                    $language = $user->language;
                }
            }
    
            /**
             * If no language use default
             */
            $language = $language ?? Yii::$app->language;
    
            // Add a cookie for next requests
            $languageCookie = new Cookie([
                'name' => self::LANG_KEY,
                'value' => $language,
                'expire' => time() + 86400 * 365,
            ]);
            Yii::$app->getResponse()->getCookies()->add($languageCookie);
    
            // store selected language to application 
            Yii::$app->language = $language;
        }
    }
    

    Now you can use this to achieve your goals, also build your priority of detecting language.

    From Angular you may set a language header for requests.

    const headers = {
        'Accept': '*/*',
        'Content-Type': 'multipart/form-data;',
        'X-Requested-With': 'XMLHttpRequest',
        'Access-Control-Allow-Credentials': 'true',
        'Access-Control-Allow-Origin': '*',
        'language': 'en'
    }