?
? ????? PHP ??? ???? ??? ?? ??
國際化(I18N)是指在設(shè)計軟件時,使它可以無需做大的改變就能夠適應(yīng)不同的語言和地區(qū)的需要。對于 Web 應(yīng)用程序, 這有著特別重要的意義,因為潛在的用戶可能會在全球范圍內(nèi)。 Yii 提供的國際化功能支持全方位信息翻譯,視圖翻譯,日期和數(shù)字格式化。
區(qū)域設(shè)置是一組參數(shù)以定義用戶希望能在他們的用戶界面所看到用戶的語言,國家和任何特殊的偏好。 它通常是由語言 ID 和區(qū)域 ID 組成。例如,ID “en-US” 代表英語和美國的語言環(huán)境。為了保持一致性, 在 Yii 應(yīng)用程序中使用的所有區(qū)域 ID 應(yīng)該規(guī)范化為?ll-CC
,其中ll
?是根據(jù)兩個或三個字母的小寫字母語言代碼?ISO-639?和?CC
?是兩個字母的國別代碼?ISO-3166。 有關(guān)區(qū)域設(shè)置的更多細(xì)節(jié)可以看?ICU 項目文檔。
在 Yii中,我們經(jīng)常用 “l(fā)anguage” 來代表一個區(qū)域。
一個 Yii 應(yīng)用使用兩種語言:yii\base\Application::$sourceLanguage 和 yii\base\Application::$language 。前者指的是寫在代碼中的語言,后者是向最終用戶顯示內(nèi)容的語言。 而信息翻譯服務(wù)主要是將文本消息從原語言翻譯到目標(biāo)語言。
可以用類似下面的應(yīng)用程序配置來配置應(yīng)用程序語言:
return [
// 設(shè)置目標(biāo)語言為俄語
'language' => 'ru-RU',
// 設(shè)置源語言為英語
'sourceLanguage' => 'en-US',
......
];
默認(rèn)的 yii\base\Application::$sourceLanguage 值是?en-US
,即美國英語。 建議你保留此默認(rèn)值不變,因為通常讓人將英語翻譯成其它語言要比將其它語言翻譯成其它語言容易得多。
你經(jīng)常需要根據(jù)不同的因素來動態(tài)地設(shè)置 yii\base\Application::$language ,如最終用戶的語言首選項。 要在應(yīng)用程序配置中配置它,你可以使用下面的語句來更改目標(biāo)語言:
// 改變目標(biāo)語言為中文
\Yii::$app->language = 'zh-CN';
消息翻譯服務(wù)用于將一條文本信息從一種語言(通常是 yii\base\Application::$sourceLanguage ) 翻譯成另一種語言(通常是 yii\base\Application::$language)。 它的翻譯原理是通過在語言文件中查找要翻譯的信息以及翻譯的結(jié)果。如果要翻譯的信息可以在語言文件中找到,會返回相應(yīng)的翻譯結(jié)果; 否則會返回原始未翻譯的信息。
為了使用消息翻譯服務(wù),需要做如下工作:
這個 Yii::t() 方法的用法如下,
echo \Yii::t('app', 'This is a string to translate!');
第一個參數(shù)指儲存消息來源的類別名稱,第二個參數(shù)指需要被翻譯的消息。
這個 Yii::t() 方法會調(diào)用?i18n
?應(yīng)用組件?來實現(xiàn)翻譯工作。這個組件可以在應(yīng)用程序中按下面的代碼來配置,
'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
?開頭的消息類別名稱都使用這個翻譯的消息來源。該 yii\i18n\PhpMessageSource 類使用 PHP 文件來存儲消息翻譯。 每 PHP 文件對應(yīng)單一類別的消息。默認(rèn)情況下,文件名應(yīng)該與類別名稱相同。但是,你可以配置 yii\i18n\PhpMessageSource::fileMap 來映射一個類別到不同名稱的 PHP 文件。在上面的例子中, 類別?app/error
?被映射到PHP文件?@app/messages/ru-RU/error.php
(假設(shè)?ru-RU
?為目標(biāo)語言)。如果沒有此配置, 該類別將被映射到?@app/messages/ru-RU/app/error.php
?。
除了在PHP文件中存儲消息來源,也可以使用下面的消息來源在不同的存儲來存儲翻譯的消息:
在要翻譯的消息里,你可以嵌入一些占位符,并讓它們通過動態(tài)的參數(shù)值來代替。你甚至可以根據(jù)目標(biāo)語言格式的參數(shù)值來使用特殊的占位符。 在本節(jié)中,我們將介紹如何用不同的方式來格式化消息。
在待翻譯的消息,可以嵌入一個或多個占位符,以便它們可以由給定的參數(shù)值取代。通過給不同的參數(shù)值,可以動態(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)翻譯的消息包含占位符時,應(yīng)該讓占位符保留原樣。這是因為調(diào)用?Yii::t()
?時,占位符將被實際參數(shù)值代替。
你可以使用?名稱占位符?或者?位置占位符,但不能兩者都用在同一個消息里。
前面的例子說明了如何使用名稱占位符。即每個占位符的格式為?{參數(shù)名稱}
?,你所提供的參數(shù)作為關(guān)聯(lián)數(shù)組, 其中數(shù)組的鍵是參數(shù)名稱(沒有大括號),數(shù)組的值是對應(yīng)的參數(shù)值。
位置占位符是使用基于零的整數(shù)序列,在調(diào)用?Yii::t()
?時會參數(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)該使用名稱占位符。這是因為參數(shù)名稱可以讓翻譯者更好的理解要被翻譯的消息。
你可以在消息的占位符指定附加格式的規(guī)則,這樣的參數(shù)值可在替換占位符之前格式化它們。在下面的例子中, 價格參數(shù)值將視為一個數(shù)并格式化為貨幣值:
$price = 100;
echo \Yii::t('app', 'Price: {0, number, currency}', $price);
注意:參數(shù)的格式化需要安裝?intl PHP 擴展。
可以使用縮寫的形式或完整的形式來格式化占位符:
short form: {PlaceholderName, ParameterType}
full form: {PlaceholderName, ParameterType, ParameterStyle}
請參閱?ICU 文檔?關(guān)于如何指定這樣的占位符的說明。
接下來我們會展示一些常用的使用方法。
參數(shù)值應(yīng)該被格式化為一個數(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);
你也可以指定一個自定義模式來格式化數(shù)字。 例如,
$sum = 42;
echo \Yii::t('app', 'Balance: {0, number, ,000,000000}', $sum);
格式化參考。
該參數(shù)值應(yīng)該被格式化為一個日期。 例如,
echo \Yii::t('app', 'Today is {0, date}', time());
你可以指定一個可選的參數(shù)格式?short
?,medium
?,long
?,或?full
?:
echo \Yii::t('app', 'Today is {0, date, short}', time());
你還可以指定一個自定義模式來格式化日期:
echo \Yii::t('app', 'Today is {0, date, yyyy-MM-dd}', time());
格式化參考。
參數(shù)值應(yīng)該被格式化為一個時間。 例如,
echo \Yii::t('app', 'It is {0, time}', time());
你可以指定一個可選的參數(shù)格式?short
?,medium
?,long
?,或?full
?:
echo \Yii::t('app', 'It is {0, time, short}', time());
你還可以指定一個自定義模式來格式化時間:
echo \Yii::t('app', 'It is {0, date, HH:mm}', time());
格式化參考。
參數(shù)值為一個數(shù)并被格式化為它的字母拼寫形式。 例如,
// 輸出:"42 is spelled as forty-two"echo \Yii::t('app', '{n,number} is spelled as {n, spellout}', ['n' => 42]);
參數(shù)值為一個數(shù)并被格式化為一個序數(shù)詞。 例如,
// 輸出:"You are the 42nd visitor here!"echo \Yii::t('app', 'You are the {n, ordinal} visitor here!', ['n' => 42]);
參數(shù)值為秒數(shù)并被格式化為持續(xù)的時間段。 例如,
// 輸出:"You are here for 47 sec. already!"echo \Yii::t('app', 'You are here for {n, duration} already!', ['n' => 47]);
不同的語言有不同的方式來表示復(fù)數(shù)。 Yii 提供一個便捷的途徑,即使是非常復(fù)雜的規(guī)則也使翻譯消息時不同的復(fù)數(shù)形式行之有效。 取之以直接處理詞形變化規(guī)則,它是足以面對某些詞形變化語言的翻譯。 例如,
// 當(dāng) $n = 0 時,輸出:"There are no cats!"// 當(dāng) $n = 1 時,輸出:"There is one cat!"// 當(dāng) $n = 42 時,輸出:"There are 42 cats!"echo \Yii::t('app', 'There {n, plural, =0{are no cats} =1{is one cat} other{are # cats}}!', ['n' => $n]);
在上面的多個規(guī)則的參數(shù)中,?=0
?意味著?n
?的值是 0 ,=1
?意味著?n
?的值是 1 , 而?other
?則是對于其它值,?#
?會被?n
?中的值給替代。
復(fù)數(shù)形式可以是某些非常復(fù)雜的語言。下面以俄羅斯為例,=1
?完全匹配?n = 1
,而?one
?匹配?21
?或?101
:
Здесь {n, plural, =0{котов нет} =1{есть один кот} one{# кот} few{# кота} many{# котов} other{# кота}}!
注意,上述信息主要是作為一個翻譯的信息,而不是一個原始消息,除非設(shè)置應(yīng)用程序的 yii\base\Application::$sourceLanguage 為?ru-RU
。
如果沒有找到一個翻譯的原始消息,復(fù)數(shù)規(guī)則 yii\base\Application::$sourceLanguage 將被應(yīng)用到原始消息。
要了解詞形變化形式,你應(yīng)該指定一個特定的語言,請參考?rules reference at unicode.org。
可以使用?select
?參數(shù)類型來選擇基于參數(shù)值的短語。例如,
// 輸出:"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
?用于處理不與它們中任何一個相匹配的值。對于每一個可能的參數(shù)值, 應(yīng)指定一個短語并把它放在在一對大括號中。
你可以指定使用默認(rèn)的翻譯,該翻譯將作為一個類別,用于不匹配任何其他翻譯的后備。這種翻譯應(yīng)標(biāo)有?*
?。 為了做到這一點以下內(nèi)容需要添加到應(yīng)用程序的配置:
//配置 i18n 組件
'i18n' => [
'translations' => [
'*' => [
'class' => 'yii\i18n\PhpMessageSource'
],
],
],
現(xiàn)在,你可以使用每一個還沒有配置的類別,這跟 Yii 1.1 的行為有點類似。該類別的消息將來自在默認(rèn)翻譯?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
?>
<h2>This message allows you to visit our site home page by one click</h2>
<?= Html::a('Go to home page', Url::home('http')) ?>
為了通過視圖文件撰寫正文可傳遞視圖名稱到?compose()
?方法中:
Yii::$app->mailer->compose('home-link') // 渲染一個視圖作為郵件內(nèi)容
->setFrom('from@domain.com')
->setTo('to@domain.com')
->setSubject('Message subject')
->send();
你也可以在?compose()
?方法中傳遞一些視圖所需參數(shù),這些參數(shù)可以在視圖文件中使用:
Yii::$app->mailer->compose('greetings', [
'user' => Yii::$app->user->identity,
'advertisement' => $adContent,
]);
你可以指定不同的視圖文件的 HTML 和純文本郵件內(nèi)容:
Yii::$app->mailer->compose([
'html' => 'contact-html',
'text' => 'contact-text',
]);
如果指定視圖名稱為純字符串,它的渲染結(jié)果將被用來作為 HTML Body,同時純文本正文將被刪除所有 HTML 實體。
視圖渲染結(jié)果可以被包裹進(jìn)布局,可使用 yii\mail\BaseMailer::htmlLayout 和 yii\mail\BaseMailer::textLayout 來設(shè)置。 它的運行方式跟常規(guī)應(yīng)用程序的布局是一樣的。布局可用于設(shè)置郵件 CSS 樣式或其他共享內(nèi)容:
<?phpuse yii\helpers\Html;
?><?php $this->beginPage() ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=<?= Yii::$app->charset ?>" />
<style type="text/css">
.heading {...}
.list {...}
.footer {...}
</style>
<?php $this->head() ?>
</head>
<body>
<?php $this->beginBody() ?>
<?= $content ?>
<div class="footer">With kind regards, <?= Yii::$app->name ?> team</div>
<?php $this->endBody() ?>
</body>
</html>
<?php $this->endPage() ?>
你可以使用?attach()
?和?attachContent()
?方法來添加附件的信息:
$message = Yii::$app->mailer->compose();
// 附件來自本地文件$message->attach('/path/to/source/file.pdf');
// 動態(tài)創(chuàng)建一個文件附件$message->attachContent('Attachment content', ['fileName' => 'attach.txt', 'contentType' => 'text/plain']);
你可以使用?embed()
?方法將圖片插入到郵件內(nèi)容。此方法返回會圖片 ID ,這將用在 "img" 標(biāo)簽中。 當(dāng)通過視圖文件來寫信時,這種方法易于使用:
Yii::$app->mailer->compose('embed-email', ['imageFileName' => '/path/to/image.jpg'])
// ...
->send();
然后在該視圖文件中,你可以使用下面的代碼:
<img src="/docs/guide/2.0/<?= $message->embed($imageFileName); ?>">
開發(fā)人員常常要檢查一下,有什么電子郵件是由應(yīng)用程序發(fā)送的,他們的內(nèi)容是什么等。這可通過yii\mail\BaseMailer::useFileTransport
?來檢查。 如果開啟這個選項,會把郵件信息保存在本地文件而不是發(fā)送它們。這些文件保存在yii\mail\BaseMailer::fileTransportPath
?中,默認(rèn)在 '@runtime/mail' 。
提示: 你可以保存這些信息到本地文件或者把它們發(fā)送出去,但不能同時兩者都做。
郵件信息文件可以在一個普通的文本編輯器中打開,這樣你就可以瀏覽實際的郵件標(biāo)題,內(nèi)容等。這種機制可以用來調(diào)試應(yīng)用程序或運行單元測試。
提示: 該郵件信息文件是會被?
\yii\mail\MessageInterface::toString()
?轉(zhuǎn)成字符串保存的,它依賴于實際在應(yīng)用程序中使用的郵件擴展。
yii\mail\BaseMailer
和?yii\mail\BaseMessage
?作為基類。這些類已經(jīng)實現(xiàn)了基本的邏輯,這在本指南中有介紹。 然而,它們的使用不是必須的,它實現(xiàn)了yii\mail\MailerInterface
?和?yii\mail\MessageInterface
?接口。 然后,你需要實現(xiàn)所有 abstract 方法來構(gòu)建解決方案。