國際化(Internationalization)

2018-02-24 15:40 更新

國際化

國際化(I18N)是指在設(shè)計軟件時,使它可以無需做大的改變就能夠適應(yīng)不同的語言和地區(qū)的需要。對于 Web 應(yīng)用程序, 這有著特別重要的意義,因為潛在的用戶可能會在全球范圍內(nèi)。 Yii 提供的國際化功能支持全方位信息翻譯,視圖翻譯,日期和數(shù)字格式化。

區(qū)域和語言

區(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ù),需要做如下工作:

  • 調(diào)用 Yii::t() 方法且在其中包含每一條要翻譯的消息;
  • 配置一個或多個消息來源,能在其中找得到要翻譯的消息和翻譯結(jié)果;
  • 讓譯者翻譯信息并將它們存儲在消息來源。

這個 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文件中存儲消息來源,也可以使用下面的消息來源在不同的存儲來存儲翻譯的消息:

  • yii\i18n\GettextMessageSource 使用 GNU Gettext 的 MO 或 PO 文件保存翻譯的消息。
  • yii\i18n\DbMessageSource 使用一個數(shù)據(jù)庫表來存儲翻譯的消息。

消息格式化

在要翻譯的消息里,你可以嵌入一些占位符,并讓它們通過動態(tài)的參數(shù)值來代替。你甚至可以根據(jù)目標(biāo)語言格式的參數(shù)值來使用特殊的占位符。 在本節(jié)中,我們將介紹如何用不同的方式來格式化消息。

消息參數(shù)

在待翻譯的消息,可以嵌入一個或多個占位符,以便它們可以由給定的參數(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ù)名稱可以讓翻譯者更好的理解要被翻譯的消息。

格式化參數(shù)

你可以在消息的占位符指定附加格式的規(guī)則,這樣的參數(shù)值可在替換占位符之前格式化它們。在下面的例子中, 價格參數(shù)值將視為一個數(shù)并格式化為貨幣值:

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

注意:參數(shù)的格式化需要安裝?intl PHP 擴(kuò)展。

可以使用縮寫的形式或完整的形式來格式化占位符:

short form: {PlaceholderName, ParameterType}
full form: {PlaceholderName, ParameterType, ParameterStyle}

請參閱?ICU 文檔?關(guān)于如何指定這樣的占位符的說明。

接下來我們會展示一些常用的使用方法。

數(shù)字

參數(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ù)并被格式化為一個序數(shù)詞。 例如,

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

持續(xù)時間

參數(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ù)

不同的語言有不同的方式來表示復(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)翻譯

你可以指定使用默認(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/*'] = [
            '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?,你可以簡單地使用類映射的同名文件。 現(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);
    }

}

你可以簡單地使用類映射的同名文件而不是使用?fileMap??,F(xiàn)在你直接可以使用?Menu::t('messages', 'new messages {messages}', ['{messages}' => 10])?。

提示: 對于小部件也可以使用 i18n 視圖,并一樣以控制器的規(guī)則來應(yīng)用它們。

翻譯框架信息

Yii 自帶了一些默認(rèn)的信息驗證錯誤和其他一些字符串的翻譯。這些信息都是在?yii?類別中。有時候你想糾正應(yīng)用程序的默認(rè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)容。這樣一來當(dāng)你的原始消息是一個有效的冗長的文字時會很方便。 然而,有時它是不能實現(xiàn)我們的需求。你可能需要執(zhí)行一些自定義處理的情況,這時請求的翻譯可能在消息翻譯源文件找不到。 這可通過使用 yii\i18n\MessageSource::EVENT_MISSING_TRANSLATION - yii\i18n\MessageSource 的事件來完成。

例如,你可能想要將所有缺失的翻譯做一個明顯的標(biāo)記,這樣它們就可以很容易地在頁面中找到。 為此,你需要先設(shè)置一個事件處理程序。這可以在應(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)在,你需要實現(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é)果。

注意:每個消息源會單獨處理它缺少的翻譯。如果是使用多個消息源,并希望他們把缺少的翻譯以同樣的方式來處理, 你應(yīng)該給它們每一個消息源指定相應(yīng)的事件處理程序。

使用?message?命令

翻譯儲存在 yii\i18n\PhpMessageSource,[[yii\i18n\GettextMessageSource|.po 文件] 或者 yii\i18n\DbMessageSource。 具體見類的附加選項。

首先,你需要創(chuàng)建一個配置文件。確定應(yīng)該保存在哪里,然后執(zhí)行命令

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

打開創(chuàng)建的文件,并按照需求來調(diào)整參數(shù)。特別注意:

  • languages: 代表你的應(yīng)用程序應(yīng)該被翻譯成什么語言的一個數(shù)組;
  • messagePath: 存儲消息文件的路徑,這應(yīng)與配置中?i18n?的?basePath?參數(shù)一致。

注意,這里不支持路徑別名,它們必須是配置文件相對路徑的位置

一旦你做好了配置文件,你就可以使用命令提取消息

./yii message path/to/config.php

然后你會發(fā)現(xiàn)你的文件(如果你已經(jīng)選擇基于文件的翻譯)在?messagePath?目錄。

視圖的翻譯

有時你可能想要翻譯一個完整的視圖文件,而不是翻譯單條文本消息。為了達(dá)到這一目的,只需簡單的翻譯視圖并在它子目錄下保存一個名稱一樣的目標(biāo)語言文件。 例如,如果你想要翻譯的視圖文件為?views/site/index.php?且目標(biāo)語言是?ru-RU,你可以將視圖翻譯并保存為?views/site/ru-RU/index.php。現(xiàn)在 每當(dāng)你調(diào)用 yii\base\View::renderFile() 或任何其它方法 (如 yii\base\Controller::render()) 來渲染?views/site/index.php?視圖, 它最終會使用所翻譯的?views/site/ru-RU/index.php。

注意:如果 yii\base\Application::$language 跟 yii\base\Application::$sourceLanguage 相同, 在翻譯視圖的存在下,將呈現(xiàn)原始視圖。

格式化日期和數(shù)字值

在?格式化輸出數(shù)據(jù)?一節(jié)可獲取詳細(xì)信息。

設(shè)置 PHP 環(huán)境

Yii 使用?PHP intl 擴(kuò)展?來提供大多數(shù) I18N 的功能, 如日期和數(shù)字格式的 yii\i18n\Formatter 類和消息格式的 yii\i18n\MessageFormatter 類。 當(dāng)?intl?擴(kuò)展沒有安裝時,兩者會提供一個回調(diào)機(jī)制。然而,該回調(diào)機(jī)制只適用于目標(biāo)語言是英語的情況下。 因此,當(dāng) I18N 對你來說必不可少時,強(qiáng)烈建議你安裝?intl

PHP intl 擴(kuò)展?是基于對于所有不同的語言環(huán)境提供格式化規(guī)則的?ICU庫。 不同版本的 ICU 中可能會產(chǎn)生不同日期和數(shù)值格式的結(jié)果。為了確保你的網(wǎng)站在所有環(huán)境產(chǎn)生相同的結(jié)果,建議你安裝與?intl?擴(kuò)展相同的版本(和 ICU 同一版本)。

要找出所使用的 PHP 是哪個版本的 ICU ,你可以運行下面的腳本,它會給出你所使用的 PHP 和 ICU 的版本。

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

此外,還建議你所使用的 ICU 版本應(yīng)等于或大于 49 的版本。這確保了可以使用本文檔描述的所有功能。例如, 低于 49 版本的 ICU 不支持使用?#?占位符來實現(xiàn)復(fù)數(shù)規(guī)則。請參閱?http://site.icu-project.org/download?獲取可用 ICU 版本的完整列表。 注意,版本編號在 4.8 之后發(fā)生了變化(如 ICU4.8,ICU49,50 ICU 等)。

另外,ICU 庫中時區(qū)數(shù)據(jù)庫的信息可能過時。要更新時區(qū)數(shù)據(jù)庫時詳情請參閱?ICU 手冊?。而對于 ICU 輸出格式使用的時區(qū)數(shù)據(jù)庫, PHP 用的時區(qū)數(shù)據(jù)庫可能跟它有關(guān)。你可以通過安裝?pecl package?timezonedb?的最新版本來更新它。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號