diff --git a/client/src/main/java/client/Main.java b/client/src/main/java/client/Main.java index 6b9db4f..e722b2d 100644 --- a/client/src/main/java/client/Main.java +++ b/client/src/main/java/client/Main.java @@ -53,6 +53,6 @@ public class Main extends Application { var addStep = FXML.load(AddStepsCtrl.class, "client", "scenes", "AddSteps.fxml"); var mainCtrl = INJECTOR.getInstance(MainCtrl.class); - mainCtrl.initialize(primaryStage, addName, foodpal, addIngredient, addStep); + mainCtrl.setup(primaryStage, addName, foodpal, addIngredient, addStep); } } \ No newline at end of file diff --git a/client/src/main/java/client/MyModule.java b/client/src/main/java/client/MyModule.java index df14aa2..f0d95ed 100644 --- a/client/src/main/java/client/MyModule.java +++ b/client/src/main/java/client/MyModule.java @@ -16,6 +16,7 @@ package client; import client.scenes.FoodpalApplicationCtrl; +import client.utils.LocaleManager; import com.google.inject.Binder; import com.google.inject.Module; import com.google.inject.Scopes; @@ -30,5 +31,7 @@ public class MyModule implements Module { binder.bind(MainCtrl.class).in(Scopes.SINGLETON); binder.bind(AddNameCtrl.class).in(Scopes.SINGLETON); binder.bind(FoodpalApplicationCtrl.class).in(Scopes.SINGLETON); + + binder.bind(LocaleManager.class).in(Scopes.SINGLETON); } } \ No newline at end of file diff --git a/client/src/main/java/client/scenes/AddIngredientCtrl.java b/client/src/main/java/client/scenes/AddIngredientCtrl.java index 11fcb25..70d6924 100644 --- a/client/src/main/java/client/scenes/AddIngredientCtrl.java +++ b/client/src/main/java/client/scenes/AddIngredientCtrl.java @@ -15,33 +15,61 @@ */ package client.scenes; +import client.utils.LocaleAware; +import client.utils.LocaleManager; import client.utils.ServerUtils; import com.google.inject.Inject; import commons.Recipe; import jakarta.ws.rs.WebApplicationException; import javafx.fxml.FXML; import javafx.scene.control.Alert; +import javafx.scene.control.Button; +import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.input.KeyEvent; import javafx.stage.Modality; import java.io.IOException; -public class AddIngredientCtrl { +public class AddIngredientCtrl implements LocaleAware { private final ServerUtils server; private final MainCtrl mainCtrl; private final FoodpalApplicationCtrl foodpalCtrl; + private final LocaleManager localeManager; + @FXML public TextField ingredient; + @FXML + public Button okButton; + + @FXML + public Button cancelButton; + + @FXML + public Label ingredientLabel; @Inject - public AddIngredientCtrl(ServerUtils server, MainCtrl mainCtrl, FoodpalApplicationCtrl foodpalCtrl) { + public AddIngredientCtrl(ServerUtils server, MainCtrl mainCtrl, + FoodpalApplicationCtrl foodpalCtrl, LocaleManager localeManager) { this.mainCtrl = mainCtrl; this.server = server; this.foodpalCtrl = foodpalCtrl; + this.localeManager = localeManager; + } + + @Override + public void updateText() { + ingredientLabel.setText(getLocaleString("add.ingredient.label")); + okButton.setText(getLocaleString("button.ok")); + cancelButton.setText(getLocaleString("button.cancel")); + } + + @Override + public LocaleManager getLocaleManager() { + return localeManager; } public void cancel() throws IOException, InterruptedException { @@ -83,4 +111,5 @@ public class AddIngredientCtrl { break; } } + } \ No newline at end of file diff --git a/client/src/main/java/client/scenes/AddNameCtrl.java b/client/src/main/java/client/scenes/AddNameCtrl.java index 354c653..73f86e6 100644 --- a/client/src/main/java/client/scenes/AddNameCtrl.java +++ b/client/src/main/java/client/scenes/AddNameCtrl.java @@ -15,28 +15,58 @@ */ package client.scenes; +import client.utils.LocaleAware; +import client.utils.LocaleManager; import client.utils.ServerUtils; import com.google.inject.Inject; import jakarta.ws.rs.WebApplicationException; +import javafx.fxml.FXML; import javafx.scene.control.Alert; +import javafx.scene.control.Button; +import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.input.KeyEvent; import javafx.stage.Modality; import java.io.IOException; -public class AddNameCtrl { +public class AddNameCtrl implements LocaleAware { private final ServerUtils server; private final MainCtrl mainCtrl; + + private final LocaleManager localeManager; + + @FXML public TextField recipeName; + @FXML + public Label recipeNameLabel; + + @FXML + public Button cancelButton; + + @FXML + public Button okButton; @Inject - public AddNameCtrl(ServerUtils server, MainCtrl mainCtrl) { + public AddNameCtrl(ServerUtils server, MainCtrl mainCtrl, LocaleManager localeManager) { this.mainCtrl = mainCtrl; this.server = server; + this.localeManager = localeManager; + } + + @Override + public void updateText() { + recipeNameLabel.setText(getLocaleString("add.recipe.label")); + okButton.setText(getLocaleString("button.ok")); + cancelButton.setText(getLocaleString("button.cancel")); + } + + @Override + public LocaleManager getLocaleManager() { + return localeManager; } public void cancel() throws IOException, InterruptedException { @@ -78,4 +108,5 @@ public class AddNameCtrl { break; } } + } \ No newline at end of file diff --git a/client/src/main/java/client/scenes/AddStepsCtrl.java b/client/src/main/java/client/scenes/AddStepsCtrl.java index e042f66..abd9f51 100644 --- a/client/src/main/java/client/scenes/AddStepsCtrl.java +++ b/client/src/main/java/client/scenes/AddStepsCtrl.java @@ -15,33 +15,60 @@ */ package client.scenes; +import client.utils.LocaleAware; +import client.utils.LocaleManager; import client.utils.ServerUtils; import com.google.inject.Inject; import commons.Recipe; import jakarta.ws.rs.WebApplicationException; import javafx.fxml.FXML; import javafx.scene.control.Alert; +import javafx.scene.control.Button; +import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.input.KeyEvent; import javafx.stage.Modality; import java.io.IOException; -public class AddStepsCtrl { +public class AddStepsCtrl implements LocaleAware { private final ServerUtils server; private final MainCtrl mainCtrl; private final FoodpalApplicationCtrl foodpalCtrl; + private final LocaleManager localeManager; @FXML public TextField preparationStep; + @FXML + public Button okButton; + + @FXML + public Button cancelButton; + + @FXML + public Label preparationStepLabel; @Inject - public AddStepsCtrl(ServerUtils server, MainCtrl mainCtrl, FoodpalApplicationCtrl foodpalCtrl) { + public AddStepsCtrl(ServerUtils server, MainCtrl mainCtrl, + FoodpalApplicationCtrl foodpalCtrl, LocaleManager localeManager) { this.mainCtrl = mainCtrl; this.server = server; this.foodpalCtrl = foodpalCtrl; + this.localeManager = localeManager; + } + + @Override + public void updateText() { + preparationStepLabel.setText(getLocaleString("add.step.label")); + okButton.setText(getLocaleString("button.ok")); + cancelButton.setText(getLocaleString("button.cancel")); + } + + @Override + public LocaleManager getLocaleManager() { + return localeManager; } public void cancel() throws IOException, InterruptedException { @@ -83,4 +110,5 @@ public class AddStepsCtrl { break; } } + } \ No newline at end of file diff --git a/client/src/main/java/client/scenes/FoodpalApplicationCtrl.java b/client/src/main/java/client/scenes/FoodpalApplicationCtrl.java index bc39741..e2b8abf 100644 --- a/client/src/main/java/client/scenes/FoodpalApplicationCtrl.java +++ b/client/src/main/java/client/scenes/FoodpalApplicationCtrl.java @@ -2,14 +2,20 @@ package client.scenes; import java.io.IOException; +import java.util.Collections; import java.util.List; +import java.util.Locale; import client.exception.UpdateException; import client.utils.DefaultRecipeFactory; +import client.utils.LocaleAware; +import client.utils.LocaleManager; import client.utils.ServerUtils; + import commons.Recipe; import jakarta.inject.Inject; +import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.control.Alert; import javafx.scene.control.Button; @@ -21,9 +27,10 @@ import javafx.scene.input.KeyCode; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; -public class FoodpalApplicationCtrl { +public class FoodpalApplicationCtrl implements LocaleAware { private final MainCtrl mainCtrl; private final ServerUtils server; + private final LocaleManager localeManager; public VBox detailsScreen; public HBox editableTitleArea; @@ -40,19 +47,10 @@ public class FoodpalApplicationCtrl { @FXML private Button flagPlButton; //already here for advanced stuff - @FXML - private Button refreshButton; - - @FXML - private Button closeButton; //already here for advanced stuff - - @FXML - private Button maximizeButton; //already here for advanced stuff - - @FXML - private Button minimizeButton; //already here for advanced stuff - // everything in the left lane + @FXML + public Label recipesLabel; + @FXML private ListView recipeList; @@ -62,13 +60,26 @@ public class FoodpalApplicationCtrl { @FXML private Button removeRecipeButton; - // === CENTER: RECIPE DETAILS === @FXML - private Label recipeNameLabel; + public Button cloneRecipeButton; + + // === CENTER: RECIPE DETAILS === @FXML private Button editRecipeTitleButton; + @FXML + public Button removeRecipeButton2; + + @FXML + public Button printRecipeButton; + + @FXML + public Label ingredientsLabel; + + @FXML + public Label preparationLabel; + @FXML private ListView ingredientsListView; @@ -82,13 +93,14 @@ public class FoodpalApplicationCtrl { private Button addPreparationStepButton; @Inject - public FoodpalApplicationCtrl(MainCtrl mainCtrl, ServerUtils server) { + public FoodpalApplicationCtrl(MainCtrl mainCtrl, ServerUtils server, LocaleManager localeManager) { this.mainCtrl = mainCtrl; this.server = server; + this.localeManager = localeManager; } - @FXML - private void initialize() throws IOException, InterruptedException { + @Override + public void initializeComponents() { // Show recipe name in the list recipeList.setCellFactory(list -> new ListCell<>() { @Override @@ -117,20 +129,51 @@ public class FoodpalApplicationCtrl { editableTitleArea.getChildren().clear(); editableTitleArea.getChildren().add(new Label(name)); } - // till the all the code from everyone is implemented for now to not have errors - private void showRecipeDetails(Recipe newRecipe) { - showName(newRecipe.getName()); - ingredientsListView.getItems().setAll(newRecipe.getIngredients()); - preparationListView.getItems().setAll(newRecipe.getPreparationSteps()); + @Override + public void updateText() { + addRecipeButton.setText(getLocaleString("menu.button.add.recipe")); + removeRecipeButton.setText(getLocaleString("menu.button.remove.recipe")); + cloneRecipeButton.setText(getLocaleString("menu.button.clone")); + editRecipeTitleButton.setText(getLocaleString("menu.button.edit")); + removeRecipeButton2.setText(getLocaleString("menu.button.remove.recipe")); + printRecipeButton.setText(getLocaleString("menu.button.print")); + + recipesLabel.setText(getLocaleString("menu.label.recipes")); + ingredientsLabel.setText(getLocaleString("menu.label.ingredients")); + preparationLabel.setText(getLocaleString("menu.label.preparation")); + + addIngredientButton.setText(getLocaleString("menu.button.add.ingredient")); + addPreparationStepButton.setText(getLocaleString("menu.button.add.step")); + } + + @Override + public LocaleManager getLocaleManager() { + return localeManager; + } + + private void showRecipeDetails(Recipe recipe) { + if (recipe == null) { + ingredientsListView.getItems().clear(); + preparationListView.getItems().clear(); + return; + } + showName(recipe.getName()); + ingredientsListView.getItems().setAll(recipe.getIngredients()); + preparationListView.getItems().setAll(recipe.getPreparationSteps()); } // Button handlers - @FXML - public void refresh() throws IOException, InterruptedException { - // TODO: someone else doing this - List recipes = server.getRecipes(); + public void refresh() { + List recipes; + try { + recipes = server.getRecipes(); + } catch (IOException | InterruptedException e) { + recipes = Collections.emptyList(); + System.err.println("Failed to load recipes: " + e.getMessage()); + } + recipeList.getItems().setAll(recipes); // Select first recipe in the list by default @@ -144,7 +187,6 @@ public class FoodpalApplicationCtrl { alert.setContentText(msg); alert.showAndWait(); } - /** * Adds a recipe, by providing a default name "Untitled recipe (n)" * The UX flow is now: @@ -252,22 +294,12 @@ public class FoodpalApplicationCtrl { // Language buttons @FXML - private void switchToEnglish() { - System.out.println("Switch language to EN"); + private void switchLocale(ActionEvent event) { + Button button = (Button)event.getSource(); + String lang = (String)button.getUserData(); + localeManager.setLocale(Locale.of(lang)); } - @FXML - private void switchToDutch() { - System.out.println("Switch language to NL"); - } - - @FXML - private void switchToPolish() { - System.out.println("Switch language to PL"); - } - - - //without caused errors @FXML private void makePrintable() { System.out.println("Recipe printed"); @@ -294,6 +326,7 @@ public class FoodpalApplicationCtrl { refresh(); } + } diff --git a/client/src/main/java/client/scenes/MainCtrl.java b/client/src/main/java/client/scenes/MainCtrl.java index 018eec1..0ad2f49 100644 --- a/client/src/main/java/client/scenes/MainCtrl.java +++ b/client/src/main/java/client/scenes/MainCtrl.java @@ -15,6 +15,10 @@ */ package client.scenes; +import client.utils.LocaleAware; +import client.utils.LocaleManager; +import jakarta.inject.Inject; +import javafx.fxml.FXML; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; @@ -22,28 +26,42 @@ import javafx.util.Pair; import java.io.IOException; -public class MainCtrl { +public class MainCtrl implements LocaleAware { + private final LocaleManager localeManager; + @FXML private Stage primaryStage; + @FXML private AddNameCtrl addNameCtrl; private Scene addName; + @FXML private FoodpalApplicationCtrl foodpalCtrl; private Scene foodpal; + @FXML private AddIngredientCtrl addIngredientCtrl; private Scene addIngredient; + @FXML private AddStepsCtrl addStepsCtrl; private Scene addStep; - public void initialize(Stage primaryStage, - Pair addName, - Pair foodpal, - Pair addIngredient, - Pair addStep - ) throws IOException, InterruptedException { + private String addNameTitle = "add.name.title"; + private String addIngredientTitle = "add.step.title"; + private String addStepTitle = "add.ingredient.title"; + + @Inject + public MainCtrl(LocaleManager localeManager) { + this.localeManager = localeManager; + } + + public void setup(Stage primaryStage, + Pair addName, + Pair foodpal, + Pair addIngredient, + Pair addStep) throws IOException, InterruptedException { this.primaryStage = primaryStage; @@ -59,14 +77,26 @@ public class MainCtrl { this.addStepsCtrl = addStep.getKey(); this.addStep = new Scene(addStep.getValue()); - showFoodpal(); primaryStage.show(); + + initialize(); // Initialize LocaleManager stuff manually since MainCtrl isn't loaded from FXML. } + @Override + public void updateText() { + addNameTitle = getLocaleString("add.recipe.title"); + addStepTitle = getLocaleString("add.step.title"); + addIngredientTitle = getLocaleString("add.ingredient.title"); + } + + @Override + public LocaleManager getLocaleManager() { + return localeManager; + } public void showAddName() { - primaryStage.setTitle("Naming recipes"); + primaryStage.setTitle(addNameTitle); primaryStage.setScene(addName); addName.setOnKeyPressed(e -> { try { @@ -77,14 +107,14 @@ public class MainCtrl { }); } - public void showFoodpal() throws IOException, InterruptedException { + public void showFoodpal() { primaryStage.setTitle("FoodPal"); primaryStage.setScene(foodpal); foodpalCtrl.refresh(); } public void showAddIngredient() { - primaryStage.setTitle("To add ingredients"); + primaryStage.setTitle(addIngredientTitle); primaryStage.setScene(addIngredient); addIngredient.setOnKeyPressed(e -> { try { @@ -97,7 +127,7 @@ public class MainCtrl { } public void showAddSteps() { - primaryStage.setTitle("To add preparation steps"); + primaryStage.setTitle(addStepTitle); primaryStage.setScene(addStep); addStep.setOnKeyPressed(e -> { try { diff --git a/client/src/main/java/client/utils/LocaleAware.java b/client/src/main/java/client/utils/LocaleAware.java new file mode 100644 index 0000000..c5bf9d6 --- /dev/null +++ b/client/src/main/java/client/utils/LocaleAware.java @@ -0,0 +1,42 @@ +package client.utils; + +import javafx.fxml.Initializable; +import java.net.URL; +import java.util.ResourceBundle; + +public interface LocaleAware extends Initializable { + + /** + * Updates all text when locale changes. + * Must be implemented by each controller. + */ + void updateText(); + + /** + * Returns the injected LocaleManager. + * Must be implemented by each controller. + */ + LocaleManager getLocaleManager(); + + @Override + default void initialize(URL location, ResourceBundle resources) { + updateText(); + + getLocaleManager().getBundleProperty().addListener((_, _, _) -> updateText()); + + initializeComponents(); + } + + default void initialize() { + initialize(null, null); + } + + default String getLocaleString(String key) { + return getLocaleManager().getBundle().getString(key); + } + + /** + * Optional function for initialization of components. Runs after localization. + */ + default void initializeComponents() {} +} diff --git a/client/src/main/java/client/utils/LocaleManager.java b/client/src/main/java/client/utils/LocaleManager.java new file mode 100644 index 0000000..edf342a --- /dev/null +++ b/client/src/main/java/client/utils/LocaleManager.java @@ -0,0 +1,47 @@ +package client.utils; + +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import java.util.Locale; +import java.util.ResourceBundle; + +public class LocaleManager { + private final ObjectProperty currentLocale = new SimpleObjectProperty<>(Locale.ENGLISH); + private final ObjectProperty currentBundle = new SimpleObjectProperty<>(); + + private static final String RESOURCE_BUNDLE_PATH = "locale/lang"; + + public LocaleManager() { + // TODO: Set currentLocale to config value instead of EN default. + updateBundle(); + currentLocale.addListener((_, _, _) -> updateBundle()); + } + + private void updateBundle() { + ResourceBundle bundle = ResourceBundle.getBundle(RESOURCE_BUNDLE_PATH, + currentLocale.get(), + ResourceBundle.Control.getControl(ResourceBundle.Control.FORMAT_PROPERTIES) + ); + currentBundle.set(bundle); + } + + @SuppressWarnings("unused") + public void setLocale(Locale locale) { + currentLocale.set(locale); + } + + @SuppressWarnings("unused") + public Locale getLocale() { + return currentLocale.get(); + } + + @SuppressWarnings("unused") + public ResourceBundle getBundle() { + return currentBundle.get(); + } + + @SuppressWarnings("unused") + public ObjectProperty getBundleProperty() { + return currentBundle; + } +} \ No newline at end of file diff --git a/client/src/main/resources/client/scenes/AddIngredient.fxml b/client/src/main/resources/client/scenes/AddIngredient.fxml index ebb0970..5aaddde 100644 --- a/client/src/main/resources/client/scenes/AddIngredient.fxml +++ b/client/src/main/resources/client/scenes/AddIngredient.fxml @@ -7,9 +7,9 @@ -