在国际化组件的学习过程中,我们已经接触过了 NumberFormatter 这种数字的格式化操作,它可以让我们将数字转换成标准格式、货币、本地语言等形式。今天我们来学习的是另一种专门用于信息格式化的类 MessageFormatter ,它主要是针对字符串的操作。

MessageFormatter 也是遵循的 ICU 规范,底层是 C 中的 ICU 操作,所以和 C 相关代码的使用方式没有太大的区别。

格式化

// 格式化
$fmt = new MessageFormatter("zh_CN", "{0,number,integer} 只猴子在 {1,number,integer} 颗树上,每只树上有 {2,number} 只猴子");
echo $fmt->format([4560, 123, 4560 / 123]), PHP_EOL;
// 4,560 只猴子在 123 颗树上,每只树上有 37.073 只猴子 $fmt = new MessageFormatter("de", "{0,number,integer} Affen auf {1,number,integer} Bäumen sind {2,number} Affen pro Baum");
echo $fmt->format([4560, 123, 4560 / 123]), PHP_EOL;
// 4.560 Affen auf 123 Bäumen sind 37,073 Affen pro Baum echo MessageFormatter::formatMessage("zh_CN", "{0,number,integer} 只猴子在 {1,number,integer} 颗树上,每只树上有 {2,number} 只猴子", [4560, 123, 4560 / 123]), PHP_EOL;
// 4,560 只猴子在 123 颗树上,每只树上有 37.073 只猴子 echo MessageFormatter::formatMessage("de", "{0,number,integer} Affen auf {1,number,integer} Bäumen sind {2,number} Affen pro Baum", [4560, 123, 4560 / 123]), PHP_EOL;
// 4.560 Affen auf 123 Bäumen sind 37,073 Affen pro Baum

看到了吗?类似于 PDO 里预编译操作的占位符。在调用 format() 方法后,就可以让这个方法里面的参数来替换占位符的内容。我们可以指定占位的所使用的参数类型和位置,{参数下标,类型,扩展类型} 这就是这个信息数据格式化的占位符的规则定义。看起来貌似很简单呀,其实它还有更多的功能,我们将在后面看到。不过需要注意的是,它只支持数字、日期、文本片段类型,文章最后的参考链接中有官方的文档可以查阅。

MessageFormatter::formatMessage() 这个静态方法可以一次性地指定语言、预操作语句以及替换参数,不需要先进行实例化再调用 format() 方法。

反格式化(根据规则获取参数数组)

能够进行格式化,当然我们也能够根据语句规则来反格式化相关的字符串从而获得对应占位符的参数列表。

// 根据格式化规则反向获取规则参数
$fmt = new MessageFormatter('zh_CN', "{0,number,integer} 只猴子在 {1,number,integer} 颗树上,每只树上有 {2,number} 只猴子");
$res = $fmt->parse("4,560 只猴子在 123 树上,每只树上有 37.073 只猴子");
var_export($res); // false
echo "ERROR: " . $fmt->getErrorMessage() . " (" . $fmt->getErrorCode() . ")\n";
// ERROR: Parsing failed: U_MESSAGE_PARSE_ERROR (6) $fmt = new MessageFormatter('en_US', "{0,number,integer} monkeys on {1,number,integer} trees make {2,number} monkeys per tree");
$res = $fmt->parse("4,560 monkeys on 123 trees make 37.073 monkeys per tree");
var_export($res);
// array (
// 0 => 4560,
// 1 => 123,
// 2 => 37.073,
// ) $fmt = new MessageFormatter('de', "{0,number,integer} Affen auf {1,number,integer} Bäumen sind {2,number} Affen pro Baum");
$res = $fmt->parse("4.560 Affen auf 123 Bäumen sind 37,073 Affen pro Baum");
var_export($res);
// array (
// 0 => 4560,
// 1 => 123,
// 2 => 37.073,
// ) $fmt = MessageFormatter::parseMessage('de', "{0,number,integer} Affen auf {1,number,integer} Bäumen sind {2,number} Affen pro Baum", "4.560 Affen auf 123 Bäumen sind 37,073 Affen pro Baum");
var_export($fmt);
// array (
// 0 => 4560,
// 1 => 123,
// 2 => 37.073,
// )

使用实例化后的 parse() 方法或者直接使用静态方法 MessageFormatter::parseMessage() 就能够实现这样的操作。需要注意的是,对于 zh_CN ,也就是中文语言区域设置来说,这个操作是会出问题的。通过 getErrorMessage() 和 getErrorCode() 就可以看到错误信息以及错误代码,可以看到对于中文来说,直接返回的错误信息就是解析失败。

设置获取规则

在实例化的对象中,我们还可以动态地修改规则语句。

// 设置获取规则
$fmt = new MessageFormatter("zh_CN", "{0, number} 猴子在 {1, number} 颗树上");
echo "默认规则: '" . $fmt->getPattern(), PHP_EOL; // 默认规则: '{0, number} 猴子在 {1, number} 颗树上'
echo "格式化结果:" . $fmt->format(array(123, 456)), PHP_EOL; // 格式化结果:123 猴子在 456 颗树上 $fmt->setPattern("{0, number} 颗树上有 {1, number} 猴子");
echo "新规则: '" . $fmt->getPattern(), PHP_EOL; // 新规则: '{0, number} 颗树上有 {1, number} 猴子'
echo "新规则格式化结果: " . $fmt->format(array(123, 456)), PHP_EOL; // 新规则格式化结果: 123 颗树上有 456 猴子

非常简单的两个方法,setPattern() 用于设置当前实例化对应的格式化规则,getPattern() 用于获取查看当前实例化对象的格式化规则。在设置了新规则之后,进行 format() 或者 parse() 时就是按照新的规则语句来执行的了。

格式化完整示例

上面说过,除了数字之外,还可以有日期格式的占位符,我们就来演示一下。

echo MessageFormatter::formatMessage('zh_CN', '今天是 {3, date, full},当前时间为 {3, time, ::Hms}, 我要准备开始 {0} 了,今天要和 {2,number,integer} 人见面,还不能忘了要交 {1,number,currency} 元的电费', ['上班', 35.33, 25, new DateTime()]), PHP_EOL;
// 今天是 2020年11月16日星期一,当前时间为 10:09:30, 我要准备开始 上班 了,今天要和 25 人见面,还不能忘了要交 ¥35.33 元的电费

在这段语句中,我们给定的参数顺序并不是按照语句中占位符出现的顺序,这样并没有影响,只需要指定对应位置的参数数组下标即可,比如第一个 {3, date, full} 指定的就是参数数组中的第4个元素(从0开始)。date 类型、time 类型都是可以指定的类型,当然我们也可以指定它们的 日期格式 比如第二个占位符我们就只显示当前的时分秒信息。

如果是字符串信息,那么只需要一个简单的 {0} 就可以了,字符串不需要太多的类型设置。而数字类型则可以直接格式化为货币等类型,就像我们之前讲过的 NumberFormatter 中可以指定的那些类型一样。

看完这一个示例是不是就感觉到这个 MessageFormatter 的强大之处了?别急,它还有更牛X的能力。

根据参数内容进行复数显示

对于复数来说,其实中文语法中并没有这样的语句,比如说一只猫是 a cat ,两只猫是 two cats 。

echo MessageFormatter::formatMessage('en_US', 'I Have {0, plural, =0{no cat} =1{a cat} other{# cats}}', [0]),PHP_EOL; // I Have no cat
echo MessageFormatter::formatMessage('en_US', 'I Have {0, plural, =0{no cat} =1{a cat} other{# cats}}', [1]),PHP_EOL; // I Have a cat
echo MessageFormatter::formatMessage('en_US', 'I Have {0, plural, =0{no cat} =1{a cat} other{# cats}}', [2]),PHP_EOL; // I Have 2 cats

虽说参数类型的 plural 是复数的意思,不过其实我们可以将它看做是一个 switch() 语句的用法。

echo MessageFormatter::formatMessage('zh_CN', '我{0, plural, =0{没有猫} other{有 # 只猫}}', [0]),PHP_EOL; // 我没有猫
echo MessageFormatter::formatMessage('zh_CN', '我{0, plural, =0{没有猫} other{有 # 只猫}}', [1]),PHP_EOL; // 我有 1 只猫
echo MessageFormatter::formatMessage('zh_CN', '我{0, plural, =0{没有猫} other{有 # 只猫}}', [2]),PHP_EOL; // 我有 2 只猫

# 号就是对应的参数值的原内容,这一套语法又让这个 MessageFormatter 类上了一个层次吧,还有呢!我们先来看看这个问题:

echo MessageFormatter::formatMessage('en_US', 'I Have {0, plural, =0{no cat} =1{a cat} other{# cats}}', [-1]),PHP_EOL; // I Have -1 cats

参数传错了,-1 只猫可不对吧,没关系,还有别的处理方式解决这个问题。

选择条件规则

// 选择表达式
echo MessageFormatter::formatMessage('en_US', 'I Have {0, choice, 0 #no cats| 1 #one cat | 2 #{0, number} cats}', [-1]),PHP_EOL; // I Have no cats
echo MessageFormatter::formatMessage('en_US', 'I Have {0, choice, 0 #no cats| 1 #one cat | 2 #{0, number} cats}', [0]),PHP_EOL; // I Have no cats
echo MessageFormatter::formatMessage('en_US', 'I Have {0, choice, 0 #no cats| 1 #one cat | 2 #{0, number} cats}', [1]),PHP_EOL; // I Have one cat
echo MessageFormatter::formatMessage('en_US', 'I Have {0, choice, 0 #no cats| 1 #one cat | 2 #{0, number} cats}', [2]),PHP_EOL; // I Have 2 cats
echo MessageFormatter::formatMessage('en_US', 'I Have {0, choice, 0 #no cats| 1 #one cat | 2 #{0, number} cats}', [10]),PHP_EOL; // I Have 10 cats

choice 这个单词就能看出来,这是一个选择相关的语法。后面的参数其实是一个区间,分别代表 <= 0 | 1 | >=2 的范围内使用哪个内容。另外,一个占位符规则里面还可以继续套占位符号的。

总结

又大开了一回眼界。文章开头的两部分内容其实并没有什么惊喜的地方,毕竟普通的字符串替换都能办到,不过越往后面可是越来越精彩啊。当然,它的相关规则语法应该还有更多,只是这些资料非常少,不管是 PHP 官方文档还是 ICU 的官方文档都没有找到过多的介绍。所以我们还是报以学习了解的态度先知道有这么回事,将来发现更有趣的资料后再来分享学习吧,也希望有使用过的朋友留言一起讨论哦!

测试代码:

https://github.com/zhangyue0503/dev-blog/blob/master/php/202011/source/5.PHP中针对区域语言标记信息的操作.php

参考文档:

https://www.php.net/manual/zh/class.messageformatter.php

http://userguide.icu-project.org/formatparse/messages

===============

关注公众号:【硬核项目经理】获取最新文章

添加微信/QQ好友:【xiaoyuezigonggong/149844827】免费得PHP、项目管理学习资料

知乎、公众号、抖音、头条搜索【硬核项目经理】

B站ID:482780532

学习PHP中的信息格式化操作的更多相关文章

  1. 学习PHP中的国际化日期格式化操作

    对于国际化功能来说,日期相关的格式化操作也是一块重头戏,毕竟不同的时区,不同的国家对于日期的表示方式都会有些不同.今天我们主要来学习的就是国际化地表示日期相关的信息内容. 日期格式化 首先就是最直接的 ...

  2. 学习PHP中的国际化功能来查看货币及日期信息

    做为一门在世界范围内广泛使用的编程语言,国际化能力往往是衡量一个编程语言是否能够大范围流行的重要内容.特别是对于 PHP 这种以 Web 页面编程为主战场的语言来说,国际化能力更是重中之重.在 PHP ...

  3. python学习笔记——爬虫中提取网页中的信息

    1 数据类型 网页中的数据类型可分为结构化数据.半结构化数据.非结构化数据三种 1.1 结构化数据 常见的是MySQL,表现为二维形式的数据 1.2 半结构化数据 是结构化数据的一种形式,并不符合关系 ...

  4. Flink 从0到1学习 —— Flink 中如何管理配置?

    前言 如果你了解 Apache Flink 的话,那么你应该熟悉该如何像 Flink 发送数据或者如何从 Flink 获取数据.但是在某些情况下,我们需要将配置数据发送到 Flink 集群并从中接收一 ...

  5. 深入学习OpenCV中图像灰度化原理,图像相似度的算法

    最近一段时间学习并做的都是对图像进行处理,其实自己也是新手,各种尝试,所以我这个门外汉想总结一下自己学习的东西,图像处理的流程.但是动起笔来想总结,一下却不知道自己要写什么,那就把自己做过的相似图片搜 ...

  6. 深入学习PHP中的JSON相关函数

    在我们当年刚刚上班的那个年代,还全是 XML 的天下,但现在 JSON 数据格式已经是各种应用传输的事实标准了.最近几年开始学习编程开发的同学可能都完全没有接触过使用 XML 来进行数据传输.当然,时 ...

  7. 学习PHP中好玩的Gmagick图像操作扩展的使用

    在 PHP 的图像处理领域,要说最出名的 GD 库为什么好,那就是因为它不需要额外安装的别的什么图像处理工具,而且是随 PHP 源码一起发布的,只需要在安装 PHP 的时候添加上编译参数就可以了. G ...

  8. 一起学习PHP中GD库的使用(一)

    又到了一个大家非常熟悉的库了,对于图像图形的处理来说,GD 库是 PHPer 们绕不过去的一道坎.从很早很早的 CMS 或者 Discuz 时代,各类开源软件在安装的时候就会明确地指出 GD 库是它们 ...

  9. 学习PHP中国际化地数字格式处理

    不知道大家有没有了解过,对于数字格式来说,西方国家会以三位为一个进位,使用逗号来分隔.比如,12345678,用标准的格式来表示的话就是 12,345,678 .不过我们中文其实并不会有这样的分隔符, ...

随机推荐

  1. 过去,我买漫画看;现在,我用Python爬虫来看

    原标题:运用Python多线程爬虫下载漫画 前言: 以前,我都是买漫画书看的,那个时候没有电脑.今天,我到网上看了一下,发现网上提供漫画看,但是时时需要网络啊!为什么不将它下载下来呢!  1.怎样实现 ...

  2. Proxifier/ProxyChains+reGeorg组合进行内网代理

    在内网渗透过程中,我们经常使用sockes代理工具,本文主要介绍攻击机为windows和linux情况下得使用方式. Win:proxifier+reGeorg 组合 Linux:proxychain ...

  3. STM32—TIMx实现编码器四倍频

    文章目录 一.储备知识 二.TIMx的编码器模式介绍 1.计数边沿设置 2.选择极性和使能 3.使能 4.计数方向 三.代码部分 一.储备知识 通过STM32的定时器编码器接口模式对编码器进行四倍频, ...

  4. 整理自己部署项目需要使用的Linux命令

    1.修改文件名: mv test  test 12.创建test文件夹: mkdir test3.解压文件至 test文件夹下: unzip test.war -d test/4.将work文件移动至 ...

  5. Vmware15的安装以及Ubunt的在虚拟机上的安装

    一.vmware15安装 1.百度网盘地址 链接:https://pan.baidu.com/s/1Lgez57n50QEW97HNdYZCfQ 提取码:9wvy 2.下载到本地后 3.双击安装程序 ...

  6. WPF使用PATH来画圆

    WPF使用Path来画圆, 在 WPF 中可以使用 Path (路径) 来画圆,而 Path 支持两种写法:xaml 代码格式.标记格式,这里介绍的是标记格式: 例子: <Path Data=& ...

  7. input text 只能输入数字 js 正则表达式

    $("#txt1").keyup(function () { $(this).val($(this).val().replace(/[^0-9.]/g, '')); }).bind ...

  8. Object 的wait()方法

    The java.lang.Object.wait() causes current thread to wait until another thread invokes the notify() ...

  9. java 循环移位输出全排列

    //题目:利用1.2.2.3.4这4个数字,用java写一个main函数打印出所有不同的排列,如12234,,2234等,要求打印出来不能有重复 1 package test123; 2 3 impo ...

  10. Verilog实例数组

    编写 Verilog 代码多年,至今才无意中发现了一种奇怪的语法,估计见过的这种的写法的人,在 FPGA 开发者中不会超过 20% 吧. 直接来看代码吧.先定义了一个简单的模块,名为 mod. mod ...