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