diff --git a/src/main/java/eu/midnightdust/yaytris/Settings.java b/src/main/java/eu/midnightdust/yaytris/Settings.java index 515d465..092d70c 100644 --- a/src/main/java/eu/midnightdust/yaytris/Settings.java +++ b/src/main/java/eu/midnightdust/yaytris/Settings.java @@ -1,6 +1,7 @@ package eu.midnightdust.yaytris; import eu.midnightdust.yaytris.util.Difficulty; +import eu.midnightdust.yaytris.util.Language; import eu.midnightdust.yaytris.util.NightJson; public class Settings { @@ -11,6 +12,7 @@ public class Settings { public static float guiScale = 3.f; public static boolean shouldScaleSpeed = true; public static Difficulty difficulty = Difficulty.NORMAL; + public static Language language = Language.ENGLISH; public static void load() { json.readJson(); diff --git a/src/main/java/eu/midnightdust/yaytris/Tetris.java b/src/main/java/eu/midnightdust/yaytris/Tetris.java index 25152d7..f088fb0 100644 --- a/src/main/java/eu/midnightdust/yaytris/Tetris.java +++ b/src/main/java/eu/midnightdust/yaytris/Tetris.java @@ -25,6 +25,7 @@ public class Tetris { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception | Error e) { System.out.printf("%s: %s\n", "Error setting system look and feel", e); } Settings.load(); + Translation.load(Settings.language.locale); HighScores.load(); timer = new Timer("Tetris falling pieces"); space = new Space(); diff --git a/src/main/java/eu/midnightdust/yaytris/Translation.java b/src/main/java/eu/midnightdust/yaytris/Translation.java new file mode 100644 index 0000000..a31fe55 --- /dev/null +++ b/src/main/java/eu/midnightdust/yaytris/Translation.java @@ -0,0 +1,19 @@ +package eu.midnightdust.yaytris; + +import eu.midnightdust.yaytris.util.NightJson; + +import java.util.HashMap; +import java.util.Map; + +public class Translation { + public static Map translationMap = new HashMap<>(); + + public static void load(String locale) { + NightJson json = new NightJson(translationMap, String.format("translation/%s.json5", locale)); + json.readJson(); + } + + public static String t(String key, Object... args) { + return String.format(translationMap.getOrDefault(key, key), args); + } +} diff --git a/src/main/java/eu/midnightdust/yaytris/ui/HighScoreMenu.java b/src/main/java/eu/midnightdust/yaytris/ui/HighScoreMenu.java index 4801d42..8356b6b 100644 --- a/src/main/java/eu/midnightdust/yaytris/ui/HighScoreMenu.java +++ b/src/main/java/eu/midnightdust/yaytris/ui/HighScoreMenu.java @@ -9,6 +9,7 @@ import java.awt.*; import java.util.ArrayList; import java.util.List; +import static eu.midnightdust.yaytris.Translation.t; import static eu.midnightdust.yaytris.ui.TetrisUI.scale; public class HighScoreMenu extends JPanel { @@ -19,7 +20,7 @@ public class HighScoreMenu extends JPanel { this.setBounds(x, y, width, height); this.setLayout(null); - this.add(new JLabel("Highscores:")); + this.add(new JLabel(t("highscores.title"))); List highscores = new ArrayList<>(); for (String key : HighScores.scores.keySet()) { @@ -34,7 +35,7 @@ public class HighScoreMenu extends JPanel { this.add(highscoreScrollPane); highscoreScrollPane.setBounds(scale(60), scale(43), scale(100), scale(80)); - JButton backButton = new JButton("Zurück"); + JButton backButton = new JButton(t("ui.back")); backButton.addActionListener(ui::openMainMenu); backButton.setBounds(scale(60), scale(140), scale(100), scale(20)); this.add(backButton); diff --git a/src/main/java/eu/midnightdust/yaytris/ui/MainMenu.java b/src/main/java/eu/midnightdust/yaytris/ui/MainMenu.java index e2d7e62..3cca145 100644 --- a/src/main/java/eu/midnightdust/yaytris/ui/MainMenu.java +++ b/src/main/java/eu/midnightdust/yaytris/ui/MainMenu.java @@ -2,6 +2,8 @@ package eu.midnightdust.yaytris.ui; import javax.swing.*; +import static eu.midnightdust.yaytris.Translation.t; + public class MainMenu extends AbstractMenu { final TetrisUI ui; @@ -10,19 +12,19 @@ public class MainMenu extends AbstractMenu { this.setBounds(x, y, width, height); this.setLayout(null); - JButton startButton = new JButton("Start"); + JButton startButton = new JButton(t("menu.start")); startButton.addActionListener(ui::startGame); this.add(startButton); - JButton settingsButton = new JButton("Einstellungen"); + JButton settingsButton = new JButton(t("menu.settings")); settingsButton.addActionListener(ui::openSettings); this.add(settingsButton); - JButton highscoreButton = new JButton("Highscores"); + JButton highscoreButton = new JButton(t("menu.highscores")); highscoreButton.addActionListener(ui::openHighscores); this.add(highscoreButton); - JButton leaveButton = new JButton("Spiel verlassen"); + JButton leaveButton = new JButton(t("menu.exit")); leaveButton.addActionListener(e -> System.exit(0)); this.add(leaveButton); } diff --git a/src/main/java/eu/midnightdust/yaytris/ui/ScoreMenu.java b/src/main/java/eu/midnightdust/yaytris/ui/ScoreMenu.java index 51505c0..d13b34b 100644 --- a/src/main/java/eu/midnightdust/yaytris/ui/ScoreMenu.java +++ b/src/main/java/eu/midnightdust/yaytris/ui/ScoreMenu.java @@ -8,6 +8,7 @@ import java.awt.*; import java.time.LocalTime; import java.time.format.DateTimeFormatter; +import static eu.midnightdust.yaytris.Translation.t; import static eu.midnightdust.yaytris.ui.TetrisUI.scale; public class ScoreMenu extends AbstractMenu { @@ -32,22 +33,22 @@ public class ScoreMenu extends AbstractMenu { this.gameOverLabel = new JLabel(); this.add(gameOverLabel); - this.currentScoreLabel = new JLabel("Score: 0"); + this.currentScoreLabel = new JLabel(t("game.score", "0")); this.add(currentScoreLabel); - this.currentLevelLabel = new JLabel("Level: 0"); + this.currentLevelLabel = new JLabel(t("game.level", "0")); this.add(currentLevelLabel); - this.currentTimeLabel = new JLabel("Zeit: 00:00"); + this.currentTimeLabel = new JLabel(t("game.time", "00:00")); this.add(currentTimeLabel); - JButton backButton = new JButton("Zurück"); + JButton backButton = new JButton(t("ui.back")); backButton.addActionListener(ui::openMainMenu); this.add(backButton); } public void updateScore(int score) { - this.currentScoreLabel.setText(String.format("Score: %s", score)); + this.currentScoreLabel.setText(t("game.score", score)); this.repaint(); } @@ -55,16 +56,16 @@ public class ScoreMenu extends AbstractMenu { LocalTime timeElapsed = LocalTime.ofNanoOfDay(LocalTime.now().toNanoOfDay() - startingTime.toNanoOfDay()); DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern(timeElapsed.getHour() > 0 ? "HH:mm:ss" : "mm:ss"); - this.currentTimeLabel.setText(String.format("Zeit: %s", timeFormatter.format(timeElapsed))); + this.currentTimeLabel.setText(t("game.time", timeFormatter.format(timeElapsed))); this.repaint(); } public void gameOver() { - this.gameOverLabel.setText("Game over :("); + this.gameOverLabel.setText(t("game.over")); } public void updateLevel(int level) { - this.currentLevelLabel.setText(String.format("Level: %s", level)); + this.currentLevelLabel.setText(t("game.level", level)); this.repaint(); } diff --git a/src/main/java/eu/midnightdust/yaytris/ui/SettingsMenu.java b/src/main/java/eu/midnightdust/yaytris/ui/SettingsMenu.java index db15ac5..f2a952f 100644 --- a/src/main/java/eu/midnightdust/yaytris/ui/SettingsMenu.java +++ b/src/main/java/eu/midnightdust/yaytris/ui/SettingsMenu.java @@ -1,11 +1,14 @@ package eu.midnightdust.yaytris.ui; import eu.midnightdust.yaytris.Settings; +import eu.midnightdust.yaytris.Translation; import eu.midnightdust.yaytris.util.Difficulty; +import eu.midnightdust.yaytris.util.Language; import javax.swing.*; import java.awt.*; +import static eu.midnightdust.yaytris.Translation.t; import static eu.midnightdust.yaytris.ui.TetrisUI.scale; import static eu.midnightdust.yaytris.ui.TetrisUI.setFontScale; @@ -17,7 +20,7 @@ public class SettingsMenu extends JPanel { this.setBounds(x, y, width, height); this.setLayout(null); - this.add(new JLabel("Musik-Lautstärke:")); + this.add(new JLabel(t("settings.volume.music"))); JSlider musicVolumeSlider = new JSlider(0, 100, Settings.musicVolume); musicVolumeSlider.addChangeListener(change -> { Settings.musicVolume = musicVolumeSlider.getValue(); @@ -25,7 +28,7 @@ public class SettingsMenu extends JPanel { }); this.add(musicVolumeSlider); - this.add(new JLabel("Effekt-Lautstärke:")); + this.add(new JLabel(t("settings.volume.sound"))); JSlider soundVolumeSlider = new JSlider(0, 100, Settings.soundVolume); soundVolumeSlider.addChangeListener(change -> { Settings.soundVolume = soundVolumeSlider.getValue(); @@ -33,7 +36,7 @@ public class SettingsMenu extends JPanel { }); this.add(soundVolumeSlider); - this.add(new JLabel("Fenster-Skalierung:")); + this.add(new JLabel(t("settings.window_scaling"))); JSlider scaleSlider = new JSlider(100, 600, (int) (Settings.guiScale * 100)); scaleSlider.addChangeListener(change -> { Settings.guiScale = scaleSlider.getValue() / 100f; @@ -41,7 +44,7 @@ public class SettingsMenu extends JPanel { }); this.add(scaleSlider); - this.add(new JLabel("Geschwindigkeitszunahme:")); + this.add(new JLabel(t("settings.scale_speed"))); JCheckBox speedCheckbox = new JCheckBox("Aktiviert", Settings.shouldScaleSpeed); speedCheckbox.addChangeListener(change -> { Settings.shouldScaleSpeed = speedCheckbox.isSelected(); @@ -49,7 +52,7 @@ public class SettingsMenu extends JPanel { }); this.add(speedCheckbox); - this.add(new JLabel("Schwierigkeit:")); + this.add(new JLabel(t("settings.difficulty"))); JComboBox difficultySelector = new JComboBox<>(Difficulty.values()); difficultySelector.setSelectedItem(Settings.difficulty); @@ -60,7 +63,20 @@ public class SettingsMenu extends JPanel { this.add(difficultySelector); difficultySelector.setSize(difficultySelector.getBounds().width,scale(10)); - JButton backButton = new JButton("Zurück"); + this.add(new JLabel(t("settings.language"))); + JComboBox languageSelector = new JComboBox<>(Language.values()); + languageSelector.setSelectedItem(Settings.language); + + languageSelector.addActionListener(action -> { + Settings.language = (Language) languageSelector.getSelectedItem(); + Settings.write(); + Translation.load(Settings.language.locale); + ui.openSettings(action); + }); + this.add(languageSelector); + languageSelector.setSize(languageSelector.getBounds().width,scale(10)); + + JButton backButton = new JButton(t("ui.back")); backButton.addActionListener(ui::openMainMenu); this.add(backButton); } @@ -68,9 +84,9 @@ public class SettingsMenu extends JPanel { @Override public Component add(Component comp) { - comp.setBounds(scale(60), scale(20+23*this.getComponentCount()-labelAmount*10), scale(100), scale(20)); + comp.setBounds(scale(60), scale(20+17*this.getComponentCount()-labelAmount*10), scale(100), scale(20)); if (comp instanceof JLabel) { - comp.setBounds(scale(60), scale(50+23*(this.getComponentCount()-1)-labelAmount*10), scale(100), scale(7)); + comp.setBounds(scale(60), scale(20+17*(this.getComponentCount())-labelAmount*10), scale(100), scale(7)); labelAmount++; } if (comp instanceof JComponent) setFontScale((JComponent) comp); diff --git a/src/main/java/eu/midnightdust/yaytris/ui/TetrisUI.java b/src/main/java/eu/midnightdust/yaytris/ui/TetrisUI.java index 40056f1..ecc07dc 100644 --- a/src/main/java/eu/midnightdust/yaytris/ui/TetrisUI.java +++ b/src/main/java/eu/midnightdust/yaytris/ui/TetrisUI.java @@ -14,6 +14,7 @@ import java.awt.event.KeyListener; import java.io.IOException; import static eu.midnightdust.yaytris.Settings.guiScale; +import static eu.midnightdust.yaytris.Translation.t; public class TetrisUI extends JFrame implements KeyListener { JLabel titleLabel; @@ -125,7 +126,7 @@ public class TetrisUI extends JFrame implements KeyListener { } public void showHighscoreDialog(int score) { - String playerName = JOptionPane.showInputDialog(null, "Gib deinen Namen ein:", "Neuer Highscore!", JOptionPane.PLAIN_MESSAGE); + String playerName = JOptionPane.showInputDialog(null, t("dialog.highscore.action"), t("dialog.highscore.title"), JOptionPane.PLAIN_MESSAGE); HighScores.addScore(playerName, score); } diff --git a/src/main/java/eu/midnightdust/yaytris/util/Language.java b/src/main/java/eu/midnightdust/yaytris/util/Language.java new file mode 100644 index 0000000..ec472c6 --- /dev/null +++ b/src/main/java/eu/midnightdust/yaytris/util/Language.java @@ -0,0 +1,17 @@ +package eu.midnightdust.yaytris.util; + +public enum Language { + ENGLISH("en_gb"), DEUTSCH("de_de"); + public final String locale; + private final String name; + + Language(String locale) { + this.locale = locale; + this.name = this.name().charAt(0) + this.name().substring(1).toLowerCase(); + } + + @Override + public String toString() { + return this.name; + } +} diff --git a/src/main/java/eu/midnightdust/yaytris/util/NightJson.java b/src/main/java/eu/midnightdust/yaytris/util/NightJson.java index 0d0be4b..39a6dc4 100644 --- a/src/main/java/eu/midnightdust/yaytris/util/NightJson.java +++ b/src/main/java/eu/midnightdust/yaytris/util/NightJson.java @@ -9,19 +9,24 @@ import java.util.*; import java.util.concurrent.atomic.AtomicReference; /* - NightJson v0.1 by Martin Prokoph + NightJson v0.2 by Martin Prokoph Extremely lightweight (and incomplete) JSON library Concept inspired by GSON */ public class NightJson { - private static final String KEY_PATTERN = "\"(-?[0-9a-zA-Z]*)\":"; + private static final String KEY_PATTERN = "\"(-?.*)\":"; Class jsonClass; + Map jsonMap; String fileLocation; public NightJson(Class jsonClass, String fileLocation) { this.jsonClass = jsonClass; this.fileLocation = fileLocation; } + public NightJson(Map jsonMap, String fileLocation) { + this.jsonMap = jsonMap; + this.fileLocation = fileLocation; + } public void writeJson() { if (fileLocation == null) return; @@ -29,17 +34,22 @@ public class NightJson { FileWriter jsonFile = new FileWriter(fileLocation); jsonFile.write("{\n"); - Iterator it = Arrays.stream(jsonClass.getFields()).iterator(); - while (it.hasNext()) { - Field field = it.next(); - jsonFile.write("\t"); - if (field.getType() == Comment.class) { - jsonFile.write(String.format("// %s\n", ((Comment) field.get(null)).commentString)); - continue; + if (jsonClass != null) { + Iterator it = Arrays.stream(jsonClass.getFields()).iterator(); + while (it.hasNext()) { + Field field = it.next(); + writeElement(jsonFile, field.get(null), field.getType(), field.getName()); + jsonFile.write(it.hasNext() ? ",\n" : "\n"); + } + } + else if (jsonMap != null) { + Iterator it = jsonMap.keySet().iterator(); + while (it.hasNext()) { + String key = it.next(); + Object value = jsonMap.get(key); + writeElement(jsonFile, jsonMap.get(key), value.getClass(), key); + jsonFile.write(it.hasNext() ? ",\n" : "\n"); } - jsonFile.write(String.format("\"%s\": ", field.getName())); - jsonFile.write(objToString(field.get(null), field.getType())); - jsonFile.write(it.hasNext() ? ",\n" : "\n"); } jsonFile.write("}"); jsonFile.close(); @@ -49,6 +59,16 @@ public class NightJson { } } + private void writeElement(FileWriter jsonFile, Object value, Class type, String name) throws IOException, IllegalAccessException { + jsonFile.write("\t"); + if (type == Comment.class) { + jsonFile.write(String.format("// %s\n", ((Comment) value).commentString)); + return; + } + jsonFile.write(String.format("\"%s\": ", name)); + jsonFile.write(objToString(value, type)); + } + public void readJson() { if (fileLocation == null) return; try { @@ -80,11 +100,20 @@ public class NightJson { for (String key : jsonKeyValuePairs.keySet()) { String currentString = jsonKeyValuePairs.get(key); //System.out.printf("Key: %s Value: %s%n", key, currentString); - Field field; - try { field = jsonClass.getField(key); - } catch (NoSuchFieldException e) {continue;} + if (jsonClass != null) { + Field field; + try { + field = jsonClass.getField(key); + } catch (NoSuchFieldException e) { + continue; + } - field.set(field, stringToFieldObj(currentString, field)); + field.set(field, stringToFieldObj(currentString, field)); + } + else if (jsonMap != null) { + //noinspection unchecked + ((Map)jsonMap).put(key, stringToObj(currentString, String.class)); // TODO: Un-hardcode string type + } } jsonFile.close(); } catch (IOException | IllegalAccessException | NoSuchElementException e) { @@ -93,7 +122,7 @@ public class NightJson { } } - private String objToString(Object value, Class type) throws IllegalAccessException { + private String objToString(Object value, Class type) { if (type == Map.class) { StringBuilder mapPairs = new StringBuilder(); Map map = (Map) value; diff --git a/tetris_scores.json5 b/tetris_scores.json5 index 61ea884..d35a8e2 100644 --- a/tetris_scores.json5 +++ b/tetris_scores.json5 @@ -1,3 +1,3 @@ { - "scores": {"Martin": 5703} + "scores": {"Martin": 6401} } \ No newline at end of file diff --git a/tetris_settings.json5 b/tetris_settings.json5 index b25b1c3..de14924 100644 --- a/tetris_settings.json5 +++ b/tetris_settings.json5 @@ -1,7 +1,8 @@ { - "musicVolume": 19, + "musicVolume": 100, "soundVolume": 100, "guiScale": 6.0, "shouldScaleSpeed": true, - "difficulty": "Normal" + "difficulty": "Normal", + "language": "Deutsch" } \ No newline at end of file diff --git a/translation/de_de.json5 b/translation/de_de.json5 new file mode 100644 index 0000000..06a8a04 --- /dev/null +++ b/translation/de_de.json5 @@ -0,0 +1,20 @@ +{ + "dialog.highscore.action": "Gib deinen Namen ein:", + "dialog.highscore.title": "Neuer Highscore!", + "game.level": "Level: %s", + "game.over": "Game over :(", + "game.score": "Score: %s", + "game.time": "Zeit: %s", + "highscores.title": "Highscores:", + "menu.exit": "Spiel verlassen", + "menu.highscores": "Highscores", + "menu.settings": "Einstellungen", + "menu.start": "Start", + "settings.difficulty": "Schwierigkeit:", + "settings.language": "Sprache:", + "settings.scale_speed": "Geschwindigkeitszunahme:", + "settings.volume.music": "Musik-Lautstärke:", + "settings.volume.sound": "Effekt-Lautstärke:", + "settings.window_scaling": "Fenster-Skalierung:", + "ui.back": "Zurück" +} \ No newline at end of file diff --git a/translation/en_gb.json5 b/translation/en_gb.json5 new file mode 100644 index 0000000..2c51ba1 --- /dev/null +++ b/translation/en_gb.json5 @@ -0,0 +1,20 @@ +{ + "dialog.highscore.action": "Enter your name:", + "dialog.highscore.title": "New Highscore!", + "game.level": "Level: %s", + "game.over": "Game over :(", + "game.score": "Score: %s", + "game.time": "Time: %s", + "highscores.title": "Highscores:", + "menu.exit": "Exit Game", + "menu.highscores": "Highscores", + "menu.settings": "Settings", + "menu.start": "Start", + "settings.difficulty": "Difficulty:", + "settings.language": "Language:", + "settings.scale_speed": "Speed Increase:", + "settings.volume.music": "Music Volume:", + "settings.volume.sound": "Sound Volume:", + "settings.window_scaling": "Window Scaling:", + "ui.back": "Back" +} \ No newline at end of file