Reputation: 75
The month names start with an uppercase letter instead of lowercase, as they should.
Some sample code I ran on my local machine:
Locale portugal = new Locale("pt");
Locale brazil = new Locale("pt", "BR");
Locale france = new Locale("fr", "FR");
Object[] params = new Object[] { new Date() };
String pattern = "Today is {0,date,long}!";
MessageFormat ptFormat = new MessageFormat(pattern, portugal);
MessageFormat brFormat = new MessageFormat(pattern, brazil);
MessageFormat frFormat = new MessageFormat(pattern, france);
StringBuffer ptResult = ptFormat.format(params, new StringBuffer(), new FieldPosition(0));
StringBuffer brResult = brFormat.format(params, new StringBuffer(), new FieldPosition(0));
StringBuffer frResult = frFormat.format(params, new StringBuffer(), null);
System.out.println("Portugal result: " + ptResult.toString());
System.out.println("Brazil result: " + brResult.toString());
System.out.println("France result: " + frResult.toString());
And this is what I got:
Portugal result: Today is 10 de Julho de 2018!
Brazil result: Today is 10 de Julho de 2018!
France result: Today is 10 juillet 2018!
So the French is correct, but for some reason the two Portuguese variants are not.
Even weirder, I tried adding the exact same code as an IDEAONE snippet, and it wouldn't localize at all.
What am I misunderstanding here?
Upvotes: 3
Views: 2268
Reputation: 338594
For the Oracle JDK & OpenJDK projects, version 9 and later switched between its own set of rules and the set of rules defined by the Unicode CLDR (see Wikipedia). See Release Notes and see OpenJDK JEP 252.
Run this code:
Month.JULY.getDisplayName( TextStyle.FULL , new Locale( "pt" ) )
In Java 8, Oracle JDK, by default using its own localization rules, we get initial-cap.
Julho
In Java 10, Oracle JDK, by default using Unicode CLDR rules, we get lowercase.
julho
In Java 10, Oracle JDK after setting the VM option -Djava.locale.providers=COMPAT,SPI
to revert to legacy behavior rather than using Unicode CLDR, we get initial-cap.
Julho
Code.
ZonedDateTime // Use modern class for a moment seen from a particular time zone.
.now() // Capture the current moment, using the JVM’s current default time zone. Better to specify a zone explicitly.
.format( // Generate a `String` representing the value of our `ZonedDateTime` object.
DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL )
.withLocale( new Locale( "pt" , "BR" ) )
) // Returns a `String` object.
11 de julho de 2018 17:57:36 NZST
Deciding an issue such as capitalization of a month name depends on a culture’s norms. Of course, those norms can vary, and reasonable people can disagree.
But at some point, decisions have to be made. A Java implementation must have a set of rules for making these localization rules.
Perhaps you are using Java 8 or earlier. My code in this Answer was produced with Java 10.
One important difference is that as of Java 9, for the Oracle JDK and OpenJDK projects, the default source of localization rules changed to using the Unicode CLDR (see Wikipedia). In Java 8 and earlier, each JVM provided their own set of rules.
Also, note that the Unicode CLDR is updated from time-to-time, and some rules may change.
So, you may well see different results for localization depending on which version of Java is in use, and on which implementation of Java is in use.
Perhaps the issue here is one of context. In some cultures, the formatting rules such as capitalization of a month name vary by whether the month is being presented alone or embedded within a date.
Let’s try translate.Google.com:
July
yields Julho
Early in the month of July
yields no início do mês de julho
12th of July, 2018
yields 12 de julho de 2018
So Google seems to vary by context, but I am not certain what is going on with that first case being initial-cap.
This context can be indicated by the use of the TextStyle
enum used in Month::getDisplayName
method. That enum offers …STANDALONE
variations.
Month.JULY.getDisplayName(
TextStyle.FULL ,
new Locale( "pt" )
)
julho
Month.JULY.getDisplayName(
TextStyle.FULL_STANDALONE ,
new Locale( "pt" )
)
julho
So no, context seems to not be an issue here when using Java 10.
You are using the troublesome old classes now supplanted by the java.time classes.
Instead of java.util.Date
, use java.time.Instant
. Both represent a moment in UTC. The modern Instant
class uses a finer resolution of nanoseconds instead of milliseconds.
Instant instant = Instant.now() ; // Capture the current moment in UTC.
instant.toString(): 2018-07-11T05:57:36.446917Z
Adjust from UTC to the time zone where the people of that region use a particular wall-clock time you desire. Apply a ZoneId
to get a ZonedDateTime
object. Time zone is completely orthogonal to locale. One relates to the content of the moment, the other affects only the localization used in generating a String to represent that content. You could, for example, use a Japan time zone with a Portuguese locale.
ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
Now, we want to generate strings representing that moment. First, generate a String
in standard ISO 8601 format extended by appending the name of the time zone in square brackets.
String output = zdt.toString() ; // Generate ISO 8601 format string.
2018-07-11T17:57:36.446917+12:00[Pacific/Auckland]
You want to localize the generated String
objects. Use DateTimeFormatter
to control the formatting of the String
to be generated. Specify a FormatStyle
to control how long or abbreviated.
Locale l_pt = new Locale( "pt" ) ;
Locale l_ptBR = new Locale( "pt" , "BR" ) ;
Locale l_FR = Locale.FRANCE ;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ) ;
String output_pt = zdt.format( f.withLocale( l_pt ) ) ;
String output_ptBR = zdt.format( f.withLocale( l_ptBR ) ) ;
String output_FR = zdt.format( f.withLocale( l_FR ) ) ;
quarta-feira, 11 de julho de 2018 17:57:36 Horário Padrão da Nova Zelândia
quarta-feira, 11 de julho de 2018 17:57:36 Horário Padrão da Nova Zelândia
mercredi 11 juillet 2018 à 17:57:36 heure normale de la Nouvelle-Zélande
For fun, let’s try FormatStyle.LONG
instead of FULL
.
11 de julho de 2018 17:57:36 NZST
11 de julho de 2018 17:57:36 NZST
11 juillet 2018 à 17:57:36 NZSTe
I enjoy using IdeOne.com to demo Java code. Unfortunately, its JVM refuses to use any Locale
except US English. So, no go for the code above.
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date
, Calendar
, & SimpleDateFormat
.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.*
classes.
Where to obtain the java.time classes?
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval
, YearWeek
, YearQuarter
, and more.
Responding to comments on this Answer…
Timeline:
java.util.Date
etc.) donated by IBM/Taligent. Unfortunately, they are poorly-designed and flawed, confusing and troublesome.java.util.Calendar
etc.) in an attempt to improve the date-time handling. But these too turn out to be poorly-designed and confusing, not a real improvement.import
statements from org.threeten.bp.*
to java.time.*
. tzdata
changes and bug fixes, but no further feature work to be done. They advise migration away from Joda-Time and on to the java.time classes.Upvotes: 7