【问题标题】:It it possible to have multiple Locales in one same MessageFormat?在同一个 MessageFormat 中可以有多个语言环境吗?
【发布时间】:2022-01-08 02:32:26
【问题描述】:

我在下面有以下 JSON 示例:

{
    "value": 946.2,
    "description": "O valor é R$ 946,20."
}

我需要使用 MessageFormat 来 JUnit 测试这个 JSON 示例,但我得到一个无效值,因为我的语言环境不是英文的。如果我将语言环境更改为英语而不是巴西葡萄牙语,我会得到一个无效的描述,因为货币值以英语显示。

这是我的代码:

import java.math.BigDecimal;
import java.text.MessageFormat;
import java.util.Locale;

Locale.setDefault(Locale.ENGLISH);

System.out.println(MessageFormat.format("""
'{'
    "value": {0},
    "description": "O valor é {0,number,currency}."
'}'
""", new BigDecimal(946.2)));

如何格式化值或描述以获得如上所示的 JSON?

【问题讨论】:

    标签: java json escaping java-17 messageformat


    【解决方案1】:

    这是一个String.format 解决方案,具有两个内部(本地化、自定义)NumberFormatters:

    import java.math.BigDecimal;
    import java.text.NumberFormat;
    import java.util.Locale;
    
    public class OtherMain {
    
      private static final Locale LOCALE_PT_BR = new Locale.Builder().
          setLanguage("pt").
          setRegion("BR").
          build();
      
      private static final NumberFormat DEFAULT_NUMBER_FMT
          = NumberFormat.getNumberInstance(Locale.US);
      private static final NumberFormat MY_SPECIAL_NUMBER_FMT
          = NumberFormat.getCurrencyInstance(LOCALE_PT_BR);
    
      static {
        // this disables "thousands sperarator", we can adjust DEFAULT_NUMBER_FMT as well...
        MY_SPECIAL_NUMBER_FMT.setGroupingUsed(false);
      }
    
      public static void main(String[] args) {
        final BigDecimal num = new BigDecimal(99946.2);
        System.out.format("""
                          {
                            "value": %s,
                            "description": "O valor é %s."
                          }
                          """,
            DEFAULT_NUMBER_FMT.format(num),
            MY_SPECIAL_NUMBER_FMT.format(num)
        );
      }
    }
    

    打印:

    {
      "value": 99,946.2,
      "description": "O valor é R$ 99946,20."
    }
    

    【讨论】:

      【解决方案2】:

      当然,没问题!但是:

      • Locale.setDefault 太苛刻了! (为了测试它是可以容忍的......但我不想查找那个错误......)
      • (我“不喜欢”MessageFormat...它在内部使用StringBuffer(性能杀手和内存转储先生)。)

      只要你能处理“复杂性”,就可以这样做:

      import java.math.BigDecimal;
      import java.text.MessageFormat;
      import java.text.NumberFormat;
      import java.util.Locale;
      
      public class MainSimple {
      
        // Brazilian Locale:
        private static final Locale LOCALE_PT_BR = new Locale.Builder().
            setLanguage("pt").
            setRegion("BR").
            build();
        // US number format:
        private static final NumberFormat DEFAULT_NUMBER_FMT = NumberFormat.getNumberInstance(Locale.US);
      
        public static void main(String[] args) {
          // MessageFormat instance, with format (with 2! parameters) and Locale:
          MessageFormat mainFmt = new MessageFormat("""
                            '{'
                                "value": {0},
                                "description": "O valor é {1,number,currency}. <-- trick 1 "
                            '}'
                            """, LOCALE_PT_BR);
          
          final BigDecimal num = new BigDecimal(946.2);
          // trick 2:
          Object[] params = {DEFAULT_NUMBER_FMT.format(num), num};
          // do it:
          System.out.println(mainFmt.format(params));
        }
      }
      

      大纲:

      • 常量:
        • 巴西(外部格式)语言环境。
        • 用于自定义(非巴西)格式/此特定数字的美国/标准(内部)数字格式。
      • MessageFormat has a constructor with locale parameter! (我们使用它,并将其称为外部格式/语言环境(mainFmt))
      • 输入我们“小技巧”的格式并使用 2 个参数:{0}{1,number,currency}
      • 我们欺骗:将第一个参数作为(美国格式的数字)String 传递,第二个参数保持不变。 (Object[] params = {DEFAULT_NUMBER_FMT.format(num), num})
      • 最后格式化(并打印出来)。

      打印:

      {
          "value": 946.2,
          "description": "O valor é R$ 946,20."
      }
      

      【讨论】:

      • 我最终编写了完全相同的代码,并发现我的用例存在另一个问题。我怎样才能拥有“O valoré R$ 1946,20”。而不是“O valoré R$ 1.946,20。”?可能吗?应该再做一个hacky代码并用正则表达式替换所有点吗?或者是否存在诸如##.# 之类的维护货币符号的模式?还有一点,你会用什么来代替 MessageFormat?我之所以选择它而不是 String.format,是因为 MessageFormat 允许我们重用变量,因为模式是定位的,而 String.format 不能。
      • 对不起,我收到{"value": 99,946.2,"description": "O valor é R$ 99.946,20."}
      • ...但99946,20(没有千位分隔符)不是“巴西货币格式”!!!
      • 如果你说“test json”和“java 17”......现在有更好的方法来“test json”......而不是使用 MessageFormat......(除了生成 json使用 MessageFormat ......这正是要测试的......)
      • 是的,你是对的,但我的用例要求隐藏千位分隔符,因为这是这里的常见做法,现在我们有一个 JSON 格式不接受的值。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-12
      • 2011-08-05
      相关资源
      最近更新 更多