Seeker
Seeker

Reputation: 1017

need space between currency symbol and amount

I'm trying to print INR format currency like this:

NumberFormat fmt = NumberFormat.getCurrencyInstance();
fmt.setCurrency(Currency.getInstance("INR"));
fmt.format(30382.50);

shows Rs30,382.50, but in India its written as Rs. 30,382.50(see http://www.flipkart.com/) how to solve without hardcoding for INR?

Upvotes: 20

Views: 10551

Answers (8)

Cardinal
Cardinal

Reputation: 2175

Take into account the following:

  1. You can't use Currency.symbol to find it in the formatted string because in some locales $ becomes US$ while Currency.symbol still returns $
  2. You should use non-breaking space instead of a normal space (default formatter does it this way). The same for checking whether space is already present (in some locales it is)
  3. For negatives amount a sign normally goes before the currency

The solution is (sorry for Kotlin code):

fun formatPrice(currencyCode: String, price: BigDecimal, locale: Locale? = null): String {
    val resLocale = locale ?: Locale.getDefault()
    val currencyFormat = NumberFormat.getCurrencyInstance(resLocale)
    // decimalFormat formats the amount in the same way as currencyFormat, but without currency symbol.
    // We can't use currency.symbol because it doesn't always match the way currency rendered in
    // the resulting string (for some locales "$" becomes "US$")
    val decimalFormat = NumberFormat.getNumberInstance(resLocale)
    decimalFormat.maximumFractionDigits = currencyFormat.maximumFractionDigits
    decimalFormat.maximumIntegerDigits = currencyFormat.maximumIntegerDigits
    decimalFormat.isGroupingUsed = currencyFormat.isGroupingUsed
    decimalFormat.minimumFractionDigits = currencyFormat.minimumFractionDigits
    decimalFormat.minimumIntegerDigits = currencyFormat.minimumIntegerDigits
    decimalFormat.roundingMode = currencyFormat.roundingMode
    currencyFormat.currency = Currency.getInstance(currencyCode)

    val priceFormatted = currencyFormat.format(price)
    val amountFormatted = decimalFormat.format(price)

    // 12 345,67 $ US
    if (priceFormatted.startsWith(amountFormatted)) {
        val suffix = priceFormatted.substringAfter(amountFormatted)
        return if (suffix.isNotEmpty() && !suffix.first().isWhitespace()) {
            "$amountFormatted $suffix" // use nbsp to prevent line break
        } else {
            priceFormatted
        }
    }

    // US$12,345.67
    if (priceFormatted.endsWith(amountFormatted)) {
        val prefix = priceFormatted.substringBefore(amountFormatted)
        return if (prefix.isNotEmpty() && !prefix.last().isWhitespace()) {
            "$prefix $amountFormatted" // use nbsp to prevent line break
        } else {
            priceFormatted
        }
    }

    // For negative amounts a sign normally goes before the currency symbol
    // -US$12,345.67
    val absAmountFormatted = decimalFormat.format(price.abs())
    if (priceFormatted.endsWith(absAmountFormatted)) {
        val prefix = priceFormatted.substringBefore(absAmountFormatted)
        if (prefix.isNotEmpty() && !prefix.last().isWhitespace()) {
            return "$prefix $absAmountFormatted" // use nbsp to prevent line break
        }
    }

    return priceFormatted
}

Testing:

Locale: BigDecimal -> default_format -> custom_with_nbsp

en: 12345.673 -> $12,345.67 -> $ 12,345.67
en: 12345.67 -> $12,345.67 -> $ 12,345.67
en: -12345.67 -> -$12,345.67 -> -$ 12,345.67
en: 12345.67 -> €12,345.67 -> € 12,345.67
en: -12345.67 -> -€12,345.67 -> -€ 12,345.67
en: 12345.67 -> PLN12,345.67 -> PLN 12,345.67
en: -12345.67 -> -PLN12,345.67 -> -PLN 12,345.67
zh_CN: 12345.67 -> US$12,345.67 -> US$ 12,345.67
fr_CA: -12345.67 -> -12 345,67 PLN -> -12 345,67 PLN
pl_PL: 12345.67 -> 12 345,67 zł -> 12 345,67 zł
fr_FR: 12345.67 -> 12 345,67 PLN -> 12 345,67 PLN

Upvotes: 2

W-S
W-S

Reputation: 524

Here what I do to add space after currency symbol:

DecimalFormat numberFormat = (DecimalFormat) NumberFormat.getCurrencyInstance(new Locale("id", "ID"));

DecimalFormatSymbols symbol = new DecimalFormatSymbols(new Locale("id", "ID"));
// Add space to currency symbol
symbol.setCurrencySymbol(symbol.getCurrencySymbol() + " ");

numberFormat.setDecimalFormatSymbols(symbol);

Upvotes: 0

Andrey Kijonok
Andrey Kijonok

Reputation: 416

Sorry for Kotlin I came here from android). As I understood there is no correct solutions for that, so that's why my solution is also hack)

fun formatBalance(
    amount: Float,
    currencyCode: String,
    languageLocale: Locale
): String {

amount can be String as well.

    val currencyFormatter: NumberFormat = NumberFormat.getCurrencyInstance(languageLocale)
    currencyFormatter.currency = Currency.getInstance(currencyCode)
    val formatted = currencyFormatter.format(amount)

formatted will get amount with currency from correct side but without space. (Example: 100$, €100)

    val amountFirstSymbol = amount.toString()[0]
    val formattedFirstSymbol = formatted[0]

    val currencySymbolIsBefore = amountFirstSymbol != formattedFirstSymbol

Then I use this little hack to understand if currency symbol is before amount. So for example amount is 100 then amountFirstSymbol will be "1". And if formatted is 100$ then formattedFirstSymbol also will be "1". That means we can put our currency symbol behind amount but now with space.

    val symbol = currencyFormatter.currency?.symbol
    return if (currencySymbolIsBefore) "$symbol $amount"
    else "$amount $symbol"

Upvotes: 0

Gabriel Ferreira
Gabriel Ferreira

Reputation: 475

An easier method, kind of workaround. For my locale, the currency symbol is "R$"

public static String moneyFormatter(double d){

    DecimalFormat fmt = (DecimalFormat) NumberFormat.getInstance();
    Locale locale = Locale.getDefault();
    String symbol = Currency.getInstance(locale).getSymbol(locale);
    fmt.setGroupingUsed(true);
    fmt.setPositivePrefix(symbol + " ");
    fmt.setNegativePrefix("-" + symbol + " ");
    fmt.setMinimumFractionDigits(2);
    fmt.setMaximumFractionDigits(2);
    return fmt.format(d);
}

Input:

moneyFormatter(225.0);

Output:

"R$ 225,00"

Upvotes: 6

depsypher
depsypher

Reputation: 1219

I don't see any easy way to do this. Here's what I came up with...

The key to getting the actual currency symbol seems to be passing the destination locale into Currency.getSymbol:

currencyFormat.getCurrency().getSymbol(locale)

Here's some code that seems like it mostly works:

public static String formatPrice(String price, Locale locale, String currencyCode) {

    NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(locale);
    Currency currency = Currency.getInstance(currencyCode);
    currencyFormat.setCurrency(currency);

    try {
        String formatted = currencyFormat.format(NumberFormat.getNumberInstance().parse(price));
        String symbol = currencyFormat.getCurrency().getSymbol(locale);

        // Different locales put the symbol on opposite sides of the amount
        // http://en.wikipedia.org/wiki/Currency_sign
        // If there is already a space (like the fr_FR locale formats things),
        // then return this as is, otherwise insert a space on either side
        // and trim the result
        if (StringUtils.contains(formatted, " " + symbol) || StringUtils.contains(formatted, symbol + " ")) {
            return formatted;
        } else {
            return StringUtils.replaceOnce(formatted, symbol, " " + symbol + " ").trim();
        }
    } catch (ParseException e) {
        // ignore
    }
    return null;
}

Upvotes: 2

Bala R
Bala R

Reputation: 108957

It's a bit of a hack but in a very similar situation, I used something like this

NumberFormat format = NumberFormat.getCurrencyInstance(new Locale("en", "in"));
String currencySymbol = format.format(0.00).replace("0.00", "");
System.out.println(format.format(30382.50).replace(currencySymbol, currencySymbol + " "));

all the currencies I had to deal with involved two decimal places so i was able to do "0.00" for all of them but if you plan to use something like Japanese Yen, this has to be tweaked. There is a NumberFormat.getCurrency().getSymbol(); but it returns INR instead for Rs. so that cannot be used for getting the currency symbol.

Upvotes: 7

Kal
Kal

Reputation: 24910

I dont think you can.

You should take a look at http://site.icu-project.org/

There might be better locale-specific currency formatting provided by icu4j.

Upvotes: 2

Jake Roussel
Jake Roussel

Reputation: 641

See if this works:

DecimalFormat fmt = (DecimalFormat) NumberFormat.getInstance();
fmt.setGroupingUsed(true);
fmt.setPositivePrefix("Rs. ");
fmt.setNegativePrefix("Rs. -");
fmt.setMinimumFractionDigits(2);
fmt.setMaximumFractionDigits(2);
fmt.format(30382.50);

Edit: Fixed the first line.

Upvotes: 6

Related Questions