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 0faabe5..4b1b81f 100644
--- a/app/src/main/java/org/toop/app/menu/CreditsMenu.java
+++ b/app/src/main/java/org/toop/app/menu/CreditsMenu.java
@@ -11,7 +11,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() {
try {
new EventFlow()
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 40118ef..aa21709 100644
--- a/app/src/main/java/org/toop/app/menu/OptionsMenu.java
+++ b/app/src/main/java/org/toop/app/menu/OptionsMenu.java
@@ -14,10 +14,14 @@ import org.toop.framework.eventbus.EventFlow;
import org.toop.local.AppContext;
import org.toop.local.LocalizationEvents;
+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");
+
private final LocalizationAsset loc = ResourceManager.get("localization.properties");
private Text chooseLang;
private Button english,dutch,german,french,italian,spanish,chinese;
@@ -65,5 +69,65 @@ public final class OptionsMenu extends Menu {
chinese.setText(loc.getString("languageChinese",currentLocale));
});
+ }
+ 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 2e498f9..9351368 100644
--- a/app/src/main/java/org/toop/local/AppContext.java
+++ b/app/src/main/java/org/toop/local/AppContext.java
@@ -7,6 +7,10 @@ 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;
new EventFlow().addPostEvent(new LocalizationEvents.LanguageHasChanged(locale.getLanguage())).asyncPostEvent();
diff --git a/app/src/main/resources/assets/localization/localization_de.properties b/app/src/main/resources/assets/localization/localization_de.properties
index 0f5a8e1..e38762e 100644
--- a/app/src/main/resources/assets/localization/localization_de.properties
+++ b/app/src/main/resources/assets/localization/localization_de.properties
@@ -34,3 +34,7 @@ gameSelectMenuEnterIP=Gib hier deine IP-Adresse ein:
# Game Menu
gameMenuHint=Tipp
gameMenuBack=Zur\u00FCck
+
+
+# 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 82%
rename from app/src/main/resources/assets/localization/localization.properties
rename to app/src/main/resources/assets/localization/localization_en_US.properties
index 18d2c89..173d083 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
-mainMenuSelectReversi=Reversi
+mainMenuSelectTicTacToe=Tic Tac Toe\u5426
+mainMenuSelectReversi=Reversi\u5426
mainMenuSelectSudoku=Sudoku
mainMenuSelectBattleship=Battleship
mainMenuSelectOther=Other
@@ -33,4 +33,7 @@ gameSelectMenuEnterIP=Enter your IP address here:
# Game Menu
gameMenuHint=Hint
-gameMenuBack=Back
\ No newline at end of file
+gameMenuBack=Back
+
+# 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 19b27fe..c6c0648 100644
--- a/app/src/main/resources/assets/localization/localization_es.properties
+++ b/app/src/main/resources/assets/localization/localization_es.properties
@@ -34,3 +34,7 @@ gameSelectMenuEnterIP=Introduce aqu\u00ED tu direcci\u00F3n IP:
# Game Menu
gameMenuHint=Pista
gameMenuBack=Atr\u00E1s
+
+
+# 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 bb6ba7e..6b71c6e 100644
--- a/app/src/main/resources/assets/localization/localization_fr.properties
+++ b/app/src/main/resources/assets/localization/localization_fr.properties
@@ -34,3 +34,7 @@ gameSelectMenuEnterIP=Entrez votre adresse IP ici :
# Game Menu
gameMenuHint=Indice
gameMenuBack=Retour
+
+
+# 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 b89a8a1..c608a87 100644
--- a/app/src/main/resources/assets/localization/localization_it.properties
+++ b/app/src/main/resources/assets/localization/localization_it.properties
@@ -35,3 +35,7 @@ gameSelectMenuEnterIP=Inserisci qui il tuo indirizzo IP:
gameMenuHint=Suggerimento
gameMenuBack=Indietro
+
+
+# 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 90fd8b9..2f3cf82 100644
--- a/app/src/main/resources/assets/localization/localization_nl.properties
+++ b/app/src/main/resources/assets/localization/localization_nl.properties
@@ -34,3 +34,7 @@ gameSelectMenuEnterIP=Voer hier je IP-adres in:
# Game Menu
gameMenuHint=Hint
gameMenuBack=Terug
+
+
+# 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 9f09b8a..2e82178 100644
--- a/app/src/main/resources/assets/localization/localization_zh.properties
+++ b/app/src/main/resources/assets/localization/localization_zh.properties
@@ -46,3 +46,7 @@ gameSelectMenuEnterIP=\u5728\u6B64\u8F93\u5165\u4F60\u7684IP\u5730\u5740\uFF1A
# Game Menu
gameMenuHint=\u63D0\u793A
gameMenuBack=\u8FD4\u56DE
+
+
+# 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