shweta
shweta

Reputation: 79

NumberFormat incorrect parse for non default locale in java

NumberFormat is incorrectly parsing a number for locale other than the default en_US. For example parsing for fr_CA gives incorrect results.

NumberFormat nf = NumberFormat.getNumberInstance(new Locale("fr_CA"));
Number num = nf.parse("2.302,52");

Output is: 2.302

Expected output: 2302,52 or 2302.52

It is basically changing the value of this number from 2 thousands something to just 2 something, which is incorrect.

See the below screenshot for details: enter image description here

What is going wrong I'm unable to identify, please help !

Upvotes: 0

Views: 1635

Answers (2)

Ordous
Ordous

Reputation: 3884

You have 2 problems here,

1) You're creating the locale incorrectly. Use Locale.CANADA_FRENCH.

2) The given number is invalid in both the default and Canadian locales. As per ICU, in fr_CA, the grouping separator is empty, and the decimal separator is ,.

As such, in your example, that uses the default locale, it treats . as a decimal separator and bails at ,, where as in the correct locale it will bail at . and return simply 2.

The correct way to write this number in fr_CA is 2302,52

So, really, Java is correct, but your assumption on the format of this number is incorrect.

Upvotes: 0

shweta
shweta

Reputation: 79

EDIT 1:

I was able to use DecimalFormat to correctly parse non default locale values by setting the decimal and grouping separator as:

        /**
         * Testing DF parse
         */
        DecimalFormat df = new DecimalFormat();
        DecimalFormatSymbols symbols = new DecimalFormatSymbols();
        symbols.setDecimalSeparator(',');
        symbols.setGroupingSeparator('.');
        df.setDecimalFormatSymbols(symbols);
        Number num = df.parse("2.302,52"); // OUTPUT is: 2302.52 -> correctly parsed

        /**
         * Round off and format
         */
        double d = num.doubleValue();
        double roundedVal = Math.round(d); // OUTPUT is: 2303.0 -> correctly rounded off
        String localeAwareVlue = df.format(roundedVal); // OUTPUT is: 2.303 -> using correct grouping separator
        valueString = localeAwareVlue;

But I really want to be able to do this dynamically at run time without having to set the separators for each language/country combination I might receive. Please help with any suggestions.

EDIT 2:

I ended up with the following final code to be able to dynamically parse and format localized values. This works for patterns like ###,###.## and ###.###,## - assumptions being -

  1. there are always only 2 decimal places in the data received
  2. decimal separator is either a comma or a period
  3. grouping separator is either a period or a comma

    String localValue = args.get(0).toString();
    // String locale = args.get(1).toString();
    
    char decChar = localValue.charAt(localValue.length()-3);
    char grpChar = ',';
    if(decChar == ',') {
        grpChar = '.';
    }
    DecimalFormat df = new DecimalFormat();
    DecimalFormatSymbols symbols = new DecimalFormatSymbols();
    symbols.setDecimalSeparator(decChar);
    symbols.setGroupingSeparator(grpChar);
    df.setDecimalFormatSymbols(symbols);
    
    Number parsedValue = 0;
    try {
        parsedValue = df.parse(localValue);
    }
    catch (ParseException pe) {
        pe.printStackTrace();
    }
    
    /**
     * Round off and format to localize
     */
    double roundedValue = Math.round(parsedValue.doubleValue());
    String localeAwareValue = df.format(roundedValue);
    
    
    // TODO - use Locale with nf instead of above method
    // currently facing issue with incorrect parse for non default locale
    /*
       NumberFormat nf = NumberFormat.getInstance(new Locale(locale));
       Number num = nf.parse(localValue);
    */
    

Needless to say I'm not satisfied with the solution, which is more of a workaround until I get a solution to the main problem - NumberFormat incorrect parse for non default locale. Therefore any hints, suggestions, solutions are still welcome.

Upvotes: 1

Related Questions