Formatting monetary amounts with Java Money

Java is very often used to develop big financial systems, yet still the JDK has little or no support for monetary arithmetics, currency conversion, formatting money for different locales etc. There’s only a Currency class which serves as a list of ISO 4217 currencies.

Fortunately we’ve got a JSR-354 standard and Moneta – the reference implementation. It provides us the Martin Fowler’s money pattern along with string formatting and currency repositories.

In this article I will focus on displaying monetary amounts for different currencies, locales and personal preferences.

Basic example: amount and ISO symbol

final Locale[] locales = {
    Locale.US,
    new Locale("nl", "NL"),
    new Locale("pl", "PL")
};

final Money amount = Money.of(12345.67, "USD");

for (Locale locale : locales) {
  MonetaryAmountFormat formatter = MonetaryFormats.getAmountFormat(locale);
  System.out.println(formatter.format(amount));
}
en_USnl_NLpl_PL
USD12,345.67USD 12.345,6712 345,67 USD

Amount with a currency symbol

final Locale[] locales = {
    Locale.US,
    new Locale("nl", "NL"),
    new Locale("pl", "PL")
};

final Money amount = Money.of(12345.67, "USD");

for (Locale locale : locales) {
  MonetaryAmountFormat formatter = MonetaryFormats.getAmountFormat(
      AmountFormatQueryBuilder.of(locale)
          .set(CurrencyStyle.SYMBOL)
          .build());
  System.out.println(formatter.format(amount));
}
en_USnl_NLpl_PL
$12,345.67USD 12.345,6712 345,67 USD

The $ currency symbol is displayed only for the United States locale. Other languages do not use it.

Amount with a full currency name

final Locale[] locales = {
    Locale.US,
    new Locale("nl", "NL"),
    new Locale("pl", "PL")
};

final Money amount = Money.of(12345.67, "USD");

for (Locale locale : locales) {
  MonetaryAmountFormat formatter = MonetaryFormats.getAmountFormat(
      AmountFormatQueryBuilder.of(locale)
          .set(CurrencyStyle.NAME)
          .build());
  System.out.println(formatter.format(amount));
}
en_USnl_NLpl_PL
US Dollar12,345.67US Dollar 12.345,6712 345,67 US Dollar

Amount without a thousands separator

final Locale[] locales = {
    Locale.US,
    new Locale("nl", "NL"),
    new Locale("pl", "PL")
};

final Money amount = Money.of(12345.67, "USD");

for (Locale locale : locales) {
  MonetaryAmountFormat formatter = MonetaryFormats.getAmountFormat(
      AmountFormatQueryBuilder.of(locale)
          .set(AmountFormatParams.GROUPING_SIZES, new int[]{2, 0})
          .build());
  System.out.println(formatter.format(amount));
}
en_USnl_NLpl_PL
USD12345.67USD 12345,6712345,67 USD

Grouping sizes are specified starting from the decimal point. In the above example we have two digits after the decimal point, and then we have no more grouping going to the left. Of course other combinations are possible, e.g. {2, 3, 4}.

Custom pattern

Using a pattern as defined by java.text.DecimalFormat.

final Locale[] locales = {
    Locale.US,
    new Locale("nl", "NL"),
    new Locale("pl", "PL")
};
final Money amount = Money.of(12345.67, "USD");
for (Locale locale : locales) {
  MonetaryAmountFormat formatter = MonetaryFormats.getAmountFormat(
      AmountFormatQueryBuilder.of(locale)
          .set(AmountFormatParams.PATTERN, "###,###.## ¤")
          .build());
  System.out.println(formatter.format(amount));
}
en_USnl_NLpl_PL
12,345.67 USD12.345,67 USD12 345,67 USD