What is the difference between {0} and {0,number,integer} - java

I'm trying to understand more about Java's MessageFormat utilities, and in examples in our codebase and elsewhere I see both {0} and {0,number,integer} being used for numbers, but I'm not sure which, if either, is preferable.
A quick test printing the differences:
import java.text.MessageFormat;
import java.text.NumberFormat;
import java.util.Locale;
public class MessageFormatTest
{
public static void main(String[] args){
MessageFormat simpleChoiceTest = new MessageFormat("{0}");
MessageFormat explicitChoiceTest = new MessageFormat("{0,number,integer}");
int[] set = new int[]{0,1,4,5,6,10,10000,24345};
Locale[] locs = new Locale[]{Locale.US,Locale.UK,Locale.FRANCE,Locale.GERMANY};
for(Locale loc : locs){
simpleChoiceTest.setLocale(loc);
explicitChoiceTest.setLocale(loc);
for(int i : set){
String simple = simpleChoiceTest.format(new Object[]{i});
String explicit = explicitChoiceTest.format(new Object[]{i});
if(!simple.equals(explicit)){
System.out.println(loc+" - "+i+":\t"+simple+
"\t"+NumberFormat.getInstance(loc).format(i));
System.out.println(loc+" - "+i+":\t"+explicit+
"\t"+NumberFormat.getIntegerInstance(loc).format(i));
}
}
}
}
}
Outputs:
fr_FR - 10000: 10 000 10 000
fr_FR - 10000: 10,000 10 000
fr_FR - 24345: 24 345 24 345
fr_FR - 24345: 24,345 24 345
de_DE - 10000: 10.000 10.000
de_DE - 10000: 10,000 10.000
de_DE - 24345: 24.345 24.345
de_DE - 24345: 24,345 24.345
Which surprised me, if anything I would have expected the {0} to not do anything to the number, and for {0,number,integer} to localize it properly. Instead, both get localized, but it seems the explicit form always uses en_US localization.
According to the linked documentation, {0} gets put through NumberFormat.getInstance(getLocale()) while while the explicit form uses NumberFormat.getIntegerInstance(getLocale()). Yet when I call those directly (the last column in the output) both seem identical, and both localize correctly.
What am I missing here?

You are right. When you use "MessageFormat("{0,number,integer}")", formatter uses default locale(en_US) at the time of initialization and numbers are marked to use Integer format in default locale(en_US) as the code below is executed during the initialization time itself.
// this method is internally called at the time of initialization
MessageFormat.makeFormat()
// line below uses default locale if locale is not
// supplied at initialization (constructor argument)
newFormat = NumberFormat.getIntegerInstance(locale);
Since you are setting the locale afterwards, there is no impact on the format pattern assigned to numbers. If you want to use the desire locale in the format for numbers, please use the locale argument at the time of initialization itself e.g. below:
MessageFormat test = new MessageFormat("{0,number,integer}", Locale.FRANCE);

In my opinion, this is a Java bug (the interface is wrong) or a documentation problem. You should open a new issue at Oracle to correct that.
As Yogendra Singh, said the instance of the formatter (DecimalFormat) is created when the MessageFormat constructor.
MessageFormat simpleChoiceTest = new MessageFormat("{0}");
System.out.println(simpleChoiceTest.getFormatsByArgumentIndex()[0]);
//Prints null
MessageFormat explicitChoiceTest = new MessageFormat("{0,number,currency}");
System.out.println(explicitChoiceTest.getFormatsByArgumentIndex()[0]);
//Prints java.text.DecimalFormat#67500
When the MessageFormat.setLocale is called it does not change the locale of its internal formatters.
At least the documentation should be changed to reflect this issue.
That is my java version:
java version "1.7.0_07"
Java(TM) SE Runtime Environment (build 1.7.0_07-b11)

Related

java.time.format.DateTimeParseException when parsing year

jdk used : 1.8
Not sure what is the issue, configuredFormat is valid one, inputTime is also valid one, really confused what is the issue.
public class Test {
public static void main(String[] args) {
String configuredFormat = "yyyyMMddHHmmssSSS";
String inputTime = "20200203164553123";
DateTimeFormatter dt = DateTimeFormatter.ofPattern(configuredFormat);
DateTimeFormatter strictTimeFormatter = dt.withResolverStyle(ResolverStyle.STRICT);
try {
LocalTime.parse(inputTime, strictTimeFormatter);
System.out.println("success");
} catch (DateTimeParseException | NullPointerException e) {
e.printStackTrace();
}
}
}
Exception I am Getting :
java.time.format.DateTimeParseException: Text '20200203164553123' could not be parsed at index 0
at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
at java.time.LocalTime.parse(LocalTime.java:441)
at com.Test.main(Test.java:20)
Lucky for you, there is an exact bug report which uses the exact same pattern that you're trying. Who better to explain than the JDK maintainers?
JDK-8031085
Workaround
DateTimeFormatter dtf = new
DateTimeFormatterBuilder()
.appendPattern("yyyyMMddHHmmss")
.appendValue(ChronoField.MILLI_OF_SECOND,3)
.toFormatter()
Adjacent value parsing is generally a hard problem. It is intended to
handle the case where the first element is variable width (the year)
and all other elements are fixed width (month, day etc). However, the
"S" pattern letter is a fraction, not a value. Specifically, the
fraction can be variable width - more or less than three digits are
possible options. Given the general case of a variable width year and
a variable width millisecond, it is not possible to determine which of
the two fields was intended to be variable.
Having said that, the implementation (and javadoc) have not ended up
as I intended. The description of "fraction" in DateTimeFormatter
describes actions in strict and lenient mode, but there is no way to
access strict or lenient mode when using
DateTimeFormatter.ofPattern(). This is a documentation bug that should
be fixed by removing the discussion of strict vs lenient.
Worse however is that the SSS pattern has therefore ended up using
strict mode when lenient mode would be appropriate. As it currently
stands, DateTimeFormatter.ofPattern("hhmmss.SSS") requires three
digits for milliseconds, when it was originally intended to require 0
to 9 (the lenient behaviour).
I tried changing the whole of the DateTimeFormatter.ofPattern() method
to use lenient parsing, and it broke no tests (which is bad in its own
way). This might be a valid fix, but only if included in JDK 8, as
once people adapt to the strict parsing it will be hard to make it
lenient.
Given that the current implementation requires three digits for SSS,
it is thus very surprising that adjacent value parsing does not apply.
Actually I agree format should be valid... this seems to be confirmed as I tried with both oracle java 8 and 9 runtime, and with java 9 it does not happen. (I tried IBM jre 8 too and it works as well)
System.out.println( System.getProperty( "java.vendor" )+" - "+System.getProperty( "java.version" ) );
String configuredFormat = "yyyyMMddHHmmssSSS";
String inputTime = "20200203164553123";
DateTimeFormatter dt = DateTimeFormatter.ofPattern(configuredFormat);
DateTimeFormatter strictTimeFormatter = dt.withResolverStyle(ResolverStyle.STRICT);
try {
//System.out.println( dt.parse( inputTime ) );
LocalTime.parse(inputTime, strictTimeFormatter);
System.out.println("success");
} catch (DateTimeParseException | NullPointerException e) {
e.printStackTrace();
}
Output
Oracle Corporation - 9.0.4
success
IBM Corporation - 1.8.0_211
success
Oracle Corporation - 1.8.0_172
java.time.format.DateTimeParseException: Text '20200203164553123' could not be parsed at index 0
at java.time.format.DateTimeFormatter.parseResolved0(Unknown Source)
at java.time.format.DateTimeFormatter.parse(Unknown Source)
at java.time.LocalTime.parse(Unknown Source)
at test.Test2.main(Test2.java:19)

How to change the calendar to Gregorian in Thailand locale

In my Java 8u51 application, I need to use Thailand locale.
However, I would like to use Gregorian Calendar instead of Buddhist Calendar.
I tried to replace java.util.Calendar 's CalendarProvider with SPI, but it has not worked out.
import java.security.AccessController;
...
import sun.util.locale.provider.LocaleProviderAdapter;
import sun.util.spi.CalendarProvider;
...
public static void main(String[] args) {
try {
System.out.println(AccessController.doPrivileged(new sun.security.action.GetPropertyAction("java.locale.providers")));
Locale l = Locale.getDefault(Locale.Category.FORMAT);
CalendarProvider provider = LocaleProviderAdapter.getAdapter(CalendarProvider.class, l).getCalendarProvider();
provider.getInstance(TimeZone.getDefault(), l);
LocaleProviderAdapter.getAdapterPreference().forEach(o -> System.out.printf("Adapter: %s%n", o));
System.out.printf("Provider: %s%n", provider.getClass());
System.out.printf("Availables: %s%n", Calendar.getAvailableCalendarTypes());
System.out.printf("Calendar: %s%n", Calendar.getInstance().getClass());
}
...
src/META-INF/services/sun.util.spi.CalendarProvider is as follows.
sun.util.locale.provider.AlwaysGregorianCalendarProviderImpl
Omit the source of AlwaysGregorianCalendarProviderImpl.
result,
$ java -jar Sample.jar -Djava.locale.providers=SPI -Duser.language=th -Duser.country=TH
SPI
Adapter: SPI
Adapter: FALLBACK
Provider: class sun.util.locale.provider.CalendarProviderImpl
Availables: [gregory, buddhist, japanese]
Calendar: class sun.util.BuddhistCalendar
Buddhist Calendar will be used.
How can I change it to a Gregorian calendar?
Self reply late.
The SPI implementation by Sun did not allow external expansion. It also checks the filenames of the divided inner classes.
Although I did not investigate in detail, it seems that it can not cope even with CLDRLocaleProviderAdapter.

Java Norwegian decimal separator different in Java 6 and 7

The following snippet behaves different in Java 6 than Java 7:
final Locale locale = new Locale("nb", "NO");
System.out.println(locale.getDisplayLanguage()); // Norwegian Bokmål
final DecimalFormatSymbols dfs = new DecimalFormatSymbols(locale);
System.out.println(dfs.getDecimalSeparator()); // Java 6: .
// Java 7: ,
Why is that? Is this change documented somewhere?
Java 6 had a number of issues regarding Locales, and this may well be one of them. Certainly, the correct separator for the Norway locale is ,.
The Oracle bug database does show quite a few bugs related to NO locale...
According to JDK 6 and JRE 6 Supported Locales and JDK 7 and JRE 7 Supported Locales, the correct/supported syntax for selecting Norwegian Bokmål is "no"/"NO".
new Locale("no", "NO") gives the correct result under both Java 6 and Java 7.
CLDR
In Java 9 and later, the OpenJDK-based implementations of Java switched to using locale data obtained from the Unicode Consortium’s Common Locale Data Repository (CLDR) (see Wikipedia) by default.
This switch provides much richer locale data than previously seen in Java. And this switch may mean that you see a change in behavior for some aspects of some locales.
See: JEP 252: Use CLDR Locale Data by Default
COMMA in Java 10
When I run this code:
System.out.println( "java.version: " + System.getProperty( "java.version" ) );
final Locale locale = new Locale( "nb" , "NO" );
System.out.println( locale.getDisplayLanguage() ); // Norwegian Bokmål
final DecimalFormatSymbols dfs = new DecimalFormatSymbols( locale );
System.out.println( dfs.getDecimalSeparator() );
I get the COMMA character.
java.version: 10.0.2
Norwegian Bokmål
,

DecimalFormat not working on Windows 7, but working on Windows 8

I have an applet which is working fine on Windows 8, but on Windows 7 I get the following error:
Exception in thread "Thread-13" java.lang.NumberFormatException: For input string: "-0,9"
at sun.misc.FloatingDecimal.readJavaFormatString(Unknown Source)
at java.lang.Float.parseFloat(Unknown Source)
at Tanc.Game$Corp.getDW(Game.java:1505)
at Tanc.Game.borders(Game.java:975)
at Tanc.Game.loose(Game.java:1068)
at Tanc.Game.gameLoop(Game.java:242)
at Tanc.Game$1.run(Game.java:144)
I have to mention that I tried on 2 different computers but the same problem. On Windows 8 I don't get this error...
And code
String zz = new DecimalFormat("#.##").format(corp400.y);
System.out.println(zz);
if (Float.parseFloat(zz) == 0.2f)
sw = true;
if (Float.parseFloat(zz) == -0.24f)
sw = false;code here
Probably problem with locale: on the computer with Win8 there are locale, where , means decimal separator, but on the other tested computers . means decimal separator.
I replaced DecimalFormat with (Math.round(corp400.y*100.0)/100.0)
But if anyone can find out how to fix DecimalFormat to work with Windows 8 and windows 7 please Comment/Answer.
Probably on windows 8 (where it works) you are using an English locale.
The problem, tough, is in the code:
String zz = new DecimalFormat("#.##").format(corp400.y);
actually uses the default locale to format corp400.y, but the JavaDoc says that Float.parseFloat
Returns a new float initialized to the value represented by the specified String, as performed by the valueOf method of class Float.
This means that, since Float.valueOf parses a floating point literal, it expects decimals separated by . not ,
The correct way to reverse your locale-dependant formatting is:
float value = new DecimalFormat("#.##").parse(zz).floatValue();
Alternatively, you could use the locale-independent formatting on both ways as in:
String zz = String.valueOf(corp400.y);
float value = Float.parseFloat(zz)

How to get all Java supported Locales

When Google you will find lot of materials to find all the supported Locales by Java. But its all confusing.
For example [http://sanjaal.com/java/tag/java-locale-tutorial/] show an output of 210 locales. But when I run the same program I get only 155. I don;t get for example ta_IN. si_LK is not output by any program.
Can someone please clear the air?
I use JDK/JRE 1.7
http://www.oracle.com/technetwork/java/javase/javase7locales-334809.html gives 111 entries.
I have a Spring Application which supports i18n and our customers can put their own localisations. What I am trying to do is to provide a list of all locales for them to select their one from.
Oh! this is confusing. Local.getISOCountries() provide LK as a country and Locale.getISOLanguages(); provide si as a language .... but si_LK which is a valid locale is not given in Locale.getAvailableLocales();
Locale loc = new Locale("ta", "IN"); // ta_IN, si_LK
System.out.printf("Name: %s%n"
+ "Country: %s; %s - %s%n"
+ "Language: %s; %s - %s%n"
+ "Script: %s - %s%n",
loc.getDisplayName(Locale.ENGLISH),
loc.getCountry(), loc.getISO3Country(), loc.getDisplayCountry(Locale.ENGLISH),
loc.getLanguage(), loc.getISO3Language(), loc.getDisplayLanguage(Locale.ENGLISH),
loc.getScript(), loc.getDisplayScript(Locale.ENGLISH));
Name: Tamil (India)
Country: IN; IND - India
Language: ta; tam - Tamil
Script: -
Name: Sinhalese (Sri Lanka)
Country: LK; LKA - Sri Lanka
Language: si; sin - Sinhalese
Script: -
Also it is possible to provide support for ones own locale (since Java 7 I believe). I made it for Esperanto, and it is doable (LocaleProvider). But in your case all might be there.
SimpleDateFormat f = new SimpleDateFormat("EEEE", loc);
System.out.println("Weekday: " + f.format(new Date()));
Unfortunately shows "Tuesday," so one needs to implement the language formats and such.
I found a project for serbo-croatian/bosnian.
The code you found on internet use the next function to extract all the locales:
Locale list[] = DateFormat.getAvailableLocales();
If we read the documentation for that function:
Returns an array of all locales for which the get*Instance methods of this class can return localized instances. The returned array represents the union of locales supported by the Java runtime and by installed DateFormatProvider implementations. It must contain at least a Locale instance equal to Locale.US
So we can understand that the number of locales returned depends on your runtime version, and your DateFormatProvider.
For example in my machine with JDK 1.6.0_27 I get 152 results.
To provide a list of all ISO countries in your JAVA Version try the following:
public static void main(String[] args) {
String[] locales = Locale.getISOCountries();
for (String countryCode : locales) {
Locale obj = new Locale("", countryCode);
System.out.println("Country Code = " + obj.getCountry()
+ ", Country Name = " + obj.getDisplayCountry());
}
}
This way you can provide a complete list of all countries but it is still dependend on the current ISO country list of your JDK.
Another way would be to use a REST-API from a provider like: https://restcountries.eu/rest/v2/all from https://restcountries.eu/
This way your program is independent of the JDK you use and is always up2date (if your chosen provider is reliable).

Categories

Resources