Java';s MessageFormat未将葡萄牙语月份本地化为小写日期

Java';s MessageFormat未将葡萄牙语月份本地化为小写日期,java,date,localization,messageformat,Java,Date,Localization,Messageformat,月份名称以大写字母开头,而不是小写 我在本地计算机上运行的一些示例代码: 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

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

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

  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!
因此,法语是正确的,但出于某种原因,这两个葡萄牙语变体并非如此

更奇怪的是,我试着添加与一个完全相同的代码,但它根本无法本地化

我在这里误解了什么?

tl;博士
  • 不同的Java实现在本地化规则上可能有所不同
  • Java实现的不同版本在本地化规则上可能有所不同
对于OracleJDK和OpenJDK项目,版本9和更高版本在自己的规则集和由定义的规则集之间切换(请参阅)。看啊看

运行以下代码:

Month.JULY.getDisplayName( TextStyle.FULL , new Locale( "pt" ) )
在Java8、OracleJDK中,默认情况下使用其自己的本地化规则,我们得到初始cap

朱略

在Java10、OracleJDK中,默认情况下,使用Unicode CLDR规则,我们得到小写

朱略

在Java10中,Oracle JDK在设置VM选项
-Djava.locale.providers=COMPAT,SPI
以恢复旧式行为而不是使用Unicode CLDR后,我们得到了初始cap

朱略

细节
  • 文化规范定义的格式规则可能因实施和版本而异
  • IdeOne.com中的拒绝本地化。只有我们英语
  • 您使用了错误的类使用java.time处理所有日期时间
代码

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

文化规范 决定一个问题,例如一个月名称的大写取决于一种文化的规范。当然,这些规范可能会有所不同,理性的人也可能不同意

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

Unicode CLDR 也许您正在使用Java8或更早版本。我在这个答案中的代码是用Java10生成的

一个重要的区别是,对于Java9,对于OracleJDK和OpenJDK项目,默认的源代码使用(请参阅)。在Java8和更早版本中,每个JVM都提供了自己的规则集

另外,请注意,Unicode CLDR会不时更新,某些规则可能会更改

因此,您可能会看到不同的本地化结果,这取决于使用的是哪个版本的Java,以及使用的是哪个Java实现

介绍的背景 也许这里的问题是背景问题。在某些区域性中,格式规则(如月份名称的大写)因月份是单独显示还是嵌入在日期中而有所不同

让我们试试translate.Google.com:

  • July
    产量
    Julho
  • 七月初
    收益率
    不在julho
  • 2018年7月12日
    2018年7月12日
所以谷歌似乎因环境而有所不同,但我不确定第一个案例是初始cap到底发生了什么

可以通过使用方法中使用的枚举来指示此上下文。该枚举提供了独立的变体

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

朱略

所以,在使用Java10时,上下文似乎不是问题

java.time 您使用的是现在被java.time类取代的麻烦的旧类

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

Instant instant = Instant.now() ;  // Capture the current moment in UTC.
instant.toString():2018-07-11T05:57:36.446917Z

从UTC调整到该地区的人们使用您想要的特定挂钟时间的时区。应用
ZoneId
以获取
zoneDateTime
对象。时区与区域设置完全正交。一个与当前内容相关,另一个只影响生成表示该内容的字符串时使用的本地化。例如,您可以使用带有葡萄牙语地区的日本时区

ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
现在,我们要生成表示那个时刻的字符串。首先,生成一个标准格式的
字符串
,通过在方括号中添加时区名称进行扩展

String output = zdt.toString() ;  // Generate ISO 8601 format string.
2018-07-11T17:57:36.446917+12:00[太平洋/奥克兰]

您希望本地化生成的
字符串
对象。用于控制要生成的
字符串的格式。指定用于控制长度或缩写的长度

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 ) ) ;
夸塔·费拉,2018年7月11日17:57:36新泽兰迪亚河畔

夸塔·费拉,2018年7月11日17:57:36新泽兰迪亚河畔

mercredi 11 juillet 2018年11月17日:57:36《新英格兰的常态》

为了好玩,让我们尝试一下,而不是
FULL

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

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

11 juillet 2018á17:57:36 NZSTe

IdeOne拒绝本地化 我喜欢使用IdeOne.com演示Java代码。不幸的是,它的JVM拒绝使用除美国英语以外的任何
语言环境。所以,不要使用上面的代码


关于java.time 该框架内置于Java8及更高版本中。这些类取代了麻烦的旧日期时间类,例如,&

该项目现已启动,建议迁移到类

要了解更多信息,请参阅。并搜索堆栈溢出以获得许多示例和解释。规格是

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

从哪里获得java.time类

  • 然后
    • 内置的
    • 标准JavaAPI的一部分,带有捆绑实现
    • <
      String output = zdt.toString() ;  // Generate ISO 8601 format string.
      
      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 ) ) ;