language-manager
August 10, 2025 Β· View on GitHub
language-manager
Language Manager is a JavaFX library that enables dynamic language switching at runtime, allowing you to update the application language without needing to refresh the scene.
π Table of Contents
- Features
- Installation
- File Structure
- Usage
- Language Properties
- Fallback Handling
- Demo
- License
- Contributing
- Issues
- Versioning
β¨ Features
π Support for multiple languages using standard .properties files.
π Change language dynamically at runtime β no need to reload the scene.
π Automatic binding for JavaFX controls such as Label, Button, TextField, CheckBox, RadioButton, ChoiceBox, ComboBox, MenuItem, and more.
π§ Programmatic binding for controls without @FXML IDs β perfect for dynamically created interfaces.
βοΈ Custom annotations to ignore or customize specific field bindings.
π§© Support for TreeItem and Tab bindings as well.
π¦ Lightweight, non-intrusive, and easy to integrate into any JavaFX project.
π¦ Installation
Add the library to your project using Maven or Gradle
Maven
Add the following to your pom.xml:
<dependency>
<groupId>io.github.snoopy137</groupId>
<artifactId>language-manager</artifactId>
<version>1.1.2</version>
</dependency>
Gradle
Add this to your build.gradle:
dependencies {
implementation 'io.github.snoopy137:language-manager:1.1.2'
}
π File Structure
Place your resource bundles in the src/main/resources folder:
src/
βββ main/
βββ resources/
βββ language.properties # Default (English)
βββ language_es.properties # Spanish
βββ language_fr.properties # French
For specific key patterns used by controls like ChoiceBox, ComboBox, etc., see the Key Structure section.
π Usage
1. FXML-Based Auto Binding
If you're using FXML, simply annotate your controller fields and call Language.autoBind(this) to automatically bind controls based on their @FXML IDs.
@FXML
private Label greeting;
@FXML
private Button submitButton;
public void initialize() {
Language.autoBind(this); // Binds all supported @FXML controls automatically
}
βΉοΈ Supported controls include Label, Button, TextField, TextArea, CheckBox, MenuItem, Tab, Tooltip, and more.
π« To exclude a specific field from being auto-bound, use the @IgnoreBind annotation:
@FXML @IgnoreBind
private Label doNotTranslate;
2. Programmatic Binding (No FXML Required)
If you're not using FXML or want to create and bind controls dynamically, you can use the @Bind annotation without @FXML. Just make sure to initialize your controls before calling Language.autoBind(this).
@Bind
private Label dynamicLabel;
public void initialize() {
dynamicLabel = new Label();
rootPane.setCenter(dynamicLabel);
Language.autoBind(this); // Binds to key "dynamicLabel"
}
3. Custom Binding Key (Optional)
Whether you're using FXML or not, you can override the default binding key (which is normally the field name) by providing a custom value to the @Bind annotation.
β With FXML
@FXML
@Bind("custom.key")
private Label greeting; // Binds to the key "custom.key" instead of the field name.
β Without FXML
@Bind("custom.key")
private Label dynamicLabel;
public void initialize() {
dynamicLabel = new Label();
Language.autoBind(this); // Binds to the key "custom.key" instead of the field name.
}
This is useful for mapping controls to translation keys that donβt match their field names or follow a naming convention.
4. Manual Binding Without Annotations
If you're creating controls programmatically (not declared as fields), you can bind them using Language.autoBindField(...). In this case, you must provide the translation key explicitly, as there's no field name to derive it from.
private void initialize() {
Label dynamicLabel = new Label();
Language.autoBindField(dynamicLabel, "dynamic.label");
}
This is ideal for dynamically created controls that aren't declared as fields or when you want precise manual control over the key used.
5. Change Language Dynamically
Switch the application's active language at runtime using:
Language.setLocale(Locale.forLanguageTag("es")); // Switch to Spanish
π This will automatically update all bound controls with the translated values from the corresponding language_es.properties file.
You can switch to any language as long as a corresponding .properties file is available (e.g., language_fr.properties for French).
π‘ If a key is missing in the selected language file, the fallback mechanism (see next section) will handle it gracefully.
ποΈ Language Properties
1. Base Name Customization
By default, the Language Manager looks for a language.properties file. You can customize the base name using:
Language.setBaseName("mybundle"); // Looks for mybundle.properties, mybundle_es.properties, etc.
This lets you organize language files however you'd like.
2. Key Structure for Complex Controls
Some controls (like ChoiceBox, ComboBox, TabPane, etc.) require a specific key format in the .properties files for their child items or tooltips to be localized properly. Example structure:
# ChoiceBox
choiceBox=Choose an option
choiceBox.0=Option1
choiceBox.1=Option2
# ComboBox
comboBox=Select an item
comboBox.0=Option 1
comboBox.1=Option 2
# TabPane
tab1=Tab 1
tab2=Tab 2
tab1.tooltip=Tab tooltip 1
tab2.tooltip=Tab tooltip 2
# ListView
listView.0=First item
listView.1=Second item
listView.2=Third item
# TreeView
treeView.0=Parent Node
treeView.0.0=Child Node 1
treeView.0.1=Child Node 2
Controller Example:
@FXML private ChoiceBox<String> choiceBox;
@FXML private ComboBox<String> comboBox;
@FXML private ListView<String> listView;
@FXML private TreeView<String> treeView;
@FXML private TabPane tabPane;
@FXML private Tab tab1, tab2;
@FXML
private void initialize() {
choiceBox.getItems().addAll("Choice 1", "Choice 2");
comboBox.getItems().addAll("Combo 1", "Combo 2");
listView.getItems().addAll("Item 1", "Item 2", "Item 3");
TreeItem<String> root = new TreeItem<>("Root");
root.getChildren().addAll(
new TreeItem<>("Child 1"),
new TreeItem<>("Child 2")
);
treeView.setRoot(root);
}
Fallback Handling
If a specific language file (e.g., language_es.properties) doesn't exist, the system automatically falls back to the default language.properties. Additionally, if a bound key is missing in the current resource bundle:
The original textProperty() (or equivalent property) of the control is preserved β it won't be overwritten with a blank or placeholder.
A warning will be logged to help you track missing translations:
Missing key 'submitButton' in resource bundle
πΈ Demo

π§ Under the Hood
Language Manager leverages ResourceBundle, SimpleObjectProperty, and Bindings to keep text in sync with the selected locale β all while avoiding the need to reinitialize scenes.
π License
MIT License
π View Javadocs
π€ Contributing
We welcome contributions! Please fork the repository and submit a pull request with your changes.
π Issues
If you encounter any bugs or have feature requests, please open an issue in the GitHub Issues section.
π Versioning
We follow Semantic Versioning for our releases. You can check out the release notes for each version on the Releases Page.