在國際化組件的學習程序中,我們已經接觸過了 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
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/295994.html
標籤:PHP
下一篇:PHP中的國際化日歷類
