From cfaf4051b75362adf081edc6b3a8bc8f85766d27 Mon Sep 17 00:00:00 2001 From: rtm516 Date: Mon, 6 Jul 2020 00:35:51 +0100 Subject: [PATCH] Add Translation support (#504) Adds full multi-language support to any Bedrock-supported language. Co-authored-by: DoctorMacc --- .gitmodules | 5 +- .../bungeecord/GeyserBungeePlugin.java | 5 +- .../command/GeyserBungeeCommandExecutor.java | 11 +- .../platform/spigot/GeyserSpigotPlugin.java | 9 +- .../command/GeyserSpigotCommandExecutor.java | 12 +- .../platform/sponge/GeyserSpongePlugin.java | 5 +- .../command/GeyserSpongeCommandExecutor.java | 7 +- .../standalone/GeyserStandaloneBootstrap.java | 3 +- .../platform/standalone/LoopbackUtil.java | 9 +- .../standalone/gui/GeyserStandaloneGUI.java | 35 +-- .../velocity/GeyserVelocityPlugin.java | 5 +- .../GeyserVelocityCommandExecutor.java | 4 +- common/src/main/resources/help.txt | 18 -- .../connector/FloodgateKeyLoader.java | 7 +- .../geysermc/connector/GeyserConnector.java | 23 +- .../connector/command/CommandManager.java | 17 +- .../command/defaults/DumpCommand.java | 19 +- .../command/defaults/HelpCommand.java | 14 +- .../command/defaults/ListCommand.java | 11 +- .../command/defaults/ReloadCommand.java | 15 +- .../connector/common/main/IGeyserMain.java | 49 ++++- .../configuration/GeyserConfiguration.java | 6 +- .../network/ConnectorServerEventHandler.java | 7 +- .../network/UpstreamPacketHandler.java | 13 +- .../network/session/GeyserSession.java | 32 +-- .../network/translators/BiomeTranslator.java | 3 +- .../translators/EntityIdentifierRegistry.java | 3 +- .../translators/PacketTranslatorRegistry.java | 7 +- ...drockPacketViolationWarningTranslator.java | 1 + .../translators/effect/EffectRegistry.java | 3 +- .../inventory/PlayerInventoryTranslator.java | 4 +- .../updater/ChestInventoryUpdater.java | 4 +- .../translators/item/ItemRegistry.java | 14 +- .../translators/item/ItemTranslator.java | 6 +- .../JavaPlayerPositionRotationTranslator.java | 3 +- .../spawn/JavaSpawnEntityTranslator.java | 3 +- .../JavaSpawnLivingEntityTranslator.java | 3 +- .../spawn/JavaSpawnPlayerTranslator.java | 3 +- .../java/scoreboard/JavaTeamTranslator.java | 17 +- .../scoreboard/JavaUpdateScoreTranslator.java | 3 +- .../block/entity/BlockEntityTranslator.java | 5 +- .../connector/scoreboard/Scoreboard.java | 3 +- .../geysermc/connector/utils/DockerCheck.java | 4 +- .../geysermc/connector/utils/FileUtils.java | 2 +- .../connector/utils/InventoryUtils.java | 3 +- .../connector/utils/LanguageUtils.java | 207 ++++++++++++++++++ .../geysermc/connector/utils/LocaleUtils.java | 22 +- .../connector/utils/LoginEncryptionUtils.java | 20 +- .../geysermc/connector/utils/SkinUtils.java | 6 +- connector/src/main/resources/config.yml | 4 +- connector/src/main/resources/languages | 1 + 51 files changed, 504 insertions(+), 191 deletions(-) delete mode 100644 common/src/main/resources/help.txt create mode 100644 connector/src/main/java/org/geysermc/connector/utils/LanguageUtils.java create mode 160000 connector/src/main/resources/languages diff --git a/.gitmodules b/.gitmodules index 207825e83..35887822e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,7 @@ [submodule "connector/src/main/resources/mappings"] path = connector/src/main/resources/mappings url = https://github.com/GeyserMC/mappings.git - branch = feature/1.16 + branch = feature/1.16 +[submodule "connector/src/main/resources/languages"] + path = connector/src/main/resources/languages + url = https://github.com/GeyserMC/languages.git diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePlugin.java b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePlugin.java index ac718cbae..38d319e15 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePlugin.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePlugin.java @@ -39,6 +39,7 @@ import org.geysermc.connector.dump.BootstrapDumpInfo; import org.geysermc.connector.ping.GeyserLegacyPingPassthrough; import org.geysermc.connector.ping.IGeyserPingPassthrough; import org.geysermc.connector.utils.FileUtils; +import org.geysermc.connector.utils.LanguageUtils; import org.geysermc.platform.bungeecord.command.GeyserBungeeCommandExecutor; import org.geysermc.platform.bungeecord.command.GeyserBungeeCommandManager; @@ -71,7 +72,7 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { this.geyserConfig = FileUtils.loadConfig(configFile, GeyserBungeeConfiguration.class); configuration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(new File(getDataFolder(), "config.yml")); } catch (IOException ex) { - getLogger().log(Level.WARNING, "Failed to read/create config.yml! Make sure it's up to date and/or readable+writable!", ex); + getLogger().log(Level.WARNING, LanguageUtils.getLocaleStringLog("geyser.config.failed"), ex); ex.printStackTrace(); } @@ -93,7 +94,7 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); if (geyserConfig.getRemote().getAuthType().equals("floodgate") && getProxy().getPluginManager().getPlugin("floodgate-bungee") == null) { - geyserLogger.severe("Auth type set to Floodgate but Floodgate not found! Disabling..."); + geyserLogger.severe(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); return; } diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/command/GeyserBungeeCommandExecutor.java b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/command/GeyserBungeeCommandExecutor.java index d1c8473bd..3b051c5c3 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/command/GeyserBungeeCommandExecutor.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/command/GeyserBungeeCommandExecutor.java @@ -33,6 +33,8 @@ import net.md_5.bungee.api.plugin.TabExecutor; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.GeyserCommand; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.utils.LanguageUtils; import java.util.ArrayList; import java.util.Arrays; @@ -52,7 +54,14 @@ public class GeyserBungeeCommandExecutor extends Command implements TabExecutor if (args.length > 0) { if (getCommand(args[0]) != null) { if (!sender.hasPermission(getCommand(args[0]).getPermission())) { - sender.sendMessage(TextComponent.fromLegacyText(ChatColor.RED + "You do not have permission to execute this command!")); + String message = ""; + if (sender instanceof GeyserSession) { + message = LanguageUtils.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", ((GeyserSession) sender).getClientData().getLanguageCode()); + } else { + message = LanguageUtils.getLocaleStringLog("geyser.bootstrap.command.permission_fail"); + } + + sender.sendMessage(TextComponent.fromLegacyText(ChatColor.RED + message)); return; } getCommand(args[0]).execute(new BungeeCommandSender(sender), args); diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java index de2b7186c..38141c520 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java @@ -37,6 +37,7 @@ import org.geysermc.connector.network.translators.world.WorldManager; import org.geysermc.connector.ping.GeyserLegacyPingPassthrough; import org.geysermc.connector.ping.IGeyserPingPassthrough; import org.geysermc.connector.utils.FileUtils; +import org.geysermc.connector.utils.LanguageUtils; import org.geysermc.platform.spigot.command.GeyserSpigotCommandExecutor; import org.geysermc.platform.spigot.command.GeyserSpigotCommandManager; import org.geysermc.platform.spigot.world.GeyserSpigotBlockPlaceListener; @@ -68,15 +69,15 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { getDataFolder().mkdir(); File bukkitConfig = new File("plugins/Geyser-Bukkit/config.yml"); if (bukkitConfig.exists()) { // Copy over old configs - getLogger().log(Level.INFO, "Existing config found in the Geyser-Bukkit folder; copying over..."); + getLogger().log(Level.INFO, LanguageUtils.getLocaleStringLog("geyser.bootstrap.config.copy_bukkit_config")); Files.copy(bukkitConfig.toPath(), new File(getDataFolder().toString() + "/config.yml").toPath()); - getLogger().log(Level.INFO, "Copied!"); + getLogger().log(Level.INFO, LanguageUtils.getLocaleStringLog("geyser.bootstrap.config.copied_bukkit_config")); } } File configFile = FileUtils.fileOrCopiedFromResource(new File(getDataFolder(), "config.yml"), "config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString())); this.geyserConfig = FileUtils.loadConfig(configFile, GeyserSpigotConfiguration.class); } catch (IOException ex) { - getLogger().log(Level.WARNING, "Failed to read/create config.yml! Make sure it's up to date and/or readable+writable!", ex); + getLogger().log(Level.WARNING, LanguageUtils.getLocaleStringLog("geyser.config.failed"), ex); ex.printStackTrace(); } @@ -92,7 +93,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); if (geyserConfig.getRemote().getAuthType().equals("floodgate") && Bukkit.getPluginManager().getPlugin("floodgate-bukkit") == null) { - geyserLogger.severe("Auth type set to Floodgate but Floodgate not found! Disabling..."); + geyserLogger.severe(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); this.getPluginLoader().disablePlugin(this); return; } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/command/GeyserSpigotCommandExecutor.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/command/GeyserSpigotCommandExecutor.java index b956a0d84..381872752 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/command/GeyserSpigotCommandExecutor.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/command/GeyserSpigotCommandExecutor.java @@ -26,13 +26,14 @@ package org.geysermc.platform.spigot.command; import lombok.AllArgsConstructor; - import org.bukkit.ChatColor; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.TabExecutor; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.GeyserCommand; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.utils.LanguageUtils; import java.util.ArrayList; import java.util.Arrays; @@ -48,7 +49,14 @@ public class GeyserSpigotCommandExecutor implements TabExecutor { if (args.length > 0) { if (getCommand(args[0]) != null) { if (!sender.hasPermission(getCommand(args[0]).getPermission())) { - sender.sendMessage(ChatColor.RED + "You do not have permission to execute this command!"); + String message = ""; + if (sender instanceof GeyserSession) { + message = LanguageUtils.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", ((GeyserSession) sender).getClientData().getLanguageCode()); + } else { + message = LanguageUtils.getLocaleStringLog("geyser.bootstrap.command.permission_fail"); + } + + sender.sendMessage(ChatColor.RED + message); return true; } getCommand(args[0]).execute(new SpigotCommandSender(sender), args); diff --git a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java index 4214255e8..3151e973c 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java @@ -38,6 +38,7 @@ import org.geysermc.connector.dump.BootstrapDumpInfo; import org.geysermc.connector.ping.GeyserLegacyPingPassthrough; import org.geysermc.connector.ping.IGeyserPingPassthrough; import org.geysermc.connector.utils.FileUtils; +import org.geysermc.connector.utils.LanguageUtils; import org.geysermc.platform.sponge.command.GeyserSpongeCommandExecutor; import org.geysermc.platform.sponge.command.GeyserSpongeCommandManager; import org.slf4j.Logger; @@ -80,7 +81,7 @@ public class GeyserSpongePlugin implements GeyserBootstrap { try { configFile = FileUtils.fileOrCopiedFromResource(new File(configDir, "config.yml"), "config.yml", (file) -> file.replaceAll("generateduuid", UUID.randomUUID().toString())); } catch (IOException ex) { - logger.warn("Failed to copy config.yml from jar path!"); + logger.warn(LanguageUtils.getLocaleStringLog("geyser.config.failed")); ex.printStackTrace(); } @@ -90,7 +91,7 @@ public class GeyserSpongePlugin implements GeyserBootstrap { config = loader.load(); this.geyserConfig = new GeyserSpongeConfiguration(configDir, config); } catch (IOException ex) { - logger.warn("Failed to load config.yml!"); + logger.warn(LanguageUtils.getLocaleStringLog("geyser.config.failed")); ex.printStackTrace(); return; } diff --git a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/command/GeyserSpongeCommandExecutor.java b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/command/GeyserSpongeCommandExecutor.java index 8f857b665..d37321ffe 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/command/GeyserSpongeCommandExecutor.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/command/GeyserSpongeCommandExecutor.java @@ -26,10 +26,10 @@ package org.geysermc.platform.sponge.command; import lombok.AllArgsConstructor; - -import org.geysermc.connector.common.ChatColor; import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.common.ChatColor; import org.geysermc.connector.command.GeyserCommand; +import org.geysermc.connector.utils.LanguageUtils; import org.spongepowered.api.command.CommandCallable; import org.spongepowered.api.command.CommandException; import org.spongepowered.api.command.CommandResult; @@ -55,7 +55,8 @@ public class GeyserSpongeCommandExecutor implements CommandCallable { if (args.length > 0) { if (getCommand(args[0]) != null) { if (!source.hasPermission(getCommand(args[0]).getPermission())) { - source.sendMessage(Text.of(ChatColor.RED + "You do not have permission to execute this command!")); + // Not ideal to use log here but we dont get a session + source.sendMessage(Text.of(ChatColor.RED + LanguageUtils.getLocaleStringLog("geyser.bootstrap.command.permission_fail"))); return CommandResult.success(); } getCommand(args[0]).execute(new SpongeCommandSender(source), args); diff --git a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java index 762b09ba5..5da4c37d9 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java @@ -40,6 +40,7 @@ import org.geysermc.connector.dump.BootstrapDumpInfo; import org.geysermc.connector.ping.GeyserLegacyPingPassthrough; import org.geysermc.connector.ping.IGeyserPingPassthrough; import org.geysermc.connector.utils.FileUtils; +import org.geysermc.connector.utils.LanguageUtils; import org.geysermc.platform.standalone.command.GeyserCommandManager; import org.geysermc.platform.standalone.gui.GeyserStandaloneGUI; @@ -107,7 +108,7 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { File configFile = FileUtils.fileOrCopiedFromResource("config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString())); geyserConfig = FileUtils.loadConfig(configFile, GeyserStandaloneConfiguration.class); } catch (IOException ex) { - geyserLogger.severe("Failed to read/create config.yml! Make sure it's up to date and/or readable+writable!", ex); + geyserLogger.severe(LanguageUtils.getLocaleStringLog("geyser.config.failed"), ex); System.exit(0); } GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); diff --git a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/LoopbackUtil.java b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/LoopbackUtil.java index 00ff14de7..8c54d141b 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/LoopbackUtil.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/LoopbackUtil.java @@ -1,12 +1,13 @@ package org.geysermc.platform.standalone; +import org.geysermc.connector.common.ChatColor; +import org.geysermc.connector.utils.LanguageUtils; + import java.io.InputStream; import java.nio.file.Files; import java.nio.file.OpenOption; import java.nio.file.Paths; -import org.geysermc.connector.common.ChatColor; - public class LoopbackUtil { private static final String checkExemption = "powershell -Command \"CheckNetIsolation LoopbackExempt -s\""; // Java's Exec feature runs as CMD, NetIsolation is only accessible from PowerShell. private static final String loopbackCommand = "powershell -Command \"CheckNetIsolation LoopbackExempt -a -n='Microsoft.MinecraftUWP_8wekyb3d8bbwe'\""; @@ -31,12 +32,12 @@ public class LoopbackUtil { Files.write(Paths.get(System.getenv("temp") + "/loopback_minecraft.bat"), loopbackCommand.getBytes(), new OpenOption[0]); process = Runtime.getRuntime().exec(startScript); - geyserLogger.info(ChatColor.AQUA + "Added loopback exemption to Windows!"); + geyserLogger.info(ChatColor.AQUA + LanguageUtils.getLocaleStringLog("geyser.bootstrap.loopback.added")); } } catch (Exception e) { e.printStackTrace(); - geyserLogger.error("Couldn't auto add loopback exemption to Windows!"); + geyserLogger.error(LanguageUtils.getLocaleStringLog("geyser.bootstrap.loopback.failed")); } } } diff --git a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/gui/GeyserStandaloneGUI.java b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/gui/GeyserStandaloneGUI.java index f4138354d..5a6b4c89d 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/gui/GeyserStandaloneGUI.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/gui/GeyserStandaloneGUI.java @@ -29,6 +29,7 @@ package org.geysermc.platform.standalone.gui; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.GeyserCommand; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.utils.LanguageUtils; import org.geysermc.platform.standalone.GeyserStandaloneLogger; import org.geysermc.platform.standalone.command.GeyserCommandManager; @@ -52,7 +53,9 @@ import java.util.concurrent.TimeUnit; public class GeyserStandaloneGUI { - private static final String[] playerTableHeadings = new String[] {"IP", "Username"}; + private static final String[] playerTableHeadings = new String[] { + LanguageUtils.getLocaleStringLog("geyser.gui.table.ip"), + LanguageUtils.getLocaleStringLog("geyser.gui.table.username")}; private static final List ramValues = new ArrayList<>(); private static final ColorPane consolePane = new ColorPane(); @@ -67,7 +70,7 @@ public class GeyserStandaloneGUI { public GeyserStandaloneGUI() { // Create the frame and setup basic settings - JFrame frame = new JFrame("Geyser Standalone"); + JFrame frame = new JFrame(LanguageUtils.getLocaleStringLog("geyser.gui.title")); frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); frame.setSize(800, 400); frame.setMinimumSize(frame.getSize()); @@ -82,8 +85,8 @@ public class GeyserStandaloneGUI { @Override public void windowClosing(WindowEvent we) { - String[] buttons = {"Yes", "No"}; - int result = JOptionPane.showOptionDialog(frame, "Are you sure you want to exit?", frame.getTitle(), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, buttons, buttons[1]); + String[] buttons = {LanguageUtils.getLocaleStringLog("geyser.gui.exit.confirm"), LanguageUtils.getLocaleStringLog("geyser.gui.exit.deny")}; + int result = JOptionPane.showOptionDialog(frame, LanguageUtils.getLocaleStringLog("geyser.gui.exit.message"), frame.getTitle(), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, buttons, buttons[1]); if (result == JOptionPane.YES_OPTION) { System.exit(0); } @@ -124,12 +127,12 @@ public class GeyserStandaloneGUI { JMenuBar menuBar = new JMenuBar(); // Create 'File' - JMenu fileMenu = new JMenu("File"); + JMenu fileMenu = new JMenu(LanguageUtils.getLocaleStringLog("geyser.gui.menu.file")); fileMenu.setMnemonic(KeyEvent.VK_F); menuBar.add(fileMenu); // 'Open Geyser folder' button - JMenuItem openButton = new JMenuItem("Open Geyser folder", KeyEvent.VK_O); + JMenuItem openButton = new JMenuItem(LanguageUtils.getLocaleStringLog("geyser.gui.menu.file.open_folder"), KeyEvent.VK_O); openButton.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_MASK)); openButton.addActionListener(e -> { try { @@ -141,40 +144,40 @@ public class GeyserStandaloneGUI { fileMenu.addSeparator(); // 'Exit' button - JMenuItem exitButton = new JMenuItem("Exit", KeyEvent.VK_X); + JMenuItem exitButton = new JMenuItem(LanguageUtils.getLocaleStringLog("geyser.gui.menu.file.exit"), KeyEvent.VK_X); exitButton.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4, InputEvent.ALT_MASK)); exitButton.addActionListener(e -> System.exit(0)); fileMenu.add(exitButton); // Create 'Commands' - commandsMenu = new JMenu("Commands"); + commandsMenu = new JMenu(LanguageUtils.getLocaleStringLog("geyser.gui.menu.commands")); commandsMenu.setMnemonic(KeyEvent.VK_C); menuBar.add(commandsMenu); // Create 'View' - JMenu viewMenu = new JMenu("View"); + JMenu viewMenu = new JMenu(LanguageUtils.getLocaleStringLog("geyser.gui.menu.view")); viewMenu.setMnemonic(KeyEvent.VK_V); menuBar.add(viewMenu); // 'Zoom in' button - JMenuItem zoomInButton = new JMenuItem("Zoom In"); + JMenuItem zoomInButton = new JMenuItem(LanguageUtils.getLocaleStringLog("geyser.gui.menu.view.zoom_in")); zoomInButton.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, InputEvent.CTRL_DOWN_MASK)); zoomInButton.addActionListener(e -> consolePane.setFont(new Font(consolePane.getFont().getName(), consolePane.getFont().getStyle(), consolePane.getFont().getSize() + 1))); viewMenu.add(zoomInButton); // 'Zoom in' button - JMenuItem zoomOutButton = new JMenuItem("Zoom Out"); + JMenuItem zoomOutButton = new JMenuItem(LanguageUtils.getLocaleStringLog("geyser.gui.menu.view.zoom_out")); zoomOutButton.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, InputEvent.CTRL_DOWN_MASK)); zoomOutButton.addActionListener(e -> consolePane.setFont(new Font(consolePane.getFont().getName(), consolePane.getFont().getStyle(), consolePane.getFont().getSize() - 1))); viewMenu.add(zoomOutButton); // 'Reset Zoom' button - JMenuItem resetZoomButton = new JMenuItem("Reset Zoom"); + JMenuItem resetZoomButton = new JMenuItem(LanguageUtils.getLocaleStringLog("geyser.gui.menu.view.reset_zoom")); resetZoomButton.addActionListener(e -> consolePane.setFont(new Font(consolePane.getFont().getName(), consolePane.getFont().getStyle(), originalFontSize))); viewMenu.add(resetZoomButton); // create 'Options' - optionsMenu = new JMenu("Options"); + optionsMenu = new JMenu(LanguageUtils.getLocaleStringLog("geyser.gui.menu.options")); viewMenu.setMnemonic(KeyEvent.VK_O); menuBar.add(optionsMenu); @@ -195,7 +198,7 @@ public class GeyserStandaloneGUI { ramValues.add(0); } ramGraph.setValues(ramValues); - ramGraph.setXLabel("Loading..."); + ramGraph.setXLabel(LanguageUtils.getLocaleStringLog("geyser.gui.graph.loading")); rightContentPane.add(ramGraph); JScrollPane playerScrollPane = new JScrollPane(playerTable); @@ -270,7 +273,7 @@ public class GeyserStandaloneGUI { } // 'Debug Mode' toggle - JCheckBoxMenuItem debugMode = new JCheckBoxMenuItem("Debug Mode"); + JCheckBoxMenuItem debugMode = new JCheckBoxMenuItem(LanguageUtils.getLocaleStringLog("geyser.gui.menu.options.toggle_debug_mode")); debugMode.setSelected(geyserStandaloneLogger.isDebug()); debugMode.addActionListener(e -> geyserStandaloneLogger.setDebug(!geyserStandaloneLogger.isDebug())); optionsMenu.add(debugMode); @@ -305,7 +308,7 @@ public class GeyserStandaloneGUI { final int freePercent = (int)(freeMemory * 100.0 / totalMemory + 0.5); ramValues.add(100 - freePercent); - ramGraph.setXLabel("Usage: " + String.format("%,d", (totalMemory - freeMemory) / MEGABYTE) + "mb (" + freePercent + "% free)"); + ramGraph.setXLabel(LanguageUtils.getLocaleStringLog("geyser.gui.graph.usage", String.format("%,d", (totalMemory - freeMemory) / MEGABYTE), freePercent)); // Trim the list int k = ramValues.size(); diff --git a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityPlugin.java b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityPlugin.java index f00119ca9..425aba183 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityPlugin.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityPlugin.java @@ -43,6 +43,7 @@ import org.geysermc.connector.dump.BootstrapDumpInfo; import org.geysermc.connector.ping.GeyserLegacyPingPassthrough; import org.geysermc.connector.ping.IGeyserPingPassthrough; import org.geysermc.connector.utils.FileUtils; +import org.geysermc.connector.utils.LanguageUtils; import org.geysermc.platform.velocity.command.GeyserVelocityCommandExecutor; import org.geysermc.platform.velocity.command.GeyserVelocityCommandManager; import org.slf4j.Logger; @@ -85,7 +86,7 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { File configFile = FileUtils.fileOrCopiedFromResource(configFolder.resolve("config.yml").toFile(), "config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString())); this.geyserConfig = FileUtils.loadConfig(configFile, GeyserVelocityConfiguration.class); } catch (IOException ex) { - logger.warn("Failed to read/create config.yml! Make sure it's up to date and/or readable+writable!", ex); + logger.warn(LanguageUtils.getLocaleStringLog("geyser.config.failed"), ex); ex.printStackTrace(); } @@ -103,7 +104,7 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); if (geyserConfig.getRemote().getAuthType().equals("floodgate") && !proxyServer.getPluginManager().getPlugin("floodgate").isPresent()) { - geyserLogger.severe("Auth type set to Floodgate but Floodgate not found! Disabling..."); + geyserLogger.severe(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); return; } diff --git a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/command/GeyserVelocityCommandExecutor.java b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/command/GeyserVelocityCommandExecutor.java index 4632f4404..fa3aaa3c3 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/command/GeyserVelocityCommandExecutor.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/command/GeyserVelocityCommandExecutor.java @@ -35,6 +35,7 @@ import net.kyori.text.TextComponent; import org.geysermc.connector.common.ChatColor; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.GeyserCommand; +import org.geysermc.connector.utils.LanguageUtils; @AllArgsConstructor public class GeyserVelocityCommandExecutor implements Command { @@ -46,7 +47,8 @@ public class GeyserVelocityCommandExecutor implements Command { if (args.length > 0) { if (getCommand(args[0]) != null) { if (!source.hasPermission(getCommand(args[0]).getPermission())) { - source.sendMessage(TextComponent.of(ChatColor.RED + "You do not have permission to execute this command!")); + // Not ideal to use log here but we dont get a session + source.sendMessage(TextComponent.of(ChatColor.RED + LanguageUtils.getLocaleStringLog("geyser.bootstrap.command.permission_fail"))); return; } getCommand(args[0]).execute(new VelocityCommandSender(source), args); diff --git a/common/src/main/resources/help.txt b/common/src/main/resources/help.txt deleted file mode 100644 index 3512ed839..000000000 --- a/common/src/main/resources/help.txt +++ /dev/null @@ -1,18 +0,0 @@ - --------------------------------------------------------------------------------- - - Oops! You attempted to run a plugin version of Geyser directly! - - This jar file is a plugin for ${plugin_type}. You can run this file as a - plugin by dropping the jar file into the "${plugin_folder}" directory. - - There is also a standalone version available that doesn't need to - be installed as a plugin, you can find it on our build server: - - http://ci.geysermc.org/ - - If you need more help, you should check out our discord: - - http://discord.geysermc.org/ - --------------------------------------------------------------------------------- \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/FloodgateKeyLoader.java b/connector/src/main/java/org/geysermc/connector/FloodgateKeyLoader.java index 617ac83eb..d4413d68b 100644 --- a/connector/src/main/java/org/geysermc/connector/FloodgateKeyLoader.java +++ b/connector/src/main/java/org/geysermc/connector/FloodgateKeyLoader.java @@ -26,6 +26,7 @@ package org.geysermc.connector; +import org.geysermc.connector.utils.LanguageUtils; import org.geysermc.connector.configuration.GeyserConfiguration; import java.nio.file.Files; @@ -37,13 +38,13 @@ public class FloodgateKeyLoader { if (floodgate != null) { Path autoKey = floodgateFolder.resolve("public-key.pem"); if (Files.exists(autoKey)) { - logger.info("Auto-loaded floodgate key"); + logger.info(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.auto_loaded")); floodgateKey = autoKey; } else { - logger.error("Auth-type set to floodgate and the public key is missing!"); + logger.error(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.missing_key")); } } else { - logger.error("Auth-type set to floodgate but floodgate is not installed!"); + logger.error(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed")); } } diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index 6cde2a2db..298f5bc82 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -50,6 +50,7 @@ import org.geysermc.connector.network.translators.item.ItemTranslator; import org.geysermc.connector.network.translators.sound.SoundHandlerRegistry; import org.geysermc.connector.network.translators.sound.SoundRegistry; import org.geysermc.connector.network.translators.world.WorldManager; +import org.geysermc.connector.utils.LanguageUtils; import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator; import org.geysermc.connector.utils.DimensionUtils; @@ -107,7 +108,7 @@ public class GeyserConnector { logger.info("******************************************"); logger.info(""); - logger.info("Loading " + NAME + " version " + VERSION); + logger.info(LanguageUtils.getLocaleStringLog("geyser.core.load", NAME, VERSION)); logger.info(""); logger.info("******************************************"); @@ -143,9 +144,9 @@ public class GeyserConnector { bedrockServer.setHandler(new ConnectorServerEventHandler(this)); bedrockServer.bind().whenComplete((avoid, throwable) -> { if (throwable == null) { - logger.info("Started Geyser on " + config.getBedrock().getAddress() + ":" + config.getBedrock().getPort()); + logger.info(LanguageUtils.getLocaleStringLog("geyser.core.start", config.getBedrock().getAddress(), String.valueOf(config.getBedrock().getPort()))); } else { - logger.severe("Failed to start Geyser on " + config.getBedrock().getAddress() + ":" + config.getBedrock().getPort()); + logger.severe(LanguageUtils.getLocaleStringLog("geyser.core.fail", config.getBedrock().getAddress(), config.getBedrock().getPort())); throwable.printStackTrace(); } }).join(); @@ -168,24 +169,24 @@ public class GeyserConnector { } double completeTime = (System.currentTimeMillis() - startupTime) / 1000D; - String message = String.format("Done (%ss)!", new DecimalFormat("#.###").format(completeTime)); + String message = LanguageUtils.getLocaleStringLog("geyser.core.finish.done", new DecimalFormat("#.###").format(completeTime)) + " "; if (isGui) { - message += " Run Commands -> help for help!"; + message += LanguageUtils.getLocaleStringLog("geyser.core.finish.gui"); } else { - message += " Run /geyser help for help!"; + message += LanguageUtils.getLocaleStringLog("geyser.core.finish.console"); } logger.info(message); } public void shutdown() { - bootstrap.getGeyserLogger().info("Shutting down Geyser."); + bootstrap.getGeyserLogger().info(LanguageUtils.getLocaleStringLog("geyser.core.shutdown")); shuttingDown = true; if (players.size() >= 1) { - bootstrap.getGeyserLogger().info("Kicking " + players.size() + " player(s)"); + bootstrap.getGeyserLogger().info(LanguageUtils.getLocaleStringLog("geyser.core.shutdown.kick.log", players.size())); for (GeyserSession playerSession : players.values()) { - playerSession.disconnect("Geyser Proxy shutting down."); + playerSession.disconnect(LanguageUtils.getPlayerLocaleString("geyser.core.shutdown.kick.message", playerSession.getClientData().getLanguageCode())); } CompletableFuture future = CompletableFuture.runAsync(new Runnable() { @@ -209,7 +210,7 @@ public class GeyserConnector { // Block and wait for the future to complete try { future.get(); - bootstrap.getGeyserLogger().info("Kicked all players"); + bootstrap.getGeyserLogger().info(LanguageUtils.getLocaleStringLog("geyser.core.shutdown.kick.done")); } catch (Exception e) { // Quietly fail } @@ -222,7 +223,7 @@ public class GeyserConnector { authType = null; this.getCommandManager().getCommands().clear(); - bootstrap.getGeyserLogger().info("Geyser shutdown successfully."); + bootstrap.getGeyserLogger().info(LanguageUtils.getLocaleStringLog("geyser.core.shutdown.done")); } public void addPlayer(GeyserSession player) { diff --git a/connector/src/main/java/org/geysermc/connector/command/CommandManager.java b/connector/src/main/java/org/geysermc/connector/command/CommandManager.java index 217a9df1f..eb75a2df4 100644 --- a/connector/src/main/java/org/geysermc/connector/command/CommandManager.java +++ b/connector/src/main/java/org/geysermc/connector/command/CommandManager.java @@ -29,6 +29,7 @@ import lombok.Getter; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.defaults.*; +import org.geysermc.connector.utils.LanguageUtils; import java.util.Collections; import java.util.HashMap; @@ -44,17 +45,17 @@ public abstract class CommandManager { public CommandManager(GeyserConnector connector) { this.connector = connector; - registerCommand(new HelpCommand(connector, "help", "Shows help for all registered commands.", "geyser.command.help")); - registerCommand(new ListCommand(connector, "list", "List all players connected through Geyser.", "geyser.command.list")); - registerCommand(new ReloadCommand(connector, "reload", "Reloads the Geyser configurations. Kicks all players when used!", "geyser.command.reload")); - registerCommand(new StopCommand(connector, "stop", "Shuts down Geyser.", "geyser.command.stop")); - registerCommand(new OffhandCommand(connector, "offhand", "Puts an items in your offhand.", "geyser.command.offhand")); - registerCommand(new DumpCommand(connector, "dump", "Dumps Geyser debug infomation for bug reports.", "geyser.command.dump")); + registerCommand(new HelpCommand(connector, "help", LanguageUtils.getLocaleStringLog("geyser.commands.help.desc"), "geyser.command.help")); + registerCommand(new ListCommand(connector, "list", LanguageUtils.getLocaleStringLog("geyser.commands.list.desc"), "geyser.command.list")); + registerCommand(new ReloadCommand(connector, "reload", LanguageUtils.getLocaleStringLog("geyser.commands.reload.desc"), "geyser.command.reload")); + registerCommand(new StopCommand(connector, "stop", LanguageUtils.getLocaleStringLog("geyser.commands.stop.desc"), "geyser.command.stop")); + registerCommand(new OffhandCommand(connector, "offhand", LanguageUtils.getLocaleStringLog("geyser.commands.offhand.desc"), "geyser.command.offhand")); + registerCommand(new DumpCommand(connector, "dump", LanguageUtils.getLocaleStringLog("geyser.commands.dump.desc"), "geyser.command.dump")); } public void registerCommand(GeyserCommand command) { commands.put(command.getName(), command); - connector.getLogger().debug("Registered command " + command.getName()); + connector.getLogger().debug(LanguageUtils.getLocaleStringLog("geyser.commands.registered", command.getName())); if (command.getAliases().isEmpty()) return; @@ -82,7 +83,7 @@ public abstract class CommandManager { GeyserCommand cmd = commands.get(label); if (cmd == null) { - connector.getLogger().error("Invalid Command! Try /geyser help for a list of commands."); + connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.commands.invalid")); return; } diff --git a/connector/src/main/java/org/geysermc/connector/command/defaults/DumpCommand.java b/connector/src/main/java/org/geysermc/connector/command/defaults/DumpCommand.java index 617c9d436..6566ecc1c 100644 --- a/connector/src/main/java/org/geysermc/connector/command/defaults/DumpCommand.java +++ b/connector/src/main/java/org/geysermc/connector/command/defaults/DumpCommand.java @@ -34,6 +34,7 @@ import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.CommandSender; import org.geysermc.connector.command.GeyserCommand; import org.geysermc.connector.dump.DumpInfo; +import org.geysermc.connector.utils.LanguageUtils; import org.geysermc.connector.utils.WebUtils; import java.io.IOException; @@ -57,37 +58,37 @@ public class DumpCommand extends GeyserCommand { @Override public void execute(CommandSender sender, String[] args) { - sender.sendMessage("Collecting dump info"); + sender.sendMessage(LanguageUtils.getLocaleStringLog("geyser.commands.dump.collecting")); String dumpData = ""; try { dumpData = MAPPER.writeValueAsString(new DumpInfo()); } catch (IOException e) { - sender.sendMessage(ChatColor.RED + "Failed to collect dump info, check console for more information"); - connector.getLogger().error("Failed to collect dump info", e); + sender.sendMessage(ChatColor.RED + LanguageUtils.getLocaleStringLog("geyser.commands.dump.collect_error")); + connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.commands.dump.collect_error_short"), e); return; } - sender.sendMessage("Uploading dump"); + sender.sendMessage(LanguageUtils.getLocaleStringLog("geyser.commands.dump.uploading")); String response; JsonNode responseNode; try { response = WebUtils.post(DUMP_URL + "documents", dumpData); responseNode = MAPPER.readTree(response); } catch (IOException e) { - sender.sendMessage(ChatColor.RED + "Failed to upload dump, check console for more information"); - connector.getLogger().error("Failed to upload dump", e); + sender.sendMessage(ChatColor.RED + LanguageUtils.getLocaleStringLog("geyser.commands.dump.upload_error")); + connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.commands.dump.upload_error_short"), e); return; } if (!responseNode.has("key")) { - sender.sendMessage(ChatColor.RED + "Failed to upload dump: " + (responseNode.has("message") ? responseNode.get("message").asText() : response)); + sender.sendMessage(ChatColor.RED + LanguageUtils.getLocaleStringLog("geyser.commands.dump.upload_error_short") + ": " + (responseNode.has("message") ? responseNode.get("message").asText() : response)); return; } String uploadedDumpUrl = DUMP_URL + responseNode.get("key").asText(); - sender.sendMessage("We've made a dump with useful information, report your issue and provide this url: " + ChatColor.DARK_AQUA + uploadedDumpUrl); + sender.sendMessage(LanguageUtils.getLocaleStringLog("geyser.commands.dump.message") + " " + ChatColor.DARK_AQUA + uploadedDumpUrl); if (!sender.isConsole()) { - connector.getLogger().info(sender.getName() + " created a GeyserDump at " + uploadedDumpUrl); + connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.commands.dump.created", sender.getName(), uploadedDumpUrl)); } } } diff --git a/connector/src/main/java/org/geysermc/connector/command/defaults/HelpCommand.java b/connector/src/main/java/org/geysermc/connector/command/defaults/HelpCommand.java index a5942ee6b..0407cf6ef 100644 --- a/connector/src/main/java/org/geysermc/connector/command/defaults/HelpCommand.java +++ b/connector/src/main/java/org/geysermc/connector/command/defaults/HelpCommand.java @@ -29,6 +29,8 @@ import org.geysermc.connector.common.ChatColor; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.CommandSender; import org.geysermc.connector.command.GeyserCommand; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.utils.LanguageUtils; import java.util.Collections; import java.util.List; @@ -48,7 +50,17 @@ public class HelpCommand extends GeyserCommand { @Override public void execute(CommandSender sender, String[] args) { - sender.sendMessage("---- Showing Help For: Geyser (Page 1/1) ----"); + int page = 1; + int maxPage = 1; + String header = ""; + + if (sender instanceof GeyserSession) { + header = LanguageUtils.getPlayerLocaleString("geyser.commands.help.header", ((GeyserSession) sender).getClientData().getLanguageCode(), page, maxPage); + } else { + header = LanguageUtils.getLocaleStringLog("geyser.commands.help.header", page, maxPage); + } + + sender.sendMessage(header); Map cmds = connector.getCommandManager().getCommands(); List commands = connector.getCommandManager().getCommands().keySet().stream().sorted().collect(Collectors.toList()); commands.forEach(cmd -> sender.sendMessage(ChatColor.YELLOW + "/geyser " + cmd + ChatColor.WHITE + ": " + cmds.get(cmd).getDescription())); diff --git a/connector/src/main/java/org/geysermc/connector/command/defaults/ListCommand.java b/connector/src/main/java/org/geysermc/connector/command/defaults/ListCommand.java index 99845ee94..0de73a5d1 100644 --- a/connector/src/main/java/org/geysermc/connector/command/defaults/ListCommand.java +++ b/connector/src/main/java/org/geysermc/connector/command/defaults/ListCommand.java @@ -25,11 +25,11 @@ package org.geysermc.connector.command.defaults; -import org.geysermc.connector.common.ChatColor; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.CommandSender; import org.geysermc.connector.command.GeyserCommand; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.utils.LanguageUtils; import java.util.stream.Collectors; @@ -45,6 +45,13 @@ public class ListCommand extends GeyserCommand { @Override public void execute(CommandSender sender, String[] args) { - sender.sendMessage(ChatColor.YELLOW + "Online Players (" + connector.getPlayers().size() + "): " + ChatColor.WHITE + connector.getPlayers().values().stream().map(GeyserSession::getName).collect(Collectors.joining(" "))); + String message = ""; + if (sender instanceof GeyserSession) { + message = LanguageUtils.getPlayerLocaleString("geyser.commands.list.message", ((GeyserSession) sender).getClientData().getLanguageCode(), connector.getPlayers().size(), connector.getPlayers().values().stream().map(GeyserSession::getName).collect(Collectors.joining(" "))); + } else { + message = LanguageUtils.getLocaleStringLog("geyser.commands.list.message", connector.getPlayers().size(), connector.getPlayers().values().stream().map(GeyserSession::getName).collect(Collectors.joining(" "))); + } + + sender.sendMessage(message); } } diff --git a/connector/src/main/java/org/geysermc/connector/command/defaults/ReloadCommand.java b/connector/src/main/java/org/geysermc/connector/command/defaults/ReloadCommand.java index 2ddd61ed8..d8bf8583b 100644 --- a/connector/src/main/java/org/geysermc/connector/command/defaults/ReloadCommand.java +++ b/connector/src/main/java/org/geysermc/connector/command/defaults/ReloadCommand.java @@ -25,12 +25,12 @@ package org.geysermc.connector.command.defaults; -import org.geysermc.connector.common.ChatColor; import org.geysermc.connector.common.PlatformType; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.CommandSender; import org.geysermc.connector.command.GeyserCommand; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.utils.LanguageUtils; public class ReloadCommand extends GeyserCommand { @@ -46,9 +46,18 @@ public class ReloadCommand extends GeyserCommand { if (!sender.isConsole() && connector.getPlatformType() == PlatformType.STANDALONE) { return; } - sender.sendMessage(ChatColor.YELLOW + "Reloading Geyser configurations... all connected bedrock clients will be kicked."); + + String message = ""; + if (sender instanceof GeyserSession) { + message = LanguageUtils.getPlayerLocaleString("geyser.commands.reload.message", ((GeyserSession) sender).getClientData().getLanguageCode()); + } else { + message = LanguageUtils.getLocaleStringLog("geyser.commands.reload.message"); + } + + sender.sendMessage(message); + for (GeyserSession session : connector.getPlayers().values()) { - session.disconnect("Geyser has been reloaded... sorry for the inconvenience!"); + session.disconnect(LanguageUtils.getPlayerLocaleString("geyser.commands.reload.kick", session.getClientData().getLanguageCode())); } connector.reload(); } diff --git a/connector/src/main/java/org/geysermc/connector/common/main/IGeyserMain.java b/connector/src/main/java/org/geysermc/connector/common/main/IGeyserMain.java index 906bd7865..a3e99ccb3 100644 --- a/connector/src/main/java/org/geysermc/connector/common/main/IGeyserMain.java +++ b/connector/src/main/java/org/geysermc/connector/common/main/IGeyserMain.java @@ -28,24 +28,39 @@ package org.geysermc.connector.common.main; import javax.swing.*; import java.io.InputStream; +import java.lang.reflect.Method; +import java.util.Locale; import java.util.Scanner; public class IGeyserMain { + /** + * Displays the run help message in the console and a message box if running with a gui + */ public void displayMessage() { String message = createMessage(); - if (System.console() == null) { + if (System.console() == null && !isHeadless()) { JOptionPane.showMessageDialog(null, message, "GeyserMC Plugin: " + this.getPluginType(), JOptionPane.ERROR_MESSAGE); } printMessage(message); } + /** + * Load and format the run help text + * + * @return The formatted message + */ private String createMessage() { String message = ""; - InputStream helpStream = IGeyserMain.class.getClassLoader().getResourceAsStream("help.txt"); + InputStream helpStream = IGeyserMain.class.getClassLoader().getResourceAsStream("languages/run-help/" + Locale.getDefault().toString() + ".txt"); + + if (helpStream == null) { + helpStream = IGeyserMain.class.getClassLoader().getResourceAsStream("languages/run-help/en_US.txt"); + } + Scanner help = new Scanner(helpStream).useDelimiter("\\Z"); String line = ""; while (help.hasNext()) { @@ -60,14 +75,44 @@ public class IGeyserMain { return message; } + /** + * Check if we are in a headless environment + * + * @return Are we in a headless environment? + */ + private boolean isHeadless() { + try { + Class graphicsEnvironment = Class.forName("java.awt.GraphicsEnvironment"); + Method isHeadless = graphicsEnvironment.getDeclaredMethod("isHeadless"); + return (Boolean)isHeadless.invoke(null); + } catch (Exception ex) { } + + return true; + } + + /** + * Simply print a message to console + * + * @param message The message to print + */ private void printMessage(String message) { System.out.print(message); } + /** + * Get the platform the plugin is for + * + * @return The string representation of the plugin platforms name + */ public String getPluginType() { return "unknown"; } + /** + * Get the folder name the plugin should go into + * + * @return The string representation of the folder + */ public String getPluginFolder() { return "unknown"; } diff --git a/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java b/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java index 5ea942c1a..5727a902f 100644 --- a/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java @@ -28,6 +28,8 @@ package org.geysermc.connector.configuration; import org.geysermc.connector.GeyserLogger; +import org.geysermc.connector.utils.LanguageUtils; + import java.nio.file.Path; import java.util.Map; @@ -111,9 +113,9 @@ public interface GeyserConfiguration { static void checkGeyserConfiguration(GeyserConfiguration geyserConfig, GeyserLogger geyserLogger) { if (geyserConfig.getConfigVersion() < CURRENT_CONFIG_VERSION) { - geyserLogger.warning("Your Geyser config is out of date! Please regenerate your config when possible."); + geyserLogger.warning(LanguageUtils.getLocaleStringLog("geyser.bootstrap.config.outdated")); } else if (geyserConfig.getConfigVersion() > CURRENT_CONFIG_VERSION) { - geyserLogger.warning("Your Geyser config is too new! Errors may occur."); + geyserLogger.warning(LanguageUtils.getLocaleStringLog("geyser.bootstrap.config.too_new")); } } } diff --git a/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java b/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java index 27b7ad8fe..ad5cb42aa 100644 --- a/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java @@ -37,6 +37,7 @@ import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.ping.IGeyserPingPassthrough; import org.geysermc.connector.utils.MessageUtils; +import org.geysermc.connector.utils.LanguageUtils; import java.net.InetSocketAddress; @@ -50,13 +51,13 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler { @Override public boolean onConnectionRequest(InetSocketAddress inetSocketAddress) { - connector.getLogger().info(inetSocketAddress + " tried to connect!"); + connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.attempt_connect", inetSocketAddress)); return true; } @Override public BedrockPong onQuery(InetSocketAddress inetSocketAddress) { - connector.getLogger().debug(inetSocketAddress + " has pinged you!"); + connector.getLogger().debug(LanguageUtils.getLocaleStringLog("geyser.network.pinged", inetSocketAddress)); GeyserConfiguration config = connector.getConfig(); @@ -108,7 +109,7 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler { bedrockServerSession.setLogging(true); bedrockServerSession.setPacketHandler(new UpstreamPacketHandler(connector, new GeyserSession(connector, bedrockServerSession))); bedrockServerSession.addDisconnectHandler(disconnectReason -> { - connector.getLogger().info("Bedrock user with ip: " + bedrockServerSession.getAddress().getAddress() + " has disconnected for reason " + disconnectReason); + connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.disconnect", bedrockServerSession.getAddress().getAddress(), disconnectReason)); GeyserSession player = connector.getPlayers().get(bedrockServerSession.getAddress()); if (player != null) { diff --git a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java index 79dcf1385..943b4568c 100644 --- a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java @@ -33,6 +33,7 @@ import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslatorRegistry; import org.geysermc.connector.utils.LoginEncryptionUtils; +import org.geysermc.connector.utils.LanguageUtils; public class UpstreamPacketHandler extends LoggingPacketHandler { @@ -47,10 +48,10 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { @Override public boolean handle(LoginPacket loginPacket) { if (loginPacket.getProtocolVersion() > GeyserConnector.BEDROCK_PACKET_CODEC.getProtocolVersion()) { - session.disconnect("Outdated Geyser proxy! I'm still on " + GeyserConnector.BEDROCK_PACKET_CODEC.getMinecraftVersion()); + session.disconnect(LanguageUtils.getPlayerLocaleString("geyser.network.outdated.server", session.getClientData().getLanguageCode(), GeyserConnector.BEDROCK_PACKET_CODEC.getMinecraftVersion())); return true; } else if (loginPacket.getProtocolVersion() < GeyserConnector.BEDROCK_PACKET_CODEC.getProtocolVersion()) { - session.disconnect("Outdated Bedrock client! Please use " + GeyserConnector.BEDROCK_PACKET_CODEC.getMinecraftVersion()); + session.disconnect(LanguageUtils.getPlayerLocaleString("geyser.network.outdated.client", session.getClientData().getLanguageCode(), GeyserConnector.BEDROCK_PACKET_CODEC.getMinecraftVersion())); return true; } @@ -70,7 +71,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { switch (packet.getStatus()) { case COMPLETED: session.connect(connector.getRemoteServer()); - connector.getLogger().info("Player connected with username " + session.getAuthData().getName()); + connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.connect", session.getAuthData().getName())); break; case HAVE_ALL_PACKS: ResourcePackStackPacket stack = new ResourcePackStackPacket(); @@ -97,7 +98,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { GeyserConfiguration.IUserAuthenticationInfo info = connector.getConfig().getUserAuths().get(bedrockUsername); if (info != null) { - connector.getLogger().info("using stored credentials for bedrock user " + session.getAuthData().getName()); + connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.auth.stored_credentials", session.getAuthData().getName())); session.authenticate(info.getEmail(), info.getPassword()); // TODO send a message to bedrock user telling them they are connected (if nothing like a motd @@ -111,6 +112,8 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { @Override public boolean handle(SetLocalPlayerAsInitializedPacket packet) { + LanguageUtils.loadGeyserLocale(session.getClientData().getLanguageCode()); + if (!session.isLoggedIn() && !session.isLoggingIn() && session.getConnector().getAuthType() == AuthType.ONLINE) { // TODO it is safer to key authentication on something that won't change (UUID, not username) if (!couldLoginUserByName(session.getAuthData().getName())) { @@ -124,7 +127,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { @Override public boolean handle(MovePlayerPacket packet) { if (session.isLoggingIn()) { - session.sendMessage("Please wait until you are logged in..."); + session.sendMessage(LanguageUtils.getPlayerLocaleString("geyser.auth.login.wait", session.getClientData().getLanguageCode())); } return translateAndDefault(packet); diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 93c619bfc..666820cc2 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -258,12 +258,11 @@ public class GeyserSession implements CommandSender { public void login() { if (connector.getAuthType() != AuthType.ONLINE) { - connector.getLogger().info( - "Attempting to login using " + connector.getAuthType().name().toLowerCase() + " mode... " + - (connector.getAuthType() == AuthType.OFFLINE ? - "authentication is disabled." : "authentication will be encrypted" - ) - ); + if (connector.getAuthType() == AuthType.OFFLINE) { + connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.auth.login.offline")); + } else { + connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.auth.login.floodgate")); + } authenticate(authData.getName()); } } @@ -274,7 +273,7 @@ public class GeyserSession implements CommandSender { public void authenticate(String username, String password) { if (loggedIn) { - connector.getLogger().severe(username + " is already logged in!"); + connector.getLogger().severe(LanguageUtils.getLocaleStringLog("geyser.auth.already_loggedin", username)); return; } @@ -299,13 +298,13 @@ public class GeyserSession implements CommandSender { PublicKey.class ); } catch (IOException | InvalidKeySpecException | NoSuchAlgorithmException e) { - connector.getLogger().error("Error while reading Floodgate key file", e); + connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.auth.floodgate.bad_key"), e); } publicKey = key; } else publicKey = null; if (publicKey != null) { - connector.getLogger().info("Loaded Floodgate key!"); + connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.auth.floodgate.loaded_key")); } downstream = new Client(remoteServer.getAddress(), remoteServer.getPort(), protocol, new TcpSessionFactory()); @@ -326,7 +325,7 @@ public class GeyserSession implements CommandSender { upstream.getSession().getAddress().getAddress().getHostAddress() )); } catch (Exception e) { - connector.getLogger().error("Failed to encrypt message", e); + connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.auth.floodgate.encrypt_fail"), e); } HandshakePacket handshakePacket = event.getPacket(); @@ -343,7 +342,7 @@ public class GeyserSession implements CommandSender { public void connected(ConnectedEvent event) { loggingIn = false; loggedIn = true; - connector.getLogger().info(authData.getName() + " (logged in as: " + protocol.getProfile().getName() + ")" + " has connected to remote java server on address " + remoteServer.getAddress()); + connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.remote.connect", authData.getName(), protocol.getProfile().getName(), remoteServer.getAddress())); playerEntity.setUuid(protocol.getProfile().getId()); playerEntity.setUsername(protocol.getProfile().getName()); @@ -352,6 +351,7 @@ public class GeyserSession implements CommandSender { // Let the user know there locale may take some time to download // as it has to be extracted from a JAR if (locale.toLowerCase().equals("en_us") && !LocaleUtils.LOCALE_MAPPINGS.containsKey("en_us")) { + // This should probably be left hardcoded as it will only show for en_us clients sendMessage("Downloading your locale (en_us) this may take some time"); } @@ -363,7 +363,7 @@ public class GeyserSession implements CommandSender { public void disconnected(DisconnectedEvent event) { loggingIn = false; loggedIn = false; - connector.getLogger().info(authData.getName() + " has disconnected from remote java server on address " + remoteServer.getAddress() + " because of " + event.getReason()); + connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.remote.disconnect", authData.getName(), remoteServer.getAddress(), event.getReason())); if (event.getCause() != null) { event.getCause().printStackTrace(); } @@ -402,7 +402,7 @@ public class GeyserSession implements CommandSender { @Override public void packetError(PacketErrorEvent event) { - connector.getLogger().warning("Downstream packet error! " + event.getCause().getMessage()); + connector.getLogger().warning(LanguageUtils.getLocaleStringLog("geyser.network.downstream_error", event.getCause().getMessage())); if (connector.getConfig().isDebugMode()) event.getCause().printStackTrace(); event.setSuppress(true); @@ -412,8 +412,8 @@ public class GeyserSession implements CommandSender { downstream.getSession().connect(); connector.addPlayer(this); } catch (InvalidCredentialsException | IllegalArgumentException e) { - connector.getLogger().info("User '" + username + "' entered invalid login info, kicking."); - disconnect("Invalid/incorrect login info"); + connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.auth.login.invalid", username)); + disconnect(LanguageUtils.getPlayerLocaleString("geyser.auth.login.invalid.kick", getClientData().getLanguageCode())); } catch (RequestException ex) { ex.printStackTrace(); } @@ -442,7 +442,7 @@ public class GeyserSession implements CommandSender { } public void close() { - disconnect("Server closed."); + disconnect(LanguageUtils.getPlayerLocaleString("geyser.network.close", getClientData().getLanguageCode())); } public void setAuthenticationData(AuthData authData) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/BiomeTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/BiomeTranslator.java index 23a36641e..2daa6b2f4 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/BiomeTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/BiomeTranslator.java @@ -31,6 +31,7 @@ import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtUtils; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.utils.FileUtils; +import org.geysermc.connector.utils.LanguageUtils; import java.io.InputStream; import java.util.Arrays; @@ -59,7 +60,7 @@ public class BiomeTranslator { biomesTag = (NbtMap) biomenbtInputStream.readTag(); BIOMES = biomesTag; } catch (Exception ex) { - GeyserConnector.getInstance().getLogger().warning("Failed to get biomes from biome definitions, is there something wrong with the file?"); + GeyserConnector.getInstance().getLogger().warning(LanguageUtils.getLocaleStringLog("geyser.toolbox.fail.biome_read")); throw new AssertionError(ex); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/EntityIdentifierRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/EntityIdentifierRegistry.java index 59f4ae8ba..1f3366678 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/EntityIdentifierRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/EntityIdentifierRegistry.java @@ -30,6 +30,7 @@ import com.nukkitx.nbt.NBTInputStream; import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtUtils; import org.geysermc.connector.utils.FileUtils; +import org.geysermc.connector.utils.LanguageUtils; import java.io.InputStream; @@ -54,7 +55,7 @@ public class EntityIdentifierRegistry { try (NBTInputStream nbtInputStream = NbtUtils.createNetworkReader(stream)) { ENTITY_IDENTIFIERS = (NbtMap) nbtInputStream.readTag(); } catch (Exception e) { - throw new AssertionError("Unable to get entities from entity identifiers", e); + throw new AssertionError(LanguageUtils.getLocaleStringLog("geyser.toolbox.fail.entity"), e); } } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/PacketTranslatorRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/PacketTranslatorRegistry.java index a11dd40af..92d2e9102 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/PacketTranslatorRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/PacketTranslatorRegistry.java @@ -33,6 +33,7 @@ import com.nukkitx.protocol.bedrock.BedrockPacket; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.utils.LanguageUtils; import org.reflections.Reflections; import java.util.HashMap; @@ -66,10 +67,10 @@ public class PacketTranslatorRegistry { BEDROCK_TRANSLATOR.translators.put(targetPacket, translator); } else { - GeyserConnector.getInstance().getLogger().error("Class " + clazz.getCanonicalName() + " is annotated as a translator but has an invalid target packet."); + GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.invalid_target", clazz.getCanonicalName())); } } catch (InstantiationException | IllegalAccessException e) { - GeyserConnector.getInstance().getLogger().error("Could not instantiate annotated translator " + clazz.getCanonicalName() + "."); + GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.failed", clazz.getCanonicalName())); } } @@ -97,7 +98,7 @@ public class PacketTranslatorRegistry { GeyserConnector.getInstance().getLogger().debug("Could not find packet for " + (packet.toString().length() > 25 ? packet.getClass().getSimpleName() : packet)); } } catch (Throwable ex) { - GeyserConnector.getInstance().getLogger().error("Could not translate packet " + packet.getClass().getSimpleName(), ex); + GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.packet.failed", packet.getClass().getSimpleName()), ex); ex.printStackTrace(); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockPacketViolationWarningTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockPacketViolationWarningTranslator.java index 9e3c14e6d..5e4633ea5 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockPacketViolationWarningTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockPacketViolationWarningTranslator.java @@ -36,6 +36,7 @@ public class BedrockPacketViolationWarningTranslator extends PacketTranslator" + entry.getValue().asText()); + GeyserConnector.getInstance().getLogger().warning(LanguageUtils.getLocaleStringLog("geyser.particle.failed_map", entry.getKey(), entry.getValue().asText())); } } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/PlayerInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/PlayerInventoryTranslator.java index 41489ac2a..7d7673c4e 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/PlayerInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/PlayerInventoryTranslator.java @@ -39,12 +39,12 @@ import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.inventory.action.InventoryActionDataTranslator; import org.geysermc.connector.network.translators.item.ItemTranslator; import org.geysermc.connector.utils.InventoryUtils; +import org.geysermc.connector.utils.LanguageUtils; import java.util.List; public class PlayerInventoryTranslator extends InventoryTranslator { - private static final ItemData UNUSUABLE_CRAFTING_SPACE_BLOCK = InventoryUtils.createUnusableSpaceBlock( - "The creative crafting grid is\nunavailable in Java Edition"); + private static final ItemData UNUSUABLE_CRAFTING_SPACE_BLOCK = InventoryUtils.createUnusableSpaceBlock(LanguageUtils.getLocaleStringLog("geyser.inventory.unusable_item.creative")); public PlayerInventoryTranslator() { super(46); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ChestInventoryUpdater.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ChestInventoryUpdater.java index 7437a26e4..e45945bc3 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ChestInventoryUpdater.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ChestInventoryUpdater.java @@ -34,11 +34,11 @@ import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.inventory.InventoryTranslator; import org.geysermc.connector.network.translators.item.ItemTranslator; import org.geysermc.connector.utils.InventoryUtils; +import org.geysermc.connector.utils.LanguageUtils; @AllArgsConstructor public class ChestInventoryUpdater extends InventoryUpdater { - private static final ItemData UNUSUABLE_SPACE_BLOCK = InventoryUtils.createUnusableSpaceBlock( - "This slot does not exist in the inventory\non Java Edition, as there is less\nrows than possible in Bedrock"); + private static final ItemData UNUSUABLE_SPACE_BLOCK = InventoryUtils.createUnusableSpaceBlock(LanguageUtils.getLocaleStringLog("geyser.inventory.unusable_item.slot")); private final int paddedSize; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java index 0979e5731..23c566d7c 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java @@ -37,16 +37,12 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.utils.FileUtils; +import org.geysermc.connector.utils.LanguageUtils; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import java.util.ArrayList; -import java.util.Base64; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; +import java.util.*; /** * Registry for anything item related. @@ -82,7 +78,7 @@ public class ItemRegistry { try { itemEntries = GeyserConnector.JSON_MAPPER.readValue(stream, itemEntriesType); } catch (Exception e) { - throw new AssertionError("Unable to load Bedrock runtime item IDs", e); + throw new AssertionError(LanguageUtils.getLocaleStringLog("geyser.toolbox.fail.runtime_bedrock"), e); } for (JsonNode entry : itemEntries) { @@ -95,7 +91,7 @@ public class ItemRegistry { try { items = GeyserConnector.JSON_MAPPER.readTree(stream); } catch (Exception e) { - throw new AssertionError("Unable to load Java runtime item IDs", e); + throw new AssertionError(LanguageUtils.getLocaleStringLog("geyser.toolbox.fail.runtime_java"), e); } int itemIndex = 0; @@ -144,7 +140,7 @@ public class ItemRegistry { try { creativeItemEntries = GeyserConnector.JSON_MAPPER.readTree(stream).get("items"); } catch (Exception e) { - throw new AssertionError("Unable to load creative items", e); + throw new AssertionError(LanguageUtils.getLocaleStringLog("geyser.toolbox.fail.creative"), e); } List creativeItems = new ArrayList<>(); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java index 6811d6bb6..7811d9c0a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java @@ -50,6 +50,7 @@ import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; import org.geysermc.connector.utils.MessageUtils; +import org.geysermc.connector.utils.LanguageUtils; import org.reflections.Reflections; import java.util.*; @@ -88,14 +89,13 @@ public abstract class ItemTranslator { for (ItemEntry item : appliedItems) { ItemTranslator registered = ITEM_STACK_TRANSLATORS.get(item.getJavaId()); if (registered != null) { - GeyserConnector.getInstance().getLogger().error("Could not instantiate annotated item translator " + clazz.getCanonicalName() + "." + - " Item translator " + registered.getClass().getCanonicalName() + " is already registered for the item " + item.getJavaIdentifier()); + GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.item.already_registered", clazz.getCanonicalName(), registered.getClass().getCanonicalName(), item.getJavaIdentifier())); continue; } ITEM_STACK_TRANSLATORS.put(item.getJavaId(), itemStackTranslator); } } catch (InstantiationException | IllegalAccessException e) { - GeyserConnector.getInstance().getLogger().error("Could not instantiate annotated item translator " + clazz.getCanonicalName() + "."); + GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.item.failed", clazz.getCanonicalName())); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerPositionRotationTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerPositionRotationTranslator.java index 8b0b82014..491779121 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerPositionRotationTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerPositionRotationTranslator.java @@ -41,6 +41,7 @@ import org.geysermc.connector.network.session.cache.TeleportCache; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; import org.geysermc.connector.utils.ChunkUtils; +import org.geysermc.connector.utils.LanguageUtils; @Translator(packet = ServerPlayerPositionRotationPacket.class) public class JavaPlayerPositionRotationTranslator extends PacketTranslator { @@ -90,7 +91,7 @@ public class JavaPlayerPositionRotationTranslator extends PacketTranslator { @@ -66,21 +65,21 @@ public class JavaTeamTranslator extends PacketTranslator { .setSuffix(MessageUtils.getBedrockMessage(packet.getSuffix())) .setUpdateType(UpdateType.UPDATE); } else { - GeyserConnector.getInstance().getLogger().error("Error while translating Team Packet " + packet.getAction() + "! Scoreboard Team " + packet.getTeamName() + " is not registered."); + GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.team.failed_not_registered", packet.getAction(), packet.getTeamName())); } break; case ADD_PLAYER: if(team != null){ team.addEntities(packet.getPlayers()); } else { - GeyserConnector.getInstance().getLogger().error("Error while translating Team Packet " + packet.getAction() + "! Scoreboard Team " + packet.getTeamName() + " is not registered."); + GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.team.failed_not_registered", packet.getAction(), packet.getTeamName())); } break; case REMOVE_PLAYER: if(team != null){ team.removeEntities(packet.getPlayers()); } else { - GeyserConnector.getInstance().getLogger().error("Error while translating Team Packet " + packet.getAction() + "! Scoreboard Team " + packet.getTeamName() + " is not registered."); + GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.team.failed_not_registered", packet.getAction(), packet.getTeamName())); } break; case REMOVE: diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaUpdateScoreTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaUpdateScoreTranslator.java index eac2ed049..827e4c7f4 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaUpdateScoreTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaUpdateScoreTranslator.java @@ -34,6 +34,7 @@ import org.geysermc.connector.scoreboard.Scoreboard; import com.github.steveice10.mc.protocol.data.game.scoreboard.ScoreboardAction; import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerUpdateScorePacket; +import org.geysermc.connector.utils.LanguageUtils; @Translator(packet = ServerUpdateScorePacket.class) public class JavaUpdateScoreTranslator extends PacketTranslator { @@ -45,7 +46,7 @@ public class JavaUpdateScoreTranslator extends PacketTranslator clazz : ref.getSubTypesOf(RequiresBlockState.class)) { @@ -81,7 +82,7 @@ public abstract class BlockEntityTranslator { try { REQUIRES_BLOCK_STATE_LIST.add((RequiresBlockState) clazz.newInstance()); } catch (InstantiationException | IllegalAccessException e) { - GeyserConnector.getInstance().getLogger().error("Could not instantiate required block state " + clazz.getCanonicalName() + "."); + GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.block_state.failed", clazz.getCanonicalName())); } } } diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java b/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java index 59d9b25f6..5fdda617f 100644 --- a/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java +++ b/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java @@ -34,6 +34,7 @@ import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import lombok.Getter; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.utils.LanguageUtils; import java.util.*; import java.util.concurrent.atomic.AtomicLong; @@ -78,7 +79,7 @@ public class Scoreboard { public Team registerNewTeam(String teamName, Set players) { if (teams.containsKey(teamName)) { - session.getConnector().getLogger().info("Ignoring team " + teamName + ". It overrides without removing old team."); + session.getConnector().getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.translator.team.failed_overrides", teamName)); return getTeam(teamName); } diff --git a/connector/src/main/java/org/geysermc/connector/utils/DockerCheck.java b/connector/src/main/java/org/geysermc/connector/utils/DockerCheck.java index 09c78da92..a6eb6c9ba 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/DockerCheck.java +++ b/connector/src/main/java/org/geysermc/connector/utils/DockerCheck.java @@ -49,8 +49,8 @@ public class DockerCheck { String output = new String(Files.readAllBytes(Paths.get("/proc/1/cgroup"))); if (output.contains("docker")) { - bootstrap.getGeyserLogger().warning("You are most likely in a Docker container, this may cause connection issues from Geyser to the Java server"); - bootstrap.getGeyserLogger().warning("We recommended using the following IP as the remote address: " + ipAddress); + bootstrap.getGeyserLogger().warning(LanguageUtils.getLocaleStringLog("geyser.bootstrap.docker_warn.line1")); + bootstrap.getGeyserLogger().warning(LanguageUtils.getLocaleStringLog("geyser.bootstrap.docker_warn.line2", ipAddress)); } } } catch (Exception e) { } // Ignore any errors, inc ip failed to fetch, process could not run or access denied diff --git a/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java b/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java index 04b6ecc4d..0b7b5c5cf 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java @@ -135,7 +135,7 @@ public class FileUtils { public static InputStream getResource(String resource) { InputStream stream = FileUtils.class.getClassLoader().getResourceAsStream(resource); if (stream == null) { - throw new AssertionError("Unable to find resource: " + resource); + throw new AssertionError(LanguageUtils.getLocaleStringLog("geyser.toolbox.fail.resource", resource)); } return stream; } diff --git a/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java b/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java index 9862159dc..325710c6d 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java @@ -140,7 +140,8 @@ public class InventoryUtils { NbtMapBuilder root = NbtMap.builder(); NbtMapBuilder display = NbtMap.builder(); - display.putString("Name", ChatColor.RESET + "Unusable inventory space"); + // Not ideal to use log here but we dont get a session + display.putString("Name", ChatColor.RESET + LanguageUtils.getLocaleStringLog("geyser.inventory.unusable_item.name")); display.putList("Lore", NbtType.STRING, Collections.singletonList(ChatColor.RESET + ChatColor.DARK_PURPLE + description)); root.put("display", display.build()); diff --git a/connector/src/main/java/org/geysermc/connector/utils/LanguageUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LanguageUtils.java new file mode 100644 index 000000000..6d353adbe --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/utils/LanguageUtils.java @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + * + */ + +package org.geysermc.connector.utils; + +import org.geysermc.connector.GeyserConnector; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; + +public class LanguageUtils { + + /** + * If we determine the locale that the user wishes to use, use that locale + */ + private static String CACHED_LOCALE; + + private static final Map LOCALE_MAPPINGS = new HashMap<>(); + + static { + // Load it as a backup in case something goes really wrong + if (!"en_US".equals(formatLocale(getDefaultLocale()))) { // getDefaultLocale() loads the locale automatically + loadGeyserLocale("en_US"); + } + } + + /** + * Loads a Geyser locale from resources, if the file doesn't exist it just logs a warning + * + * @param locale Locale to load + */ + public static void loadGeyserLocale(String locale) { + locale = formatLocale(locale); + + InputStream localeStream = GeyserConnector.class.getClassLoader().getResourceAsStream("languages/texts/" + locale + ".properties"); + + // Load the locale + if (localeStream != null) { + Properties localeProp = new Properties(); + try { + localeProp.load(new InputStreamReader(localeStream, StandardCharsets.UTF_8)); + } catch (Exception e) { + throw new AssertionError(getLocaleStringLog("geyser.language.load_failed", locale), e); + } + + // Insert the locale into the mappings + LOCALE_MAPPINGS.put(locale, localeProp); + } else { + if (!locale.toLowerCase().equals(getDefaultLocale().toLowerCase())) { // The default locale was invalid fallback to en_us + GeyserConnector.getInstance().getLogger().warning(getLocaleStringLog("geyser.language.missing_file", locale)); + } + } + } + + /** + * Get a formatted language string with the default locale for Geyser + * + * @param key Language string to translate + * @param values Values to put into the string + * @return Translated string or the original message if it was not found in the given locale + */ + public static String getLocaleStringLog(String key, Object... values) { + return getPlayerLocaleString(key, getDefaultLocale(), values); + } + + /** + * Get a formatted language string with the given locale for Geyser + * + * @param key Language string to translate + * @param locale Locale to translate to + * @param values Values to put into the string + * @return Translated string or the original message if it was not found in the given locale + */ + public static String getPlayerLocaleString(String key, String locale, Object... values) { + locale = formatLocale(locale); + + Properties properties = LOCALE_MAPPINGS.get(locale); + String formatString = properties.getProperty(key); + + // Try and get the key from the default locale + if (formatString == null) { + properties = LOCALE_MAPPINGS.get(formatLocale(getDefaultLocale())); + formatString = properties.getProperty(key); + } + + // Try and get the key from en_US (this should only ever happen in development) + if (formatString == null) { + properties = LOCALE_MAPPINGS.get("en_US"); + formatString = properties.getProperty(key); + } + + // Final fallback + if (formatString == null) { + formatString = key; + } + + return MessageFormat.format(formatString.replace("&", "\u00a7"), values); + } + + /** + * Cleans up and formats a locale string + * + * @param locale The locale to format + * @return The formatted locale + */ + private static String formatLocale(String locale) { + try { + String[] parts = locale.toLowerCase().split("_"); + String newLocale = parts[0] + "_" + parts[1].toUpperCase(); + switch (newLocale) { // Fallback to the closest language if we don't support it but Bedrock does. + case "es_MX": + return "es_ES"; + case "pt_BR": + return "pt_PT"; + case "fr_CA": + return "fr_FR"; + default: + return newLocale; + } + } catch (Exception e) { + return locale; + } + } + + /** + * Get the default locale that Geyser should use + * @return the current default locale + */ + public static String getDefaultLocale() { + if (CACHED_LOCALE != null) return CACHED_LOCALE; // We definitely know the locale the user is using + String locale; + boolean isValid = true; + if (GeyserConnector.getInstance() != null && + GeyserConnector.getInstance().getConfig() != null && + GeyserConnector.getInstance().getConfig().getDefaultLocale() != null) { // If the config option for getDefaultLocale does not equal null, use that + locale = formatLocale(GeyserConnector.getInstance().getConfig().getDefaultLocale()); + if (isValidLanguage(locale)) { + CACHED_LOCALE = locale; + return locale; + } else { + isValid = false; + } + } + locale = formatLocale(Locale.getDefault().getLanguage() + "_" + Locale.getDefault().getCountry()); + if (!isValidLanguage(locale)) { // Bedrock does not support this language + locale = "en_US"; + } + if (GeyserConnector.getInstance() != null && + GeyserConnector.getInstance().getConfig() != null && (GeyserConnector.getInstance().getConfig().getDefaultLocale() == null || !isValid)) { // Means we should use the system locale for sure + CACHED_LOCALE = locale; + } + return locale; + } + + /** + * Ensures that the given locale is supported by Bedrock + * @param locale the locale to validate + * @return true if the given locale is supported by Bedrock and by extension Geyser + */ + private static boolean isValidLanguage(String locale) { + boolean result = true; + if (FileUtils.class.getResource("/languages/texts/" + locale + ".properties") == null) { + result = false; + if (GeyserConnector.getInstance() != null && GeyserConnector.getInstance().getLogger() != null) { // Could be too early for these to be initialized + GeyserConnector.getInstance().getLogger().warning(locale + " is not a valid Bedrock language."); // We can't translate this since we just loaded an invalid language + } + } else { + if (!LOCALE_MAPPINGS.containsKey(locale)) { + loadGeyserLocale(locale); + } + } + return result; + } + + public static void init() { + // no-op + } +} diff --git a/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java index 787525913..285846a97 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java @@ -48,8 +48,6 @@ public class LocaleUtils { private static final Map ASSET_MAP = new HashMap<>(); - private static final String DEFAULT_LOCALE = (GeyserConnector.getInstance().getConfig().getDefaultLocale() != null ? GeyserConnector.getInstance().getConfig().getDefaultLocale() : "en_us"); - private static String smallestURL = ""; static { @@ -60,7 +58,7 @@ public class LocaleUtils { // Download the latest asset list and cache it generateAssetCache(); - downloadAndLoadLocale(DEFAULT_LOCALE); + downloadAndLoadLocale(LanguageUtils.getDefaultLocale()); } /** @@ -82,7 +80,7 @@ public class LocaleUtils { // Make sure we definitely got a version if (latestInfoURL.isEmpty()) { - throw new Exception("Unable to get latest Minecraft version"); + throw new Exception(LanguageUtils.getLocaleStringLog("geyser.locale.fail.latest_version")); } // Get the individual version manifest @@ -105,7 +103,7 @@ public class LocaleUtils { ASSET_MAP.put(entry.getKey(), asset); } } catch (Exception e) { - GeyserConnector.getInstance().getLogger().info("Failed to load locale asset cache: " + (!e.getMessage().isEmpty() ? e.getMessage() : e.getStackTrace())); + GeyserConnector.getInstance().getLogger().info(LanguageUtils.getLocaleStringLog("geyser.locale.fail.asset_cache", (!e.getMessage().isEmpty() ? e.getMessage() : e.getStackTrace()))); } } @@ -119,7 +117,7 @@ public class LocaleUtils { // Check the locale isn't already loaded if (!ASSET_MAP.containsKey("minecraft/lang/" + locale + ".json") && !locale.equals("en_us")) { - GeyserConnector.getInstance().getLogger().warning("Invalid locale requested to download and load: " + locale); + GeyserConnector.getInstance().getLogger().warning(LanguageUtils.getLocaleStringLog("geyser.locale.fail.invalid", locale)); return; } @@ -170,7 +168,7 @@ public class LocaleUtils { try { localeStream = new FileInputStream(localeFile); } catch (FileNotFoundException e) { - throw new AssertionError("Unable to load locale: " + locale + " (" + e.getMessage() + ")"); + throw new AssertionError(LanguageUtils.getLocaleStringLog("geyser.locale.fail.file", locale, e.getMessage())); } // Parse the file as json @@ -178,7 +176,7 @@ public class LocaleUtils { try { localeObj = GeyserConnector.JSON_MAPPER.readTree(localeStream); } catch (Exception e) { - throw new AssertionError("Unable to load Java edition lang map for " + locale, e); + throw new AssertionError(LanguageUtils.getLocaleStringLog("geyser.locale.fail.json", locale), e); } // Parse all the locale fields @@ -192,7 +190,7 @@ public class LocaleUtils { // Insert the locale into the mappings LOCALE_MAPPINGS.put(locale.toLowerCase(), langMap); } else { - GeyserConnector.getInstance().getLogger().warning("Missing locale file: " + locale); + GeyserConnector.getInstance().getLogger().warning(LanguageUtils.getLocaleStringLog("geyser.locale.fail.missing", locale)); } } @@ -204,7 +202,7 @@ public class LocaleUtils { private static void downloadEN_US(File localeFile) { try { // Let the user know we are downloading the JAR - GeyserConnector.getInstance().getLogger().info("Downloading Minecraft JAR to extract en_us locale, please wait... (this may take some time depending on the speed of your internet connection)"); + GeyserConnector.getInstance().getLogger().info(LanguageUtils.getLocaleStringLog("geyser.locale.download.en_us")); GeyserConnector.getInstance().getLogger().debug("Download URL: " + smallestURL); // Download the smallest JAR (client or server) @@ -233,7 +231,7 @@ public class LocaleUtils { // Delete the nolonger needed client/server jar Files.delete(tmpFilePath); } catch (Exception e) { - throw new AssertionError("Unable to download and extract en_us locale!", e); + throw new AssertionError(LanguageUtils.getLocaleStringLog("geyser.locale.fail.en_us"), e); } } @@ -247,7 +245,7 @@ public class LocaleUtils { public static String getLocaleString(String messageText, String locale) { Map localeStrings = LocaleUtils.LOCALE_MAPPINGS.get(locale.toLowerCase()); if (localeStrings == null) - localeStrings = LocaleUtils.LOCALE_MAPPINGS.get(DEFAULT_LOCALE); + localeStrings = LocaleUtils.LOCALE_MAPPINGS.get(LanguageUtils.getDefaultLocale()); return localeStrings.getOrDefault(messageText, messageText); } diff --git a/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java index f9a7fec26..3d4dd506a 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java @@ -156,18 +156,20 @@ public class LoginEncryptionUtils { private static int AUTH_DETAILS_FORM_ID = 1337; public static void showLoginWindow(GeyserSession session) { - SimpleFormWindow window = new SimpleFormWindow("Login", "You need a Java Edition account to play on this server."); - window.getButtons().add(new FormButton("Login with Minecraft")); - window.getButtons().add(new FormButton("Disconnect")); + String userLanguage = session.getClientData().getLanguageCode(); + SimpleFormWindow window = new SimpleFormWindow(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.notice.title", userLanguage), LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.notice.desc", userLanguage)); + window.getButtons().add(new FormButton(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.notice.btn_login", userLanguage))); + window.getButtons().add(new FormButton(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.notice.btn_disconnect", userLanguage))); session.sendForm(window, AUTH_FORM_ID); } public static void showLoginDetailsWindow(GeyserSession session) { - CustomFormWindow window = new CustomFormBuilder("Login Details") - .addComponent(new LabelComponent("Enter the credentials for your Minecraft: Java Edition account below.")) - .addComponent(new InputComponent("Email/Username", "account@geysermc.org", "")) - .addComponent(new InputComponent("Password", "123456", "")) + String userLanguage = session.getClientData().getLanguageCode(); + CustomFormWindow window = new CustomFormBuilder(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.details.title", userLanguage)) + .addComponent(new LabelComponent(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.details.desc", userLanguage))) + .addComponent(new InputComponent(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.details.email", userLanguage), "account@geysermc.org", "")) + .addComponent(new InputComponent(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.details.pass", userLanguage), "123456", "")) .build(); session.sendForm(window, AUTH_DETAILS_FORM_ID); @@ -203,8 +205,8 @@ public class LoginEncryptionUtils { if (response != null) { if (response.getClickedButtonId() == 0) { showLoginDetailsWindow(session); - } else if (response.getClickedButtonId() == 1) { - session.disconnect("Login is required"); + } else if(response.getClickedButtonId() == 1) { + session.disconnect(LanguageUtils.getPlayerLocaleString("geyser.auth.login.form.disconnect", session.getClientData().getLanguageCode())); } } else { showLoginWindow(session); diff --git a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java index 9e0712477..225885769 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java @@ -246,7 +246,7 @@ public class SkinUtils { } } } catch (Exception e) { - GeyserConnector.getInstance().getLogger().error("Failed getting skin for " + entity.getUuid(), e); + GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.skin.fail", entity.getUuid()), e); } if (skinAndCapeConsumer != null) skinAndCapeConsumer.accept(skinAndCape); @@ -257,7 +257,7 @@ public class SkinUtils { public static void handleBedrockSkin(PlayerEntity playerEntity, BedrockClientData clientData) { GameProfileData data = GameProfileData.from(playerEntity.getProfile()); - GeyserConnector.getInstance().getLogger().info("Registering bedrock skin for " + playerEntity.getUsername() + " (" + playerEntity.getUuid() + ")"); + GeyserConnector.getInstance().getLogger().info(LanguageUtils.getLocaleStringLog("geyser.skin.bedrock.register", playerEntity.getUsername(), playerEntity.getUuid())); try { byte[] skinBytes = com.github.steveice10.mc.auth.util.Base64.decode(clientData.getSkinData().getBytes("UTF-8")); @@ -270,7 +270,7 @@ public class SkinUtils { SkinProvider.storeBedrockSkin(playerEntity.getUuid(), data.getSkinUrl(), skinBytes); SkinProvider.storeBedrockGeometry(playerEntity.getUuid(), geometryNameBytes, geometryBytes); } else { - GeyserConnector.getInstance().getLogger().info("Unable to load bedrock skin for '" + playerEntity.getUsername() + "' as they are likely using a customised skin"); + GeyserConnector.getInstance().getLogger().info(LanguageUtils.getLocaleStringLog("geyser.skin.bedrock.fail", playerEntity.getUsername())); GeyserConnector.getInstance().getLogger().debug("The size of '" + playerEntity.getUsername() + "' skin is: " + clientData.getSkinImageWidth() + "x" + clientData.getSkinImageHeight()); } diff --git a/connector/src/main/resources/config.yml b/connector/src/main/resources/config.yml index eb4c1ddca..91f95e6ed 100644 --- a/connector/src/main/resources/config.yml +++ b/connector/src/main/resources/config.yml @@ -78,8 +78,8 @@ allow-third-party-ears: false # Allow a fake cooldown indicator to be sent. Bedrock players do not see a cooldown as they still use 1.8 combat show-cooldown: true -# The default locale if we dont have the one the client requested -default-locale: en_us +# The default locale if we dont have the one the client requested. Uncomment to not use the default system language. +# default-locale: en_us # Configures if chunk caching should be enabled or not. This keeps an individual # record of each block the client loads in. While this feature does allow for a few diff --git a/connector/src/main/resources/languages b/connector/src/main/resources/languages new file mode 160000 index 000000000..08be7fdd7 --- /dev/null +++ b/connector/src/main/resources/languages @@ -0,0 +1 @@ +Subproject commit 08be7fdd7bd3c1ade46fa8968c04d3d67bb0d378