國(guó)際化(I18N)是指在設(shè)計(jì)軟件時(shí),使它可以無(wú)需做大的改變就能夠適應(yīng)不同的語(yǔ)言和地區(qū)的需要。對(duì)于 Web 應(yīng)用程序, 這有著特別重要的意義,因?yàn)闈撛诘挠脩艨赡軙?huì)在全球范圍內(nèi)。 Yii 提供的國(guó)際化功能支持全方位信息翻譯,視圖翻譯,日期和數(shù)字格式化。
區(qū)域設(shè)置是一組參數(shù)以定義用戶希望能在他們的用戶界面所看到用戶的語(yǔ)言,國(guó)家和任何特殊的偏好。 它通常是由語(yǔ)言 ID 和區(qū)域 ID 組成。例如,ID “en-US” 代表英語(yǔ)和美國(guó)的語(yǔ)言環(huán)境。為了保持一致性, 在 Yii 應(yīng)用程序中使用的所有區(qū)域 ID 應(yīng)該規(guī)范化為?ll-CC
,其中?ll
?是根據(jù)兩個(gè)或三個(gè)字母的小寫字母語(yǔ)言代碼?ISO-639?和?CC
?是兩個(gè)字母的國(guó)別代碼?ISO-3166。 有關(guān)區(qū)域設(shè)置的更多細(xì)節(jié)可以看?ICU 項(xiàng)目文檔。
在 Yii中,我們經(jīng)常用 “l(fā)anguage” 來(lái)代表一個(gè)區(qū)域。
一個(gè) Yii 應(yīng)用使用兩種語(yǔ)言:yii\base\Application::$sourceLanguage 和 yii\base\Application::$language 。前者指的是寫在代碼中的語(yǔ)言,后者是向最終用戶顯示內(nèi)容的語(yǔ)言。 而信息翻譯服務(wù)主要是將文本消息從原語(yǔ)言翻譯到目標(biāo)語(yǔ)言。
可以用類似下面的應(yīng)用程序配置來(lái)配置應(yīng)用程序語(yǔ)言:
return [
// 設(shè)置目標(biāo)語(yǔ)言為俄語(yǔ)
'language' => 'ru-RU',
// 設(shè)置源語(yǔ)言為英語(yǔ)
'sourceLanguage' => 'en-US',
......
];
默認(rèn)的 yii\base\Application::$sourceLanguage 值是?en-US
,即美國(guó)英語(yǔ)。 建議你保留此默認(rèn)值不變,因?yàn)橥ǔW屓藢⒂⒄Z(yǔ)翻譯成其它語(yǔ)言要比將其它語(yǔ)言翻譯成其它語(yǔ)言容易得多。
你經(jīng)常需要根據(jù)不同的因素來(lái)動(dòng)態(tài)地設(shè)置 yii\base\Application::$language ,如最終用戶的語(yǔ)言首選項(xiàng)。 要在應(yīng)用程序配置中配置它,你可以使用下面的語(yǔ)句來(lái)更改目標(biāo)語(yǔ)言:
// 改變目標(biāo)語(yǔ)言為中文
\Yii::$app->language = 'zh-CN';
消息翻譯服務(wù)用于將一條文本信息從一種語(yǔ)言(通常是 yii\base\Application::$sourceLanguage ) 翻譯成另一種語(yǔ)言(通常是 yii\base\Application::$language)。 它的翻譯原理是通過在語(yǔ)言文件中查找要翻譯的信息以及翻譯的結(jié)果。如果要翻譯的信息可以在語(yǔ)言文件中找到,會(huì)返回相應(yīng)的翻譯結(jié)果; 否則會(huì)返回原始未翻譯的信息。
為了使用消息翻譯服務(wù),需要做如下工作:
這個(gè) Yii::t() 方法的用法如下,
echo \Yii::t('app', 'This is a string to translate!');
第一個(gè)參數(shù)指儲(chǔ)存消息來(lái)源的類別名稱,第二個(gè)參數(shù)指需要被翻譯的消息。
這個(gè) Yii::t() 方法會(huì)調(diào)用?i18n
?應(yīng)用組件?來(lái)實(shí)現(xiàn)翻譯工作。這個(gè)組件可以在應(yīng)用程序中按下面的代碼來(lái)配置,
'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 所支持的消息來(lái)源。模式?app*
?表示所有以?app
?開頭的消息類別名稱都使用這個(gè)翻譯的消息來(lái)源。該 yii\i18n\PhpMessageSource 類使用 PHP 文件來(lái)存儲(chǔ)消息翻譯。 每 PHP 文件對(duì)應(yīng)單一類別的消息。默認(rèn)情況下,文件名應(yīng)該與類別名稱相同。但是,你可以配置 yii\i18n\PhpMessageSource::fileMap 來(lái)映射一個(gè)類別到不同名稱的 PHP 文件。在上面的例子中, 類別?app/error
?被映射到PHP文件?@app/messages/ru-RU/error.php
(假設(shè)?ru-RU
?為目標(biāo)語(yǔ)言)。如果沒有此配置, 該類別將被映射到?@app/messages/ru-RU/app/error.php
?。
除了在PHP文件中存儲(chǔ)消息來(lái)源,也可以使用下面的消息來(lái)源在不同的存儲(chǔ)來(lái)存儲(chǔ)翻譯的消息:
在要翻譯的消息里,你可以嵌入一些占位符,并讓它們通過動(dòng)態(tài)的參數(shù)值來(lái)代替。你甚至可以根據(jù)目標(biāo)語(yǔ)言格式的參數(shù)值來(lái)使用特殊的占位符。 在本節(jié)中,我們將介紹如何用不同的方式來(lái)格式化消息。
在待翻譯的消息,可以嵌入一個(gè)或多個(gè)占位符,以便它們可以由給定的參數(shù)值取代。通過給不同的參數(shù)值,可以動(dòng)態(tài)地改變翻譯內(nèi)容的消息。 在下面的例子中,占位符?{username}
?在?“Hello, {username}!”
?中將分別被?'Alexander'
和'Qiang'
?所替換。
$username = 'Alexander';
// 輸出:“Hello, Alexander”
echo \Yii::t('app', 'Hello, {username}!', [
'username' => $username,
]);
$username = 'Qiang';
// 輸出:“Hello, Qiang”
echo \Yii::t('app', 'Hello, {username}!', [
'username' => $username,
]);
當(dāng)翻譯的消息包含占位符時(shí),應(yīng)該讓占位符保留原樣。這是因?yàn)檎{(diào)用?Yii::t()
?時(shí),占位符將被實(shí)際參數(shù)值代替。
你可以使用?名稱占位符?或者?位置占位符,但不能兩者都用在同一個(gè)消息里。
前面的例子說(shuō)明了如何使用名稱占位符。即每個(gè)占位符的格式為?{參數(shù)名稱}
?,你所提供的參數(shù)作為關(guān)聯(lián)數(shù)組, 其中數(shù)組的鍵是參數(shù)名稱(沒有大括號(hào)),數(shù)組的值是對(duì)應(yīng)的參數(shù)值。
位置占位符是使用基于零的整數(shù)序列,在調(diào)用?Yii::t()
?時(shí)會(huì)參數(shù)值根據(jù)它們出現(xiàn)位置的順序分別進(jìn)行替換。 在下面的例子中,位置占位符?{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);
提示:大多數(shù)情況下你應(yīng)該使用名稱占位符。這是因?yàn)閰?shù)名稱可以讓翻譯者更好的理解要被翻譯的消息。
你可以在消息的占位符指定附加格式的規(guī)則,這樣的參數(shù)值可在替換占位符之前格式化它們。在下面的例子中, 價(jià)格參數(shù)值將視為一個(gè)數(shù)并格式化為貨幣值:
$price = 100;
echo \Yii::t('app', 'Price: {0, number, currency}', $price);
注意:參數(shù)的格式化需要安裝?intl PHP 擴(kuò)展。
可以使用縮寫的形式或完整的形式來(lái)格式化占位符:
short form: {PlaceholderName, ParameterType}
full form: {PlaceholderName, ParameterType, ParameterStyle}
請(qǐng)參閱?ICU 文檔?關(guān)于如何指定這樣的占位符的說(shuō)明。
接下來(lái)我們會(huì)展示一些常用的使用方法。
參數(shù)值應(yīng)該被格式化為一個(gè)數(shù)。例如,
$sum = 42;
echo \Yii::t('app', 'Balance: {0, number}', $sum);
你可以指定參數(shù)的格式為?integer
(整型),currency
?(貨幣),或者?percent
?(百分?jǐn)?shù)):
$sum = 42;
echo \Yii::t('app', 'Balance: {0, number, currency}', $sum);
你也可以指定一個(gè)自定義模式來(lái)格式化數(shù)字。 例如,
$sum = 42;
echo \Yii::t('app', 'Balance: {0, number, ,000,000000}', $sum);
該參數(shù)值應(yīng)該被格式化為一個(gè)日期。 例如,
echo \Yii::t('app', 'Today is {0, date}', time());
你可以指定一個(gè)可選的參數(shù)格式?short
?,medium
?,long
?,或?full
?:
echo \Yii::t('app', 'Today is {0, date, short}', time());
你還可以指定一個(gè)自定義模式來(lái)格式化日期:
echo \Yii::t('app', 'Today is {0, date, yyyy-MM-dd}', time());
參數(shù)值應(yīng)該被格式化為一個(gè)時(shí)間。 例如,
echo \Yii::t('app', 'It is {0, time}', time());
你可以指定一個(gè)可選的參數(shù)格式?short
?,medium
?,long
?,或?full
?:
echo \Yii::t('app', 'It is {0, time, short}', time());
你還可以指定一個(gè)自定義模式來(lái)格式化時(shí)間:
echo \Yii::t('app', 'It is {0, date, HH:mm}', time());
參數(shù)值為一個(gè)數(shù)并被格式化為它的字母拼寫形式。 例如,
// 輸出:"42 is spelled as forty-two"
echo \Yii::t('app', '{n,number} is spelled as {n, spellout}', ['n' => 42]);
參數(shù)值為一個(gè)數(shù)并被格式化為一個(gè)序數(shù)詞。 例如,
// 輸出:"You are the 42nd visitor here!"
echo \Yii::t('app', 'You are the {n, ordinal} visitor here!', ['n' => 42]);
參數(shù)值為秒數(shù)并被格式化為持續(xù)的時(shí)間段。 例如,
// 輸出:"You are here for 47 sec. already!"
echo \Yii::t('app', 'You are here for {n, duration} already!', ['n' => 47]);
不同的語(yǔ)言有不同的方式來(lái)表示復(fù)數(shù)。 Yii 提供一個(gè)便捷的途徑,即使是非常復(fù)雜的規(guī)則也使翻譯消息時(shí)不同的復(fù)數(shù)形式行之有效。 取之以直接處理詞形變化規(guī)則,它是足以面對(duì)某些詞形變化語(yǔ)言的翻譯。 例如,
// 當(dāng) $n = 0 時(shí),輸出:"There are no cats!"
// 當(dāng) $n = 1 時(shí),輸出:"There is one cat!"
// 當(dāng) $n = 42 時(shí),輸出:"There are 42 cats!"
echo \Yii::t('app', 'There {n, plural, =0{are no cats} =1{is one cat} other{are # cats}}!', ['n' => $n]);
在上面的多個(gè)規(guī)則的參數(shù)中,?=0
?意味著?n
?的值是 0 ,=1
?意味著?n
?的值是 1 , 而?other
?則是對(duì)于其它值,?#
?會(huì)被?n
?中的值給替代。
復(fù)數(shù)形式可以是某些非常復(fù)雜的語(yǔ)言。下面以俄羅斯為例,=1
?完全匹配?n = 1
,而?one
?匹配?21
?或?101
:
Здесь {n, plural, =0{котов нет} =1{есть один кот} one{# кот} few{# кота} many{# котов} other{# кота}}!
注意,上述信息主要是作為一個(gè)翻譯的信息,而不是一個(gè)原始消息,除非設(shè)置應(yīng)用程序的 yii\base\Application::$sourceLanguage 為ru-RU
。
如果沒有找到一個(gè)翻譯的原始消息,復(fù)數(shù)規(guī)則 yii\base\Application::$sourceLanguage 將被應(yīng)用到原始消息。
要了解詞形變化形式,你應(yīng)該指定一個(gè)特定的語(yǔ)言,請(qǐng)參考?rules reference at unicode.org。
可以使用?select
?參數(shù)類型來(lái)選擇基于參數(shù)值的短語(yǔ)。例如,
// 輸出:"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',
]);
在上面的表達(dá)中,?female
?和?male
?是可能的參數(shù)值,而?other
?用于處理不與它們中任何一個(gè)相匹配的值。對(duì)于每一個(gè)可能的參數(shù)值, 應(yīng)指定一個(gè)短語(yǔ)并把它放在在一對(duì)大括號(hào)中。
你可以指定使用默認(rèn)的翻譯,該翻譯將作為一個(gè)類別,用于不匹配任何其他翻譯的后備。這種翻譯應(yīng)標(biāo)有?*
?。 為了做到這一點(diǎn)以下內(nèi)容需要添加到應(yīng)用程序的配置:
//配置 i18n 組件
'i18n' => [
'translations' => [
'*' => [
'class' => 'yii\i18n\PhpMessageSource'
],
],
],
現(xiàn)在,你可以使用每一個(gè)還沒有配置的類別,這跟 Yii 1.1 的行為有點(diǎn)類似。該類別的消息將來(lái)自在默認(rèn)翻譯?basePath
?中的一個(gè)文件, 該文件在?@app/messages
?:
echo Yii::t('not_specified_category', 'message from unspecified category');
該消息將來(lái)自?@app/messages/<LanguageCode>/not_specified_category.php
?。
如果你想翻譯一個(gè)模塊的消息,并避免使用單一翻譯文件的所有信息,你可以按照下面的方式來(lái)翻譯:
<?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);
}
}
在上面的例子中,我們使用通配符匹配,然后過濾了所需的文件中的每個(gè)類別。取之使用?fileMap
?,你可以簡(jiǎn)單地使用類映射的同名文件。 現(xiàn)在你可以直接使用?Module::t('validation', 'your custom validation message')
?或?Module::t('form', 'some form label')
。
上述模塊的翻譯規(guī)則也同樣適用于小部件的翻譯規(guī)則,例如:
<?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);
}
}
你可以簡(jiǎn)單地使用類映射的同名文件而不是使用?fileMap
??,F(xiàn)在你直接可以使用?Menu::t('messages', 'new messages {messages}', ['{messages}' => 10])
?。
提示: 對(duì)于小部件也可以使用 i18n 視圖,并一樣以控制器的規(guī)則來(lái)應(yīng)用它們。
Yii 自帶了一些默認(rèn)的信息驗(yàn)證錯(cuò)誤和其他一些字符串的翻譯。這些信息都是在?yii
?類別中。有時(shí)候你想糾正應(yīng)用程序的默認(rèn)信息翻譯。 為了做到這一點(diǎn),需配置?i18n
?應(yīng)用組件?如下:
'i18n' => [
'translations' => [
'yii' => [
'class' => 'yii\i18n\PhpMessageSource',
'sourceLanguage' => 'en-US',
'basePath' => '@app/messages'
],
],
],
現(xiàn)在可以把你修改過的翻譯放在?@app/messages/<language>/yii.php
。
如果翻譯的消息在消息源文件里找不到,Yii 將直接顯示該消息內(nèi)容。這樣一來(lái)當(dāng)你的原始消息是一個(gè)有效的冗長(zhǎng)的文字時(shí)會(huì)很方便。 然而,有時(shí)它是不能實(shí)現(xiàn)我們的需求。你可能需要執(zhí)行一些自定義處理的情況,這時(shí)請(qǐng)求的翻譯可能在消息翻譯源文件找不到。 這可通過使用 yii\i18n\MessageSource::EVENT_MISSING_TRANSLATION - yii\i18n\MessageSource 的事件來(lái)完成。
例如,你可能想要將所有缺失的翻譯做一個(gè)明顯的標(biāo)記,這樣它們就可以很容易地在頁(yè)面中找到。 為此,你需要先設(shè)置一個(gè)事件處理程序。這可以在應(yīng)用程序配置中進(jìn)行:
'components' => [
// ...
'i18n' => [
'translations' => [
'app*' => [
'class' => 'yii\i18n\PhpMessageSource',
'fileMap' => [
'app' => 'app.php',
'app/error' => 'error.php',
],
'on missingTranslation' => ['app\components\TranslationEventHandler', 'handleMissingTranslation']
],
],
],
],
現(xiàn)在,你需要實(shí)現(xiàn)自己的事件處理程序:
<?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 是由事件處理程序設(shè)置,它將顯示翻譯結(jié)果。
注意:每個(gè)消息源會(huì)單獨(dú)處理它缺少的翻譯。如果是使用多個(gè)消息源,并希望他們把缺少的翻譯以同樣的方式來(lái)處理, 你應(yīng)該給它們每一個(gè)消息源指定相應(yīng)的事件處理程序。
message
?命令翻譯儲(chǔ)存在 yii\i18n\PhpMessageSource,[[yii\i18n\GettextMessageSource|.po 文件] 或者 yii\i18n\DbMessageSource。 具體見類的附加選項(xiàng)。
首先,你需要?jiǎng)?chuàng)建一個(gè)配置文件。確定應(yīng)該保存在哪里,然后執(zhí)行命令
./yii message/config path/to/config.php
打開創(chuàng)建的文件,并按照需求來(lái)調(diào)整參數(shù)。特別注意:
languages
: 代表你的應(yīng)用程序應(yīng)該被翻譯成什么語(yǔ)言的一個(gè)數(shù)組;messagePath
: 存儲(chǔ)消息文件的路徑,這應(yīng)與配置中?i18n
?的?basePath
?參數(shù)一致。注意,這里不支持路徑別名,它們必須是配置文件相對(duì)路徑的位置
一旦你做好了配置文件,你就可以使用命令提取消息
./yii message path/to/config.php
然后你會(huì)發(fā)現(xiàn)你的文件(如果你已經(jīng)選擇基于文件的翻譯)在?messagePath
?目錄。
有時(shí)你可能想要翻譯一個(gè)完整的視圖文件,而不是翻譯單條文本消息。為了達(dá)到這一目的,只需簡(jiǎn)單的翻譯視圖并在它子目錄下保存一個(gè)名稱一樣的目標(biāo)語(yǔ)言文件。 例如,如果你想要翻譯的視圖文件為?views/site/index.php
?且目標(biāo)語(yǔ)言是?ru-RU
,你可以將視圖翻譯并保存為?views/site/ru-RU/index.php
。現(xiàn)在 每當(dāng)你調(diào)用 yii\base\View::renderFile() 或任何其它方法 (如 yii\base\Controller::render()) 來(lái)渲染?views/site/index.php
?視圖, 它最終會(huì)使用所翻譯的?views/site/ru-RU/index.php
。
注意:如果 yii\base\Application::$language 跟 yii\base\Application::$sourceLanguage 相同, 在翻譯視圖的存在下,將呈現(xiàn)原始視圖。
在?格式化輸出數(shù)據(jù)?一節(jié)可獲取詳細(xì)信息。
Yii 使用?PHP intl 擴(kuò)展?來(lái)提供大多數(shù) I18N 的功能, 如日期和數(shù)字格式的 yii\i18n\Formatter 類和消息格式的 yii\i18n\MessageFormatter 類。 當(dāng)?intl
?擴(kuò)展沒有安裝時(shí),兩者會(huì)提供一個(gè)回調(diào)機(jī)制。然而,該回調(diào)機(jī)制只適用于目標(biāo)語(yǔ)言是英語(yǔ)的情況下。 因此,當(dāng) I18N 對(duì)你來(lái)說(shuō)必不可少時(shí),強(qiáng)烈建議你安裝?intl
。
PHP intl 擴(kuò)展?是基于對(duì)于所有不同的語(yǔ)言環(huán)境提供格式化規(guī)則的?ICU庫(kù)。 不同版本的 ICU 中可能會(huì)產(chǎn)生不同日期和數(shù)值格式的結(jié)果。為了確保你的網(wǎng)站在所有環(huán)境產(chǎn)生相同的結(jié)果,建議你安裝與?intl
?擴(kuò)展相同的版本(和 ICU 同一版本)。
要找出所使用的 PHP 是哪個(gè)版本的 ICU ,你可以運(yùn)行下面的腳本,它會(huì)給出你所使用的 PHP 和 ICU 的版本。
<?php
echo "PHP: " . PHP_VERSION . "\n";
echo "ICU: " . INTL_ICU_VERSION . "\n";
此外,還建議你所使用的 ICU 版本應(yīng)等于或大于 49 的版本。這確保了可以使用本文檔描述的所有功能。例如, 低于 49 版本的 ICU 不支持使用?#
?占位符來(lái)實(shí)現(xiàn)復(fù)數(shù)規(guī)則。請(qǐng)參閱?http://site.icu-project.org/download?獲取可用 ICU 版本的完整列表。 注意,版本編號(hào)在 4.8 之后發(fā)生了變化(如 ICU4.8,ICU49,50 ICU 等)。
另外,ICU 庫(kù)中時(shí)區(qū)數(shù)據(jù)庫(kù)的信息可能過時(shí)。要更新時(shí)區(qū)數(shù)據(jù)庫(kù)時(shí)詳情請(qǐng)參閱?ICU 手冊(cè)?。而對(duì)于 ICU 輸出格式使用的時(shí)區(qū)數(shù)據(jù)庫(kù), PHP 用的時(shí)區(qū)數(shù)據(jù)庫(kù)可能跟它有關(guān)。你可以通過安裝?pecl package?timezonedb
?的最新版本來(lái)更新它。
更多建議: