I need to format a price (as BigDecimal) to the polish currency format based on ISO 4217. This format should always have 2 decimals according to https://en.wikipedia.org/wiki/ISO_4217#Active_codes and according to my acceptance criterias.
So I have the Java-Locale object for pl_PL and the related CurrencyUnit 985 from ISO 4217, which I can see in the debugger. Now i have some issues when I want to format the following prices:
12.34000 => 12.34 (ok)
12.500 => 12.5 (not ok, expected is 12.50)
12.000 => 12 (not ok, expected is 12.00)
So I did some debugging and I discovered that Java read the pl_PL related pricing DecimalPattern from the class FormatData_pl_PL.java, where it is defined as the following:
{ "NumberPatterns",
new String[] {
"#,##0.###;-#,##0.###", // decimal pattern
"#,##0.## \u00A4;-#,##0.## \u00A4", // currency pattern
"#,##0%" // percent pattern
}
},
The pattern above will be used in this context:
NumberFormat format = NumberFormat.getCurrencyInstance(locale);
DecimalFormat decimalFormat = (DecimalFormat) format;
String pattern = getSimplePattern(locale, currency);
decimalFormat.applyPattern(pattern);
return decimalFormat.format(price);
So the given currency pattern seems not to be correct. According to ISO 4217 it should be "#,##0.00", isn´t it?
How can I handle this issue?
Related
I tried a lot.But was unable to find a solution.
I have a code for formatting currency. I am using the below code :
NumberFormat numberFormat = NumberFormat.getCurrencyInstance(locale);
Here I am facing an issue.
Consider a case with France locale. In my case, locale can be en_FR and fr_FR.
DecimalFormatSymbols decimalFormats = new DecimalFormatSymbols();
decimalFormats.setCurrencySymbol(currencySymbol);
((DecimalFormat) numberFormat).setDecimalFormatSymbols(decimalFormats);
formattedCurrency = numberFormat.format(Double.valueOf(number));
So if the locale is en_FR, the formattedCurrency value will be € 10.00 and if the locale is fr_FR, the value will be 10.00 €.
So I would like to know the role of language code in this calculation methods.Since en_FR has en, I guess it is by default taking as en_US. So currency symbol coming left of price.
I need to get 10.00 € always if the country is France irrespective of language code. Is there are other way to get the currency formatting based on country, rather than locale?
Just specify the country code , instead of language and country
try to modify like below for you :
String countryCode="FR"; // or use "String countryCode=Locale.getDefault().getCountry();" for system default locale
NumberFormat numberFormat = NumberFormat.getCurrencyInstance(new Locale(countryCode));
Note:
The Locale points to language and not to country specific , above code will work as expected in most scenario where the ISO country code and language code will be the same.
FYI
You can simply specify FRANCE as your Locale, and it will always print it on the right hand side for you.
String formattedCurrency = DecimalFormat.getCurrencyInstance(Locale.FRANCE).format(10.00);
Output:
10,00 €
Edit:
You can get the user's Locale:
Locale currentLocale = Locale.getDefault();
And pass that as the parameter:
String formattedCurrency = DecimalFormat.getCurrencyInstance(currentLocale).format(10.00);
I'm not in France, but for me it will print:
€10.00
Which is correct for Ireland.
2nd Edit:
You can get the country name from the currentLocale above, by getting the display country - but this is stored as a String
String country = currentLocale.getDisplayCountry();
Then convert it to a Locale to use as a parameter
public static Locale getLocaleFromString(String localeString) {
return new Locale(localeString, "");
}
Note: this method is an edited snippet from here
And use it in your formatter then
Locale l = getLocaleFromString(country);
String number = DecimalFormat.getCurrencyInstance(l).format(10.00);
I have a requirement to show a number value like 123456789.905 in the following format 123,456,789.90. But the comma separation changes depending on the locale selected in the phone (as if with US English selected comma separation is 3 places and if India English is selected it is like 12,34,56,789.90).
How can I format my Double?
So, java.text.NumberFormat doesn't slove the problem, unfortunately, but com.ibm.icu.text.NumberFormat does.
You can use this:
Double d = 123456789.905;
com.ibm.icu.text.NumberFormat format = com.ibm.icu.text.NumberFormat.getNumberInstance(new Locale("en", "in"));
format.setMinimumFractionDigits(2);
format.setMaximumFractionDigits(2);
System.out.println(format.format(d));
This outputs: 12,34,56,789.90.
For the generic case, use java.text.NumberFormat:
NumberFormat nf = NumberFormat.getInstance();
String formatted = nf.format(yourDoubleValue);
By default getInstance() returns a NumberFormat that is configured as appropriate for the current Locale. You can change the configuration yourself, too.
The "comma separation" is called "grouping".
For the specific case of grouping in an Indian currency format, see: Displaying Currency in Indian Numbering Format
Try this one:
try {
Locale l = Locale.getDefault();
NumberFormat nf = NumberFormat.getInstance(l);
String formato = NumberFormat.getInstance().format(your_data);
} catch (Exception e) {
e.printStackTrace();}
Use NumberFormat, which helps you to format and parse numbers for any locale.
Your code can be completely independent of the locale conventions for
decimal points, thousands-separators, or even the particular decimal
digits used, or whether the number format is even decimal.
Locale fmtLocale = Locale.getDefault();
NumberFormat formatter = NumberFormat.getInstance(fmtLocale);
formatter.format(your_number);
Hm, I have not found for any locale in NumberFormat.getAvailableLocales() a format with only two digits between grouping signs (for example for new Locale("en", "IN")). So I think you have to use DecimalFormat-pattern like this:
DecimalFormat df = new DecimalFormat("##,##,##,##,##.###");
System.out.println(df.format(123456789.905));
// Output: 1.23.45.67.89,905
It is not exactly the same since DecimalFormat is not able to have varying counts of grouping sizes, but maybe this is acceptable for you.
NumberFormat nf = NumberFormat.getInstance(Locale.getDefault());
double value = nf.parse(iValue).doubleValue();
I have a simple EditText, which allows the user to enter a number such as 45.60 (example for American Dollar). I then format this number using the following method:
public String format() {
NumberFormat formatter = NumberFormat.getCurrencyInstance(Locale.getDefault());
return formatter.format(amount.doubleValue());
}
And on my Android phone, the language is set to English (United States) - hence the Locale.getDefault() should return the US locale (and it does).
Now the edit text is correctly updated to: $45.60 (hence formatting the entered number works).
However if I attempt to parse the above String "$45.60" using the following method:
NumberFormat numberFormat = NumberFormat.getInstance(Locale.getDefault());
Number result = numberFormat.parse("$45.60");
It fails with:
java.lang.IllegalArgumentException: Failed to parse amount $45.60 using locale en_US.
If I set my phone to English/ UK, formatting this "45.60" to "£45.60" works correctly (as for US), however parsing "£45.60" fails, just as it does for the above US sample.
However, if I set my phone to German (Germany), formatting "45,60" to "45,60€" works correctly, AND parsing "45,60€" works correctly as well!
The only difference I see between those three currencies: The Euro is appended to the amount, while the Dollar and the Pound are prepended to the amount.
Does anyone have an idea, why the same code works for Euro, but not for Pound and Dollar? Am I missing something?
I also created a unit test, to reproduce the issue:
public void testCreateStringBased() throws Exception {
// For German locale
CurrencyAmount amount = new CurrencyAmount("25,46€", Locale.GERMANY);
assertEquals(25.46, amount.getAsDouble());
// For French locale
amount = new CurrencyAmount("25,46€", Locale.FRANCE);
assertEquals(25.46, amount.getAsDouble());
// For US locale
amount = new CurrencyAmount("$25.46", Locale.US);
assertEquals(25.46, amount.getAsDouble());
// For UK locale
amount = new CurrencyAmount("£25.46", Locale.UK);
assertEquals(25.46, amount.getAsDouble());
}
CurrencyAmount basically wraps the code I posted for parsing currency strings, except that it takes the given locale instead of the default locale. In the above example, the test succeeds for the GERMANY and FRANCE locale but fails for US and UK locale.
Since the answers that have been suggested thus far, did not completely solve the problem, I took a painfully amateurish approach:
String value = "$24,76"
value = value.replace(getCurrencySymbol(locale), StringUtils.EMPTY);
NumberFormat numberFormat = NumberFormat.getInstance(locale);
Number result = numberFormat.parse(value);
So now I simply strip the String value off it's currency symbol... This way I can process everything I want, such as: 45.78 or 45,78 or $45.78 or 45,78€ ....
Whatever the input, the currency symbol is simply stripped and I end up with the plain number. My unittests (see OP) now complete successfully.
If anyone comes up with something better, please let me know.
Try following:
NumberFormat numberFormat = new DecimalFormat("¤#.00", new DecimalFormatSymbols(Locale.UK));
numberFormat.parse("£123.5678");
¤ - currency sign, expects matches with currency symbol by Locale.
other pattern symbols you can see by following link http://docs.oracle.com/javase/6/docs/api/java/text/DecimalFormat.html
Try NumberFormat.getCurrencyInstance().parse() instead of NumberFormat.getInstance().parse().
You must know the locale of the string you wish to parse in order to have a locale-aware parser. The GBP string parse to a numeric ONLY when the NumberFormat's locale is en_GB; there is no such thing as a "universal" parser.
For example, how does the string "12.000" parse? For en-us, the answer is twelve; for de-de, the answer is twelve-thousand.
Always use NumberFormat.getCurrencyInstance( java.util.Locale ) to parse currency amounts.
I'm using below adapted from https://dzone.com/articles/currency-format-validation-and
import java.math.BigDecimal;
import org.apache.commons.validator.routines.*;
BigDecimalValidator currencyValidator = CurrencyValidator.getInstance();
BigDecimal parsedCurrency = currencyValidator.validate(currencyString);
if ( parsedCurrency == null ) {
throw new Exception("Invalid currency format (please also ensure it is UTF-8)");
}
If you need to insure the correct Locale is being used per user look at
Change locale on login
Sorry, but any answer provided are misleading. This is what I would call a BUG in Java.
An example like this explains it better. If I want to print a value in EUR using Locale.US and then I parse it again, it fails unless I specify on the DecimalFormat the currency (EUR). Using dollars, it works:
DecimalFormat df = new DecimalFormat("¤#,##0.00", new DecimalFormatSymbols(Locale.US));
df.setCurrency(Currency.getInstance("EUR"));
BigDecimal value = new BigDecimal("1.23");
String text = df.format(value);
System.out.println(text);
DecimalFormat df2 = new DecimalFormat("¤#,##0.00", new DecimalFormatSymbols(Locale.US));
df2.setParseBigDecimal(true);
BigDecimal parsed = (BigDecimal) df2.parse(text);
BigDecimalAsserts.assertBigDecimalEquals("parsed value is the same of the original", value, parsed);
How can I get a NumberFormat (or DecimalFormat) instance corresponding to an ISO 4217 currency code (such as "EUR" or "USD") in order to format prices correctly?
Note 1: The problem I'm having is that the NumberFormat/DecimalFormat classes have a
getCurrencyInstance(Locale locale) method but I can't figure out how
to get to a Locale object from an ISO 4217 currency code.
Note 2: There is also a java.util.Currency class which has a getInstance(String currencyCode) method (returning the Currency
instance for a given ISO 4217 currency code) but again I can't figure
out how to get from a Currency object to a NumberFormat
instance...
I'm not sure I understood this correctly, but you could try something like:
public class CurrencyTest
{
#Test
public void testGetNumberFormatForCurrencyCode()
{
NumberFormat format = NumberFormat.getInstance();
format.setMaximumFractionDigits(2);
Currency currency = Currency.getInstance("USD");
format.setCurrency(currency);
System.out.println(format.format(1234.23434));
}
}
Output:
1,234.23
Notice that I set the maximum amount of fractional digits separately, the NumberFormat.setCurrency doesn't touch the maximum amount of fractional digits:
Sets the currency used by this number format when formatting currency
values. This does not update the minimum or maximum number of fraction
digits used by the number format.
Locale can be used both to get the standard currency for the Locale and to print any currency symbol properly in the locale you specify. These are two distinct operations, and not really related.
From the Java Internationalization tutorial, you first get an instance of the Currency using either the Locale or the ISO code. Then you can print the symbol using another Locale. So if you get the US Currency from the en_US Locale, and call getSymbol() it will print "$". But if you call getSymbol(Locale) with the British Locale, it will print "USD".
So if you don't care what your current user's locale is, and you just care about the currencies, then you can ignore the Locale in all cases.
If you care about representing the currency symbol correctly based on your current user, then you need to get the Locale of the user specific to the user's location.
The mapping is sometimes one to many... Like the euro is used in many countries (locales)...
Just because the currency code is the same the format might be different as this example shows:
private static Collection<Locale> getLocalesFromIso4217(String iso4217code) {
Collection<Locale> returnValue = new LinkedList<Locale>();
for (Locale locale : NumberFormat.getAvailableLocales()) {
String code = NumberFormat.getCurrencyInstance(locale).
getCurrency().getCurrencyCode();
if (iso4217code.equals(code)) {
returnValue.add(locale);
}
}
return returnValue;
}
public static void main(String[] args) {
System.out.println(getLocalesFromIso4217("USD"));
System.out.println(getLocalesFromIso4217("EUR"));
for (Locale locale : getLocalesFromIso4217("EUR")) {
System.out.println(locale + "=>" + NumberFormat.getCurrencyInstance(locale).format(1234));
}
}
Output
[en_US, es_US, es_EC, es_PR]
[pt_PT, el_CY, fi_FI, en_MT, sl_SI, ga_IE, fr_BE, es_ES, de_AT, nl_NL, el_GR, it_IT, en_IE, fr_LU, nl_BE, ca_ES, sr_ME, mt_MT, fr_FR, de_DE, de_LU]
pt_PT=>1.234,00 €
el_CY=>€1.234,00
fi_FI=>1 234,00 €
en_MT=>€1,234.00
sl_SI=>€ 1.234
ga_IE=>€1,234.00
fr_BE=>1.234,00 €
es_ES=>1.234,00 €
de_AT=>€ 1.234,00
nl_NL=>€ 1.234,00
el_GR=>1.234,00 €
it_IT=>€ 1.234,00
en_IE=>€1,234.00
fr_LU=>1 234,00 €
nl_BE=>1.234,00 €
ca_ES=>€ 1.234,00
sr_ME=>€ 1.234,00
mt_MT=>€1,234.00
fr_FR=>1 234,00 €
de_DE=>1.234,00 €
de_LU=>1.234,00 €
For completeness, though I've never used it, you might want to give Joda Money a try. If it's as good as Joda-Time, it probably is easier and more powerful than the standard JDK stuff.
Try the following method:
private static NumberFormat getNumberFormat(String currencyCode)
{
Currency currency = Currency.getInstance(currencyCode);
Locale[] locales = NumberFormat.getAvailableLocales();
for (Locale locale : locales)
{
NumberFormat numberFormat = NumberFormat.getCurrencyInstance(locale);
if (numberFormat.getCurrency() == currency)
return numberFormat;
}
return null;
}
public class PriceHelper {
public static String formatPrice(Context context, String currencyCode,
double price) {
if (price == 0) {
return context.getString(R.string.free);
}
Currency currency = Currency.getInstance(currencyCode);
NumberFormat format = NumberFormat.getCurrencyInstance();
format.setCurrency(currency);
return format.format(price);
}
}
I'm trying save a values' input field to a BigDecimal. Which already works.
But it produces strange results if I enter decimal deliminator that is not of the locale type.
eg:
class Payment {
BigDecimal amount;
}
<p:inputText id="amount" value="#{payment.amount}">
<f:convertNumber locale="en"/>
</p:inputText>
<h:outputText value="#{payment.amount}" />
If I input 10,10
I get: 1,010.00
So the value is taken as 1010
How could I work around this? What am I doing wrong here?
ty
The commas are not significant when parsing an English-locale number. Java's raw Number types will not retain any formatting information - that's just presentation data.
The logic for the inputText with a NumberConverter goes like this:
NumberFormat formatter = NumberFormat.getNumberInstance(Locale.ENGLISH);
// NumberConverter turns input string into Number
Number number = formatter.parse("10,10");
// Expression language coerces the Number to BigDecimal
BigDecimal decimal = BigDecimal.valueOf(number.doubleValue());
// On output back to browser:
String output = formatter.format(decimal);
System.out.println(output);
The outputText doesn't have a converter, so will merely call toString() on its value binding (the BigDecimal.)
I would expect the results to be 1,010 and 1010.0 respectively.
The behavior of NumberConverter is documented in the javadoc. The rules for EL type coercion are documented in JSR 245:
Coerce A to Number type N
If A is Number, coerce quietly to type N using the following algorithm:
If N is BigDecimal,
If A is a BigInteger, return new BigDecimal(A)
Otherwise, return new BigDecimal(A.doubleValue())
If you want to use the user's browser locale to interpret number formats, remove the locale attribute. If you want a converter to treat both periods and commas as decimal separators, provide your own Converter implementation.
As McDowel clearly stated in the comments - EN locale states that , is a delimiter for thousands and . is a delimiter for decimal point.. if you like to use "," as decimal separator, use a locale that has given format (for example french (fr))
In java i would suggest following:
// Using french locale as it is in form of "123 456 789,012345"
DecimalFormat df = (DecimalFormat)
NumberFormat.getInstance(Locale.FRENCH);
df.setParseBigDecimal(true);
// Replace all dots (due to the french format) so we handle "." as well as ","
hodnota = hodnota.replace('.', ',');
try {
return (BigDecimal) df.parseObject(hodnota);
} catch(ParseException e) {
// TODO: What ever you desire
}