没有不值得去解决的问题,也没有不值得去学习的技术!

Analyzing and Resolving a Yii 2 Advanced Template i18n Message Translation Bug

查看 \vendor\yiisoft\yii2\i18n\I18N.php,文件映射的类别名:yii、app,始终定义类别 yii 和 app 。前者指的是 Yii 核心中使用的消息框架代码,而后者是指自定义应用程序代码的默认消息类别。其判断类别:app 是否存在,如果不存在,则覆盖,但是,其判断的层级仅为第一级,没有深入至更深的层级,因此,自定义的类别:app 会被覆盖

1. The configuration code for the i18n application component is as follows:

PHP

    'components' => [
        'i18n' => [
            'translations' => [
                'model/*' => [
                    'class' => 'yii\i18n\PhpMessageSource',
                    'forceTranslation' => true,
                    'basePath' => '@common/messages',
                ],
                '*' => [
                    'class' => 'yii\i18n\PhpMessageSource',
                    'forceTranslation' => true,
                    'basePath' => '@app/messages',
                    'fileMap' => [
                        'app' => 'app.php',
                        'error' => 'error.php',
                        'success' => 'success.php',
                    ],
                ],
            ],
        ],
    ],

2. Request the API in Postman. In the Headers, set Accept-Language:zh-CN. When the target language is Simplified Chinese and the file mapping category name is success, it is mapped to the PHP file @app/messages/zh-CN/success.php, as shown in Figure 1.

Request the API in Postman. In the Headers, set Accept-Language:zh-CN. When the target language is Simplified Chinese and the file mapping category name is success, it is mapped to the PHP file @app/messages/zh-CN/success.php
Figure 1
PHP

return ['code' => 10000, 'message' => Yii::t('success', '127016'), 'data' => ['items' => array_values($models)]];

3. Request the API in Postman. In the Headers, set Accept-Language:zh-CN. When the target language is Simplified Chinese and the file mapping category name is app, it is mapped to the PHP file @app/messages/zh-CN/app.php, as shown in Figure 2.

Request the API in Postman. In the Headers, set Accept-Language:zh-CN. When the target language is Simplified Chinese and the file mapping category name is app, it is mapped to the PHP file @app/messages/zh-CN/app.php
Figure 2
PHP

$planLog->opinion = Yii::t('app', Yii::t('app', Yii::t('app', 302016), ['accepted_group_name' => $acceptedGroupName, 'accepted_user_nick' => $identity->user_nick]));

4. Request the API in Postman. In the Headers, set Accept-Language:en-US. When the target language is American English and the file mapping category name is success, it is mapped to the PHP file @app/messages/en-US/success.php, as shown in Figure 3.

Request the API in Postman. In the Headers, set Accept-Language:en-US. When the target language is American English and the file mapping category name is success, it is mapped to the PHP file @app/messages/en-US/success.php
Figure 3

5. Request the API in Postman. In the Headers, set Accept-Language:en-US. When the target language is American English and the file mapping category name is app, it is not mapped to the PHP file @app/messages/en-US/app.php. The message to be translated is not found in the language file, so the original untranslated message is returned, namely: 302011. As shown in Figure 4.

Request the API in Postman. In the Headers, set Accept-Language:en-US. When the target language is American English and the file mapping category name is app, it is not mapped to the PHP file @app/messages/en-US/app.php. The message to be translated is not found in the language file, so the original untranslated message is returned, namely: 302011.
Figure 4

6. Edit E:\wwwroot\pcs-api\vendor\yiisoft\yii2\i18n\MessageSource.php. When the target language is Simplified Chinese, the response is: 11. When the target language is American English, the response is: 21. That is, when the target language is American English and the file mapping category name is app, the step of translating the specified message is skipped. As shown in Figure 5 and Figure 6.

When the target language is Simplified Chinese, the response is: 11
Figure 5
When the target language is American English, the response is: 21. That is, when the target language is American English and the file mapping category name is app, the step of translating the specified message is skipped
Figure 6
PHP

        Yii::t('app', '302011');
        exit;

PHP

    public function translate($category, $message, $language)
    {
        if ($this->forceTranslation || $language !== $this->sourceLanguage) {
            echo 1;
            exit;
            return $this->translateMessage($category, $message, $language);
        }

        echo 2;
        exit;

        return false;
    }

7. When the target language is American English and the file mapping category name is app, the value of $this->forceTranslation is false, as shown in Figure 7.

When the target language is American English and the file mapping category name is app, the value of $this->forceTranslation is false
Figure 7
PHP

    public function translate($category, $message, $language)
    {
        var_dump($this->forceTranslation);
        exit;
        if ($this->forceTranslation || $language !== $this->sourceLanguage) {
            echo 1;
            exit;
            return $this->translateMessage($category, $message, $language);
        }

        echo 2;
        exit;

        return false;
    }

8. When the target language is American English and the file mapping category name is success, the value of $this->forceTranslation is true, as shown in Figure 8.

When the target language is American English and the file mapping category name is success, the value of $this->forceTranslation is true
Figure 8
PHP

        Yii::t('success', '302011');
        exit;

9. Review \vendor\yiisoft\yii2\i18n\I18N.php. The file mapping category names yii and app always define the categories yii and app. The former refers to the message framework code used in the Yii core, while the latter refers to the default message category for custom application code. It checks whether the category app exists, and overrides it if it does not. However, this check only applies to the first level and does not go deeper. Therefore, the custom category app is overridden, as shown in Figure 9.

Review \vendor\yiisoft\yii2\i18n\I18N.php. The file mapping category names yii and app always define the categories yii and app. The former refers to the message framework code used in the Yii core, while the latter refers to the default message category for custom application code. It checks whether the category app exists, and overrides it if it does not. However, this check only applies to the first level and does not go deeper. Therefore, the custom category app is overridden
Figure 9
PHP

    /**
     * @var array list of [[MessageSource]] configurations or objects. The array keys are message
     * category patterns, and the array values are the corresponding [[MessageSource]] objects or the configurations
     * for creating the [[MessageSource]] objects.
     *
     * The message category patterns can contain the wildcard `*` at the end to match multiple categories with the same prefix.
     * For example, `app/*` matches both `app/cat1` and `app/cat2`.
     *
     * The `*` category pattern will match all categories that do not match any other category patterns.
     *
     * This property may be modified on the fly by extensions who want to have their own message sources
     * registered under their own namespaces.
     *
     * The category `yii` and `app` are always defined. The former refers to the messages used in the Yii core
     * framework code, while the latter refers to the default message category for custom application code.
     * By default, both of these categories use [[PhpMessageSource]] and the corresponding message files are
     * stored under `@yii/messages` and `@app/messages`, respectively.
     *
     * You may override the configuration of both categories.
     */
    public $translations;


    /**
     * Initializes the component by configuring the default message categories.
     */
    public function init()
    {
        parent::init();
        if (!isset($this->translations['yii']) && !isset($this->translations['yii*'])) {
            $this->translations['yii'] = [
                'class' => 'yii\i18n\PhpMessageSource',
                'sourceLanguage' => 'en-US',
                'basePath' => '@yii/messages',
            ];
        }

        if (!isset($this->translations['app']) && !isset($this->translations['app*'])) {
            $this->translations['app'] = [
                'class' => 'yii\i18n\PhpMessageSource',
                'sourceLanguage' => Yii::$app->sourceLanguage,
                'basePath' => '@app/messages',
            ];
        }
    }

10. The solution is as follows: change the file mapping category name from app to application, so it is mapped to the PHP file application.php. Update the configuration code for the i18n application component as follows:

PHP

    'components' => [
        'i18n' => [
            'translations' => [
                'model/*' => [
                    'class' => 'yii\i18n\PhpMessageSource',
                    'forceTranslation' => true,
                    'basePath' => '@common/messages',
                ],
                '*' => [
                    'class' => 'yii\i18n\PhpMessageSource',
                    'forceTranslation' => true,
                    'basePath' => '@app/messages',
                    'fileMap' => [
                        'application' => 'application.php',
                        'error' => 'error.php',
                        'success' => 'success.php',
                    ],
                ],
            ],
        ],
    ],

11. Rename the file \common\messages\en-US\app.php to \common\messages\en-US\application.php, rename the file \common\messages\zh-CN\app.php to \common\messages\zh-CN\application.php, rename the file \api\messages\en-US\app.php to \api\messages\en-US\application.php, and rename the file \api\messages\zh-CN\app.php to \api\messages\zh-CN\application.php. Do the same for all other application directories. Batch replace Yii::t(‘app’, with Yii::t(‘application’,. As shown in Figure 10.

Rename the file \common\messages\en-US\app.php to \common\messages\en-US\application.php, rename the file \common\messages\zh-CN\app.php to \common\messages\zh-CN\application.php, rename the file \api\messages\en-US\app.php to \api\messages\en-US\application.php, and rename the file \api\messages\zh-CN\app.php to \api\messages\zh-CN\application.php. Do the same for all other application directories. Batch replace Yii::t('app', with Yii::t('application',
Rename the file \common\messages\en-US\app.php to \common\messages\en-US\application.php, rename the file \common\messages\zh-CN\app.php to \common\messages\zh-CN\application.php, rename the file \api\messages\en-US\app.php to \api\messages\en-US\application.php, and rename the file \api\messages\zh-CN\app.php to \api\messages\zh-CN\application.php. Do the same for all other application directories. Batch replace Yii::t(‘app’, with Yii::t(‘application’,

12. When the target language is American English and the file mapping category name is application, it is now mapped to the PHP file @app/messages/en-US/application.php, as shown in Figure 11.

When the target language is American English and the file mapping category name is application, it is now mapped to the PHP file @app/messages/en-US/application.php
Figure 11

关于作者 · 持续学习与技术实践

我是一名拥有 15 年以上开发经验的技术从业者,自 2013 年起持续运营个人技术博客,记录工作与个人项目中遇到的真实问题,以及解决问题过程中的探索、实践和思考。

除了技术写作,我也在持续开发和维护自己的独立项目,探索新技术和新工具在实际场景中的应用。我始终相信,没有不值得去解决的问题,也没有不值得去学习的技术。

如果这篇文章对你有所帮助,欢迎浏览博客中的其他文章,也欢迎交流技术经验与想法。

关于我  ·  GitHub  ·  邮件联系