本地化
使用语言环境
CodeIgniter 提供了多种工具,帮助开发者将应用本地化为不同语言。虽然应用的完整本地化是一个复杂 的主题,但在应用中切换受支持的语言字符串却非常简单。
配置语言环境
设置默认语言环境
每个站点都会有一个默认的语言环境。可在 app/Config/App.php 中设置:
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class App extends BaseConfig
{
// ...
public string $defaultLocale = 'en';
// ...
}
此值可设为应用管理文本及格式所用的任意字符串。 建议使用 BCP 47 语言代码, 例如美式英语使用 en-US,法语(法国)使用 fr-FR。 访问 W3C 网站 可查看更易读的相关介绍。
若找不到精确匹配,系统会自动回退到更通用的语言代码。
例如,若语言环境设为 en-US 但未配置专属的语言文件,系统将退而使用 en 目录下的配置。
反之,若 app/Language/en-US 目录存在,则优先使用该目录。
语言环境检测
重要
语言环境检测仅适用于使用 IncomingRequest 类的 Web 请求。 命令行请求不支持这些功能。
请求期间检测正确的语言环境支持两种方法。
如需直接设置语言环境,请参阅 设置当前语言环境。
自 v4.4.0 起,新增 IncomingRequest::setValidLocales() 方法,用于
设置(和重置)来自 Config\App::$supportedLocales 配置的有效语言环境。
内容协商
可在 app/Config/App.php 中设置两个额外的配置项,使内容协商自动进行。 第一个值告诉 Request 类需要协商语言环境,因此将其设置为 true:
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class App extends BaseConfig
{
// ...
public bool $negotiateLocale = true;
// ...
}
启用后,系统会根据你在 $supportLocales 中定义的语言环境数组自动协商正确的语言。如果
支持的语言与请求的语言之间找不到匹配项,将使用 $supportedLocales 中的第一项。在以下
示例中,如果找不到匹配项,将使用 en 语言环境:
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class App extends BaseConfig
{
// ...
public array $supportedLocales = ['en', 'es', 'fr-FR'];
// ...
}
在路由中
第二种方法使用自定义占位符来检测所需的语言环境并在 Request 上设置它。
占位符 {locale} 可作为路由中的一个段放置。如果存在,匹配段的内容
即为当前语言环境:
$routes->get('{locale}/books', 'App\Books::index');
在此示例中,如果用户尝试访问 http://example.com/fr/books,则语言环境会
设置为 fr,前提是其已配置为有效语言环境。
如果值与 app/Config/App.php 中 $supportedLocales 定义的有效语言环境不匹配,
将使用默认语言环境替代,除非设置为仅使用 App 配置文件中定义的语言环境:
$routes->useSupportedLocalesOnly(true);
备注
useSupportedLocalesOnly() 方法自 v4.3.0 起可用。
设置当前语言环境
IncomingRequest 语言环境
如需直接设置语言环境,可使用 IncomingRequest 类 中的 setLocale() 方法:
/** @var \CodeIgniter\HTTP\IncomingRequest $request */
$request->setLocale('ja');
设置语言环境前,必须先设置有效语言环境。因为尝试设置 无效语言环境会导致设置为 默认语言环境。
默认情况下,有效语言环境在 app/Config/App.php 的 Config\App::$supportedLocales 中定义:
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class App extends BaseConfig
{
// ...
public array $supportedLocales = ['en', 'es', 'fr-FR'];
// ...
}
备注
自 v4.4.0 起,新增 IncomingRequest::setValidLocales() 方法,
用于设置(和重置)有效语言环境。如需动态更改有效语言环境,请使用此方法。
Language 类的语言环境
lang() 函数使用的 Language 类也包含当前语言环境。
该环境在实例化时设置为 IncomingRequest 的语言环境。
如需在实例化 Language 类后更改语言环境,请使用
Language::setLocale() 方法。
/** @var \CodeIgniter\Language\Language $lang */
$lang = service('language');
$lang->setLocale('ja');
获取当前语言环境
可通过 getLocale() 方法从 IncomingRequest 对象获取当前语言环境。
如果控制器继承了 CodeIgniter\Controller,可通过 $this->request 获取:
<?php
namespace App\Controllers;
class UserController extends BaseController
{
public function index()
{
$locale = $this->request->getLocale();
}
}
此外,也可使用 Services 类 获取当前请求:
$locale = service('request')->getLocale();
语言本地化
创建语言文件
语言字符串存储在 app/Language 目录中,每种支持的语言(语言环境)各有一个子目录:
app/
Language/
en/
App.php
fr/
App.php
备注
语言文件没有命名空间。
语言文件没有特定的命名规范。文件应按逻辑命名,以描述其包含的内容类型。例如, 假设你想创建一个包含错误消息的文件,可以简单地命名为:Errors.php。
在文件中,返回一个数组,数组中的每个元素对应一个语言键和要返回的字符串:
<?php
return [
'languageKey' => 'The actual message to be shown.',
];
备注
语言键的开头和结尾不能包含点号(.)。
也支持嵌套定义:
<?php
return [
'languageKey' => [
'nested' => [
'key' => 'The actual message to be shown.',
],
],
];
<?php
return [
'errorEmailMissing' => 'You must submit an email address',
'errorURLMissing' => 'You must submit a URL',
'errorUsernameMissing' => 'You must submit a username',
'nested' => [
'error' => [
'message' => 'A specific error message',
],
],
];
基本用法
可使用 lang() 辅助函数从任意语言文件中检索文本,将文件名和语言键作为
第一个参数传入,以点号(.)分隔。
例如,要从 Errors.php 语言文件中加载 errorEmailMissing 字符串,
可以执行以下操作:
echo lang('Errors.errorEmailMissing');
对于嵌套定义,可以执行以下操作:
echo lang('Errors.nested.error.message');
如果当前语言环境的文件中不存在请求的语言键(在 语言回退 之后),将原样返回该字符串。
在此示例中,如果不存在,将返回 Errors.errorEmailMissing 或 Errors.nested.error.message。
替换参数
将数组作为第二个参数传递给 lang() 函数,即可替换语言字符串中的占位符。
由此可轻松实现数字替换与格式化:
<?php
// The language file, Tests.php:
return [
'apples' => 'I have {0, number} apples.',
'men' => 'The top {1, number} men out-performed the remaining {0, number}',
'namedApples' => 'I have {number_apples, number, integer} apples.',
];
// Displays "I have 3 apples."
echo lang('Tests.apples', [3]);
占位符中的第一项对应数组中的索引(如果是数字索引):
// Displays "The top 23 men out-performed the remaining 20"
echo lang('Tests.men', [20, 23]);
也可以使用命名键来使结构更清晰:
// Displays "I have 3 apples."
echo lang('Tests.namedApples', ['number_apples' => 3]);
显然,不只是能做数字替换。根据底层库的 官方 ICU 文档, 可以替换以下类型的数据:
数字 — 整数、货币、百分比
日期 — short, medium, long, full
时间 — short, medium, long, full
spellout — 拼写数字(如 34 变为 thirty-four)
序数
持续时间
以下是一些示例:
<?php
// The language file, Tests.php
return [
'shortTime' => 'The time is now {0, time, short}.',
'mediumTime' => 'The time is now {0, time, medium}.',
'longTime' => 'The time is now {0, time, long}.',
'fullTime' => 'The time is now {0, time, full}.',
'shortDate' => 'The date is now {0, date, short}.',
'mediumDate' => 'The date is now {0, date, medium}.',
'longDate' => 'The date is now {0, date, long}.',
'fullDate' => 'The date is now {0, date, full}.',
'spelledOut' => '34 is {0, spellout}',
'ordinal' => 'The ordinal is {0, ordinal}',
'duration' => 'It has been {0, duration}',
];
// Displays "The time is now 11:18 PM"
echo lang('Tests.shortTime', [time()]);
// Displays "The time is now 11:18:50 PM"
echo lang('Tests.mediumTime', [time()]);
// Displays "The time is now 11:19:09 PM CDT"
echo lang('Tests.longTime', [time()]);
// Displays "The time is now 11:19:26 PM Central Daylight Time"
echo lang('Tests.fullTime', [time()]);
// Displays "The date is now 8/14/16"
echo lang('Tests.shortDate', [time()]);
// Displays "The date is now Aug 14, 2016"
echo lang('Tests.mediumDate', [time()]);
// Displays "The date is now August 14, 2016"
echo lang('Tests.longDate', [time()]);
// Displays "The date is now Sunday, August 14, 2016"
echo lang('Tests.fullDate', [time()]);
// Displays "34 is thirty-four"
echo lang('Tests.spelledOut', [34]);
// Displays "It has been 408,676:24:35"
echo lang('Tests.ordinal', [time()]);
建议阅读 MessageFormatter 类和底层 ICU 格式化的相关文档,以更好地了解其功能, 如执行条件替换、复数形式等。前面提供的两个链接都会让你清楚地了解可用选项。
指定语言环境
如需在替换参数时使用不同的语言环境,可将语言环境作为
第三个参数传递给 lang() 函数。
<?php
// Displays "The time is now 23:21:28 GMT-5"
echo lang('Test.longTime', [time()], 'ru-RU');
// Displays "£7.41"
echo lang('{price, number, currency}', ['price' => 7.41], 'en-GB');
// Displays "$7.41"
echo lang('{price, number, currency}', ['price' => 7.41], 'en-US');
如需更改当前语言环境,请参阅 Language 类的语言环境。
嵌套数组
语言文件还支持嵌套数组,使处理列表等操作更加容易。
<?php
// Language/en/Fruit.php
return [
'list' => [
'Apples',
'Bananas',
'Grapes',
'Lemons',
'Oranges',
'Strawberries',
],
];
// Displays "Apples, Bananas, Grapes, Lemons, Oranges, Strawberries"
echo implode(', ', lang('Fruit.list'));
语言回退
如果你为某个语言环境设置了一组消息,例如 Language/en/App.php,可以为该语言环境添加变体, 每个变体各自位于一个文件夹中,例如 Language/en-US/App.php。
只需为该语言环境变体提供不同的消息值即可。任何缺失的消息 定义会自动从主语言环境设置中获取。
不仅如此 — 本地化甚至可以一直回退到英文(en), 以防框架新增了消息而你还来不及翻译。
因此,如果使用 fr-CA 语言环境,本地化消息将首先在 Language/fr-CA 目录中查找,
然后在 Language/fr 目录中查找,最后在 Language/en 目录中查找。
系统消息翻译
我们有一套”官方”系统消息翻译,位于 独立仓库 中。
可以下载该仓库,将其中的 Language 文件夹
复制到 app 文件夹中。引入的翻译会自动
生效,因为 App 命名空间映射到了 app 文件夹。
另一种更好的做法是在项目中运行以下命令:
composer require codeigniter4/translations
翻译消息会自动生效, 因为翻译文件夹会被正确映射。
覆盖系统消息翻译
框架提供了 系统消息翻译, 已安装的包也可能提供了消息翻译。
如需覆盖某些语言消息,在 app/Language 目录中创建语言文件。 然后,在文件中仅返回想要覆盖的数组。
通过命令生成翻译文件
在 4.5.0 版本加入.
可以自动生成和更新 app 文件夹中的翻译文件。该命令会搜索 lang() 函数的使用,
结合 app/Language 中当前的翻译键,并定义来自 Config\App 的语言环境 defaultLocale。
操作完成后,需要自行翻译语言键。
该命令能够正常识别嵌套键,如 File.array.nested.text。
之前保存的键不会改变。
php spark lang:find
<?php
// Controllers/Translation/Lang.php
$message = lang('Text.info.success');
$message2 = lang('Text.paragraph');
// The following will be saved in Language/en/Text.php
return [
'info' => [
'success' => 'Text.info.success',
],
'paragraph' => 'Text.paragraph',
];
备注
命令扫描文件夹时,会跳过 app/Language。
命令生成的语言文件可能不符合编码标准。
建议对其进行格式化。例如,如果已安装 php-cs-fixer,可运行 vendor/bin/php-cs-fixer fix ./app/Language。
更新前,可以预览命令找到的翻译:
php spark lang:find --verbose --show-new
--verbose 的详细输出还会显示无效键列表。例如:
...
Files found: 10
New translates found: 30
Bad translates found: 5
+------------------------+---------------------------------+
| Bad Key | Filepath |
+------------------------+---------------------------------+
| ..invalid_nested_key.. | app/Controllers/Translation.php |
| .invalid_key | app/Controllers/Translation.php |
| TranslationBad | app/Controllers/Translation.php |
| TranslationBad. | app/Controllers/Translation.php |
| TranslationBad... | app/Controllers/Translation.php |
+------------------------+---------------------------------+
All operations done!
如需更精确的搜索,可以指定要扫描的语言环境或目录。
php spark lang:find --dir Controllers/Translation --locale en --show-new
运行以下命令可获取详细信息:
php spark lang:find --help
通过命令同步翻译文件
在 4.6.0 版本加入.
在完成当前语言的翻译后,可能需要为另一种语言创建文件。可以使用 spark lang:find 命令来辅助完成。
但是,它可能无法检测所有翻译,特别是那些带有动态设置参数的翻译,如 lang('App.status.' . $key, ['payload' => 'John'], 'en')。
为确保不遗漏翻译,最好的做法是复制已完成的语言文件并手动翻译。这种方法可以保留命令可能忽略的唯一键。
只需执行:
// 指定新/更新翻译的语言环境
php spark lang:sync --target ru
// 或设置原始语言环境
php spark lang:sync --locale en --target ru
结果将获得带有翻译键的文件。 如果目标语言环境中存在重复键,这些键会被保留。
警告
不匹配的键在新翻译中会被删除!