Yii 2 国际化(I18N)实战指南:Locale 配置、消息翻译与多语言格式化
【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址: https://gitcode.com/gh_mirrors/yi/yii2
本文以 Yii 2 框架的国际化(Internationalisation, I18N)能力为核心,系统讲解如何在应用中引入语言环境(Locale)与目标语言、使用Yii::t()与消息源(MessageSource)完成文本翻译、借助 ICU 消息格式对数字、日期、复数与性别选择进行本地化格式化,并深入message命令行工具的配置与使用。读完本文,你将能够独立为一个 Yii 2 应用搭建完整的多语言体系,并理解其底层实现原理。
Locale 与语言环境配置
Locale(语言环境)是一组定义用户语言、国家/地区及界面显示偏好的参数集合,通常由一个 ID 标识,该 ID 由语言标识与地区标识组成,例如en-US表示"英语(美国)"。
为保证一致性,Yii 要求所有标识符使用规范形式ll-CC:其中ll是符合 ISO-639 标准的 2 或 3 字母语言代码,CC是符合 ISO-3166 标准的 2 字母国家/地区代码。关于 Locale 的完整概念可参考 ICU 项目文档。在 Yii 的语境中,"语言"(langue)一词经常被用来指代 Locale。
一个 Yii 应用会涉及两种语言:
- 源语言(sourceLanguage):源代码中消息文本所使用的语言,即开发者书写消息原文的语言;
- 目标语言(language):用于向最终用户展示文本的语言。
消息翻译服务(message translation service)正是负责把消息从源语言翻译到目标语言的核心服务。
在配置文件中设置两种语言
在应用配置中可同时指定目标语言与源语言:
return [ // 设定目标语言为法语(法国) 'language' => 'fr-FR', // 设定源语言为英语(美国) 'sourceLanguage' => 'en-US', ...... ];源语言的默认值是en-US。官方建议保持该值不变,因为通常更容易找到能将英语翻译成其他语言的译者,而非将一种非英语语言翻译成另一种非英语语言。
动态修改目标语言
目标语言常常需要根据用户偏好等因素动态设置。此时可以不修改配置文件,而在代码中直接赋值:
// 将目标语言改为法语(法国) \Yii::$app->language = 'fr-FR';Tip: 如果你的代码不同部分使用不同的源语言,可以按下一节所述的方式在局部修改源语言的值。
从源码结构看,language与sourceLanguage分别对应yii\base\Application::$language与yii\base\Application::$sourceLanguage两个属性,前者默认值即en-US,二者在组件初始化时被读取并传递给 i18n 组件使用。
消息翻译服务与Yii::t()
消息翻译服务负责把文本消息从一种语言(通常是源语言)翻译为另一种语言(通常是目标语言)。其工作方式是:在消息源(message source)中检索待翻译消息,消息源中存储着原文与译文的对应关系;若找到则返回对应译文,否则原样返回消息原文。
使用消息翻译服务需要完成三步:
- 将待翻译文本包裹在
Yii::t()方法调用中; - 配置一个或多个消息源,供翻译服务检索译文;
- 让译者翻译消息并存入消息源。
Yii::t()的调用方式
echo \Yii::t('app', 'This is a string to translate!');其中第二个参数是要翻译的文本消息,第一个参数是消息所属的**类别(category)**名称。
从 BaseYii::t() 源码 可以看到其底层调用链:Yii::t()实际会调用应用组件i18n的translate()方法,将类别、消息、参数与目标语言一并传入:
public static function t($category, $message, $params = [], $language = null) { if (static::$app !== null) { return static::$app->getI18n()->translate($category, $message, $params, $language ?: static::$app->language); } // 应用未初始化时的降级处理:仅做简单的占位符替换 $placeholders = []; foreach ((array) $params as $name => $value) { $placeholders['{' . $name . '}'] = $value; } return ($placeholders === []) ? $message : strtr($message, $placeholders); }配置 i18n 组件与消息源
i18n是 Yii 内置的应用组件,可在应用配置中如下配置:
'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开头的消息类别都使用该消息源翻译。
PhpMessageSource使用 PHP 文件存放翻译,每个 PHP 文件对应一个类别的消息。默认情况下文件名必须与类别名相同,但你可以通过配置fileMap(文件映射表)将某个类别映射到采用其他命名方式(或拆分到子目录)的 PHP 文件。以上面示例来说,类别app/error会对应 PHP 文件@app/messages/fr-FR/error.php(假设fr-FR为目标语言);若不配置fileMap,该类别则对应@app/messages/fr-FR/app/error.php。
从 PhpMessageSource 源码 看,消息文件路径由basePath、语言代码与类别名拼接而成:{basePath}/{语言}/{类别}.php,类别中的/或\会被视为命名空间分隔符。同时该实现会对语言代码与类别名做合法性校验,防止路径穿越(如拒绝包含..段、绝对路径或php://流包装器的类别),这从安全角度保证了消息文件不会逃逸出basePath。
消息源的三种存储形态
除 PHP 文件外,Yii 还提供两种消息源:
yii\i18n\GettextMessageSource:使用 GNU Gettext 的 MO/PO 文件维护译文,可通过basePath、catalog、useMoFile(是否优先读取编译后的 MO 文件)与useBigEndian(字节序)等属性配置;yii\i18n\DbMessageSource:使用数据库存储译文。
DbMessageSource依赖两张表:source_message(待翻译源消息)与message(译文),表名可通过sourceMessageTable与messageTable自定义,数据库连接由db属性指定。表结构可通过框架自带的迁移初始化(迁移文件):
yii migrate --migrationPath=@yii/i18n/migrations/该消息源还支持通过enableCaching、cache与cachingDuration属性对译文做缓存,避免每次翻译都查询数据库。
消息源解析与回退机制
在 I18N::translate() 源码 中,翻译流程分两步:先由消息源完成"查找译文",再对译文做"参数格式化":
public function translate($category, $message, $params, $language) { $messageSource = $this->getMessageSource($category); $translation = $messageSource->translate($category, $message, $language); if ($translation === false) { // 未找到译文时,直接用源语言格式化源消息 return $this->format($message, $params, $messageSource->sourceLanguage); } return $this->format($translation, $params, $language); }类别与消息源的匹配逻辑在 getMessageSource() 中:先精确匹配类别名,再按模式后缀通配符*匹配(如app*),最后才尝试全局通配*。另外在 I18N::init() 中,框架默认注册了yii与app两个类别,分别使用@yii/messages与@app/messages作为basePath。
值得注意的还有 MessageSource::translate() 的优化:除非设置forceTranslation = true,当目标语言与源语言相同时,消息不会被翻译(直接返回false,交由上层按源语言格式化)。而 PhpMessageSource::loadMessages() 还实现了语言回退合并:请求fr-FR时若找不到完整地区文件,会尝试加载更通用的fr译文,再将精确地区译文覆盖其上。
消息格式化:占位符与 ICU 消息格式
翻译消息时,可以在消息中嵌入"占位符(valeurs à remplacer)",运行时根据参数值动态替换;还可以使用特殊的占位符语法,让替换值按目标语言进行格式化。下面逐一介绍各种格式化方式。
消息占位符(参数)
在待翻译消息中可以嵌入一个或多个占位符,通过传入不同参数值动态改变消息内容。下面的示例中,消息'Hello, {username}!'的占位符{username}分别被替换为'Alexander'与'Qiang':
$username = 'Alexander'; // 输出翻译后的消息,将 {username} 替换为 "Alexander" echo \Yii::t('app', 'Hello, {username}!', [ 'username' => $username, ]); $username = 'Qiang'; // 输出翻译后的消息,将 {username} 替换为 "Qiang" echo \Yii::t('app', 'Hello, {username}!', [ 'username' => $username, ]);译者在翻译包含占位符的消息时必须原样保留占位符,因为占位符会在调用Yii::t()时才被真实值替换。
同一消息中,命名占位符与位置占位符不能混用,只能二选一。
- 命名占位符:形如
{nom},调用时传入关联数组,键为占位符名(不含花括号),值为替换值; - 位置占位符:使用从 0 开始的整数作为占位符名,调用时按数组位置依次替换。下面示例中
{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);Tip: 大多数情况下应优先使用命名占位符,因为名称能让译者更准确地理解待翻译消息的含义。
从 I18N::format() 源码 可以看到:当参数数组为空时直接返回原文;当消息中包含 ICU 格式特征(正则{\s*[\w.]+\s*,)时走MessageFormatter格式化;否则执行简单的strtr()占位符替换。
占位符的格式化语法
可以在占位符中附加格式化规则,作用于替换值。下面的示例把price当作数字并按货币格式输出:
$price = 100; echo \Yii::t('app', 'Price: {0,number,currency}', $price);Note: 占位符的格式化需要安装 PHP 的 intl 扩展。
占位符格式化支持短格式与完整格式两种写法:
短格式:{name,type} 完整格式:{name,type,style}Note: 如果消息中需要
{、}、'、#等特殊字符,请用单引号'包起来:echo Yii::t('app', "Example of string with ''-escaped characters'': '{' '}' '{test}' {count,plural,other{''count'' value is # '#{}'}}", ['count' => 3]);
完整的格式规范由 ICU MessageFormat 定义。在 MessageFormatter 源码 中,格式化优先使用 PHP intl 扩展的\MessageFormatter类,并对命名参数做了预处理(replaceNamedArguments会把命名参数映射为数字参数再交给 intl);若 intl 未安装则退回到fallbackFormat()实现。
数字(number)
占位符被当作数字处理:
$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);自定义模式中可用的特殊字符参见 ICU DecimalFormat 文档的 "Special Pattern Characters" 一节。
注意:占位符总是按目标语言环境格式化,也就是说,你无法在不改变翻译 Locale 的情况下修改千分位/小数点分隔符、货币符号等。若需要这类自定义能力,应改用yii\i18n\Formatter::asDecimal()与yii\i18n\Formatter::asCurrency()。
日期(date)
占位符按日期格式化:
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());时间(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());数字拼读(spellout)
占位符被当作数字并格式化为拼读形式:
// 输出 "42 is spelled as forty-two" echo \Yii::t('app', '{n,number} is spelled as {n,spellout}', ['n' => 42]);默认按基数(cardinal)拼读,可以修改为序数拼读:
// 输出 "I am forty-seventh agent" echo \Yii::t('app', 'I am {n,spellout,%spellout-ordinal} agent', ['n' => 47]);注意:spellout,与%之间不能有空格。要查询某个 Locale 可用的选项列表,可参考 ICU 文档 "Numbering schemas, Spellout" 部分。
序数(ordinal)
占位符被当作数字并格式化为序数:
// 输出 "You are the 42nd visitor here!" echo \Yii::t('app', 'You are the {n,ordinal} visitor here!', ['n' => 42]);序数对某些语言(如西班牙语)支持更多格式:
// 输出 471ª echo \Yii::t('app', '{n,ordinal,%digits-ordinal-feminine}', ['n' => 471]);注意:ordinal,与%之间不能有空格。
时长(duration)
占位符被当作秒数并格式化为时长:
// 输出 "You are here for 47 sec. already!" echo \Yii::t('app', 'You are here for {n,duration} already!', ['n' => 47]);时长也支持其他格式:
// 输出 130:53:47 echo \Yii::t('app', '{n,duration,%in-numerals}', ['n' => 471227]);注意:duration,与%之间不能有空格。
复数(plural)
不同语言对复数的标记规则差异很大。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表示恰好为 0,=1表示恰好为 1;other表示其他任意值;#会被替换为按目标语言格式化后的n值。
有些语言的复数形式非常复杂。下面的俄语示例中,=1表示n恰好为 1,而one对应 21 或 101 这类值:
Здесь {n,plural,=0{котов нет} =1{есть один кот} one{# кот} few{# кота} many{# котов} other{# кота}}!other、few、many等特殊参数名随语言而异,具体某个 Locale 应使用哪些参数名,可参考 Unicode CLDR 的复数规则文档("Plural Rules, Cardinal")。
Note: 上面的俄语消息主要用于译文而非源消息,除非你把应用的源语言设为
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偏移量offset: 1意味着#将替换为n - 1,因此当likeCount = 2时命中one分支(2 - 1 = 1),输出 "You and one other person liked this"。
在 MessageFormatter 的 fallback 实现 中可以看到,即使没有 intl 扩展,plural也支持offset、精确值=n、one与other分支的简单处理;但更复杂的few、many等规则依赖 intl 才能正确工作。
序数选择(selectordinal)
selectordinal参数用于根据目标语言的序数语言规则,从多个字符串中选择一个。例如:
$n = 3; echo \Yii::t('app', 'You are the {n,selectordinal,one{#st} two{#nd} few{#rd} other{#th}} visitor', ['n' => $n]);- 英文输出:
You are the 3rd visitor - 俄语译文与输出:
'You are the {n,selectordinal,one{#st} two{#nd} few{#rd} other{#th}} visitor' => 'Вы {n,selectordinal,other{#-й}} посетитель',输出:
Вы 3-й посетитель - 法语译文与输出:
'You are the {n,selectordinal,one{#st} two{#nd} few{#rd} other{#th}} visitor' => 'Vous êtes le {n,selectordinal,one{#er} other{#e}} visiteur'输出:
Vous êtes le 3e visiteur
该格式与复数格式非常接近。具体某个 Locale 应使用哪些参数名,可参考 Unicode CLDR 的 "Plural Rules, Ordinal" 部分。
选择(select)
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加载。
结合 I18N::getMessageSource() 的实现可以确认匹配顺序:精确类别名 → 带*前缀模式(如app*)→ 全局*,最后仍无法匹配时才抛出InvalidConfigException。
模块消息的翻译
如果希望翻译某个模块的消息、并避免把所有模块消息堆进同一个翻译文件,可以这样组织:
<?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把每个类别映射到所需文件;也可以不使用fileMap,改用"同名文件"的默认映射约定。此后可以直接调用Module::t('validation', 'your custom validation message')或Module::t('form', 'some form label')。
这里用到的技巧是:模块在init()阶段动态地向Yii::$app->i18n->translations数组中注册自己的消息源——这正是 I18N::$translations 被设计为可随时修改的原因(源码注释明确说明该属性可被扩展在运行时注册自己的消息源)。
Widget 消息的翻译
上述模块规则同样适用于 Widget,例如:
<?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])。
Note: 对 Widget 而言,你还可以使用 i18n 视图(view),规则与控制器视图一致(见下文"视图翻译"一节)。
覆盖框架自带消息
Yii 自带验证错误等默认消息的翻译,这些消息全部属于yii类别。框架的真实翻译文件存放在 framework/messages/ 下,每个语言一个子目录(如 framework/messages/fr/ 即框架内置的法语翻译)。如需修正框架默认翻译,可如下配置i18n组件:
'i18n' => [ 'translations' => [ 'yii' => [ 'class' => 'yii\i18n\PhpMessageSource', 'sourceLanguage' => 'en-US', 'basePath' => '@app/messages' ], ], ],然后将修正后的翻译放入@app/messages/<language>/yii.php即可。这与 I18N::init() 中默认注册的yii类别配置同构——框架默认将yii类别指向@yii/messages,你的配置会覆盖它。
处理缺失的翻译
即使消息源中找不到翻译,Yii 也会原样显示请求的消息内容。只要源消息本身是完整通顺的句子,这种"原样回退"行为就非常实用。但有时这还不够——你可能希望在消息缺失时做一些额外处理。为此可以使用yii\i18n\MessageSource::EVENT_MISSING_TRANSLATION(missingTranslation 事件)。
例如,你希望把所有缺失翻译标记上醒目的内容,以便在页面上快速定位。首先在应用配置中注册事件处理器:
'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,该值将作为翻译结果被输出。
Note: 每个消息源独立处理各自的缺失翻译。如果使用多个消息源并希望它们以相同方式处理缺失消息,需要给每个消息源都绑定对应的事件处理器。
结合 MessageSource::translateMessage() 的源码可以看到完整机制:查不到译文时触发EVENT_MISSING_TRANSLATION,若事件对象携带了translatedMessage则作为译文缓存并返回,否则返回false交由上层按源语言直接格式化。
使用message命令提取与管理翻译
翻译可以存放在 PHP 文件、.po 文件或数据库中,具体选项请参考对应类。命令的完整实现位于 framework/console/controllers/MessageController.php。
生成配置文件模板
首先需要创建一个配置文件。确定存放位置后执行:
./yii message/config-template path/to/config.php该命令会将框架自带的模板(源码中actionConfigTemplate()通过复制@yii/views/messageConfig.php实现)拷贝到目标路径。打开生成的文件并按需调整参数,特别注意以下两项:
languages:应用需要翻译成的语言代码数组;messagePath:消息文件存放目录,必须与应用配置中i18n的basePath保持一致。
动态生成配置文件
也可以使用./yii message/config命令,通过命令行参数动态生成配置文件。例如:
./yii message/config --languages=de,ja --messagePath=messages path/to/config.php查看全部可用选项:
./yii help message/config从 actionConfig() 源码 看,该命令会把当前命令的所有选项值导出为 PHP 数组并写入目标文件,若文件已存在会交互式询问是否覆盖。
提取消息
配置完成后,用下面的命令真正提取消息:
./yii message path/to/config.php也可以附加选项动态覆盖提取参数。提取完成后,如果选择的是文件型翻译,译文文件会出现在messagePath目录下(每个语言一个子目录,如messagePath/fr-FR/)。
提取配置参数详解
actionExtract() 源码 展示了提取流程:扫描sourcePath下的源码文件 → 通过translator标记(默认为Yii::t/\Yii::t)定位待翻译消息 → 按languages逐个语言写出译文文件。框架自身就使用该机制维护翻译,其真实配置见 framework/messages/config.php,常用参数包括:
| 参数 | 说明 |
|---|---|
sourcePath | 源码根目录,字符串必填,默认@yii |
messagePath | 译文存放根目录,字符串必填 |
languages | 目标语言代码数组,例如['zh-CN', 'de'] |
translator | 用于定位待翻译消息的函数名标记,可传字符串或数组 |
sort | 是否按键排序新老消息;false时未翻译新消息与已翻译旧消息分离存放 |
overwrite | 是否用合并结果覆盖消息文件,默认true |
removeUnused | 是否删除源码中已不存在的消息,默认false |
markUnused | 是否用一对@@包裹不再使用的消息,默认true |
except | 不需要处理的文件/目录模式列表(如/messages、/tests、/vendor) |
only | 只处理匹配的文件模式(如*.php) |
format | 生成文件格式,可为php、db或po |
db | db格式使用的数据库连接组件 ID |
sourceMessageTable/messageTable | db格式的源消息表与译文表名 |
catalog | po格式的目录名(catalog) |
phpFileHeader/phpDocBlock | 生成 PHP 文件时的文件头与 DocBlock 注释(2.0.13+) |
视图翻译
除了逐条翻译消息文本,有时你可能希望翻译整个视图脚本。做法很简单:翻译视图并保存在以目标语言代码命名的子目录中。例如,你翻译了视图views/site/index.php,且目标语言为fr-FR,就把译文保存为views/site/fr-FR/index.php。此后每次调用yii\base\View::renderFile()(或任何调用它的方法,如yii\base\Controller::render())渲染views/site/index.php时,实际渲染的将是views/site/fr-FR/index.php。
Note: 当目标语言与源语言相同时,会渲染原始视图,而不管是否存在已翻译的视图。
日期与数字格式化
关于yii\i18n\Formatter组件对日期、时间与数字的完整格式化能力(如asDate()、asTime()、asDecimal()、asCurrency()等方法),详见数据格式化一节。
PHP 运行环境:intl 扩展与 ICU 版本
Yii 依赖 PHP 的 intl 扩展 提供大部分国际化功能,包括yii\i18n\Formatter的日期/数字格式化与yii\i18n\MessageFormatter的消息格式化。两个类都在 intl 未安装时提供降级实现,但降级实现只在目标语言为英语时才能正常工作,因此强烈建议按需安装intl。
PHP intl 扩展基于 ICU 库,后者提供了各种 Locale 的知识库与格式化规则。不同版本的 ICU 可能导致日期与数字格式化的结果不同。为确保网站在所有环境中输出一致,建议在所有环境安装相同版本的intl扩展(从而使用相同版本的 ICU)。
可以用下面的脚本查看当前 PHP 与 ICU 的版本:
<?php echo "PHP: " . PHP_VERSION . "\n"; echo "ICU: " . INTL_ICU_VERSION . "\n"; echo "ICU Data: " . INTL_ICU_DATA_VERSION . "\n";建议使用大于等于 48 的 ICU 版本,这能保证本文描述的所有功能可用。例如,ICU 49 之前的版本不支持复数规则中的#占位符。注意 ICU 在 4.8 之后改变了版本编号方式(如 ICU 4.8、ICU 49、ICU 50……)。
此外,ICU 库自带的时区数据库信息可能过时。虽然格式化使用的是 ICU 的时区数据库,但 PHP 自带的时区数据也可能需要更新,可以通过安装最新版的 pecltimezonedb包来更新。
小结
至此,你已经掌握 Yii 2 国际化的完整脉络:
- 语言环境模型:
sourceLanguage(源语言)与language(目标语言)两个属性共同驱动整个翻译流程,前者默认en-US; - 翻译入口:
Yii::t()→i18n组件 → 消息源,消息源支持 PHP 文件、Gettext 文件与数据库三种形态,并通过通配符模式、fileMap与语言回退机制灵活组织; - 格式化体系:基于 ICU MessageFormat 的占位符系统覆盖了数字、货币、日期、时间、拼读、序数、时长、复数、序数选择与性别选择等场景,
MessageFormatter在缺少 intl 时提供有限降级; - 工程化工具:
message命令(config-template/config/extract)负责从源码扫描并生成各语言译文文件; - 环境要求:建议统一安装 intl 扩展并使用 ICU ≥ 48,以保证格式化行为在各环境一致。
相关实现均可在此仓库中直接查阅:I18N 组件、消息源基类、PHP 消息源、消息格式化器、message 命令,以及框架自身使用的消息提取配置与内置多语言翻译。
【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址: https://gitcode.com/gh_mirrors/yi/yii2
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考