2 追蹤者

國際化

國際化 (I18N) 指的是設計軟體應用程式的過程,使其能夠適應各種語言和地區,而無需工程變更。對於 Web 應用程式來說,這尤其重要,因為潛在使用者可能遍布全球。Yii 提供了全方位的 I18N 功能,支援訊息翻譯、視圖翻譯、日期和數字格式化。

地區設定與語言

地區設定

地區設定是一組參數,用於定義使用者的語言、國家以及使用者希望在其使用者介面中看到的任何特殊變體偏好。它通常由一個 ID 識別,該 ID 由語言 ID 和地區 ID 組成。

例如,ID en-US 代表「英語和美國」的地區設定。

為了保持一致性,Yii 應用程式中使用的所有地區設定 ID 都應標準化為 ll-CC 格式,其中 ll 是根據 ISO-639 的兩個或三個字母的小寫語言代碼,CC 是根據 ISO-3166 的兩個字母的國家代碼。有關地區設定的更多詳細資訊,請參閱 ICU 專案的文件

語言

在 Yii 中,我們經常使用術語「語言」來指稱地區設定。

Yii 應用程式使用兩種語言

  • 來源語言:指的是原始碼中的文字訊息所使用的語言。
  • 目標語言:這是應該用於向終端使用者顯示內容的語言。

所謂的訊息翻譯服務主要將文字訊息從來源語言翻譯成目標語言。

設定

您可以在「應用程式設定」中設定應用程式語言,如下所示

return [
    // set target language to be Russian
    'language' => 'ru-RU',
    
    // set source language to be English
    'sourceLanguage' => 'en-US',
    
    ......
];

來源語言 的預設值為 en-US,表示美式英語。建議 您保持此預設值不變。通常,找到可以從「英語翻譯成其他語言」的人比找到可以從「非英語翻譯成非英語」的人容易得多。

您通常需要根據不同的因素(例如終端使用者的語言偏好)動態設定 目標語言。您可以使用以下陳述式來變更目標語言,而不是在應用程式設定中設定它

// change target language to Chinese
\Yii::$app->language = 'zh-CN';

提示:如果您的來源語言在程式碼的不同部分有所不同,您可以覆寫不同訊息來源的來源語言,這將在下一節中描述。

訊息翻譯

從來源語言到目標語言

訊息翻譯服務將文字訊息從一種語言(通常是 來源語言)翻譯成另一種語言(通常是 目標語言)。

它透過在訊息來源中查找要翻譯的訊息來執行翻譯,訊息來源儲存原始訊息和翻譯後的訊息。如果找到訊息,將傳回相應的翻譯訊息;否則,將傳回未翻譯的原始訊息。

如何實作

若要使用訊息翻譯服務,您主要需要完成以下工作

  1. 將每個需要翻譯的文字訊息包裝在對 Yii::t() 方法的呼叫中。
  2. 設定一個或多個訊息來源,訊息翻譯服務可以在其中查找翻譯後的訊息。
  3. 讓翻譯人員翻譯訊息並將其儲存在訊息來源中。

1. 包裝文字訊息

方法 Yii::t() 可以像下面這樣使用,

echo \Yii::t('app', 'This is a string to translate!');

其中第二個參數指的是要翻譯的文字訊息,而第一個參數指的是用於對訊息進行分類的類別名稱。

2. 設定一個或多個訊息來源

Yii::t() 方法將呼叫 i18n 應用程式組件 translate 方法來執行實際的翻譯工作。該組件可以在應用程式設定中設定如下,

'components' => [
    // ...
    'i18n' => [
        'translations' => [
            'app*' => [
                'class' => 'yii\i18n\PhpMessageSource',
                //'basePath' => '@app/messages',
                //'sourceLanguage' => 'en-US',
                'fileMap' => [
                    'app' => 'app.php',
                    'app/error' => 'error.php',
                ],
            ],
        ],
    ],
],

在上面的程式碼中,正在設定 yii\i18n\PhpMessageSource 支援的訊息來源。

使用 * 符號的類別萬用字元

模式 app* 表示所有名稱以 app 開頭的訊息類別都應使用此訊息來源進行翻譯。

3. 讓翻譯人員翻譯訊息並將其儲存在訊息來源中

yii\i18n\PhpMessageSource 類別使用具有簡單 PHP 陣列的 PHP 檔案來儲存訊息翻譯。這些檔案包含 來源語言 中的訊息到 目標語言 中的翻譯的對應。

資訊:您可以使用 message 指令 自動產生這些 PHP 檔案,這將在本章稍後介紹。

每個 PHP 檔案對應於單一類別的訊息。預設情況下,檔案名稱應與類別名稱相同。app/messages/nl-NL/main.php 的範例:

<?php

/**
* Translation map for nl-NL
*/
return [
    'welcome' => 'welkom'
];

檔案對應

您可以設定 fileMap,將類別對應到具有不同命名方法的 PHP 檔案。

在上面的範例中,類別 app/error 對應到 PHP 檔案 @app/messages/ru-RU/error.php(假設 ru-RU 是目標語言)。但是,如果沒有此設定,類別將對應到 @app/messages/ru-RU/app/error.php

其他儲存類型

除了將訊息儲存在 PHP 檔案中,您還可以使用以下訊息來源將翻譯後的訊息儲存在不同的儲存中

訊息格式化

翻譯訊息時,您可以嵌入一些佔位符,並讓動態參數值替換它們。您甚至可以使用特殊的佔位符語法,讓參數值根據目標語言進行格式化。在本小節中,我們將描述格式化訊息的不同方法。

訊息參數

在要翻譯的訊息中,您可以嵌入一個或多個參數(也稱為佔位符),以便可以用給定的值替換它們。透過給定不同的值集,您可以動態地改變翻譯後的訊息。在以下範例中,訊息 'Hello, {username}!' 中的佔位符 {username} 將分別被 'Alexander''Qiang' 替換。

$username = 'Alexander';
// display a translated message with username being "Alexander"
echo \Yii::t('app', 'Hello, {username}!', [
    'username' => $username,
]);

$username = 'Qiang';
// display a translated message with username being "Qiang"
echo \Yii::t('app', 'Hello, {username}!', [
    'username' => $username,
]);

在翻譯包含佔位符的訊息時,您應該保持佔位符不變。這是因為當您呼叫 Yii::t() 來翻譯訊息時,佔位符將被實際值替換。

您可以使用具名佔位符位置佔位符,但不能在單一訊息中同時使用兩者。

先前的範例示範了如何使用具名佔位符。也就是說,每個佔位符都以 {name} 的格式寫入,並且您提供一個關聯陣列,其鍵是佔位符名稱(不帶大括號),其值是要替換的相應值佔位符。

位置佔位符使用從零開始的整數序列作為名稱,這些名稱根據它們在 Yii::t() 呼叫中的位置被提供的值替換。在以下範例中,位置佔位符 {0}{1}{2} 將分別被 $price$count$subtotal 的值替換。

$price = 100;
$count = 2;
$subtotal = 200;
echo \Yii::t('app', 'Price: {0}, Count: {1}, Subtotal: {2}', [$price, $count, $subtotal]);

在單一位置參數的情況下,其值可以在不將其包裝到陣列中的情況下指定

echo \Yii::t('app', 'Price: {0}', $price);

提示:在大多數情況下,您應該使用具名佔位符。這是因為名稱將使翻譯人員更好地理解正在翻譯的整個訊息。

參數格式化

您可以在訊息的佔位符中指定額外的格式化規則,以便可以在參數值替換佔位符之前正確格式化它們。在以下範例中,價格參數值將被視為數字並格式化為貨幣值

$price = 100;
echo \Yii::t('app', 'Price: {0,number,currency}', $price);

注意:參數格式化需要安裝 intl PHP 擴展

您可以使用簡短形式或完整形式來指定帶有格式化的佔位符

short form: {name,type}
full form: {name,type,style}

注意:如果您需要使用特殊字元,例如 {}'#,請將它們包裝在 '

echo Yii::t('app', "Example of string with ''-escaped characters'': '{' '}' '{test}' {count,plural,other{''count'' value is # '#{}'}}", ['count' => 3]);

完整的格式在 ICU 文件 中描述。在下面,我們將展示一些常見用法。

數字

參數值被視為數字。例如,

$sum = 42;
echo \Yii::t('app', 'Balance: {0,number}', $sum);

您可以將可選參數樣式指定為 integercurrencypercent

$sum = 42;
echo \Yii::t('app', 'Balance: {0,number,currency}', $sum);

您也可以指定自訂模式來格式化數字。例如,

$sum = 42;
echo \Yii::t('app', 'Balance: {0,number,,000,000000}', $sum);

自訂格式中使用的字元可以在 ICU API 參考 的「特殊模式字元」部分中找到。

該值始終根據您要翻譯成的地區設定進行格式化,即,您無法在不更改翻譯地區設定的情況下更改小數或千位分隔符、貨幣符號等。如果您需要自訂這些,可以使用 yii\i18n\Formatter::asDecimal()yii\i18n\Formatter::asCurrency()

日期

參數值應格式化為日期。例如,

echo \Yii::t('app', 'Today is {0,date}', time());

您可以將可選參數樣式指定為 shortmediumlongfull

echo \Yii::t('app', 'Today is {0,date,short}', time());

您也可以指定自訂模式來格式化日期值

echo \Yii::t('app', 'Today is {0,date,yyyy-MM-dd}', time());

格式化參考.

時間

參數值應格式化為時間。例如,

echo \Yii::t('app', 'It is {0,time}', time());

您可以將可選參數樣式指定為 shortmediumlongfull

echo \Yii::t('app', 'It is {0,time,short}', time());

您也可以指定自訂模式來格式化時間值

echo \Yii::t('app', 'It is {0,date,HH:mm}', time());

格式化參考.

拼寫

參數值應被視為數字並格式化為拼寫。例如,

// may produce "42 is spelled as forty-two"
echo \Yii::t('app', '{n,number} is spelled as {n,spellout}', ['n' => 42]);

預設情況下,數字拼寫為基數。可以更改

// may produce "I am forty-seventh agent"
echo \Yii::t('app', 'I am {n,spellout,%spellout-ordinal} agent', ['n' => 47]);

請注意,spellout,% 之間不應有空格。

要取得您正在使用的地區設定可用的選項列表,請查看 https://intl.rmcreative.ru/ 上的「編號架構、拼寫」。

序數

參數值應被視為數字並格式化為序數名稱。例如,

// may produce "You are the 42nd visitor here!"
echo \Yii::t('app', 'You are the {n,ordinal} visitor here!', ['n' => 42]);

序數支援更多種格式化語言的方式,例如西班牙語

// may produce 471ª
echo \Yii::t('app', '{n,ordinal,%digits-ordinal-feminine}', ['n' => 471]);

請注意,ordinal,% 之間不應有空格。

要取得您正在使用的地區設定可用的選項列表,請查看 https://intl.rmcreative.ru/ 上的「編號架構、序數」。

持續時間

參數值應被視為秒數並格式化為持續時間字串。例如,

// may produce "You are here for 47 sec. already!"
echo \Yii::t('app', 'You are here for {n,duration} already!', ['n' => 47]);

持續時間支援更多種格式化方式

// may produce 130:53:47
echo \Yii::t('app', '{n,duration,%in-numerals}', ['n' => 471227]);

請注意,duration,% 之間不應有空格。

要取得您正在使用的地區設定可用的選項列表,請查看 https://intl.rmcreative.ru/ 上的「編號架構、持續時間」。

複數

不同的語言有不同的變形複數的方式。Yii 提供了一種方便的方式來翻譯不同複數形式的訊息,即使對於非常複雜的規則也運作良好。無需直接處理變形規則,只需在某些情況下提供變形詞的翻譯就足夠了。例如,

// When $n = 0, it may produce "There are no cats!"
// When $n = 1, it may produce "There is one cat!"
// When $n = 42, it may produce "There are 42 cats!"
echo \Yii::t('app', 'There {n,plural,=0{are no cats} =1{is one cat} other{are # cats}}!', ['n' => $n]);

在上面的複數規則參數中,= 表示顯式值。因此,=0 表示正好為零,=1 表示正好為一。other 代表任何其他值。# 會被替換為根據目標語言格式化的 n 值。

在某些語言中,複數形式可能非常複雜。在以下俄語範例中,=1 完全匹配 n = 1,而 one 匹配 21101

Здесь {n,plural,=0{котов нет} =1{есть один кот} one{# кот} few{# кота} many{# котов} other{# кота}}!

這些 otherfewmany 和其他特殊參數名稱因語言而異。要了解您應該為特定地區設定指定哪些參數名稱,請參閱 https://intl.rmcreative.ru/ 上的「複數規則、基數」。或者,您可以參閱 unicode.org 上的規則參考

注意:上面的俄語訊息主要用作翻譯後的訊息,而不是原始訊息,除非您將應用程式的 來源語言 設定為 ru-RU 並從俄語翻譯。

當找不到在 Yii::t() 呼叫中指定的原始訊息的翻譯時,來源語言 的複數規則將應用於原始訊息。

當字串如下所示時,有一個 offset 參數

$likeCount = 2;
echo Yii::t('app', 'You {likeCount,plural,
    offset: 1
    =0{did not like this}
    =1{liked this}
    one{and one other person liked this}
    other{and # others liked this}
}', [
    'likeCount' => $likeCount
]);

// You and one other person liked this

序數選擇

selectordinal 的參數類型旨在根據您要翻譯成的地區設定的序數語言規則選擇字串

$n = 3;
echo Yii::t('app', 'You are the {n,selectordinal,one{#st} two{#nd} few{#rd} other{#th}} visitor', ['n' => $n]);
// For English it outputs:
// You are the 3rd visitor

// Translation
'You are the {n,selectordinal,one{#st} two{#nd} few{#rd} other{#th}} visitor' => 'Вы {n,selectordinal,other{#-й}} посетитель',

// For Russian translation it outputs:
// Вы 3-й посетитель

格式與用於複數的格式非常接近。要了解您應該為特定地區設定指定哪些參數,請參閱 https://intl.rmcreative.ru/ 上的「複數規則、序數」。或者,您可以參閱 unicode.org 上的規則參考

選擇

您可以使用 select 參數類型根據參數值選擇短語。例如,

// It may produce "Snoopy is a dog and it loves Yii!"
echo \Yii::t('app', '{name} is a {gender} and {gender,select,female{she} male{he} other{it}} loves Yii!', [
    'name' => 'Snoopy',
    'gender' => 'dog',
]);

在上面的表達式中,femalemale 都是可能的參數值,而 other 處理與它們都不匹配的值。在每個可能的參數值之後,您應該指定一個短語並將其括在一對大括號中。

指定預設訊息來源

您可以指定預設訊息來源,該來源將用作與任何已設定類別都不匹配的類別的回退。您可以透過設定萬用字元類別 * 來執行此操作。為了做到這一點,請將以下內容新增至應用程式設定

//configure i18n component

'i18n' => [
    'translations' => [
        '*' => [
            'class' => 'yii\i18n\PhpMessageSource'
        ],
    ],
],

現在您可以使用類別而無需設定每個類別,這類似於 Yii 1.1 行為。類別的訊息將從預設翻譯 basePath 下的檔案載入,即 @app/messages

echo Yii::t('not_specified_category', 'message from unspecified category');

訊息將從 @app/messages/<LanguageCode>/not_specified_category.php 載入。

翻譯模組訊息

如果您想要翻譯模組的訊息,並避免為所有訊息使用單一翻譯檔案,您可以像下面這樣做

<?php

namespace app\modules\users;

use Yii;

class Module extends \yii\base\Module
{
    public $controllerNamespace = 'app\modules\users\controllers';

    public function init()
    {
        parent::init();
        $this->registerTranslations();
    }

    public function registerTranslations()
    {
        Yii::$app->i18n->translations['modules/users/*'] = [
            'class' => 'yii\i18n\PhpMessageSource',
            'sourceLanguage' => 'en-US',
            'basePath' => '@app/modules/users/messages',
            'fileMap' => [
                'modules/users/validation' => 'validation.php',
                'modules/users/form' => 'form.php',
                ...
            ],
        ];
    }

    public static function t($category, $message, $params = [], $language = null)
    {
        return Yii::t('modules/users/' . $category, $message, $params, $language);
    }

}

在上面的範例中,我們使用萬用字元進行匹配,然後按每個需要的檔案篩選每個類別。您可以直接使用類別對應到同名檔案的慣例,而不是使用 fileMap。現在您可以直接使用 Module::t('validation', 'your custom validation message')Module::t('form', 'some form label')

翻譯小工具訊息

上面應用於模組的相同規則也適用於小工具,例如

<?php

namespace app\widgets\menu;

use yii\base\Widget;
use Yii;

class Menu extends Widget
{

    public function init()
    {
        parent::init();
        $this->registerTranslations();
    }

    public function registerTranslations()
    {
        $i18n = Yii::$app->i18n;
        $i18n->translations['widgets/menu/*'] = [
            'class' => 'yii\i18n\PhpMessageSource',
            'sourceLanguage' => 'en-US',
            'basePath' => '@app/widgets/menu/messages',
            'fileMap' => [
                'widgets/menu/messages' => 'messages.php',
            ],
        ];
    }

    public function run()
    {
        echo $this->render('index');
    }

    public static function t($category, $message, $params = [], $language = null)
    {
        return Yii::t('widgets/menu/' . $category, $message, $params, $language);
    }

}

您可以直接使用類別對應到同名檔案的慣例,而不是使用 fileMap。現在您可以直接使用 Menu::t('messages', 'new messages {messages}', ['{messages}' => 10])

注意:對於小工具,您也可以使用 i18n 視圖,其規則與應用於控制器的規則相同。

翻譯框架訊息

Yii 隨附驗證錯誤和一些其他字串的預設翻譯訊息。這些訊息都在類別 yii 中。有時您想要更正應用程式的預設框架訊息翻譯。為了做到這一點,請設定 i18n 應用程式組件,如下所示

'i18n' => [
    'translations' => [
        'yii' => [
            'class' => 'yii\i18n\PhpMessageSource',
            'sourceLanguage' => 'en-US',
            'basePath' => '@app/messages'
        ],
    ],
],

現在您可以將調整後的翻譯放在 @app/messages/<language>/yii.php

處理遺失的翻譯

即使翻譯在來源中遺失,Yii 仍會顯示請求的訊息內容。在您的原始訊息是有效的詳細文字的情況下,此行為非常方便。但是,有時這還不夠。您可能需要對情況執行一些自訂處理,當請求的翻譯在來源中遺失時。這可以使用 missingTranslation 事件來達成,此事件屬於 yii\i18n\MessageSource

例如,您可能想要用一些顯著的東西標記所有遺失的翻譯,以便可以在頁面上輕鬆找到它們。首先,您需要設定一個事件處理常式。這可以在應用程式配置中完成

'components' => [
    // ...
    'i18n' => [
        'translations' => [
            'app*' => [
                'class' => 'yii\i18n\PhpMessageSource',
                'fileMap' => [
                    'app' => 'app.php',
                    'app/error' => 'error.php',
                ],
                'on missingTranslation' => ['app\components\TranslationEventHandler', 'handleMissingTranslation']
            ],
        ],
    ],
],

現在您需要實作您自己的事件處理常式

<?php

namespace app\components;

use yii\i18n\MissingTranslationEvent;

class TranslationEventHandler
{
    public static function handleMissingTranslation(MissingTranslationEvent $event)
    {
        $event->translatedMessage = "@MISSING: {$event->category}.{$event->message} FOR LANGUAGE {$event->language} @";
    }
}

如果事件處理常式設定了 yii\i18n\MissingTranslationEvent::$translatedMessage,它將會顯示為翻譯結果。

注意:每個訊息來源會個別處理其遺失的翻譯。如果您正在使用多個訊息來源,並希望它們以相同的方式處理遺失的翻譯,您應該將相應的事件處理常式指派給它們每一個。

使用 message 命令

翻譯可以儲存在 php 檔案.po 檔案資料庫 中。請參閱特定類別以取得其他選項。

首先,您需要建立一個組態檔。決定您要將其儲存在哪裡,然後發出命令

./yii message/config-template path/to/config.php

開啟建立的檔案並調整參數以符合您的需求。請特別注意

  • languages:一個陣列,表示您的應用程式應翻譯成的語言;
  • messagePath:儲存訊息檔案的路徑,應與組態中聲明的 i18nbasePath 參數相符。

您也可以使用 './yii message/config' 命令透過 cli 動態產生具有指定選項的組態檔。例如,您可以像下面這樣設定 languagesmessagePath 參數

./yii message/config --languages=de,ja --messagePath=messages path/to/config.php

若要取得可用選項的清單,請執行以下命令

./yii help message/config

一旦您完成組態檔,您最終可以使用以下命令提取您的訊息

./yii message path/to/config.php

此外,您可以使用選項來動態變更提取的參數。

然後您會在您的 messagePath 目錄中找到您的檔案(如果您已選擇基於檔案的翻譯)。

檢視翻譯

有時您可能想要翻譯整個檢視腳本,而不是翻譯個別的文字訊息。為了實現此目標,只需翻譯檢視並將其儲存在子目錄下,該子目錄的名稱與目標語言相同。例如,如果您想要翻譯檢視腳本 views/site/index.php,且目標語言為 ru-RU,您可以翻譯該檢視並將其儲存為檔案 views/site/ru-RU/index.php。現在,每當您呼叫 yii\base\View::renderFile() 或任何調用此方法的方法(例如 yii\base\Controller::render())來渲染檢視 views/site/index.php 時,它最終會渲染翻譯後的檢視 views/site/ru-RU/index.php,而不是原始檢視。

注意:如果目標語言來源語言 相同,則無論是否存在翻譯後的檢視,都將渲染原始檢視。

格式化日期和數字值

請參閱 資料格式化 章節以取得詳細資訊。

設定 PHP 環境

Yii 使用 PHP intl 擴充功能 來提供其大部分 I18N 功能,例如 yii\i18n\Formatter 類別的日期和數字格式化,以及使用 yii\i18n\MessageFormatter 的訊息格式化。當未安裝 intl 擴充功能時,這兩個類別都提供了後備機制。但是,後備實作僅適用於英文目標語言。因此,強烈建議在需要 I18N 時安裝 intl

PHP intl 擴充功能 基於 ICU 函式庫,該函式庫為所有不同的語言環境提供了知識和格式化規則。不同版本的 ICU 可能會產生不同的日期和數字值格式化結果。為了確保您的網站在所有環境中產生相同的結果,建議您在所有環境中安裝相同版本的 intl 擴充功能(以及相同版本的 ICU)。

若要找出 PHP 使用的 ICU 版本,您可以執行以下腳本,這將為您提供正在使用的 PHP 和 ICU 版本。

<?php
echo "PHP: " . PHP_VERSION . "\n";
echo "ICU: " . INTL_ICU_VERSION . "\n";
echo "ICU Data: " . INTL_ICU_DATA_VERSION . "\n";

也建議您使用等於或大於 49 的 ICU 版本。這將確保您可以使用本文档中描述的所有功能。例如,低於 49 的 ICU 版本不支援在複數規則中使用 # 佔位符。請參閱 https://icu.unicode.org/download 以取得可用 ICU 版本的完整清單。請注意,版本編號在 4.8 版本之後已變更(例如,ICU 4.8、ICU 49、ICU 50 等)

此外,ICU 函式庫附帶的時區資料庫中的資訊可能已過時。請參閱 ICU 手冊 以取得有關更新時區資料庫的詳細資訊。雖然對於輸出格式化,使用的是 ICU 時區資料庫,但 PHP 使用的時區資料庫也可能相關。您可以透過安裝最新版本的 pecl 套件 timezonedb 來更新它。

發現錯字或您認為此頁面需要改進?
在 github 上編輯 !