I would like to ask for a help with internationalization of buttons in JavaFX built in dialogs.
Translations for buttons are available in https://github.com/openjdk/jfx/tree/master/modules/javafx.controls/src/main/resources/com/sun/javafx/scene/control/skin/resources There are some other countries and also fallback to English variant.
From code POV translatiosn are loaded by this class: https://github.com/openjdk/jfx/blob/master/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/skin/resources/ControlResources.java
public final class ControlResources {
// Translatable properties
private static final String BASE_NAME = "com/sun/javafx/scene/control/skin/resources/controls";
public static String getString(String key) {
return ResourceBundle.getBundle(BASE_NAME).getString(key);
}
}
To add custom translation I would create new controls_xx.properties file in resources of my application under following package: com/sun/javafx/scene/control/skin/resources/controls.
The issue is that "java-modules" do not allow to have one package in multiple modules.
What are the possibilities to load custom translations for JavaFX dialogs?
Note: disabling "java-modules" is not an option.
Thank you
EDIT: I'm aware of this thread: Javafx Internationalization with custom language
Ideally, the javafx.controls
module would make use of the ResourceBundleProvider
SPI. This would let your own module provide an implementation that would find the custom resource bundles. But since JavaFX does not make use of said SPI (as of version 21.0.1), and since split packages are not allowed, another solution is needed.
One option is to make use of --patch-module
. For instance, if you wanted to add translations for Russian (which is not one of the provided languages in JavaFX 21), then you would create the properties file in the correct package:
com/sun/javafx/scene/control/skin/resources/controls_ru.properties
And place that in some directory; let's say that directory is named bundles
for this example. Then at run-time, you would use the following option:
--patch-module javafx.controls=bundles
Note: This assumes bundles
is relative to the working directory. If that's not the case, then use the proper relative path or an absolute path.
Here's a full example using Java 21.0.1, JavaFX 21.0.1, and Gradle 8.5.
module-info
module app {
requires javafx.controls;
exports sample to
javafx.graphics;
}
sample.Main
package sample;
import java.util.Locale;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
Locale.setDefault(Locale.of("ru"));
var dialogBtn = new Button("Show dialog");
dialogBtn.setOnAction(
e -> {
var alert = new Alert(AlertType.CONFIRMATION);
alert.getButtonTypes().setAll(ButtonType.YES, ButtonType.NO);
alert.initOwner(primaryStage);
alert.setContentText("This text is intentionally English.");
alert.show();
});
var root = new StackPane(dialogBtn);
primaryStage.setScene(new Scene(root, 500, 300));
primaryStage.setTitle("Demo");
primaryStage.show();
}
}
controls_ru.properties
Dialog.yes.button = Да
Dialog.no.button = Нет
Dialog.confirm.title = Подтверждение
Dialog.confirm.header = Подтверждение
Note: There are many more properties you'd have to set to properly add a language, but these are all that were needed for this example.
These translations were taken from Google Translate. I have no idea how accurate they are.
English | Russian |
---|---|
Yes | Да |
No | Нет |
Confirmation | Подтверждение |
settings.gradle.kts
rootProject.name = "app"
build.gradle.kts
plugins {
id("org.openjfx.javafxplugin") version "0.1.0"
application
}
group = "sample"
version = "0.1.0"
application {
mainModule = "app"
mainClass = "sample.Main"
}
javafx {
modules("javafx.controls")
version = "21.0.1"
}
repositories {
mavenCentral()
}
tasks {
named<JavaExec>("run") {
jvmArgs("--patch-module=javafx.controls=${file("bundles")}")
}
}
<PROJECT_DIRECTORY>
│ build.gradle.kts
│ settings.gradle.kts
│
├───bundles
│ └───com
│ └───sun
│ └───javafx
│ └───scene
│ └───control
│ └───skin
│ └───resources
│ controls_ru.properties
│
└───src
└───main
└───java
│ module-info.java
│
└───sample
Main.java
Note the bundles
directory is not under src/main/resources
. You don't want to package this in your own module, as doing so will cause a split-package error. Unfortunately, this means you have to find a way to deploy bundles
alongside your application. I assume doing this would be relatively easy using a tool like jpackage
, but I haven't tried it.
From executing the Gradle run
task.