跳至正文

自定义错误消息

可以更改消息的内容以适应自定义用例;它可以包含将被有用信息替换的占位符。

下面的占位符始终可用;根据原始消息,甚至可以使用更多。

占位符 描述
{message_code} 消息代码
{node_name} 消息绑定的节点名称
{node_path} 消息绑定的节点路径
{node_type} 消息绑定的节点类型
{expected_signature} 预期类型的字符串表示形式
{source_value} 赋予节点的源值
{original_message} 定制之前的原始消息

用法:

try {
    (new \CuyZ\Valinor\MapperBuilder())
        ->mapper()
        ->map(SomeClass::class, [/* … */]);
} catch (\CuyZ\Valinor\Mapper\MappingError $error) {
    foreach ($error->messages() as $message) {
        if ($message->code() === 'some_code') {
            $message = $message
                ->withParameter('some_parameter', 'some custom value')
                ->withBody('new message / {message_code} / {some_parameter}');
        }

        // new message / some_code / some custom value
        echo $message;
    }
}

这些消息使用 [ICU 库] 进行格式化,使占位符能够使用高级语法来执行正确的翻译,例如货币支持。

try {
    (new \CuyZ\Valinor\MapperBuilder())->mapper()->map('int<0, 100>', 1337);
} catch (\CuyZ\Valinor\Mapper\MappingError $error) {
    $message = $error->messages()->toArray()[0];

    if (is_numeric($message->sourceValue())) {
        $message = $message->withBody(
            'Invalid amount {source_value, number, currency}'
        );    
    } 

    // Invalid amount: $1,337.00
    echo $message->withLocale('en_US');

    // Invalid amount: £1,337.00
    echo $message->withLocale('en_GB');

    // Invalid amount: 1 337,00 €
    echo $message->withLocale('fr_FR');
}

有关可用语法的更多信息,请参阅 [ICU 文档]。

警告

如果未安装intl扩展,则可以使用填充程序来替换占位符,但它不会处理如上所述的高级语法。

更深入的消息定制/翻译

对于更深入的消息更改,可以使用格式化程序来自定义正文和参数。

注意

可以将格式化程序添加到消息中

翻译

格式化程序TranslationMessageFormatter可用于翻译消息的内容。

该库提供了所有可以返回的消息的列表;可以使用自定义翻译来填充或修改此列表。

\CuyZ\Valinor\Mapper\Tree\Message\Formatter\TranslationMessageFormatter::default()
    // Create/override a single entry…
    ->withTranslation('fr', 'some custom message', 'un message personnalisé')
    // …or several entries.
    ->withTranslations([
        'some custom message' => [
            'en' => 'Some custom message',
            'fr' => 'Un message personnalisé',
            'es' => 'Un mensaje personalizado',
        ], 
        'some other message' => [
            // …
        ], 
    ])
    ->format($message);

替换映射表

格式化程序MessageMapFormatter可用于提供消息替换列表。它可以用一个数组实例化,其中每个键代表:

  • 要替换的消息的代码
  • 要替换的消息正文
  • 要替换的消息的类名

如果没有找到这些内容,消息的内容将保持不变,除非为类提供了默认内容。

如果找到这些键之一,则将使用数组条目来替换消息的内容。该条目可以是纯文本,也可以是可调用的,将消息作为参数并返回字符串;例如,建议在使用自定义翻译服务的情况下使用可调用函数,以避免无用的贪婪操作。

在任何情况下,内容都可以包含如上所述的占位符。

(new \CuyZ\Valinor\Mapper\Tree\Message\Formatter\MessageMapFormatter([
    // Will match if the given message has this exact code
    'some_code' => 'New content / code: {message_code}',

    // Will match if the given message has this exact content
    'Some message content' => 'New content / previous: {original_message}',

    // Will match if the given message is an instance of `SomeError`
    SomeError::class => 'New content / value: {source_value}',

    // A callback can be used to get access to the message instance
    OtherError::class => function (NodeMessage $message): string {
        if ($message->path() === 'foo.bar') {
            return 'Some custom message';
        }

        return $message->body();
    },

    // For greedy operation, it is advised to use a lazy-callback
    'foo' => fn () => $this->customTranslator->translate('foo.bar'),
]))
    ->defaultsTo('some default message')
    // …or…
    ->defaultsTo(fn () => $this->customTranslator->translate('default_message'))
    ->format($message);

多个格式化器

可以使用AggregateMessageFormatter将多个格式化程序连接到一个格式化程序中。然后可以轻松地将这个实例注入到处理消息的服务中。

格式化程序将以与聚合相同的顺序被调用。

(new \CuyZ\Valinor\Mapper\Tree\Message\Formatter\AggregateMessageFormatter(
    new \CuyZ\Valinor\Mapper\Tree\Message\Formatter\LocaleMessageFormatter('fr'),
    new \CuyZ\Valinor\Mapper\Tree\Message\Formatter\MessageMapFormatter([
        // …
    ],
    \CuyZ\Valinor\Mapper\Tree\Message\Formatter\TranslationMessageFormatter::default(),
))->format($message)