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