本地化

使用区域设置

CodeIgniter 提供了一些工具来帮助您将应用程序本地化为不同的语言。虽然应用程序的完全本地化是一个复杂的过程,但用不同的支持语言替换应用程序中的字符串非常简单。

语言字符串存储在 **app/Language** 目录中,每个支持的语言都有一个子目录。

app/
    Language/
        en/
            App.php
        fr/
            App.php

重要

区域设置检测仅适用于使用 IncomingRequest 类的基于 Web 的请求。命令行请求将没有这些功能。

配置区域设置

设置默认区域设置

每个网站都将有一个默认的语言/区域设置。这可以在 **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 设置了语言文件,那么这些文件将被使用,因为更具体的 en-US 不存在。但是,如果在 **app/Language/en-US** 目录中存在语言目录,那么该目录将首先被使用。

区域设置检测

有两种方法支持在请求期间检测正确的区域设置。第一种是“设置并忘记”方法,它将自动执行 内容协商,以确定要使用的正确区域设置。第二种方法允许您在路由中指定一个段,该段将用于设置区域设置。

如果您需要直接设置区域设置,请参阅 设置当前区域设置

从 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} 可以作为您路由中的一个段放置。如果存在,匹配段的内容将是您的区域设置

<?php

$routes->get('{locale}/books', 'App\Books::index');

在此示例中,如果用户尝试访问 **http://example.com/fr/books**,则区域设置将设置为 fr,假设它被配置为有效的区域设置。

如果该值与 **app/Config/App.php** 中 $supportedLocales 中定义的有效区域设置不匹配,则将使用默认区域设置代替,除非您设置为仅使用 App 配置文件中定义的支持区域设置

<?php

$routes->useSupportedLocalesOnly(true);

注意

自 v4.3.0 起可以使用 useSupportedLocalesOnly() 方法。

设置当前区域设置

如果您想直接设置区域设置,可以使用 IncomingRequest::setLocale(string $locale)。您必须在 **app/Config/App.php** 中设置支持的区域设置

<?php

namespace Config;

use CodeIgniter\Config\BaseConfig;

class App extends BaseConfig
{
    // ...

    public array $supportedLocales = ['en', 'es', 'fr-FR'];

    // ...
}

注意

任何尝试设置此数组中未包含的区域设置的行为都将导致 默认区域设置 被设置。

获取当前区域设置

始终可以通过 getLocale() 方法从 IncomingRequest 对象中获取当前区域设置。如果您的控制器扩展了 CodeIgniter\Controller,则可以通过 $this->request 获取。

<?php

namespace App\Controllers;

class UserController extends BaseController
{
    public function index()
    {
        $locale = $this->request->getLocale();
    }
}

或者,您可以使用 Services 类 来获取当前请求。

<?php

$locale = service('request')->getLocale();

语言本地化

创建语言文件

注意

语言文件没有命名空间。

语言没有特定的命名约定要求。文件应以逻辑方式命名以描述其包含的内容类型。例如,假设您要创建一个包含错误消息的文件。您可以简单地将其命名为:**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 字符串,您将执行以下操作。

<?php

echo lang('Errors.errorEmailMissing');

对于嵌套定义,您将执行以下操作。

<?php

echo lang('Errors.nested.error.message');

如果请求的语言键在当前区域设置的文件中不存在,则字符串将原样返回。在本例中,如果它不存在,它将返回 'Errors.errorEmailMissing' 或 'Errors.nested.error.message'。

替换参数

注意

以下函数都需要在您的系统上加载 intl 扩展才能正常工作。如果扩展未加载,则不会尝试进行任何替换。您可以在 Sitepoint 上找到一个很好的概述。

您可以将一个值数组传递给 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]);

占位符中的第一个项目对应于数组中项目的索引,如果它是数字。

<?php

// Displays "The top 23 men out-performed the remaining 20"
echo lang('Tests.men', [20, 23]);

您也可以使用命名键,如果您愿意,这将使您更容易保持事情井井有条。

<?php

// Displays "I have 3 apples."
echo lang('Tests.namedApples', ['number_apples' => 3]);

显然,您可以做的不仅仅是数字替换。根据官方 ICU 文档,以下类型的可以被替换。

  • 数字 - 整数、货币、百分比

  • 日期 - 短、中、长、全

  • 时间 - 短、中、长、全

  • 拼写 - 拼写出数字(例如,34 变为三十四)

  • 序数

  • 持续时间

以下是一些示例

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

嵌套数组

语言文件还允许嵌套数组,以使使用列表等更容易。

<?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**。

您只需要为那些针对该区域设置变体进行不同本地化的消息提供值。任何缺少的消息定义将自动从主区域设置中提取。

它变得更好 - 本地化可以一直回退到英语,以防框架中添加了新消息,而您还没有机会为您的区域设置翻译它们。

因此,如果您使用的是区域设置 fr-CA,那么本地化消息将首先在 **Language/fr-CA** 目录中查找,然后在 **Language/fr** 目录中查找,最后在 **Language/en** 目录中查找。

消息翻译

我们在 自己的仓库 中拥有“官方”的翻译集。

您可以下载该仓库,并将它的 **Language** 文件夹复制到您的 **app** 文件夹中。由于 App 命名空间映射到您的 **app** 文件夹,因此合并的翻译将自动被识别。

或者,更好的做法是在您的项目中运行以下命令

composer require codeigniter4/translations

由于翻译文件夹被正确映射,因此翻译后的消息将自动被识别。