From 547ea55300eb0f56b58e88037888b83c8122cff1 Mon Sep 17 00:00:00 2001
From: lieght <49651652+BAFGdeJong@users.noreply.github.com>
Date: Fri, 3 Oct 2025 03:19:38 +0200
Subject: [PATCH] Changed bundled resources to use static base name instead.
Added options menu with selectable language
---
.../java/org/toop/app/menu/CreditsMenu.java | 2 +-
.../java/org/toop/app/menu/OptionsMenu.java | 74 ++++++++++-
.../main/java/org/toop/local/AppContext.java | 5 +
.../localization/localization_de.properties | 5 +-
...operties => localization_en_US.properties} | 9 +-
.../localization/localization_es.properties | 5 +-
.../localization/localization_fr.properties | 5 +-
.../localization/localization_it.properties | 5 +-
.../localization/localization_nl.properties | 5 +-
.../localization/localization_zh.properties | 5 +-
.../toop/framework/asset/ResourceLoader.java | 15 +--
.../asset/resources/LocalizationAsset.java | 121 ++++++++++++++++--
.../asset/types/BundledResource.java | 5 +
13 files changed, 228 insertions(+), 33 deletions(-)
rename app/src/main/resources/assets/localization/{localization.properties => localization_en_US.properties} (69%)
diff --git a/app/src/main/java/org/toop/app/menu/CreditsMenu.java b/app/src/main/java/org/toop/app/menu/CreditsMenu.java
index 95910ce..a4e46ed 100644
--- a/app/src/main/java/org/toop/app/menu/CreditsMenu.java
+++ b/app/src/main/java/org/toop/app/menu/CreditsMenu.java
@@ -8,7 +8,7 @@ import java.util.Locale;
public final class CreditsMenu extends Menu {
private Locale currentLocale = AppContext.getLocale();
- private LocalizationAsset loc = ResourceManager.get("localization.properties");
+ private LocalizationAsset loc = ResourceManager.get("localization_en_us.properties");
public CreditsMenu() {
}
}
\ No newline at end of file
diff --git a/app/src/main/java/org/toop/app/menu/OptionsMenu.java b/app/src/main/java/org/toop/app/menu/OptionsMenu.java
index 272c08a..7cf7107 100644
--- a/app/src/main/java/org/toop/app/menu/OptionsMenu.java
+++ b/app/src/main/java/org/toop/app/menu/OptionsMenu.java
@@ -1,14 +1,84 @@
package org.toop.app.menu;
+import javafx.geometry.Pos;
+import javafx.scene.control.Button;
+import javafx.scene.control.ChoiceBox;
+import javafx.scene.control.Label;
+import javafx.scene.layout.Region;
+import javafx.scene.layout.StackPane;
+import javafx.scene.layout.VBox;
+import org.toop.app.App;
import org.toop.framework.asset.ResourceManager;
import org.toop.framework.asset.resources.LocalizationAsset;
import org.toop.local.AppContext;
+import java.awt.GraphicsDevice;
+import java.awt.GraphicsEnvironment;
import java.util.Locale;
public final class OptionsMenu extends Menu {
private Locale currentLocale = AppContext.getLocale();
- private LocalizationAsset loc = ResourceManager.get("localization.properties");
+ private LocalizationAsset loc = ResourceManager.get("localization");
+
public OptionsMenu() {
- }
+ final Region background = createBackground();
+
+ GraphicsDevice currentScreen = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()[0];
+
+ LocalizationAsset locFiles = ResourceManager.get(LocalizationAsset.class, "localization");
+ final Label selectLanguageLabel = new Label(loc.getString("optionsMenuLabelSelectLanguage", currentLocale));
+ final ChoiceBox selectLanguage = new ChoiceBox<>();
+ selectLanguage.setValue(currentLocale);
+ for (Locale locFile : locFiles.getAvailableLocales()) {
+ selectLanguage.getItems().add(locFile);
+ }
+
+ selectLanguage.setOnAction((event) -> {
+ Locale selectedLocale = selectLanguage.getSelectionModel().getSelectedItem();
+ AppContext.setLocale(selectedLocale);
+ App.pop();
+ App.push(new OptionsMenu());
+ });
+
+// GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+// GraphicsDevice[] devices = ge.getScreenDevices();
+// final ChoiceBox selectScreen = new ChoiceBox<>();
+// for (GraphicsDevice screen : devices) {
+// selectScreen.getItems().add(screen);
+// }
+//
+// selectScreen.setOnAction((event) -> {
+// int selectedIndex = selectScreen.getSelectionModel().getSelectedIndex();
+// Object selectedItem = selectScreen.getSelectionModel().getSelectedItem();
+//
+// System.out.println("Selection made: [" + selectedIndex + "] " + selectedItem);
+// System.out.println(" ChoiceBox.getValue(): " + selectScreen.getValue());
+// });
+//
+// final ChoiceBox selectWindowSize = new ChoiceBox<>();
+// for (DisplayMode displayMode : currentScreen.getDisplayModes()) {
+// selectWindowSize.getItems().add(displayMode);
+// }
+//
+//// if (currentScreen.isFullScreenSupported()) {}
+// final CheckBox setFullscreen = new CheckBox("Fullscreen");
+
+ final VBox optionsBox = new VBox(10, selectLanguageLabel, selectLanguage);
+ optionsBox.setAlignment(Pos.CENTER);
+ optionsBox.setPickOnBounds(false);
+ optionsBox.setTranslateY(50);
+ optionsBox.setTranslateX(25);
+
+ final Button credits = createButton("Credits", () -> { App.push(new CreditsMenu()); });
+ final Button options = createButton("Exit Options", () -> { App.push(new MainMenu()); });
+ final Button quit = createButton("Quit", () -> { App.quitPopup(); });
+
+ final VBox controlBox = new VBox(10, credits, options, quit);
+ controlBox.setAlignment(Pos.BOTTOM_LEFT);
+ controlBox.setPickOnBounds(false);
+ controlBox.setTranslateY(-50);
+ controlBox.setTranslateX(25);
+
+ pane = new StackPane(background, optionsBox, controlBox);
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/org/toop/local/AppContext.java b/app/src/main/java/org/toop/local/AppContext.java
index 3b1bb47..1dfb20e 100644
--- a/app/src/main/java/org/toop/local/AppContext.java
+++ b/app/src/main/java/org/toop/local/AppContext.java
@@ -5,9 +5,14 @@ import java.util.Locale;
public class AppContext {
private static Locale currentLocale = Locale.getDefault();
+ public static void setLocale(Locale locale) {
+ currentLocale = locale;
+ }
+
public static void setCurrentLocale(Locale locale) {
currentLocale = locale;
}
+
public static Locale getLocale() {
return currentLocale;
}
diff --git a/app/src/main/resources/assets/localization/localization_de.properties b/app/src/main/resources/assets/localization/localization_de.properties
index dc46278..87605a9 100644
--- a/app/src/main/resources/assets/localization/localization_de.properties
+++ b/app/src/main/resources/assets/localization/localization_de.properties
@@ -14,4 +14,7 @@ mainMenuSelectQuit=Beenden
# Quit Menu text and buttons
quitMenuTextSure=Bist du sicher?
quitMenuButtonYes=Ja
-quitMenuButtonNo=Nein
\ No newline at end of file
+quitMenuButtonNo=Nein
+
+# Options menu
+optionsMenuLabelSelectLanguage=Sprache:
\ No newline at end of file
diff --git a/app/src/main/resources/assets/localization/localization.properties b/app/src/main/resources/assets/localization/localization_en_US.properties
similarity index 69%
rename from app/src/main/resources/assets/localization/localization.properties
rename to app/src/main/resources/assets/localization/localization_en_US.properties
index fcbc8ac..1a03593 100644
--- a/app/src/main/resources/assets/localization/localization.properties
+++ b/app/src/main/resources/assets/localization/localization_en_US.properties
@@ -2,8 +2,8 @@
windowTitle=ISY Games Selector
# Main Menu buttons
-mainMenuSelectTicTacToe=Tic Tac Toe\u5426
-mainMenuSelectReversi=Reversi\u5426
+mainMenuSelectTicTacToe=Tic Tac Toe
+mainMenuSelectReversi=Reversi
mainMenuSelectSudoku=Sudoku
mainMenuSelectBattleship=Battleship
mainMenuSelectOther=Other
@@ -14,4 +14,7 @@ mainMenuSelectQuit=Quit
# Quit Menu text and buttons
quitMenuTextSure=Are you sure?
quitMenuButtonYes=Yes
-quitMenuButtonNo=No
\ No newline at end of file
+quitMenuButtonNo=No
+
+# Options menu
+optionsMenuLabelSelectLanguage=Language:
\ No newline at end of file
diff --git a/app/src/main/resources/assets/localization/localization_es.properties b/app/src/main/resources/assets/localization/localization_es.properties
index 60439ab..39475fd 100644
--- a/app/src/main/resources/assets/localization/localization_es.properties
+++ b/app/src/main/resources/assets/localization/localization_es.properties
@@ -14,4 +14,7 @@ mainMenuSelectQuit=Salir
# Quit Menu text and buttons
quitMenuTextSure=\u00BFEst\u00E1s seguro?
quitMenuButtonYes=S\u00ED
-quitMenuButtonNo=No
\ No newline at end of file
+quitMenuButtonNo=No
+
+# Options menu
+optionsMenuLabelSelectLanguage=Idioma:
\ No newline at end of file
diff --git a/app/src/main/resources/assets/localization/localization_fr.properties b/app/src/main/resources/assets/localization/localization_fr.properties
index 75d9f4a..cd8a4d3 100644
--- a/app/src/main/resources/assets/localization/localization_fr.properties
+++ b/app/src/main/resources/assets/localization/localization_fr.properties
@@ -14,4 +14,7 @@ mainMenuSelectQuit=Quitter
# Quit Menu text and buttons
quitMenuTextSure=\u00CAtes-vous s\u00FBr?
quitMenuButtonYes=Oui
-quitMenuButtonNo=Non
\ No newline at end of file
+quitMenuButtonNo=Non
+
+# Options menu
+optionsMenuLabelSelectLanguage=Langue:
\ No newline at end of file
diff --git a/app/src/main/resources/assets/localization/localization_it.properties b/app/src/main/resources/assets/localization/localization_it.properties
index 75a8896..83a2160 100644
--- a/app/src/main/resources/assets/localization/localization_it.properties
+++ b/app/src/main/resources/assets/localization/localization_it.properties
@@ -14,4 +14,7 @@ mainMenuSelectQuit=Esci
# Quit Menu text and buttons
quitMenuTextSure=Sei sicuro?
quitMenuButtonYes=S\u00EC
-quitMenuButtonNo=No
\ No newline at end of file
+quitMenuButtonNo=No
+
+# Options menu
+optionsMenuLabelSelectLanguage=Lingua:
\ No newline at end of file
diff --git a/app/src/main/resources/assets/localization/localization_nl.properties b/app/src/main/resources/assets/localization/localization_nl.properties
index 4c3eb30..415fdb8 100644
--- a/app/src/main/resources/assets/localization/localization_nl.properties
+++ b/app/src/main/resources/assets/localization/localization_nl.properties
@@ -14,4 +14,7 @@ mainMenuSelectQuit=Afsluiten
# Quit Menu text and buttons
quitMenuTextSure=Weet je het zeker?
quitMenuButtonYes=Ja
-quitMenuButtonNo=Nee
\ No newline at end of file
+quitMenuButtonNo=Nee
+
+# Options menu
+optionsMenuLabelSelectLanguage=Taal:
\ No newline at end of file
diff --git a/app/src/main/resources/assets/localization/localization_zh.properties b/app/src/main/resources/assets/localization/localization_zh.properties
index f703670..7003c4c 100644
--- a/app/src/main/resources/assets/localization/localization_zh.properties
+++ b/app/src/main/resources/assets/localization/localization_zh.properties
@@ -27,4 +27,7 @@ quitMenuTextSure=\u4F60\u786E\u5B9A\u5417\uFF1F
quitMenuButtonYes=\u662F
# ?
quitMenuButtonNo=\u5426
-# ?
\ No newline at end of file
+# ?
+
+# Options menu
+optionsMenuLabelSelectLanguage=\u8BED\u8A00:
\ No newline at end of file
diff --git a/framework/src/main/java/org/toop/framework/asset/ResourceLoader.java b/framework/src/main/java/org/toop/framework/asset/ResourceLoader.java
index f8af740..4e69a50 100644
--- a/framework/src/main/java/org/toop/framework/asset/ResourceLoader.java
+++ b/framework/src/main/java/org/toop/framework/asset/ResourceLoader.java
@@ -158,6 +158,7 @@ public class ResourceLoader {
Map bundledResources = new HashMap<>();
for (File file : files) {
+ boolean skipAdd = false;
BaseResource resource = resourceMapper(file, BaseResource.class);
switch (resource) {
case null -> {
@@ -165,13 +166,11 @@ public class ResourceLoader {
}
case BundledResource br -> {
String key = resource.getClass().getName() + ":" + br.getBaseName();
- if (bundledResources.containsKey(key)) {
- bundledResources.get(key).loadFile(file);
- resource = (BaseResource) bundledResources.get(key);
- } else {
- br.loadFile(file);
- bundledResources.put(key, br);
- }
+ if (!bundledResources.containsKey(key)) {bundledResources.put(key, br);}
+ bundledResources.get(key).loadFile(file);
+ resource = (BaseResource) bundledResources.get(key);
+ assets.add(new ResourceMeta<>(br.getBaseName(), resource));
+ skipAdd = true;
}
case PreloadResource pr -> pr.load();
default -> {
@@ -181,7 +180,7 @@ public class ResourceLoader {
BaseResource finalResource = resource;
boolean alreadyAdded = assets.stream()
.anyMatch(a -> a.getResource() == finalResource);
- if (!alreadyAdded) {
+ if (!alreadyAdded && !skipAdd) {
assets.add(new ResourceMeta<>(file.getName(), resource));
}
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/LocalizationAsset.java b/framework/src/main/java/org/toop/framework/asset/resources/LocalizationAsset.java
index c2efcc1..cf18f14 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/LocalizationAsset.java
+++ b/framework/src/main/java/org/toop/framework/asset/resources/LocalizationAsset.java
@@ -8,33 +8,90 @@ import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
+/**
+ * Represents a localization resource asset that loads and manages property files
+ * containing key-value pairs for different locales.
+ *
+ * This class implements {@link LoadableResource} to support loading/unloading
+ * and {@link BundledResource} to represent resources that can contain multiple
+ * localized bundles.
+ *
+ *
+ * Files handled by this class must have the {@code .properties} extension,
+ * optionally with a locale suffix, e.g., {@code messages_en_US.properties}.
+ *
+ *
+ *
+ * Example usage:
+ *
{@code
+ * LocalizationAsset asset = new LocalizationAsset(new File("messages_en_US.properties"));
+ * asset.load();
+ * String greeting = asset.getString("hello", Locale.US);
+ * }
+ *
+ */
@FileExtension({"properties"})
public class LocalizationAsset extends BaseResource implements LoadableResource, BundledResource {
- private final Map bundles = new HashMap<>();
- private boolean isLoaded = false;
- private final Locale fallback = Locale.forLanguageTag("");
+ /** Map of loaded resource bundles, keyed by locale. */
+ private final Map bundles = new HashMap<>();
+
+ /** Flag indicating whether this asset has been loaded. */
+ private boolean isLoaded = false;
+
+ /** Basename of the given asset */
+ private final String baseName = "localization";
+
+ /** Fallback locale used when no matching locale is found. */
+ private final Locale fallback = Locale.forLanguageTag("en_US");
+
+ /**
+ * Constructs a new LocalizationAsset for the specified file.
+ *
+ * @param file the property file to load
+ */
public LocalizationAsset(File file) {
super(file);
}
+ /**
+ * Loads the resource file into memory and prepares localized bundles.
+ */
@Override
public void load() {
loadFile(getFile());
isLoaded = true;
}
+ /**
+ * Unloads all loaded resource bundles, freeing memory.
+ */
@Override
public void unload() {
bundles.clear();
isLoaded = false;
}
+ /**
+ * Checks whether this asset has been loaded.
+ *
+ * @return {@code true} if the asset is loaded, {@code false} otherwise
+ */
@Override
public boolean isLoaded() {
return isLoaded;
}
+ /**
+ * Retrieves a localized string for the given key and locale.
+ * If an exact match for the locale is not found, a fallback
+ * matching the language or the default locale will be used.
+ *
+ * @param key the key of the string
+ * @param locale the desired locale
+ * @return the localized string
+ * @throws MissingResourceException if no resource bundle is available for the locale
+ */
public String getString(String key, Locale locale) {
Locale target = findBestLocale(locale);
ResourceBundle bundle = bundles.get(target);
@@ -43,6 +100,13 @@ public class LocalizationAsset extends BaseResource implements LoadableResource,
return bundle.getString(key);
}
+ /**
+ * Finds the best matching locale among loaded bundles.
+ * Prefers an exact match, then language-only match, then fallback.
+ *
+ * @param locale the desired locale
+ * @return the best matching locale
+ */
private Locale findBestLocale(Locale locale) {
if (bundles.containsKey(locale)) return locale;
for (Locale l : bundles.keySet()) {
@@ -51,13 +115,24 @@ public class LocalizationAsset extends BaseResource implements LoadableResource,
return fallback;
}
+ /**
+ * Returns an unmodifiable set of all locales for which bundles are loaded.
+ *
+ * @return available locales
+ */
public Set getAvailableLocales() {
return Collections.unmodifiableSet(bundles.keySet());
}
+ /**
+ * Loads a specific property file as a resource bundle.
+ * The locale is extracted from the file name if present.
+ *
+ * @param file the property file to load
+ * @throws RuntimeException if the file cannot be read
+ */
@Override
public void loadFile(File file) {
- String baseName = getBaseName(file.getName());
try (InputStreamReader reader =
new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)) {
Locale locale = extractLocale(file.getName(), baseName);
@@ -68,20 +143,40 @@ public class LocalizationAsset extends BaseResource implements LoadableResource,
isLoaded = true;
}
+ /**
+ * Returns the base name of the underlying file (without locale or extension).
+ *
+ * @return the base name
+ */
@Override
public String getBaseName() {
- return getBaseName(getFile().getName());
+ return this.baseName;
}
- private String getBaseName(String fileName) {
- int underscoreIndex = fileName.indexOf('_');
- int dotIndex = fileName.lastIndexOf('.');
- if (underscoreIndex > 0) {
- return fileName.substring(0, underscoreIndex);
- }
- return fileName.substring(0, dotIndex);
- }
+// /**
+// * Extracts the base name from a file name.
+// *
+// * @param fileName the file name
+// * @return base name without locale or extension
+// */
+// private String getBaseName(String fileName) {
+// int dotIndex = fileName.lastIndexOf('.');
+// String nameWithoutExtension = (dotIndex > 0) ? fileName.substring(0, dotIndex) : fileName;
+//
+// int underscoreIndex = nameWithoutExtension.indexOf('_');
+// if (underscoreIndex > 0) {
+// return nameWithoutExtension.substring(0, underscoreIndex);
+// }
+// return nameWithoutExtension;
+// }
+ /**
+ * Extracts a locale from a file name based on the pattern "base_LOCALE.properties".
+ *
+ * @param fileName the file name
+ * @param baseName the base name
+ * @return extracted locale, or fallback if none found
+ */
private Locale extractLocale(String fileName, String baseName) {
int underscoreIndex = fileName.indexOf('_');
int dotIndex = fileName.lastIndexOf('.');
diff --git a/framework/src/main/java/org/toop/framework/asset/types/BundledResource.java b/framework/src/main/java/org/toop/framework/asset/types/BundledResource.java
index 1b7f97e..ceb0f5f 100644
--- a/framework/src/main/java/org/toop/framework/asset/types/BundledResource.java
+++ b/framework/src/main/java/org/toop/framework/asset/types/BundledResource.java
@@ -66,4 +66,9 @@ public interface BundledResource {
* @return the base name used to identify this bundled resource
*/
String getBaseName();
+
+// /**
+// Returns the name
+// */
+// String getDefaultName();
}
\ No newline at end of file