Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaFX dialog internationalization in java module based project

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

like image 417
Petr Štechmüller Avatar asked Dec 29 '25 21:12

Petr Štechmüller


1 Answers

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.


Example

Here's a full example using Java 21.0.1, JavaFX 21.0.1, and Gradle 8.5.

Source Code

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

Bundle Properties File

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 Подтверждение

Gradle Files

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 Structure

<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.

Output

From executing the Gradle run task.

screenshot of running application

like image 96
Slaw Avatar answered Dec 31 '25 09:12

Slaw



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!