I've just started writing my first JavaFX 2 application after learning the basics and would like to internationalize it.
I notice that in JavaFX 1.x, the scripting language allowed for very simple internationalization of strings. Are there any similar features in JavaFX 2?
Basically: what is the best practice for internationalizing a JavaFX 2 application?
Starting with JDK 11, Oracle will remove JavaFX from the JDK, though will continue to provide commercial support for it in Oracle JDK 8 at least until 2022, according to Oracle's blog.
JavaFX is a set of graphics and media packages that enables developers to design, create, test, debug, and deploy rich client applications that operate consistently across diverse platforms.
The basic steps (among others) of a java app internationalizing, are Locale
lizing and resource bundling. In JavaFX, you can use FXMLLoader#setResources()
for that purposes. Here a SSCCE demo to demonstrate it. The codes are self-descriptive.
Demo package structure:
bundledemo |------ BundleDemo.java |------ MyController.java |------ MyView.fxml bundles |------ MyBundle_en.properties |------ MyBundle_kg.properties
MyBundle_en.properties
key1=Name Surname key2=How are you?
MyBundle_kg.properties
key1=Aты Жөнү key2=Кандайсың?
MyView.fxml
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.layout.*?> <?import javafx.scene.control.*?> <?import javafx.scene.*?> <BorderPane fx:controller="bundledemo.MyController" xmlns:fx="http://javafx.com/fxml"> <top> <!-- This label's text will be set by the controller --> <Label fx:id="lblTextByController"/> </top> <center> <!-- This label's text will be taken from the bundle automatically --> <Label text="%key2"/> </center> </BorderPane>
MyController.java
package bundledemo; import java.net.URL; import java.util.ResourceBundle; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.Label; public class MyController implements Initializable { @FXML private Label lblTextByController; private ResourceBundle bundle; @Override public void initialize(URL location, ResourceBundle resources) { bundle = resources; lblTextByController.setText(bundle.getString("key1")); } }
BundleDemo.java
package bundledemo; // imports are ignored. public class BundleDemo extends Application { private Stage stage; @Override public void start(Stage primaryStage) { stage = primaryStage; Button btnEN = new Button(); btnEN.setText("English"); btnEN.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { loadView(new Locale("en", "EN")); } }); Button btnKG = new Button(); btnKG.setText("Kyrgyz"); btnKG.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { loadView(new Locale("kg", "KG")); } }); VBox root = new VBox(20); root.getChildren().add(HBoxBuilder.create().spacing(10).style("-fx-background-color: gray").padding(new Insets(5)).children(btnEN, btnKG).build()); root.getChildren().add(new StackPane()); primaryStage.setScene(new Scene(root, 300, 250)); primaryStage.show(); } private void loadView(Locale locale) { try { FXMLLoader fxmlLoader = new FXMLLoader(); fxmlLoader.setResources(ResourceBundle.getBundle("bundles.MyBundle", locale)); Pane pane = (BorderPane) fxmlLoader.load(this.getClass().getResource("MyView.fxml").openStream()); // replace the content StackPane content = (StackPane) ((VBox) stage.getScene().getRoot()).getChildren().get(1); content.getChildren().clear(); content.getChildren().add(pane); } catch (IOException ex) { ex.printStackTrace(); } } public static void main(String[] args) { launch(args); } }
Screenshot:
If your internationalized text needs to be rendered in a font that might be on the user's target system, then you can either:
OR
If the required font is not available, then the internationalized text might be displayed as unintelligible gibberish, even though everything else about the setup is fine.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With