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

SymfonyCakePHPGitHub 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}とすることになる。注意していればこのエラーも目にすることはなかったかもしれない。