Search code examples
javainternationalizationicumessageformat

How to customize currency formatting in MessageFormat in ICU4J


I have a system that generates a lot of documents. Its contents are defined in ResourceBundles.

I want to customize the way MessageFormat prints currency values. Sometimes I want it to display currencies without fraction digits (but not always).

This should be working as expected but it is not:

System.err.println(
  com.ibm.icu.text.MessageFormat.format(
      "{0,number,\u00A4#}", 
      new com.ibm.icu.util.CurrencyAmount(1, 
        com.ibm.icu.util.Currency.getInstance("USD"))));

Unfortunately it prints out:

US$1,00

Does anyone of you use custom formats for currency in resource bundle 'properties' files?

I don't want to change it system wide.

And by the way this works fine with java.text.MessageFormat.


Solution

  • OK, I read your question once again. I don't really know why you want to chop down the cents part (in US, it makes sense in Korea or Japan as they don't use them at all).
    Anyway, I don't think it is a good idea to just cut-off cents part, but if you want to do it, it is as simple as using NumberFormat with setMaximumIntegerDigits(int).

    BTW, I still don't know I know why by using resource bundles you can't use NumberFormat. You still can call formatter in MessageFormat.format():

    NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance(Locale.US);
    currencyFormatter.setMaximumFractionDigits(0);
    System.err.println(MessageFormat.format("Some amount: {0}.",
            currencyFormatter.format(1d)));
    

    Predictably it prints out:

    Some amount: $1.

    If you need to retain the currency, I'd suggest to play with setCurrency(Currency) method by retain local format - you are asking this question in Internalization tag anyway.


    Edit: Including information about MessageFormat capabilities

    If you need to use custom currency formats for a Locale, you actually need to instantiate MessageFormat class (regular static MessageFormat.format(String, Object...) won't work in web applications for it uses default Locale - Locale.getDefault(Locale.Category.FORMAT) in Java 7 - server Locale if you prefer).

    So what you really want is to write a helper method (sorry, no bonus) that will look similar to this (out of memory, sorry):

    public static String format(String pattern, Locale locale, Object... args) {
      final String emptyPattern = "";
      final FieldPosition zero = new FieldPosition(0);
      MessageFormat fmt = new MessageFormat(emptyPattern, locale);
      StringBuffer buf = new StringBuffer(); // I just love it...
    
      fmt.applyPattern(pattern);
      fmt.format(args, buf, zero);
    
      return buf.toString();
    }
    

    For performance reasons, you might think of creating StringBuffer once and then clean it all the time, but I leave optimizations to yourself.
    You also would need to modify patterns a bit and I will explain in a moment why:

    String pattern = "{1}{0,number,\u00A4#}";
    

    You would need to pass the amount and the currency symbol and left to translators where to place the symbol and how to format value for a Locale (don't forget to add comments to properties file!).