Java 程序在 jpackage 时使用了错误的语言环境

Posted

技术标签:

【中文标题】Java 程序在 jpackage 时使用了错误的语言环境【英文标题】:Java program uses wrong locale when jpackaged 【发布时间】:2021-11-17 17:31:42 【问题描述】:

我正在解析表示德式数字的字符串(即,十进制逗号和用于分组数千的可选句号),例如,“2.804,13”;这只是根据我想要的Locale 使用DecimalFormat 完成的:

package loc_test;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Locale;

public class ParseNumbers 
    static final DecimalFormat df = (DecimalFormat) NumberFormat.getNumberInstance(Locale.GERMANY);

    public static void main(String[] args) throws Exception 
        for (String s : new String[]  "2.815,53", "2815,53" ) 
            System.out.println(String.format("%s \t-> %s", s, df.parse(s)));
        
    

这给出了所需的输出,例如,当从命令行编译和运行时:

2.815,53        -> 2815.53
2815,53         -> 2815.53

但是,当我将其捆绑为 jpackage 映像(./gradlew jpackageImage 使用 Gradle 和 Badass Runtime Plugin)并运行生成的二进制文件时,输出似乎表明使用了错误的语言环境:

2.815,53    -> 2.815
2815,53     -> 281553

我尝试了一些东西(徒劳无功),问题似乎不在代码本身:

    main 中添加了Locale.setDefault(Locale.GERMANY)Locale.getDefault() 将显示德语语言环境,但没有效果。 通过 Java 命令行参数显式设置 -Duser.country=DE-Duser.language=deSystem.getProperty(...) 显示预期值,但同样没有效果。 (此外,在命令行中,它也可以将这些设置为系统默认值。) 在 jpackage Java 命令行参数中添加了-Djava.locale.providers=COMPAT,CLDR,SPI(来自this thread)。同样,我可以从main 验证该属性设置是否正确,但没有区别。 (而且似乎是早期 Java 版本的问题。)

我首先使用 Java 16 和 Gradle 7.2 发现了问题;并且在更新到 Java 17 和 Gradle 7.3 后仍然存在。该问题在英文 Linux 系统和德文 Windows 机器上都出现过。

--infojpackageImage 命令一起使用,Gradle 会告诉我它正在使用哪个 JDK:

Starting process 'command '.../.gradle/jdks/jdk-17+35/bin/jpackage''. Working directory: .../LocTest/app Command: .../.gradle/jdks/jdk-17+35/bin/jpackage --type app-image --input .../LocTest/app/build/install/app/lib --main-jar app.jar --main-class LocTest.App --dest .../LocTest/app/build/jpackage --name app --runtime-image .../LocTest/app/build/jre --java-options -Duser.country=DE --java-options -Duser.language=de --java-options -Djava.locale.providers=COMPAT,CLDR,SPI

当我使用 .gradle/jdks/jdk-17+35/bin/java 运行课程时,即运行:

~/.gradle/jdks/jdk-17+35/bin/java -cp app/build/classes/java/main loc_test.App`

这样,问题不会出现;数字是正确的。

但是,当我使用与 jpackage 映像捆绑的(假定相同的)JRE 时,问题确实发生了,即,我使用以下方法获得了 错误 数字:

./app/build/jpackage/app/lib/runtime/bin/java -cp app/build/classes/java/main/ loc_test.App

为什么java 在打包时会忽略语言环境? 我是否在这里遗漏了什么,或者这可能是 jpackage 或插件中的错误,或者是 Java 版本本身中的错误?

整个 Gradle 示例项目(非常小)can be found at Github.

【问题讨论】:

您是否在运行时以编程方式验证了 Java 的版本? 现代 Java 默认首先使用 CLDR。你正在推翻那个。为了排除故障,您的更改可能是合理的,但我想确保您知道JEP 252: Use CLDR Locale Data by Default @BasilBourque,感谢您的指点 - CLDR 似乎不是问题的一部分:当我按照接受的答案所指出的那样添加正确的模块时,无论是否设置,我都会得到正确的输出语言环境提供者。 (所以我将把它排除在外。) 【参考方案1】:

检查运行时映像中包含哪些模块。

例如,当我在 JDK 17 上运行 java --list-modules 时,我注意到了这个模块:

jdk.localeddata

我不知道这是否需要它,但我敢打赌,除非特别要求,否则 jpackage 不会包含该模块。

运行./app/build/jpackage/app/lib/runtime/bin/java --list-modules并与~/.gradle/jdks/jdk-17+35/bin/java --list-modules比较以确认。然后考虑用jlink 制作一个包含jdk.localedata 的图像,如果它缺少测试这个假设。

【讨论】:

谢谢,确实是这个问题。 jpackage java 的模块列表由一个条目java.base@17.0.1 组成。在build.gradle 中将modules = ['jdk.localedata'] 添加到我的runtime 配置后,它也会显示出来,并且数字被正确解析。

以上是关于Java 程序在 jpackage 时使用了错误的语言环境的主要内容,如果未能解决你的问题,请参考以下文章

在 Linux 上创建 JPackage 安装程序 Java 应用程序时 linux-menu-group 应该是啥?

是啥导致了这个“jpackage 不存在”错误?

首发! JDK14之jpackage命令尝鲜

JPackage 不生成工作 exe 和 bugs 文件夹

jpackage在Java 16中可以投入生产

如果应用程序打开,Jpackage MSI 升级未完成