directory search
阅读前篇 简介 Yii 是什么 从 Yii 1.1 升级 入门 安装 Yii 运行应用 第一次问候 使用Forms 数据库应用 使用 Gii 生成代码 进阶 应用结构 概述 入口脚本 应用(Applications) 应用组件(Application Components) 控制器(Controllers) 模型(Models) 视图(views) 模块(Modules) 过滤器(Filters) 小部件(Widgets) 前端资源(Assets) 扩展(Extensions) 请求处理 运行概述 启动引导(Bootstrapping) 路由和创建URL 请求(Requests) 响应(Responses) Sessions 和 Cookies 错误处理(Handling Errors) 日志(Logging) 关键概念 组件(Component) 属性(Property) 事件(Events) 行为(Behaviors) 配置(Configurations) 别名(Aliases) 类自动加载(Autoloading) 服务定位器(Service Locator) 依赖注入容器(Dependency Injection Container) 配合数据库工作 数据库访问 (Data Access Objects) 查询生成器(Query Builder) 活动记录(Active Record) 数据库迁移(Migrations) Sphinx Redis MongoDB Elasticsearch 接收用户数据 创建表单(Creating Forms) 输入验证(Validating Input) 文件上传(Uploading Files) 收集列表输入(Collecting Tabular Input) 多模型的复合表单(Getting Data for Multiple Models) 显示数据 格式化输出数据(Data Formatting) 分页(Pagination) 排序(Sorting) 数据提供器(Data Providers) 数据小部件(Data Widgets) 客户端脚本使用(Working with Client Scripts) 主题(Theming) 安全 认证(Authentication) 授权(Authorization) 处理密码(Working with Passwords) 客户端认证(Auth Clients) 最佳安全实践(Best Practices) 缓存 概述 数据缓存 片段缓存 页面缓存 HTTP 缓存 RESTfull Web服务 快速入门(Quick Start) 资源(Resources) 控制器(Controllers) 路由(Routing) 格式化响应(Response Formatting) 授权认证(Authentication) 速率限制(Rate Limiting) 版本(Versioning) 错误处理(Error Handling) 开发工具 调试工具栏和调试器 使用Gii生成代码 生成API文档 测试 概述(Overview) 配置测试环境(Testing environment setup) 单元测试(Unit Tests) 功能测试(Function Tests) 验收测试(Acceptance Tests) 测试夹具(Fixtures) 高级专题 高级应用模板 创建自定义应用程序结构 控制台命令 核心验证器(Core Validators) 国际化 收发邮件 性能优化 共享主机环境 模板引擎 集成第三方代码 小部件 Bootstrap 小部件 Jquery UI 助手类 概述 Array 助手(ArrayHelper) Html 助手(Html) Url 助手(Url)
characters

国际化

国际化

国际化(I18N)是指在设计软件时,使它可以无需做大的改变就能够适应不同的语言和地区的需要。对于 Web 应用程序, 这有着特别重要的意义,因为潜在的用户可能会在全球范围内。 Yii 提供的国际化功能支持全方位信息翻译,视图翻译,日期和数字格式化。

区域和语言

区域设置是一组参数以定义用户希望能在他们的用户界面所看到用户的语言,国家和任何特殊的偏好。 它通常是由语言 ID 和区域 ID 组成。例如,ID “en-US” 代表英语和美国的语言环境。为了保持一致性, 在 Yii 应用程序中使用的所有区域 ID 应该规范化为 ll-CC,其中ll 是根据两个或三个字母的小写字母语言代码 ISO-639 和 CC 是两个字母的国别代码 ISO-3166。 有关区域设置的更多细节可以看 ICU 项目文档。

在 Yii中,我们经常用 “language” 来代表一个区域。

一个 Yii 应用使用两种语言:yii\base\Application::$sourceLanguage 和 yii\base\Application::$language 。前者指的是写在代码中的语言,后者是向最终用户显示内容的语言。 而信息翻译服务主要是将文本消息从原语言翻译到目标语言。

可以用类似下面的应用程序配置来配置应用程序语言:

return [
    // 设置目标语言为俄语
    'language' => 'ru-RU',
    
    // 设置源语言为英语
    'sourceLanguage' => 'en-US',
    
    ......
];

默认的 yii\base\Application::$sourceLanguage 值是 en-US,即美国英语。 建议你保留此默认值不变,因为通常让人将英语翻译成其它语言要比将其它语言翻译成其它语言容易得多。

你经常需要根据不同的因素来动态地设置 yii\base\Application::$language ,如最终用户的语言首选项。 要在应用程序配置中配置它,你可以使用下面的语句来更改目标语言:

// 改变目标语言为中文
\Yii::$app->language = 'zh-CN';

消息翻译

消息翻译服务用于将一条文本信息从一种语言(通常是 yii\base\Application::$sourceLanguage ) 翻译成另一种语言(通常是 yii\base\Application::$language)。 它的翻译原理是通过在语言文件中查找要翻译的信息以及翻译的结果。如果要翻译的信息可以在语言文件中找到,会返回相应的翻译结果; 否则会返回原始未翻译的信息。

为了使用消息翻译服务,需要做如下工作:

  • 调用 Yii::t() 方法且在其中包含每一条要翻译的消息;
  • 配置一个或多个消息来源,能在其中找得到要翻译的消息和翻译结果;
  • 让译者翻译信息并将它们存储在消息来源。

这个 Yii::t() 方法的用法如下,

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

第一个参数指储存消息来源的类别名称,第二个参数指需要被翻译的消息。

这个 Yii::t() 方法会调用 i18n 应用组件 来实现翻译工作。这个组件可以在应用程序中按下面的代码来配置,

'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 文件对应单一类别的消息。默认情况下,文件名应该与类别名称相同。但是,你可以配置 yii\i18n\PhpMessageSource::fileMap 来映射一个类别到不同名称的 PHP 文件。在上面的例子中, 类别 app/error 被映射到PHP文件 @app/messages/ru-RU/error.php(假设 ru-RU 为目标语言)。如果没有此配置, 该类别将被映射到 @app/messages/ru-RU/app/error.php 。

除了在PHP文件中存储消息来源,也可以使用下面的消息来源在不同的存储来存储翻译的消息:

  • yii\i18n\GettextMessageSource 使用 GNU Gettext 的 MO 或 PO 文件保存翻译的消息。
  • yii\i18n\DbMessageSource 使用一个数据库表来存储翻译的消息。

消息格式化

在要翻译的消息里,你可以嵌入一些占位符,并让它们通过动态的参数值来代替。你甚至可以根据目标语言格式的参数值来使用特殊的占位符。 在本节中,我们将介绍如何用不同的方式来格式化消息。

消息参数

在待翻译的消息,可以嵌入一个或多个占位符,以便它们可以由给定的参数值取代。通过给不同的参数值,可以动态地改变翻译内容的消息。 在下面的例子中,占位符 {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,
]);

当翻译的消息包含占位符时,应该让占位符保留原样。这是因为调用 Yii::t() 时,占位符将被实际参数值代替。

你可以使用 名称占位符 或者 位置占位符,但不能两者都用在同一个消息里。

前面的例子说明了如何使用名称占位符。即每个占位符的格式为 {参数名称} ,你所提供的参数作为关联数组, 其中数组的键是参数名称(没有大括号),数组的值是对应的参数值。

位置占位符是使用基于零的整数序列,在调用 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);

提示:大多数情况下你应该使用名称占位符。这是因为参数名称可以让翻译者更好的理解要被翻译的消息。

格式化参数

你可以在消息的占位符指定附加格式的规则,这样的参数值可在替换占位符之前格式化它们。在下面的例子中, 价格参数值将视为一个数并格式化为货币值:

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

注意:参数的格式化需要安装 intl PHP 扩展。

可以使用缩写的形式或完整的形式来格式化占位符:

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

请参阅 ICU 文档 关于如何指定这样的占位符的说明。

接下来我们会展示一些常用的使用方法。

数字

参数值应该被格式化为一个数。例如,

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

你可以指定参数的格式为 integer(整型),currency (货币),或者 percent (百分数):

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

你也可以指定一个自定义模式来格式化数字。 例如,

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

格式化参考。

日期

该参数值应该被格式化为一个日期。 例如,

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

你可以指定一个可选的参数格式 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());

格式化参考。

时间

参数值应该被格式化为一个时间。 例如,

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

你可以指定一个可选的参数格式 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());

格式化参考。

拼写

参数值为一个数并被格式化为它的字母拼写形式。 例如,

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

序数词

参数值为一个数并被格式化为一个序数词。 例如,

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

持续时间

参数值为秒数并被格式化为持续的时间段。 例如,

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

复数

不同的语言有不同的方式来表示复数。 Yii 提供一个便捷的途径,即使是非常复杂的规则也使翻译消息时不同的复数形式行之有效。 取之以直接处理词形变化规则,它是足以面对某些词形变化语言的翻译。 例如,

// 当 $n = 0 时,输出:"There are no cats!"// 当 $n = 1 时,输出:"There is one cat!"// 当 $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]);

在上面的多个规则的参数中, =0 意味着 n 的值是 0 ,=1 意味着 n 的值是 1 , 而 other 则是对于其它值, # 会被 n 中的值给替代。

复数形式可以是某些非常复杂的语言。下面以俄罗斯为例,=1 完全匹配 n = 1,而 one 匹配 21 或 101

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

注意,上述信息主要是作为一个翻译的信息,而不是一个原始消息,除非设置应用程序的 yii\base\Application::$sourceLanguage 为 ru-RU

如果没有找到一个翻译的原始消息,复数规则 yii\base\Application::$sourceLanguage 将被应用到原始消息。

要了解词形变化形式,你应该指定一个特定的语言,请参考 rules reference at unicode.org。

选择

可以使用 select 参数类型来选择基于参数值的短语。例如,

// 输出:"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',
]);

在上面的表达中, female 和 male 是可能的参数值,而 other 用于处理不与它们中任何一个相匹配的值。对于每一个可能的参数值, 应指定一个短语并把它放在在一对大括号中。

指定默认翻译

你可以指定使用默认的翻译,该翻译将作为一个类别,用于不匹配任何其他翻译的后备。这种翻译应标有 * 。 为了做到这一点以下内容需要添加到应用程序的配置:

//配置 i18n 组件

'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

?>
<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') // 渲染一个视图作为邮件内容
    ->setFrom('from@domain.com')
    ->setTo('to@domain.com')
    ->setSubject('Message subject')
    ->send();

你也可以在 compose() 方法中传递一些视图所需参数,这些参数可以在视图文件中使用:

Yii::$app->mailer->compose('greetings', [
    'user' => Yii::$app->user->identity,
    'advertisement' => $adContent,
]);

你可以指定不同的视图文件的 HTML 和纯文本邮件内容:

Yii::$app->mailer->compose([
    'html' => 'contact-html',
    'text' => 'contact-text',
]);

如果指定视图名称为纯字符串,它的渲染结果将被用来作为 HTML Body,同时纯文本正文将被删除所有 HTML 实体。

视图渲染结果可以被包裹进布局,可使用 yii\mail\BaseMailer::htmlLayout 和 yii\mail\BaseMailer::textLayout 来设置。 它的运行方式跟常规应用程序的布局是一样的。布局可用于设置邮件 CSS 样式或其他共享内容:

<?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');

// 动态创建一个文件附件$message->attachContent('Attachment content', ['fileName' => 'attach.txt', 'contentType' => 'text/plain']);

嵌入图片

你可以使用 embed() 方法将图片插入到邮件内容。此方法返回会图片 ID ,这将用在 "img" 标签中。 当通过视图文件来写信时,这种方法易于使用:

Yii::$app->mailer->compose('embed-email', ['imageFileName' => '/path/to/image.jpg'])
    // ...
    ->send();

然后在该视图文件中,你可以使用下面的代码:

<img src="/docs/guide/2.0/<?= $message->embed($imageFileName); ?>">

测试和调试

开发人员常常要检查一下,有什么电子邮件是由应用程序发送的,他们的内容是什么等。这可通过yii\mail\BaseMailer::useFileTransport 来检查。 如果开启这个选项,会把邮件信息保存在本地文件而不是发送它们。这些文件保存在yii\mail\BaseMailer::fileTransportPath 中,默认在 '@runtime/mail' 。

提示: 你可以保存这些信息到本地文件或者把它们发送出去,但不能同时两者都做。

邮件信息文件可以在一个普通的文本编辑器中打开,这样你就可以浏览实际的邮件标题,内容等。这种机制可以用来调试应用程序或运行单元测试。

提示: 该邮件信息文件是会被 \yii\mail\MessageInterface::toString() 转成字符串保存的,它依赖于实际在应用程序中使用的邮件扩展。

创建自己的邮件解决方案

为了创建你自己的邮件解决方案,你需要创建两个类,一个用于 “Mailer”,另一个用于 “Message”。 你可以使用 yii\mail\BaseMailer和 yii\mail\BaseMessage 作为基类。这些类已经实现了基本的逻辑,这在本指南中有介绍。 然而,它们的使用不是必须的,它实现了yii\mail\MailerInterface 和 yii\mail\MessageInterface 接口。 然后,你需要实现所有 abstract 方法来构建解决方案。
Previous article: Next article: