10

我正在尝试用 java.text.MessageFormat 做一个简单的逻辑:

MessageFormat cf = new MessageFormat(
"{0,choice, 1<hello|5<{1,choice,1<more than one|4<more than four}}");

 Object[] array = {3, 1};
 System.out.println(cf.format(array));

带字:如果第一个参数大于1,打印“hello”,如果大于5,第二个参数大于1,打印“more than one”,如果第二个参数大于4,打印“more than 4” ”。

我发现没有人说这是不可能的,但我得到了一个 IllegalArgumentException:

Choice Pattern incorrect: 1<hello|5<{1,choice,1<more than one|4<more than four}

有没有办法我可以做到这一点?谢谢!

整个堆栈跟踪:

Exception in thread "main" java.lang.IllegalArgumentException: Choice Pattern incorrect:  1<hello|5<{1,choice,1<more than one|4<more than four}
at java.text.MessageFormat.makeFormat(Unknown Source)
at java.text.MessageFormat.applyPattern(Unknown Source)
at java.text.MessageFormat.<init>(Unknown Source)
at test.Test5.main(Test5.java:18)
Caused by: java.lang.IllegalArgumentException
    at java.text.ChoiceFormat.applyPattern(Unknown Source)
    at java.text.ChoiceFormat.<init>(Unknown Source)
    ... 4 more
4

2 回答 2

12

如果你这样写模式,ChoiceFormat就无法解析格式,因为它无法知道像格式分隔符(|)这样的控制字符是用于内部格式还是外部格式。但是如果你引用嵌套的格式,你可以告诉解析器引用的文本不包含它应该解析的任何控制字符。然后ChoiceFormat将只返回包含另一个ChoiceFormat模式的文本。

如果MessageFormat类应用了 a ChoiceFormat,它会再次将结果解析为 aMessageFormat以处理额外的参数处理,然后再处理内部ChoiceFormat.

因此,如果您像这样编写模式,则代码可以工作:

MessageFormat cf = new MessageFormat(
    "{0,choice, 1<hello|5<'{1,choice,1<more than one|4<more than four}'}");

Object[] array = {3, 1};
System.out.println(cf.format(array));
于 2014-07-25T15:03:15.763 回答
2

正如@Reboot 所提到的,与这些类的部分混淆是在这里ChoiceFormat特别处理的MessageFormat.subformat()

        subFormatter = formats[i];
        if (subFormatter instanceof ChoiceFormat) {
            arg = formats[i].format(obj);
            if (arg.indexOf('{') >= 0) {
                subFormatter = new MessageFormat(arg, locale);
                obj = arguments;
                arg = null;
            }
        }

这个 hack 允许 aMessageFormat包含 a ChoiceFormat,它本身包含 a MessageFormat

new ChoiceFormat("0#none|1#one|1<{0}").format(3);                                // "{0}"
new MessageFormat("{0,choice,0#none|1#one|1<{0}}").format(new Object[] { 3 });   // "3"

当然,作为一种特殊情况,ChoiceFormat嵌套在 aMessageFormat中可能包含嵌套的ChoiceFormat,只要您正确转义/引用即可。

这些类在语法/解析中有很多松散性。与解析/转义/引用是“嵌套”的 Java 或 bash 不同,这里的解析是“急切的”……但它确实有效。

我写了一些课程来帮助对抗这种疯狂。这些课程不会尝试重新发明轮子;它们只是使已经存在的底层结构和嵌套可见。但是它们允许您绕过所有引用问题,并且它们支持任意嵌套深度。

在我自己的项目中,我已将它们连接到 XML。这让我可以定义这样的消息:

<message key="how.many.items">
    <text>There </text>
    <choice argnum="0">
        <option limit="0">
            <text>are no items</text>
        </option>
        <option limit="1">
            <text>is one item</text>
        </option>
        <option limit="1&lt;">
            <text>are </text>
            <choice argnum="0">
                <option limit="0">
                    <number argnum="0"/>
                </option>
                <option limit="100">
                    <text>way too many</text>
                </option>
            </choice>
            <text>items</text>
        </option>
    </choice>
    <text>.</text>
</message>

有关详细信息,请参阅MessageFmt

于 2020-06-27T16:25:16.510 回答