ICU の MessageFormat で同じパラメータを二度使うと U_ARGUMENT_TYPE_MISMATCH が出る
MessageFormat に渡すパターンの中でplural
で使ったパラメータをもう一度使おうとすると U_ARGUMENT_TYPE_MISMATCH になる。
<?php echo msgfmt_format_message('en_US', <<<'END' {count, plural, =0 {no items} one {1 item} other {{count} items} } END , ['count' => 12345]); if (intl_get_error_code()) echo intl_get_error_message(); // Inconsistent types declared for an argument: U_ARGUMENT_TYPE_MISMATCH
SymfonyやCakePHPのGitHub issueに出ていた回避策として、#
を使えばよい。
<?php echo msgfmt_format_message('en_US', <<<'END' {count, plural, =0 {no items} one {1 item} other {# items} } END , ['count' => 12345]); if (intl_get_error_code()) echo intl_get_error_message(); // 12,345 items
回避策はともかく、何故タイプミスマッチエラーになるのか、という説明がissueにはないのでICUのソースを追って調べてみた。
パラメータを単に{count}
と指定すると、文字列型になる。一方、{count, plural, ...}
とすると内部的には浮動小数になって、相互のタイプが非互換なため、メッセージのコンパイルがエラーになるようだ。
つまり#
は{count, number}
でも(冗長だが)よく、それならばplural
の外側にも適用できる。
実際、ドキュメントには#
は{××, number}
の略記とある。
<?php echo msgfmt_format_message('en_US', <<<'END' {count, number} {count, plural, one {item} other {items} } END , ['count' => 12345]); if (intl_get_error_code()) echo intl_get_error_message(); // 12,345 items
そもそもの話として単に{count}
とすると桁区切りされないので、普通は{count, number}
とすることになる。注意していればこのエラーも目にすることはなかったかもしれない。