學習PHP中的信息格式化操做

在國際化組件的學習過程當中,咱們已經接觸過了 NumberFormatter 這種數字的格式化操做,它可讓咱們將數字轉換成標準格式、貨幣、本地語言等形式。今天咱們來學習的是另外一種專門用於信息格式化的類 MessageFormatter ,它主要是針對字符串的操做。數組

MessageFormatter 也是遵循的 ICU 規範,底層是 C 中的 ICU 操做,因此和 C 相關代碼的使用方式沒有太大的區別。ide

格式化

// 格式化
$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() 方法。測試

反格式化(根據規則獲取參數數組)

可以進行格式化,固然咱們也可以根據語句規則來反格式化相關的字符串從而得到對應占位符的參數列表。orm

// 根據格式化規則反向獲取規則參數
$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() 時就是按照新的規則語句來執行的了。字符串

格式化完整示例

上面說過,除了數字以外,還能夠有日期格式的佔位符,咱們就來演示一下。get

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 類型都是能夠指定的類型,固然咱們也能夠指定它們的 日期格式 好比第二個佔位符咱們就只顯示當前的時分秒信息。it

若是是字符串信息,那麼只須要一個簡單的 {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 的官方文檔都沒有找到過多的介紹。因此咱們仍是報以學習瞭解的態度先知道有這麼回事,未來發現更有趣的資料後再來分享學習吧,也但願有使用過的朋友留言一塊兒討論哦!

測試代碼:

相關文章
相關標籤/搜索