How to customize currency formatting in MessageFormat in ICU4J - java

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.

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!).

Related

How to use String.format(Locale, ...)?

I am new in Android development and I'm trying this code:
String hms = String.format("%02d:%02d:%02d",
TimeUnit.MILLISECONDS.toHours(milliSeconds),
TimeUnit.MILLISECONDS.toMinutes(milliSeconds) -
TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(milliSeconds)),
TimeUnit.MILLISECONDS.toSeconds(milliSeconds) -
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(milliSeconds)));
I get the message :
Implicitly using the default locale is a common source of bugs:
Use String.format(Locale, ...) instead
I have no idea how to modify the code in order to implement the recommendation.
Most likely you don't need to do anything. That's a warning telling you that you didn't specify a locale for String.format, so it's using the default locale. That can cause a bug in some circumstances, but it's unlikely to in yours. If you want to be careful you can pass in an explicit locale, or you can just ignore the warning. Formatting numbers like this without any type of currency is fairly safe.
(The bugs you'll see are if the locale your device is in has specific formatting rules for things. The big one I know of that's hit me is that Turkish has a letter i who's capital symbol is different than the english letter I.)

How do I format a JSR-385 Quantity with fixed number of decimal digits?

I'm trying to convert hard-coded formatting to a Java Units API implementation.
The existing code outputs (for this example, the temperature value in degrees) with two decimal places. For example, 38.70°C. While I'd like to allow the user to specify their own formatting code (which is the end-goal of the change), I think it would be useful to keep the legacy behavior to give people a chance to migrate.
The existing code looks like:
return String.format("%.2f\u00B0C", this.temperature);
The code I'm trying to use looks like:
DecimalFormat numberFormat = (DecimalFormat) DecimalFormat.getInstance();
numberFormat.setMinimumFractionDigits(2);
numberFormat.setMaximumFractionDigits(2);
NumberDelimiterQuantityFormat formatter =
NumberDelimiterQuantityFormat.builder()
.setNumberFormat(numberFormat)
.setDelimiter("")
.setUnitFormat(SimpleUnitFormat.getInstance())
.build();
return formatter.format(temperature);
It does format, but not with the specified precision. I'd expect 38.70°C but instead get 38.70000076293945℃.
If I just do
numberFormat.format(temperature.getValue().floatValue());
then it does format correctly ("38.70"). So I think the DecimalFormat is basically OK.
I considered just manually building my formatting. However that doesn't really work for what I want to do - pass in the NumberDelimiterQuantityFormat (or applicable interface).
Can anyone suggest an appropriate way to format a Quantity<> with fixed decimal precision?
First off, I'm completely unfamiliar with the Java Unit API and this implementation, but this seemed like an interesting question, so I looked into it.
I had a look at the implementation of NumberDelimiterQuantityFormat and right there in the implementation of the format method it modifies the maxiumFractionDigits of the NumberFormat depending on the fraction
if (quantity != null && quantity.getValue() != null) {
fract = getFractionDigitsCount(quantity.getValue().doubleValue());
}
if (fract > 1) {
numberFormat.setMaximumFractionDigits(fract + 1);
}
Source
This makes little sense to me for two reasons:
It negates the whole reason to have a NumberFormat in the first place especially in context with floating point numbers where it's virtually impossible to avoid superfluous fraction digits.
It modifies the internal state of the NumberDelimiterQuantityFormat in a method where it isn't expected.
I should have checked first, but there is actually an issue about this, which is "being analyzed" for several months now. Maybe it would make sense to ask in there.

How to always write numbers with decimal points instead of commas with Android

I write files and read them back. I use a comma delimited file format so that Excel can read the files easily. This works fine for me but my Dutch friend can't get the program to work. The problem is that when I write out 52.345 what ends up in the file is 52,345 and when I read it back I get 52 and then 345 as the next field.
This is not a matter of formatting numbers as this behavior happens from many sources, from edit text that the user enters, from character sequences converted to strings, as well as from numbers being written with format commands. There are hundreds of instances in the code so I need something universal that covers all default behavior and forces it to Locale.US
What I need is a way to program the app so it makes the tablet to just act like a Locale.US tablet in every way, keyboard, number writing, etc. I am not looking for a way to change one line of code.
You can use String.format (String format, Object... args) with not mandatory Locale argument, specified explicitely to Locale.US :
Assuming plong is double or float and not long, cause no need decimal point for long.
waypointArray[0][2] = String.format(Locale.US, "%f", pLong);
Or, if you need this for all the application, this in onCreate of the activity, or in a class extending application :
Locale.setDefault(Locale.US);
Configuration config = new Configuration();
config.locale = Locale.US;
getBaseContext().getResources().updateConfiguration(config,
getBaseContext().getResources().getDisplayMetrics());

Inserting custom String between number and decimal - Java DecimalFormat

Hello and thank you in advance for the help.
I am having some trouble formatting using a Java function to mark up a price in HTML.
It seems that, no matter what I do, I cannot insert custom content between the numbers and the decimal (throws Illegal Argument Exception). Is there any known way to achieve the following:
NumberFormat nf = getNumberFormat("'<span class=\"dollars\">'##'</span></span class=\"decimal\">'.'</span></span class=\"cents\">'00'</span>'", locale);
nf.format(number);
Assume locale and number are correctly initialized.
If you look at the docs for DecimalFormat you'll see that they talk about the prefix and the suffix text - but not putting arbitrary text within a number.
It sounds like you should basically write this bit of formatting yourself - possibly using DecimalFormat for each section of the number.
You might consider using String.format(String pattern, Object... arguments). You can pass your simply formatted numbers as arguments.

Format negative amount of USD with a minus sign, not brackets (Java)

How do I get NumberFormat.getCurrencyInstance() to print negative USD currency values with a minus sign?
It requires a little tweaking of the DecimalFormat returned by NumberFormat.getCurrencyInstance() to do it in a locale-independent manner. Here's what I did (tested on Android):
DecimalFormat formatter = (DecimalFormat)NumberFormat.getCurrencyInstance();
String symbol = formatter.getCurrency().getSymbol();
formatter.setNegativePrefix(symbol+"-"); // or "-"+symbol if that's what you need
formatter.setNegativeSuffix("");
IIRC, Currency.getSymbol() may not return a value for all locales for all systems, but it should work for the major ones (and I think it has a reasonable fallback on its own, so you shouldn't have to do anything)
Here is one I always end up using either in a java class or via the fmt:formatNumber jstl tag:
DecimalFormat format = new DecimalFormat("$#,##0.00;$-#,##0.00");
String formatted = format.format(15.5);
It always produces at least a $0.00 and is consistent when displayed. Also includes thousands seperators where needed. You can move the minus sign in front of the dollar sign if that is your requirement.
It's probably best to create your own DecimalFormat if you want a specific format rather than relying on the default.
Edit: You could probably also cast the result of NumberFormat.getCurrencyInstance() to DecimalFormat and adjust it to your preferences.
Try:
NumberFormat.getCurrencyInstance(Locale.CANADA);
NumberFormat.getCurrencyInstance(Locale.UK);
Why poi REFUSES to support the FIRST option in excel currency formatting is beyond me!
I don't like using the DecimalFormat for currency because your end cell value becomes a non-numeric with the introduction of the currency symbol. While working for a major financial institution, I was tasked with resolving this formatting issue. The core idea of this change is, because POI refuses to be reasonable and have comprehensive support of Excel's native options, I will infiltrate their code and change their values at the core. The following is my WORKAROUND:
private static final String CURRENCY_FORMAT_OVERRIDE = "\"$\"#,##0.00_);-\"$\"#,##0.00";
private static final String CURRENCY_FORMAT_TARGET = "\"$\"#,##0.00_);(\"$\"#,##0.00)";
static { // static class level initializer
Field field = org.apache.poi.ss.usermodel.BuiltinFormats.class.getDeclaredField("_formats");
field.setAccessible(true);
String[] _formats = (String[])field.get(new org.apache.poi.ss.usermodel.BuiltinFormats());
for(int i = 0; i < _formats.length; ++i) {
if(_formats[i].equals(CURRENCY_FORMAT_TARGET)) {
_formats[i]=CURRENCY_FORMAT_OVERRIDE;
System.out.println("TAKE THAT, POI!!!");
}
}
}
Since I faced this problem again, I did some research and found a more resilient solution provided by the ICU:
NumberFormatter
.withLocale(...)
.unit(Currency.getInstance("USD"))
.sign(SignDisplay.AUTO) // "123", "0", and "-123"
.format(123)
.toString();
Check the API documentation of NumberFormatter for more details.

Categories