Java的MessageFormat无法将葡萄牙语的月份本地化为小写

时间:2018-07-10 23:43:08

标签: java date localization messageformat

月份名称以大写字母开头,而不是小写as they should

我在本地计算机上运行了一些示例代码:

  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());

这就是我得到的:

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!

所以法语是正确的,但由于某些原因,这两个葡萄牙语变体不正确。

即使很奇怪,我也尝试添加与IDEAONE snippet完全相同的代码,并且它根本不会本地化。

我在这里误会什么?

1 个答案:

答案 0 :(得分:2)

tl; dr

  • Java的不同实现可能会在本地化规则方面有所差异。
  • Java实现的不同版本的本地化规则可能会有所不同。

对于Oracle JDK和OpenJDK项目,版本9和更高版本在其自己的规则集和Unicode CLDR定义的规则集之间切换(请参阅Wikipedia)。请参阅Release NotesOpenJDK JEP 252

运行以下代码:

Month.JULY.getDisplayName( TextStyle.FULL , new Locale( "pt" ) )

在Java 8中,默认情况下,Oracle JDK使用其自己的本地化规则,我们获得了首字母大写。

  

朱利叶

在Java 10,Oracle JDK中,默认情况下使用Unicode CLDR 规则,我们将小写。

  

julho

在Java 10中,将VM选项-Djava.locale.providers=COMPAT,SPI设置为恢复为旧行为而不是使用Unicode CLDR之后,Oracle JDK便获得了首字母大写。

  

朱利叶

详细信息

  • 文化规范定义的格式设置规则可能会因JVM的实现方式和版本而异。
  • IdeOne.com中的JVM拒绝本地化。仅美国英语。 See proof
  • 您使用了错误的类。 使用 java.time 进行所有日期时间处理。

代码。

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.
  

2018年7月11日NZST 17:57:36

文化规范

确定诸如月姓大写之类的问题取决于文化的规范。当然,这些规范可能会有所不同,并且理性的人可能会不同意。

但是在某些时候,必须做出决定。 Java实现必须具有一组用于制定这些本地化规则的规则。

Unicode CLDR

也许您正在使用Java 8或更早版本。我在此答案中的代码是使用Java 10制作的。

一个重要的区别是,从Java 9开始,对于Oracle JDK和OpenJDK项目,localization rules changed的默认来源是使用Unicode CLDR的(请参阅Wikipedia)。在Java 8和更早版本中,每个JVM提供了自己的规则集。

此外,请注意,Unicode CLDR会不时更新,并且某些规则可能会更改。

因此,您可能会看到不同的本地化结果,具体取决于所使用的Java版本和所使用的Java实现。

演示文稿的上下文

也许这里的问题是上下文之一。在某些文化中,格式规则(例如,月份名称的大写)因月份是单独显示还是嵌入在日期中而异。

让我们尝试翻译。Google.com:

  • July产生Julho
  • Early in the month of July产生no início do mês de julho
  • 12th of July, 2018产生12 de julho de 2018

因此Google似乎因上下文而异,但是我不确定第一种情况是首字母大写情况是什么。

可以通过使用TextStyle方法中使用的Month::getDisplayName枚举来指示此上下文。该枚举提供了…STANDALONE个变体。

Month.JULY.getDisplayName(
    TextStyle.FULL , 
    new Locale( "pt" )
)
  

julho

Month.JULY.getDisplayName(
    TextStyle.FULL_STANDALONE , 
    new Locale( "pt" )
)
  

julho

所以不,使用Java 10时上下文似乎不是问题。

java.time

您正在使用麻烦的旧类,而这些旧类现在已被 java.time 类取代。

使用java.util.Date代替java.time.Instant。两者都代表UTC的时刻。现代的Instant类使用的是纳秒级而不是毫秒级的更精细分辨率。

Instant instant = Instant.now() ;  // Capture the current moment in UTC.
  

instant.toString():2018-07-11T05:57:36.446917Z

从UTC调整到该地区的人使用您想要的特定挂钟时间的时区。应用ZoneId获得ZonedDateTime对象。时区与语言环境完全正交。一个与当下的内容有关,另一个仅影响用于生成表示该内容的字符串的本地化。例如,您可以将日本时区与葡萄牙语一起使用。

ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;

现在,我们要生成表示该时刻的字符串。首先,生成标准ISO 8601格式的String,方法是将时区的名称附加在方括号中。

String output = zdt.toString() ;  // Generate ISO 8601 format string.
  

2018-07-11T17:57:36.446917 + 12:00 [太平洋/奥克兰]

您要本地化生成的String对象。使用DateTimeFormatter控制要生成的String的格式。指定FormatStyle可以控制多长时间或缩写。

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,2018年7月11日17:57:36HorárioPadrãoda NovaZelândia

     

quarta-feira,2018年7月11日17:57:36HorárioPadrãoda NovaZelândia

     

mercredi 11 juillet 2018à17:57:36 heure normale de laNouvelle-Zélande

为了娱乐,我们尝试使用FormatStyle.LONG而不是FULL

  

2018年7月11日NZST 17:57:36

     

2018年7月11日NZST 17:57:36

     

2018年6月11日NZSTe 17:57:36

IdeOne拒绝本地化

我喜欢使用IdeOne.com演示Java代码。不幸的是,其JVM拒绝使用除美国英语以外的任何Locale。因此,不要使用上面的代码。


关于 java.time

java.time框架已内置在Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendarSimpleDateFormat

目前位于Joda-Timemaintenance mode项目建议迁移到java.time类。

要了解更多信息,请参见Oracle Tutorial。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310

您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*类。

在哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展了java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。您可能会在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore


时间简史

回复对此答案的评论…

时间轴:

  • Sun Microsystems启动Java 1.0,其中包含IBM / Taligent捐赠的一些日期时间类(java.util.Date等)。不幸的是,它们的设计欠佳,有缺陷,令人困惑和麻烦。
  • Sun在Java 1.1(java.util.Calendar等)中添加了更多类,以改善日期时间处理。但是事实证明,这些设计不当且令人困惑,不是真正的改进。
  • 几年后,一个人Stephen Colebourne创立了 Joda-Time 项目,以创建一个综合的,复杂的日期时间处理库,这在IT行业中是首创。巨大的成功。最受欢迎的Java库之一,通常会添加到许多开发人员的项目中。
  • 后来,后来成为Oracle的
  • Sun和JCP community最终意识到,最早的Java版本附带的旧日期时间类是不够的。这一认可为同一斯蒂芬·科尔本(Stephen Colebourne)推出JSR 310(新的日期时间API规范,该规范包含在Java平台中)打开了大门。该项目基于Joda-Time的概念,但是是一个全新的重写。新的重写基于创建Joda-Time的经验,并询问“如果我们知道然后我们现在知道什么,我们将如何设计Joda-Time?”。
  • JSR 310的实现是作为 java.time 包构建的,将包含在即将发布的Java 8中。
  • 为确保更广泛地接受 java.time API,斯蒂芬·科尔本(Stephen Colebourne)启动了开源ThreeTen-Backport项目,模仿了与 java.time 中相同的API。 em>,但是能够在Java 6和Java 7上运行。不是所有的 java.time 功能,而是大部分功能。目的是使早期Java上的开发人员能够开始使用API​​。然后,当移至Java的更高版本时,他们要做的只是将import语句从org.threeten.bp.*更改为java.time.*
  • 随着Java 8成功交付,Stephen Colebourne等人。将Joda-Time项目移至维护模式,仍在进行tzdata更改和错误修复,以积极方式进行更新,但无需进行进一步的功能工作。他们建议从Joda-Time迁移到 java.time 类。
  • Stephen Colebourne还启动了一个开放源代码ThreeTen-Extra项目,以开发可能会或可能不会最终提交到更高版本的JSR 310的其他功能(成为其他 java.time 类)。同时,这些额外功能( java.time 功能的扩展)作为库提供给那些发现有用的开发人员。