2 位关注者

国际化

国际化(I18N)是指设计软件应用程序的过程,以便可以在不进行工程更改的情况下适应各种语言和地区。对于 Web 应用程序,这一点尤其重要,因为潜在用户可能是全球性的。Yii 提供了全方位的 I18N 功能,支持消息翻译、视图翻译、日期和数字格式化。

语言环境和语言

语言环境

语言环境是一组参数,用于定义用户的语言、国家/地区以及用户希望在其用户界面中看到的任何特殊变体偏好。它通常由一个包含语言 ID 和区域 ID 的 ID 来标识。

例如,ID en-US 代表“英语和美国”的语言环境。

为了一致性,Yii 应用程序中使用的所有语言环境 ID 应规范化为 ll-CC 格式,其中 ll 是根据 ISO-639 的二或三字母小写语言代码,CC 是根据 ISO-3166 的二字母国家/地区代码。有关语言环境的更多详细信息,请参阅 ICU 项目的文档

语言

在 Yii 中,我们经常使用“语言”一词来指代语言环境。

Yii 应用程序使用两种语言

  • 源语言:这指的是源代码中文本消息的书写语言。
  • 目标语言:这是用于向最终用户显示内容的语言。

所谓的“消息翻译服务”主要负责将文本消息从源语言翻译成目标语言。

配置

您可以在“应用程序配置”中配置应用程序语言,如下所示

return [
    // set target language to be Russian
    'language' => 'ru-RU',
    
    // set source language to be English
    'sourceLanguage' => 'en-US',
    
    ......
];

默认情况下,源语言en-US,表示美式英语。建议您保持此默认值不变。通常,找到可以将“英语翻译成其他语言”的人比找到可以将“非英语翻译成非英语”的人容易得多。

您通常需要根据不同的因素(例如最终用户的语言偏好)动态设置目标语言。您可以使用以下语句更改目标语言,而不是在应用程序配置中进行配置。

// change target language to Chinese
\Yii::$app->language = 'zh-CN';

提示:如果您的源语言在代码的不同部分有所不同,则可以为不同的消息源覆盖源语言,这将在下一节中介绍。

消息翻译

从源语言到目标语言

消息翻译服务将文本消息从一种语言(通常是源语言)翻译成另一种语言(通常是目标语言)。

它通过在消息源中查找要翻译的消息来执行翻译,消息源存储原始消息和翻译后的消息。如果找到该消息,则返回相应的翻译后的消息;否则,将返回未翻译的原始消息。

如何实现

要使用消息翻译服务,您主要需要执行以下操作

  1. 将需要翻译的每个文本消息包装在对Yii::t() 方法的调用中。
  2. 配置一个或多个消息源,消息翻译服务可以在其中查找翻译后的消息。
  3. 让翻译人员翻译消息并将它们存储在消息源中。

1. 包装文本消息

Yii::t() 方法可以像下面这样使用,

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

其中第二个参数指的是要翻译的文本消息,而第一个参数指的是用于对消息进行分类的类别的名称。

2. 配置一个或多个消息源

Yii::t() 方法将调用 i18n 应用程序组件translate 方法来执行实际的翻译工作。该组件可以在应用程序配置中配置如下,

'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 开头的所有消息类别都应使用此消息源进行翻译。

3. 让翻译人员翻译消息并将它们存储在消息源中

yii\i18n\PhpMessageSource 类使用带有简单 PHP 数组的 PHP 文件来存储消息翻译。这些文件包含 源语言 中消息到 目标语言 中翻译的映射。

信息:您可以使用message 命令自动生成这些 PHP 文件,这将在本章后面介绍。

每个 PHP 文件对应于单个类别的消息。默认情况下,文件名应与类别名称相同。例如 app/messages/nl-NL/main.php:

<?php

/**
* Translation map for nl-NL
*/
return [
    'welcome' => 'welkom'
];

文件映射

您可以配置fileMap 以将类别映射到使用不同命名方法的 PHP 文件。

在上面的示例中,类别 app/error 映射到 PHP 文件 @app/messages/ru-RU/error.php(假设 ru-RU 是目标语言)。但是,如果没有此配置,类别将映射到 @app/messages/ru-RU/app/error.php

其他存储类型

除了将消息存储在 PHP 文件中之外,您还可以使用以下消息源将翻译后的消息存储在不同的存储中

消息格式化

翻译消息时,您可以嵌入一些占位符并让它们被动态参数值替换。您甚至可以使用特殊的占位符语法根据目标语言格式化参数值。在本小节中,我们将介绍不同的消息格式化方法。

消息参数

在要翻译的消息中,您可以嵌入一个或多个参数(也称为占位符),以便它们可以被给定的值替换。通过提供不同的值集,您可以动态地改变翻译后的消息。在下面的示例中,消息 'Hello, {username}!' 中的占位符 {username} 将分别被 'Alexander''Qiang' 替换。

$username = 'Alexander';
// display a translated message with username being "Alexander"
echo \Yii::t('app', 'Hello, {username}!', [
    'username' => $username,
]);

$username = 'Qiang';
// display a translated message with username being "Qiang"
echo \Yii::t('app', 'Hello, {username}!', [
    'username' => $username,
]);

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

您可以在一条消息中使用命名占位符位置占位符,但不能同时使用两者。

前面的示例展示了如何使用命名占位符。也就是说,每个占位符都以 {name} 的格式编写,并且您提供一个关联数组,其键是占位符名称(不带花括号),其值是相应的占位符要替换的值。

位置占位符使用以零为基数的整数序列作为名称,这些名称将根据它们在 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]);

如果只有一个位置参数,则可以在不将其包装到数组中的情况下指定其值。

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

提示:在大多数情况下,您应该使用命名占位符。这是因为名称将使翻译人员更好地理解正在翻译的整个消息。

参数格式化

您可以在消息的占位符中指定其他格式化规则,以便在参数值替换占位符之前正确地格式化它们。在下面的示例中,价格参数值将被视为数字并格式化为货币值

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

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

您可以使用短格式或完整格式来指定带有格式的占位符

short form: {name,type}
full form: {name,type,style}

注意:如果您需要使用特殊字符,例如 {}'#,请将它们包装在 '

echo Yii::t('app', "Example of string with ''-escaped characters'': '{' '}' '{test}' {count,plural,other{''count'' value is # '#{}'}}", ['count' => 3]);

完整格式在ICU 文档中进行了描述。在下面,我们将展示一些常见用法。

数字

参数值被视为数字。例如,

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

您可以指定一个可选的参数样式,例如 integercurrencypercent

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

您还可以指定自定义模式来格式化数字。例如,

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

自定义格式中使用的字符可以在ICU API 参考的“特殊模式字符”部分中找到。

该值始终根据您要翻译到的区域设置进行格式化,即,您无法在不更改翻译区域设置的情况下更改小数或千位分隔符、货币符号等。如果您需要自定义这些,可以使用yii\i18n\Formatter::asDecimal()yii\i18n\Formatter::asCurrency()

日期

参数值应格式化为日期。例如,

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

您可以指定一个可选的参数样式,例如 shortmediumlongfull

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());

您可以指定一个可选的参数样式,例如 shortmediumlongfull

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

您还可以指定自定义模式来格式化时间值

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

格式化参考.

拼写

参数值应被视为数字并格式化为拼写。例如,

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

默认情况下,数字将拼写为基数。它可以更改

// may produce "I am forty-seventh agent"
echo \Yii::t('app', 'I am {n,spellout,%spellout-ordinal} agent', ['n' => 47]);

请注意,spellout, 后面和 % 前面不应该有空格。

要获取您正在使用的区域设置可用的选项列表,请在https://intl.rmcreative.ru/处查看“编号方案,拼写”。

序数

参数值应被视为数字并格式化为序数名称。例如,

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

序数支持更多用于西班牙语等语言的格式化方式

// may produce 471ª
echo \Yii::t('app', '{n,ordinal,%digits-ordinal-feminine}', ['n' => 471]);

请注意,ordinal, 后面和 % 前面不应该有空格。

要获取您正在使用的区域设置可用的选项列表,请在https://intl.rmcreative.ru/处查看“编号方案,序数”。

持续时间

参数值应被视为秒数并格式化为时间持续时间字符串。例如,

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

持续时间支持更多格式化方式

// may produce 130:53:47
echo \Yii::t('app', '{n,duration,%in-numerals}', ['n' => 471227]);

请注意,duration, 后面和 % 前面不应该有空格。

要获取您正在使用的区域设置可用的选项列表,请在https://intl.rmcreative.ru/处查看“编号方案,持续时间”。

复数

不同的语言有不同的复数变格方式。Yii 提供了一种方便的方法来翻译不同复数形式的消息,即使对于非常复杂的规则也能很好地工作。无需直接处理变格规则,只需在某些情况下提供变格词的翻译即可。例如,

// When $n = 0, it may produce "There are no cats!"
// When $n = 1, it may produce "There is one cat!"
// When $n = 42, it may produce "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 表示正好为零,=1 表示正好为一。other 代表任何其他值。# 将根据目标语言格式化 n 的值替换。

在某些语言中,复数形式可能非常复杂。在下面的俄语示例中,=1 匹配正好 n = 1,而 one 匹配 21101

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

这些 otherfewmany 和其他特殊参数名称因语言而异。要了解您应该为特定区域设置指定哪些参数,请参阅https://intl.rmcreative.ru/上的“复数规则,基数”。或者,您可以参考unicode.org 上的规则参考

注意:上面的俄语示例消息主要用作翻译后的消息,而不是原始消息,除非您将应用程序的源语言设置为 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

序数选择

selectordinal的参数类型旨在根据您要翻译到的语言环境的序数语言规则选择字符串。

$n = 3;
echo Yii::t('app', 'You are the {n,selectordinal,one{#st} two{#nd} few{#rd} other{#th}} visitor', ['n' => $n]);
// For English it outputs:
// You are the 3rd visitor

// Translation
'You are the {n,selectordinal,one{#st} two{#nd} few{#rd} other{#th}} visitor' => 'Вы {n,selectordinal,other{#-й}} посетитель',

// For Russian translation it outputs:
// Вы 3-й посетитель

格式与复数使用的格式非常接近。要了解应为特定语言环境指定哪些参数,请参阅https://intl.rmcreative.ru/上的“复数规则,序数”。或者,您可以参考unicode.org上的规则参考

选择

您可以使用select参数类型根据参数值选择短语。例如,

// It may produce "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',
]);

在上面的表达式中,femalemale都是可能的参数值,而other处理与这两个值都不匹配的值。在每个可能的参数值之后,您应该指定一个短语并将其括在花括号中。

指定默认消息源

您可以指定默认消息源,该消息源将用作不匹配任何已配置类别的类别的回退。您可以通过配置通配符类别*来做到这一点。为此,请将以下内容添加到应用程序配置中

//configure i18n component

'i18n' => [
    'translations' => [
        '*' => [
            'class' => 'yii\i18n\PhpMessageSource'
        ],
    ],
],

现在您可以使用类别而无需配置每个类别,这类似于Yii 1.1的行为。类别的消息将从默认翻译basePath(即@app/messages)下的文件中加载。

echo Yii::t('not_specified_category', 'message from unspecified category');

消息将从@app/messages//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,您可以简单地使用类别映射到同名文件的约定。现在您可以直接使用Module::t('validation', 'your custom validation message')Module::t('form', 'some form label')

翻译小部件消息

上面应用于模块的相同规则也可以应用于小部件,例如

<?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])

注意:对于小部件,您还可以使用i18n视图,并对它们应用与控制器相同的规则。

翻译框架消息

Yii附带了验证错误和其他一些字符串的默认翻译消息。这些消息都属于类别yii。有时您希望为您的应用程序更正默认的框架消息翻译。为此,请配置以下i18n 应用程序组件

'i18n' => [
    'translations' => [
        'yii' => [
            'class' => 'yii\i18n\PhpMessageSource',
            'sourceLanguage' => 'en-US',
            'basePath' => '@app/messages'
        ],
    ],
],

现在您可以将调整后的翻译放置到@app/messages//yii.php中。

处理缺失的翻译

即使源代码中缺少翻译,Yii也会显示请求的消息内容。如果您的原始消息是有效的详细文本,这种行为非常方便。但是,有时这还不够。当源代码中缺少请求的翻译时,您可能需要执行一些自定义处理。这可以通过使用missingTranslation事件yii\i18n\MessageSource来实现。

例如,您可能希望将所有缺少的翻译标记为一些显眼的内容,以便可以在页面上轻松找到它们。首先,您需要设置一个事件处理程序。这可以在应用程序配置中完成

'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,它将显示为翻译结果。

注意:每个消息源分别处理其缺少的翻译。如果您使用多个消息源并希望它们以相同的方式处理缺少的翻译,则应将相应的事件处理程序分配给每个消息源。

使用message命令

翻译可以存储在php 文件.po 文件数据库中。有关其他选项,请参阅特定类。

首先,您需要创建一个配置文件。确定要将其存储在何处,然后发出命令

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

打开创建的文件并调整参数以适合您的需求。请特别注意

  • languages:表示您的应用程序应翻译成的语言的数组;
  • messagePath:存储消息文件的路径,应与配置中指定的i18nbasePath参数匹配。

您还可以使用'./yii message/config'命令通过cli动态生成具有指定选项的配置文件。例如,您可以设置languagesmessagePath参数,如下所示

./yii message/config --languages=de,ja --messagePath=messages path/to/config.php

要获取可用选项的列表,请执行以下命令

./yii help message/config

完成配置文件后,您可以使用以下命令最终提取消息

./yii message path/to/config.php

此外,您可以使用选项动态更改提取参数。

然后您将在messagePath目录中找到您的文件(如果您选择了基于文件的翻译)。

视图翻译

有时,您可能希望翻译整个视图脚本,而不是翻译单个文本消息。要实现此目标,只需翻译视图并将其保存在与目标语言名称相同的子目录下。例如,如果您想翻译视图脚本views/site/index.php且目标语言为ru-RU,则可以翻译视图并将其另存为文件views/site/ru-RU/index.php。现在,每当您调用yii\base\View::renderFile()或调用此方法的任何方法(例如yii\base\Controller::render())来呈现视图views/site/index.php时,它最终将呈现翻译后的视图views/site/ru-RU/index.php

注意:如果目标语言源语言相同,则无论是否存在翻译后的视图,都将呈现原始视图。

格式化日期和数字值

有关详细信息,请参阅数据格式化部分。

设置PHP环境

Yii使用PHP intl扩展提供其大部分I18N功能,例如yii\i18n\Formatter类的日期和数字格式以及使用yii\i18n\MessageFormatter的消息格式。当未安装intl扩展时,这两个类都提供了一种回退机制。但是,回退实现仅适用于英语目标语言。因此,强烈建议您在需要I18N时安装intl

PHP intl扩展基于ICU库,该库提供所有不同语言环境的知识和格式化规则。不同版本的ICU可能会产生不同的日期和数字值的格式化结果。为了确保您的网站在所有环境中都能产生相同的结果,建议您在所有环境中安装相同版本的intl扩展(以及相同版本的ICU)。

要了解PHP使用哪个版本的ICU,您可以运行以下脚本,它将为您提供正在使用的PHP和ICU版本。

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

还建议您使用等于或大于版本49的ICU版本。这将确保您可以使用本文档中描述的所有功能。例如,低于49的ICU版本不支持在复数规则中使用#占位符。有关可用ICU版本的完整列表,请参阅https://icu.unicode.org/download。请注意,版本号在4.8版本发布后已更改(例如,ICU 4.8、ICU 49、ICU 50等)。

此外,ICU库附带的时区数据库中的信息可能已过时。有关更新时区数据库的详细信息,请参阅ICU手册。虽然输出格式化使用ICU时区数据库,但PHP使用的时区数据库也可能相关。您可以通过安装最新版本的pecl软件包timezonedb来更新它。

发现错别字或您认为此页面需要改进?
在github上编辑它 !