是否可以在同一个 MessageFormat 中使用多个 Locales?

It it possible to have multiple Locales in one same MessageFormat?

我有以下 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?

好的,没问题!但是:

  • 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));
  }
}

大纲:

  • 常数:
    • 巴西(外部格式)语言环境。
    • US/Standard 自定义(非巴西)formatting/this 特定数字的(内部)数字格式。
  • MessageFormat has a constructor with locale parameter! (我们使用那个,并称它为外部format/locale(mainFmt))
  • Int 格式我们“稍微耍点儿花招”并使用 2 个参数:{0}{1,number,currency}
  • 我们的技巧是:将第一个参数作为(美国格式的数字)String传递,第二个作为有。 (Object[] params = {DEFAULT_NUMBER_FMT.format(num), num})
  • 最终格式化(&打印出来)。

打印:

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

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

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."
}