From 2be16da939db9bcab143c83338406f594112dcc0 Mon Sep 17 00:00:00 2001 From: Shiewk Date: Wed, 30 Jul 2025 13:37:30 +0200 Subject: [PATCH] Internationalization support --- build.gradle | 2 +- .../smoderation/paper/SModerationPaper.java | 31 +++ .../smoderation/paper/command/BanCommand.java | 8 +- .../paper/command/EnderchestSeeCommand.java | 9 +- .../paper/command/InvseeCommand.java | 19 +- .../paper/command/KickCommand.java | 6 +- .../paper/command/ModLogsCommand.java | 27 +-- .../paper/command/MuteCommand.java | 6 +- .../paper/command/OfflineTPCommand.java | 10 +- .../paper/command/SocialSpyCommand.java | 16 +- .../paper/command/UnbanCommand.java | 5 +- .../paper/command/UnmuteCommand.java | 2 +- .../paper/command/VanishCommand.java | 38 +--- .../command/argument/DurationArgument.java | 4 +- .../argument/OfflinePlayerArgument.java | 2 +- .../command/argument/PlayerUUIDArgument.java | 2 +- .../smoderation/paper/input/ChatInput.java | 4 +- .../inventory/ConfirmationInventory.java | 16 +- .../paper/inventory/CustomInventory.java | 6 + .../smoderation/paper/inventory/SModMenu.java | 207 +++++++++--------- .../paper/listener/SocialSpyListener.java | 11 +- .../paper/listener/VanishListener.java | 10 +- .../paper/punishments/Punishment.java | 94 ++------ .../paper/punishments/PunishmentType.java | 14 +- .../paper/translation/TranslatorManager.java | 48 ++++ .../smoderation/paper/util/ByteUtil.java | 2 +- .../smoderation/paper/util/CommandUtil.java | 28 ++- .../smoderation/paper/util/PlayerUtil.java | 52 +---- .../smoderation/paper/util/TimeUtil.java | 165 ++++---------- src/main/resources/paper-plugin.yml | 2 +- .../smoderation/translations/en_us.json | 107 +++++++++ 31 files changed, 470 insertions(+), 483 deletions(-) create mode 100644 src/main/java/de/shiewk/smoderation/paper/translation/TranslatorManager.java create mode 100644 src/main/resources/smoderation/translations/en_us.json diff --git a/build.gradle b/build.gradle index 587f469..e4a1224 100644 --- a/build.gradle +++ b/build.gradle @@ -30,7 +30,7 @@ runPaper { } runServer { - minecraftVersion("1.21.7") + minecraftVersion("1.21.8") downloadPlugins { // for testing from other client versions modrinth("ViaVersion", "5.4.1") diff --git a/src/main/java/de/shiewk/smoderation/paper/SModerationPaper.java b/src/main/java/de/shiewk/smoderation/paper/SModerationPaper.java index 556e6a5..8e6dc99 100644 --- a/src/main/java/de/shiewk/smoderation/paper/SModerationPaper.java +++ b/src/main/java/de/shiewk/smoderation/paper/SModerationPaper.java @@ -1,23 +1,30 @@ package de.shiewk.smoderation.paper; +import com.google.gson.Gson; import de.shiewk.smoderation.paper.command.*; import de.shiewk.smoderation.paper.config.SModerationConfig; import de.shiewk.smoderation.paper.input.ChatInput; import de.shiewk.smoderation.paper.input.ChatInputListener; import de.shiewk.smoderation.paper.listener.*; import de.shiewk.smoderation.paper.storage.PunishmentContainer; +import de.shiewk.smoderation.paper.translation.TranslatorManager; import de.shiewk.smoderation.paper.util.SchedulerUtil; import io.papermc.paper.command.brigadier.Commands; import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents; +import net.kyori.adventure.key.Key; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.text.logger.slf4j.ComponentLogger; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.minimessage.tag.Tag; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.plugin.java.JavaPlugin; import java.io.File; +import java.util.Locale; import static de.shiewk.smoderation.paper.command.VanishCommand.isVanished; import static de.shiewk.smoderation.paper.command.VanishCommand.toggleVanish; @@ -26,6 +33,7 @@ import static org.bukkit.Bukkit.getPluginManager; public final class SModerationPaper extends JavaPlugin { + public static final Gson gson = new Gson(); public static final PunishmentContainer container = new PunishmentContainer(); public static ComponentLogger LOGGER = null; public static SModerationPaper PLUGIN = null; @@ -38,12 +46,23 @@ public final class SModerationPaper extends JavaPlugin { public static final TextColor INACTIVE_COLOR = NamedTextColor.GRAY; public static final TextComponent CHAT_PREFIX = text("SM \u00BB ").color(PRIMARY_COLOR); + private final TranslatorManager translatorManager = new TranslatorManager( + Key.key("smoderation", "translations"), + createMiniMessage(), + "smoderation/translations/", + new Locale[] { + Locale.forLanguageTag("en-US") + } + ); + @Override public void onLoad() { LOGGER = getComponentLogger(); PLUGIN = this; CONFIG = new SModerationConfig(this.getConfig(), this); SAVE_FILE = new File(this.getDataFolder().getAbsolutePath() + "/container.gz"); + LOGGER.info("Loading translations"); + translatorManager.load(); } @Override @@ -109,4 +128,16 @@ public final class SModerationPaper extends JavaPlugin { public static void setTextureProvider(SkinTextureProvider textureProvider) { SModerationPaper.textureProvider = textureProvider; } + + private MiniMessage createMiniMessage() { + return MiniMessage.builder() + .tags(TagResolver.builder() + .resolver(TagResolver.resolver("prefix", Tag.inserting(CHAT_PREFIX))) + .resolver(TagResolver.resolver("primary", Tag.styling(style -> style.color(PRIMARY_COLOR)))) + .resolver(TagResolver.resolver("secondary", Tag.styling(style -> style.color(SECONDARY_COLOR)))) + .resolver(TagResolver.standard()) + .build() + ) + .build(); + } } diff --git a/src/main/java/de/shiewk/smoderation/paper/command/BanCommand.java b/src/main/java/de/shiewk/smoderation/paper/command/BanCommand.java index 1aba3a0..452586e 100644 --- a/src/main/java/de/shiewk/smoderation/paper/command/BanCommand.java +++ b/src/main/java/de/shiewk/smoderation/paper/command/BanCommand.java @@ -40,7 +40,7 @@ public final class BanCommand implements CommandProvider { private int banWithoutReason(CommandContext context) throws CommandSyntaxException { if (SModerationPaper.CONFIG.shouldForceReason()){ - CommandUtil.error("Please provide a reason."); + CommandUtil.errorTranslatable("smod.command.ban.fail.forceReason"); } UUID sender = CommandUtil.getSenderUUID(context.getSource()); UUID target = context.getArgument("player", UUID.class); @@ -62,17 +62,17 @@ public final class BanCommand implements CommandProvider { Player targetPlayer = Bukkit.getPlayer(target); if (duration == 0){ if (targetPlayer == null){ - CommandUtil.error("You can't ban an offline player for less than 1ms."); + CommandUtil.errorTranslatable("smod.command.ban.fail.tooShort"); } else { KickCommand.executeKick(sender, targetPlayer, reason); } return; } if (sender.equals(target)) { - CommandUtil.error("You can't ban yourself."); + CommandUtil.errorTranslatable("smod.command.ban.fail.self"); } else { if (targetPlayer != null && targetPlayer.hasPermission("smod.preventban")){ - CommandUtil.error("This player can't be banned."); + CommandUtil.errorTranslatable("smod.command.ban.fail.protect"); } else { final Punishment punishment = Punishment.ban( System.currentTimeMillis(), diff --git a/src/main/java/de/shiewk/smoderation/paper/command/EnderchestSeeCommand.java b/src/main/java/de/shiewk/smoderation/paper/command/EnderchestSeeCommand.java index 3276ab2..0d78630 100644 --- a/src/main/java/de/shiewk/smoderation/paper/command/EnderchestSeeCommand.java +++ b/src/main/java/de/shiewk/smoderation/paper/command/EnderchestSeeCommand.java @@ -7,15 +7,14 @@ import com.mojang.brigadier.tree.LiteralCommandNode; import de.shiewk.smoderation.paper.util.CommandUtil; import io.papermc.paper.command.brigadier.CommandSourceStack; import io.papermc.paper.command.brigadier.argument.ArgumentTypes; -import net.kyori.adventure.text.Component; import org.bukkit.entity.Player; import java.util.Collection; import java.util.List; -import static de.shiewk.smoderation.paper.SModerationPaper.*; import static io.papermc.paper.command.brigadier.Commands.argument; import static io.papermc.paper.command.brigadier.Commands.literal; +import static net.kyori.adventure.text.Component.translatable; public final class EnderchestSeeCommand implements CommandProvider { @@ -32,11 +31,7 @@ public final class EnderchestSeeCommand implements CommandProvider { private int openEnderChest(CommandContext context) throws CommandSyntaxException { Player sender = CommandUtil.getExecutingPlayer(context.getSource()); Player target = CommandUtil.getPlayerSingle(context, "player"); - sender.sendMessage(CHAT_PREFIX.append( - Component.text("Opening ender chest of ").color(PRIMARY_COLOR) - .append(target.teamDisplayName().colorIfAbsent(SECONDARY_COLOR)) - .append(Component.text(".")) - )); + sender.sendMessage(translatable("smod.command.ecsee.opening", target.teamDisplayName())); sender.openInventory(target.getEnderChest()); return Command.SINGLE_SUCCESS; } diff --git a/src/main/java/de/shiewk/smoderation/paper/command/InvseeCommand.java b/src/main/java/de/shiewk/smoderation/paper/command/InvseeCommand.java index 7085d71..9c87f58 100644 --- a/src/main/java/de/shiewk/smoderation/paper/command/InvseeCommand.java +++ b/src/main/java/de/shiewk/smoderation/paper/command/InvseeCommand.java @@ -14,10 +14,9 @@ import org.bukkit.entity.Player; import java.util.Collection; import java.util.List; -import static de.shiewk.smoderation.paper.SModerationPaper.*; import static io.papermc.paper.command.brigadier.Commands.argument; import static io.papermc.paper.command.brigadier.Commands.literal; -import static net.kyori.adventure.text.Component.text; +import static net.kyori.adventure.text.Component.translatable; public final class InvseeCommand implements CommandProvider { @@ -44,13 +43,9 @@ public final class InvseeCommand implements CommandProvider { Player sender = CommandUtil.getExecutingPlayer(context.getSource()); Player target = CommandUtil.getPlayerSingle(context, "player"); if (sender.equals(target)){ - CommandUtil.error("You can't open your own inventory."); + CommandUtil.errorTranslatable("smod.command.invsee.fail.self"); } - sender.sendMessage(CHAT_PREFIX.append( - text("Opening inventory of ").color(PRIMARY_COLOR) - .append(target.teamDisplayName().colorIfAbsent(SECONDARY_COLOR)) - .append(text(".")) - )); + sender.sendMessage(translatable("smod.command.invsee.opening", target.teamDisplayName())); new InvSeeInventory(sender, target).open(); return Command.SINGLE_SUCCESS; } @@ -59,13 +54,9 @@ public final class InvseeCommand implements CommandProvider { Player sender = CommandUtil.getExecutingPlayer(context.getSource()); Player target = CommandUtil.getPlayerSingle(context, "player"); if (sender.equals(target)){ - CommandUtil.error("You can't open your own inventory."); + CommandUtil.errorTranslatable("smod.command.invsee.fail.self"); } - sender.sendMessage(CHAT_PREFIX.append( - text("Opening inventory of ").color(PRIMARY_COLOR) - .append(target.teamDisplayName().colorIfAbsent(SECONDARY_COLOR)) - .append(text(".")) - )); + sender.sendMessage(translatable("smod.command.invsee.opening", target.teamDisplayName())); new InvSeeEquipmentInventory(sender, target).open(); return Command.SINGLE_SUCCESS; } diff --git a/src/main/java/de/shiewk/smoderation/paper/command/KickCommand.java b/src/main/java/de/shiewk/smoderation/paper/command/KickCommand.java index e7e0f52..7340203 100644 --- a/src/main/java/de/shiewk/smoderation/paper/command/KickCommand.java +++ b/src/main/java/de/shiewk/smoderation/paper/command/KickCommand.java @@ -44,7 +44,7 @@ public final class KickCommand implements CommandProvider { private int kickWithoutReason(CommandContext context) throws CommandSyntaxException { if (SModerationPaper.CONFIG.shouldForceReason()){ - CommandUtil.error("Please provide a reason."); + CommandUtil.errorTranslatable("smod.command.kick.fail.forceReason"); } UUID sender = CommandUtil.getSenderUUID(context.getSource()); Player target = CommandUtil.getPlayerSingle(context, "player"); @@ -55,9 +55,9 @@ public final class KickCommand implements CommandProvider { public static void executeKick(UUID sender, Player target, String reason) throws CommandSyntaxException { UUID targetId = target.getUniqueId(); if (sender.equals(targetId)) { - CommandUtil.error("You can't kick yourself."); + CommandUtil.errorTranslatable("smod.command.kick.fail.self"); } else if (target.hasPermission("smod.preventkick")){ - CommandUtil.error("This player can't be kicked."); + CommandUtil.errorTranslatable("smod.command.kick.fail.protect"); } final Punishment punishment = Punishment.kick( System.currentTimeMillis(), diff --git a/src/main/java/de/shiewk/smoderation/paper/command/ModLogsCommand.java b/src/main/java/de/shiewk/smoderation/paper/command/ModLogsCommand.java index f89595d..a67f6cf 100644 --- a/src/main/java/de/shiewk/smoderation/paper/command/ModLogsCommand.java +++ b/src/main/java/de/shiewk/smoderation/paper/command/ModLogsCommand.java @@ -3,24 +3,23 @@ package de.shiewk.smoderation.paper.command; import com.mojang.brigadier.Command; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.tree.LiteralCommandNode; -import de.shiewk.smoderation.paper.SModerationPaper; import de.shiewk.smoderation.paper.command.argument.PlayerUUIDArgument; import de.shiewk.smoderation.paper.punishments.Punishment; -import de.shiewk.smoderation.paper.punishments.PunishmentType; import de.shiewk.smoderation.paper.util.CommandUtil; import de.shiewk.smoderation.paper.util.PlayerUtil; import de.shiewk.smoderation.paper.util.TimeUtil; import io.papermc.paper.command.brigadier.CommandSourceStack; -import net.kyori.adventure.text.Component; import org.bukkit.command.CommandSender; import java.util.Collection; import java.util.List; import java.util.UUID; -import static de.shiewk.smoderation.paper.SModerationPaper.*; +import static de.shiewk.smoderation.paper.SModerationPaper.container; import static io.papermc.paper.command.brigadier.Commands.argument; import static io.papermc.paper.command.brigadier.Commands.literal; +import static net.kyori.adventure.text.Component.text; +import static net.kyori.adventure.text.Component.translatable; public final class ModLogsCommand implements CommandProvider { @@ -38,21 +37,17 @@ public final class ModLogsCommand implements CommandProvider { CommandSender sender = context.getSource().getSender(); UUID uuid = context.getArgument("player", UUID.class); String name = PlayerUtil.offlinePlayerName(uuid); - sender.sendMessage(CHAT_PREFIX.append(Component.text("Player ").color(PRIMARY_COLOR) - .append(Component.text(name).color(SECONDARY_COLOR)) - .append(Component.text(" (%s)".formatted(uuid)).color(INACTIVE_COLOR)))); - final List punishments = SModerationPaper.container.findAll(p -> p.to.equals(uuid) && p.isActive()); + sender.sendMessage(translatable("smod.command.modlogs.heading", text(name), text(uuid.toString()))); + final List punishments = container.findAll(p -> p.to.equals(uuid) && p.isActive()); for (Punishment punishment : punishments) { - sender.sendMessage(Component.text("- is currently ").color(PRIMARY_COLOR) - .append(Component.text(punishment.type == PunishmentType.BAN ? "banned" : "muted").color(SECONDARY_COLOR)) - .append(Component.text(" until ").color(PRIMARY_COLOR)) - .append(Component.text(TimeUtil.calendarTimestamp(punishment.until)).color(SECONDARY_COLOR)) - .append(Component.text(" (in %s)".formatted(TimeUtil.formatTimeLong(punishment.until - System.currentTimeMillis()))).color(INACTIVE_COLOR)) - .append(Component.text(". Reason: ").color(PRIMARY_COLOR)) - .append(Component.text(punishment.reason).color(SECONDARY_COLOR))); + sender.sendMessage(translatable("smod.command.modlogs." + punishment.type.name().toLowerCase(), + TimeUtil.calendarTimestamp(punishment.until), + TimeUtil.formatTimeLong(punishment.until - System.currentTimeMillis()), + text(punishment.reason) + )); } if (punishments.isEmpty()){ - sender.sendMessage(Component.text("- has no punishments.").color(PRIMARY_COLOR)); + sender.sendMessage(translatable("smod.command.modlogs.none")); } return Command.SINGLE_SUCCESS; } diff --git a/src/main/java/de/shiewk/smoderation/paper/command/MuteCommand.java b/src/main/java/de/shiewk/smoderation/paper/command/MuteCommand.java index 7405b5a..65edd3c 100644 --- a/src/main/java/de/shiewk/smoderation/paper/command/MuteCommand.java +++ b/src/main/java/de/shiewk/smoderation/paper/command/MuteCommand.java @@ -40,7 +40,7 @@ public final class MuteCommand implements CommandProvider { private int muteWithoutReason(CommandContext context) throws CommandSyntaxException { if (SModerationPaper.CONFIG.shouldForceReason()){ - CommandUtil.error("Please provide a reason."); + CommandUtil.errorTranslatable("smod.command.mute.fail.forceReason"); } UUID sender = CommandUtil.getSenderUUID(context.getSource()); UUID target = context.getArgument("player", UUID.class); @@ -60,11 +60,11 @@ public final class MuteCommand implements CommandProvider { public static void executeMute(UUID sender, UUID target, long duration, String reason) throws CommandSyntaxException { if (sender.equals(target)) { - CommandUtil.error("You can't mute yourself."); + CommandUtil.errorTranslatable("smod.command.mute.fail.self"); } else { Player targetPlayer = Bukkit.getPlayer(target); if (targetPlayer != null && targetPlayer.hasPermission("smod.preventmute")){ - CommandUtil.error("This player can't be muted."); + CommandUtil.errorTranslatable("smod.command.mute.fail.protect"); } else { final Punishment punishment = Punishment.mute( System.currentTimeMillis(), diff --git a/src/main/java/de/shiewk/smoderation/paper/command/OfflineTPCommand.java b/src/main/java/de/shiewk/smoderation/paper/command/OfflineTPCommand.java index d9b6501..432b048 100644 --- a/src/main/java/de/shiewk/smoderation/paper/command/OfflineTPCommand.java +++ b/src/main/java/de/shiewk/smoderation/paper/command/OfflineTPCommand.java @@ -16,10 +16,10 @@ import org.bukkit.event.player.PlayerTeleportEvent; import java.util.Collection; import java.util.List; -import static de.shiewk.smoderation.paper.SModerationPaper.*; import static io.papermc.paper.command.brigadier.Commands.argument; import static io.papermc.paper.command.brigadier.Commands.literal; import static net.kyori.adventure.text.Component.text; +import static net.kyori.adventure.text.Component.translatable; public final class OfflineTPCommand implements CommandProvider { @@ -40,15 +40,11 @@ public final class OfflineTPCommand implements CommandProvider { Location location = player.getLocation(); if (location == null) { - CommandUtil.error("This player's location is unknown."); + CommandUtil.errorTranslatable("smod.command.offlinetp.fail.unknown"); } sender.teleportAsync(location, PlayerTeleportEvent.TeleportCause.COMMAND); - sender.sendMessage(CHAT_PREFIX.append( - text("Teleporting you to ").color(PRIMARY_COLOR) - .append(text(PlayerUtil.offlinePlayerName(player.getUniqueId())).colorIfAbsent(SECONDARY_COLOR)) - .append(text(".")) - )); + sender.sendMessage(translatable("smod.command.offlinetp.teleporting", text(PlayerUtil.offlinePlayerName(player.getUniqueId())))); return Command.SINGLE_SUCCESS; } diff --git a/src/main/java/de/shiewk/smoderation/paper/command/SocialSpyCommand.java b/src/main/java/de/shiewk/smoderation/paper/command/SocialSpyCommand.java index c5d53a3..2eeca91 100644 --- a/src/main/java/de/shiewk/smoderation/paper/command/SocialSpyCommand.java +++ b/src/main/java/de/shiewk/smoderation/paper/command/SocialSpyCommand.java @@ -6,15 +6,13 @@ import com.mojang.brigadier.tree.LiteralCommandNode; import de.shiewk.smoderation.paper.listener.SocialSpyListener; import de.shiewk.smoderation.paper.util.CommandUtil; import io.papermc.paper.command.brigadier.CommandSourceStack; -import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.command.CommandSender; import java.util.Collection; import java.util.List; -import static de.shiewk.smoderation.paper.SModerationPaper.CHAT_PREFIX; import static io.papermc.paper.command.brigadier.Commands.literal; -import static net.kyori.adventure.text.Component.text; +import static net.kyori.adventure.text.Component.translatable; public final class SocialSpyCommand implements CommandProvider { @@ -29,17 +27,17 @@ public final class SocialSpyCommand implements CommandProvider { private int toggleSocialSpy(CommandContext context) { CommandSender sender = context.getSource().getSender(); final boolean enabled = SocialSpyListener.toggle(sender); - sender.sendMessage(CHAT_PREFIX.append(text("SocialSpy ").append( - enabled ? - text("enabled").color(NamedTextColor.GREEN) : - text("disabled").color(NamedTextColor.RED) - ).append(text(".")))); + if (enabled){ + sender.sendMessage(translatable("smod.command.socialspy.enabled")); + } else { + sender.sendMessage(translatable("smod.command.socialspy.disabled")); + } return Command.SINGLE_SUCCESS; } @Override public String getCommandDescription() { - return "Enables socialspy mode (you can see private messages of other players)"; + return "Enables SocialSpy mode (allows you to see private messages of other players)"; } @Override diff --git a/src/main/java/de/shiewk/smoderation/paper/command/UnbanCommand.java b/src/main/java/de/shiewk/smoderation/paper/command/UnbanCommand.java index 46ffdbf..5165e97 100644 --- a/src/main/java/de/shiewk/smoderation/paper/command/UnbanCommand.java +++ b/src/main/java/de/shiewk/smoderation/paper/command/UnbanCommand.java @@ -1,5 +1,6 @@ package de.shiewk.smoderation.paper.command; +import com.mojang.brigadier.Command; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.tree.LiteralCommandNode; @@ -39,9 +40,9 @@ public final class UnbanCommand implements CommandProvider { punishment.undo(senderUUID); punishment.broadcastUndo(SModerationPaper.container); } else { - CommandUtil.error("This player is not banned."); + CommandUtil.errorTranslatable("smod.command.unban.fail.notBanned"); } - return com.mojang.brigadier.Command.SINGLE_SUCCESS; + return Command.SINGLE_SUCCESS; } @Override diff --git a/src/main/java/de/shiewk/smoderation/paper/command/UnmuteCommand.java b/src/main/java/de/shiewk/smoderation/paper/command/UnmuteCommand.java index bd9605d..b00149e 100644 --- a/src/main/java/de/shiewk/smoderation/paper/command/UnmuteCommand.java +++ b/src/main/java/de/shiewk/smoderation/paper/command/UnmuteCommand.java @@ -40,7 +40,7 @@ public final class UnmuteCommand implements CommandProvider { punishment.undo(senderUUID); punishment.broadcastUndo(SModerationPaper.container); } else { - CommandUtil.error("This player is not muted."); + CommandUtil.errorTranslatable("smod.command.unmute.fail.notMuted"); } return Command.SINGLE_SUCCESS; } diff --git a/src/main/java/de/shiewk/smoderation/paper/command/VanishCommand.java b/src/main/java/de/shiewk/smoderation/paper/command/VanishCommand.java index dc72f7a..71e4c6f 100644 --- a/src/main/java/de/shiewk/smoderation/paper/command/VanishCommand.java +++ b/src/main/java/de/shiewk/smoderation/paper/command/VanishCommand.java @@ -24,7 +24,7 @@ import java.util.List; import static de.shiewk.smoderation.paper.SModerationPaper.*; import static io.papermc.paper.command.brigadier.Commands.argument; import static io.papermc.paper.command.brigadier.Commands.literal; -import static net.kyori.adventure.text.Component.text; +import static net.kyori.adventure.text.Component.*; public final class VanishCommand implements CommandProvider { @@ -51,7 +51,7 @@ public final class VanishCommand implements CommandProvider { private int toggleVanishForTargets(CommandContext context) throws CommandSyntaxException { List targets = context.getArgument("targets", PlayerSelectorArgumentResolver.class).resolve(context.getSource()); if (targets.isEmpty()){ - CommandUtil.error("No player was found."); + CommandUtil.errorTranslatable("smod.command.vanish.fail.noPlayersFound"); } else { for (Player target : targets) { toggleVanish(target); @@ -72,7 +72,7 @@ public final class VanishCommand implements CommandProvider { @Override public String getCommandDescription() { - return "Toggles vanish mode which prevents other players from seeing you're online"; + return "Toggles vanish mode which prevents other players from seeing you're online."; } @Override @@ -80,7 +80,7 @@ public final class VanishCommand implements CommandProvider { return List.of("smvanish", "smodvanish", "v", "smv"); } - private static final ObjectArrayList vanishedPlayers = new ObjectArrayList<>(1); + private static final ObjectArrayList vanishedPlayers = new ObjectArrayList<>(0); public static void toggleVanish(Player player){ final boolean newStatus = !isVanished(player); @@ -92,14 +92,9 @@ public final class VanishCommand implements CommandProvider { if (newStatus){ vanishedPlayers.add(player); for (CommandSender sender : SModerationPaper.container.collectBroadcastTargets()) { - sender.sendMessage(CHAT_PREFIX.append( - player.displayName().colorIfAbsent(SECONDARY_COLOR) - ).append(text() - .content(" vanished.") - .color(PRIMARY_COLOR) - )); + sender.sendMessage(translatable("smod.command.vanish.broadcast.on", player.teamDisplayName())); } - player.sendMessage(CHAT_PREFIX.append(text("You are now vanished.").color(PRIMARY_COLOR))); + player.sendMessage(translatable("smod.command.vanish.toggle.on")); player.setVisibleByDefault(false); for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { if (onlinePlayer.hasPermission("smod.vanish.see")){ @@ -109,14 +104,9 @@ public final class VanishCommand implements CommandProvider { } else { vanishedPlayers.remove(player); for (CommandSender sender : container.collectBroadcastTargets()) { - sender.sendMessage(CHAT_PREFIX.append( - player.displayName().colorIfAbsent(SECONDARY_COLOR) - ).append(text() - .content(" re-appeared.") - .color(PRIMARY_COLOR) - )); + sender.sendMessage(translatable("smod.command.vanish.broadcast.off", player.teamDisplayName())); } - player.sendMessage(CHAT_PREFIX.append(text("You are no longer vanished.").color(PRIMARY_COLOR))); + player.sendMessage(translatable("smod.command.vanish.toggle.off")); player.setVisibleByDefault(true); } } @@ -153,17 +143,13 @@ public final class VanishCommand implements CommandProvider { public static void listVanishedPlayersTo(CommandSender receiver){ if (vanishedPlayers.isEmpty()){ - receiver.sendMessage(CHAT_PREFIX.append( - text().content("No players are currently vanished.").color(PRIMARY_COLOR) - )); + receiver.sendMessage(translatable("smod.command.vanish.list.none")); } else { - Component vanishList = CHAT_PREFIX.append( - text().content("The following players are currently vanished: ").color(PRIMARY_COLOR) - ); + Component vanishList = empty(); for (ObjectListIterator iterator = vanishedPlayers.iterator(); iterator.hasNext(); ) { Player vanishedPlayer = iterator.next(); vanishList = vanishList.append( - vanishedPlayer.displayName().colorIfAbsent(SECONDARY_COLOR) + vanishedPlayer.teamDisplayName().colorIfAbsent(SECONDARY_COLOR) ); if (iterator.hasNext()){ vanishList = vanishList.append( @@ -171,7 +157,7 @@ public final class VanishCommand implements CommandProvider { ); } } - receiver.sendMessage(vanishList); + receiver.sendMessage(translatable("smod.command.vanish.list", vanishList)); } } } diff --git a/src/main/java/de/shiewk/smoderation/paper/command/argument/DurationArgument.java b/src/main/java/de/shiewk/smoderation/paper/command/argument/DurationArgument.java index 71ffea7..269f6c4 100644 --- a/src/main/java/de/shiewk/smoderation/paper/command/argument/DurationArgument.java +++ b/src/main/java/de/shiewk/smoderation/paper/command/argument/DurationArgument.java @@ -24,7 +24,7 @@ public final class DurationArgument implements CustomArgumentType.Converted 2_592_000_000L; case "y" -> 31_536_000_000L; default -> { - CommandUtil.error("Invalid time span '%s'".formatted(result.group(2))); + CommandUtil.errorTranslatable("smod.argument.duration.fail.invalid", result.group(2)); throw new UnknownError(); // can't happen } }; diff --git a/src/main/java/de/shiewk/smoderation/paper/command/argument/OfflinePlayerArgument.java b/src/main/java/de/shiewk/smoderation/paper/command/argument/OfflinePlayerArgument.java index e3e03e3..3af7886 100644 --- a/src/main/java/de/shiewk/smoderation/paper/command/argument/OfflinePlayerArgument.java +++ b/src/main/java/de/shiewk/smoderation/paper/command/argument/OfflinePlayerArgument.java @@ -22,7 +22,7 @@ public final class OfflinePlayerArgument implements CustomArgumentType.Converted if (player != null){ return player; } else { - CommandUtil.error("That player is not cached."); + CommandUtil.errorTranslatable("smod.argument.offlinePlayer.fail.notCached"); throw new AssertionError(); // can't happen } } diff --git a/src/main/java/de/shiewk/smoderation/paper/command/argument/PlayerUUIDArgument.java b/src/main/java/de/shiewk/smoderation/paper/command/argument/PlayerUUIDArgument.java index 47d55ea..39d132e 100644 --- a/src/main/java/de/shiewk/smoderation/paper/command/argument/PlayerUUIDArgument.java +++ b/src/main/java/de/shiewk/smoderation/paper/command/argument/PlayerUUIDArgument.java @@ -27,7 +27,7 @@ public final class PlayerUUIDArgument implements CustomArgumentType.Converted meta.displayName(applyFormatting(Component.text("Yes").color(NamedTextColor.GREEN)))); - noStack.editMeta(meta -> meta.displayName(applyFormatting(Component.text("No").color(NamedTextColor.RED)))); + yesStack.setData(DataComponentTypes.ITEM_NAME, renderComponent(player, applyFormatting(translatable("smod.confirm.yes")))); + noStack.setData(DataComponentTypes.ITEM_NAME, renderComponent(player, applyFormatting(translatable("smod.confirm.no")))); ItemStack confirmation = new ItemStack(Material.PAPER); - confirmation.editMeta(meta -> meta.displayName(applyFormatting(Component.text(prompt).color(NamedTextColor.GOLD)))); + confirmation.setData(DataComponentTypes.ITEM_NAME, renderComponent(player, prompt.colorIfAbsent(NamedTextColor.GOLD))); inventory.setItem(reversed ? 4 : 0, noStack); inventory.setItem(2, confirmation); diff --git a/src/main/java/de/shiewk/smoderation/paper/inventory/CustomInventory.java b/src/main/java/de/shiewk/smoderation/paper/inventory/CustomInventory.java index 337b64b..925c55f 100644 --- a/src/main/java/de/shiewk/smoderation/paper/inventory/CustomInventory.java +++ b/src/main/java/de/shiewk/smoderation/paper/inventory/CustomInventory.java @@ -2,7 +2,9 @@ package de.shiewk.smoderation.paper.inventory; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.TextDecoration; +import net.kyori.adventure.translation.GlobalTranslator; import org.bukkit.Material; +import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; @@ -22,4 +24,8 @@ public interface CustomInventory extends InventoryHolder { default Component applyFormatting(Component component){ return component.decoration(TextDecoration.ITALIC, false); } + + static Component renderComponent(Player viewer, Component component){ + return GlobalTranslator.render(component.children(component.children().stream().map(c -> renderComponent(viewer, c)).toList()), viewer.locale()); + } } diff --git a/src/main/java/de/shiewk/smoderation/paper/inventory/SModMenu.java b/src/main/java/de/shiewk/smoderation/paper/inventory/SModMenu.java index 4a3315e..de7c972 100644 --- a/src/main/java/de/shiewk/smoderation/paper/inventory/SModMenu.java +++ b/src/main/java/de/shiewk/smoderation/paper/inventory/SModMenu.java @@ -11,6 +11,7 @@ import de.shiewk.smoderation.paper.util.PlayerUtil; import de.shiewk.smoderation.paper.util.SchedulerUtil; import de.shiewk.smoderation.paper.util.TimeUtil; import io.papermc.paper.datacomponent.DataComponentTypes; +import io.papermc.paper.datacomponent.item.ItemLore; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.format.NamedTextColor; @@ -29,7 +30,6 @@ import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -37,37 +37,38 @@ import java.util.function.Consumer; import java.util.function.Predicate; import static de.shiewk.smoderation.paper.SModerationPaper.*; -import static net.kyori.adventure.text.Component.text; +import static de.shiewk.smoderation.paper.inventory.CustomInventory.renderComponent; +import static net.kyori.adventure.text.Component.*; public class SModMenu extends PageableCustomInventory { public enum Filter { - ACTIVE("Active punishments", Punishment::isActive), - OLD("Old punishments", p -> !p.isActive()), - ALL("All punishments", p -> true); + ACTIVE(translatable("smod.menu.filter.active"), Punishment::isActive), + OLD(translatable("smod.menu.filter.expired"), p -> !p.isActive()), + ALL(translatable("smod.menu.filter.all"), p -> true); public static final Material ICON = Material.HOPPER; - public final String name; + public final Component name; public final Predicate filter; - Filter(String name, Predicate filter) { + Filter(Component name, Predicate filter) { this.name = name; this.filter = filter; } } public enum Sort { - EXPIRY("Expiry", Comparator.comparingLong(p -> p.until)), - TIME("Date", Comparator.comparingLong(p -> p.time)), - PLAYER_NAME("Player name", (p1, p2) -> String.CASE_INSENSITIVE_ORDER.compare(PlayerUtil.offlinePlayerName(p1.to), PlayerUtil.offlinePlayerName(p2.to))), - MODERATOR_NAME("Moderator name", (p1, p2) -> String.CASE_INSENSITIVE_ORDER.compare(PlayerUtil.offlinePlayerName(p1.by), PlayerUtil.offlinePlayerName(p2.by))); + EXPIRY(translatable("smod.menu.sort.expiry"), Comparator.comparingLong(p -> p.until)), + TIME(translatable("smod.menu.sort.time"), Comparator.comparingLong(p -> p.time)), + PLAYER_NAME(translatable("smod.menu.sort.playerName"), (p1, p2) -> String.CASE_INSENSITIVE_ORDER.compare(PlayerUtil.offlinePlayerName(p1.to), PlayerUtil.offlinePlayerName(p2.to))), + MODERATOR_NAME(translatable("smod.menu.sort.moderatorName"), (p1, p2) -> String.CASE_INSENSITIVE_ORDER.compare(PlayerUtil.offlinePlayerName(p1.by), PlayerUtil.offlinePlayerName(p2.by))); public static final Material ICON = Material.COMPARATOR; - public final String name; + public final Component name; public final Comparator comparator; - Sort(String name, Comparator comparator) { + Sort(Component name, Comparator comparator) { this.name = name; this.comparator = comparator; } @@ -90,7 +91,7 @@ public class SModMenu extends PageableCustomInventory { public SModMenu(Player player, PunishmentContainer container) { this.player = player; this.container = container; - this.inventory = Bukkit.createInventory(this, 54, text("SMod Menu")); + this.inventory = Bukkit.createInventory(this, 54, translatable("smod.menu")); reload(); } @@ -122,7 +123,7 @@ public class SModMenu extends PageableCustomInventory { // chat event is async SchedulerUtil.scheduleForEntity(PLUGIN, player, this::open); } - }, text("Enter your search query in chat").color(SECONDARY_COLOR), 30); + }, translatable("smod.menu.search.query").color(SECONDARY_COLOR), 30); } @Override @@ -195,87 +196,90 @@ public class SModMenu extends PageableCustomInventory { private ItemStack createFilterItem(){ final Filter filter = getFilter(); final ItemStack stack = new ItemStack(Filter.ICON); - stack.editMeta(meta -> { - meta.displayName(applyFormatting(text("Filter: " + filter.name).color(PRIMARY_COLOR))); - ArrayList lore = new ArrayList<>(); - lore.add(Component.empty()); - for (Filter value : Filter.values()) { - final boolean selected = filter == value; - Component filterText = applyFormatting(text((selected ? "\u00BB " : "") + value.name).color(selected ? SECONDARY_COLOR : INACTIVE_COLOR)); - lore.add(filterText); - } - lore.add(Component.empty()); - lore.add(applyFormatting(text("\u00BB Click to switch filter").color(NamedTextColor.GOLD))); - meta.lore(lore); - }); - filterStack = stack; - return stack; + stack.setData(DataComponentTypes.ITEM_NAME, renderComponent(player, translatable("smod.menu.filter", filter.name).color(PRIMARY_COLOR))); + ItemLore.Builder loreBuilder = ItemLore.lore(); + + loreBuilder.addLine(empty()); + for (Filter value : Filter.values()) { + final boolean selected = filter == value; + Component filterText = renderComponent(player, applyFormatting(text((selected ? "\u00BB " : ""), selected ? SECONDARY_COLOR : INACTIVE_COLOR).append(value.name))); + loreBuilder.addLine(filterText); + } + loreBuilder.addLine(empty()); + loreBuilder.addLine(renderComponent(player, applyFormatting(translatable("smod.menu.filter.switch", NamedTextColor.GOLD)))); + + stack.setData(DataComponentTypes.LORE, loreBuilder.build()); + return filterStack = stack; } private ItemStack createTypeItem(){ final PunishmentType type = getType(); final ItemStack stack = new ItemStack(Material.CHEST); - stack.editMeta(meta -> { - meta.displayName(applyFormatting(text("Type: " + (type == null ? "All" : type.name)).color(PRIMARY_COLOR))); - ArrayList lore = new ArrayList<>(); - lore.add(Component.empty()); - final Consumer addToLore = value -> { - final boolean selected = type == value; - Component typeText = applyFormatting(text((selected ? "\u00BB " : "") + (value == null ? "All" : value.name)).color(selected ? SECONDARY_COLOR : INACTIVE_COLOR)); - lore.add(typeText); - }; - addToLore.accept(null); - for (PunishmentType value : PunishmentType.values()) { - addToLore.accept(value); - } - lore.add(Component.empty()); - lore.add(applyFormatting(text("\u00BB Click to switch type").color(NamedTextColor.GOLD))); - meta.lore(lore); - }); + stack.setData(DataComponentTypes.ITEM_NAME, renderComponent(player, translatable("smod.menu.type", (type == null ? translatable("smod.menu.type.all") : type.name))).color(PRIMARY_COLOR)); + + ItemLore.Builder loreBuilder = ItemLore.lore(); + loreBuilder.addLine(empty()); + final Consumer addToLore = value -> { + final boolean selected = type == value; + Component typeText = renderComponent(player, applyFormatting(text((selected ? "\u00BB " : ""), selected ? SECONDARY_COLOR : INACTIVE_COLOR).append(value == null ? translatable("smod.menu.type.all") : value.name))); + loreBuilder.addLine(typeText); + }; + addToLore.accept(null); + for (PunishmentType value : PunishmentType.values()) { + addToLore.accept(value); + } + + loreBuilder.addLine(empty()); + loreBuilder.addLine(renderComponent(player, applyFormatting(translatable("smod.menu.type.switch", NamedTextColor.GOLD)))); + + stack.setData(DataComponentTypes.LORE, loreBuilder); return typeStack = stack; } private ItemStack createSortItem(){ final Sort sort = getSort(); final ItemStack stack = new ItemStack(Sort.ICON); - stack.editMeta(meta -> { - meta.displayName(applyFormatting(text("Sort by: " + sort.name).color(PRIMARY_COLOR))); - ArrayList lore = new ArrayList<>(); - lore.add(Component.empty()); - for (Sort value : Sort.values()) { - final boolean selected = sort == value; - Component sortText = applyFormatting(text((selected ? "\u00BB " : "") + value.name).color(selected ? SECONDARY_COLOR : INACTIVE_COLOR)); - lore.add(sortText); - } - lore.add(Component.empty()); - lore.add(applyFormatting(text("\u00BB Click to switch sorting option").color(NamedTextColor.GOLD))); - meta.lore(lore); - }); - sortStack = stack; - return stack; + stack.setData(DataComponentTypes.ITEM_NAME, renderComponent(player, translatable("smod.menu.sort", sort.name).color(PRIMARY_COLOR))); + + ItemLore.Builder loreBuilder = ItemLore.lore(); + loreBuilder.addLine(empty()); + + for (Sort value : Sort.values()) { + final boolean selected = sort == value; + Component sortText = renderComponent(player, applyFormatting(text((selected ? "\u00BB " : ""), selected ? SECONDARY_COLOR : INACTIVE_COLOR).append(value.name))); + loreBuilder.addLine(sortText); + } + + loreBuilder.addLine(empty()); + loreBuilder.addLine(renderComponent(player, applyFormatting(translatable("smod.menu.sort.switch", NamedTextColor.GOLD)))); + + stack.setData(DataComponentTypes.LORE, loreBuilder); + return sortStack = stack; } private ItemStack createSearchItem(){ final ItemStack stack = new ItemStack(Material.FLOWER_BANNER_PATTERN); - stack.editMeta(meta -> { - try { - stack.setData(DataComponentTypes.HIDE_ADDITIONAL_TOOLTIP); - } catch (NoSuchFieldError e) { - // that component is no longer present under that name, - // we just create the stack without it instead of throwing - } - meta.displayName(applyFormatting(text("Search").color(PRIMARY_COLOR))); - final ArrayList lore = new ArrayList<>(List.of( - Component.empty(), - applyFormatting(text("Current search query: %s".formatted(searchQuery == null ? "None" : "\"" + searchQuery + "\"")).color(SECONDARY_COLOR)), - Component.empty(), - applyFormatting(text("\u00BB Click to enter new search query").color(NamedTextColor.GOLD)) - )); - if (searchQuery != null){ - lore.add(applyFormatting(text("\u00BB Right click to remove search query").color(NamedTextColor.GOLD))); - } - meta.lore(lore); - }); + + try { + stack.setData(DataComponentTypes.HIDE_ADDITIONAL_TOOLTIP); + } catch (NoSuchFieldError e) { + // that component is no longer present under that name, + // we just create the stack without it instead of throwing + } + + stack.setData(DataComponentTypes.ITEM_NAME, renderComponent(player, translatable("smod.menu.search", PRIMARY_COLOR))); + + ItemLore.Builder loreBuilder = ItemLore.lore(); + loreBuilder.addLines(List.of( + empty(), + renderComponent(player, applyFormatting(translatable("smod.menu.search.current", searchQuery == null ? translatable("smod.menu.search.none") : text('"' + searchQuery + '"'))).color(SECONDARY_COLOR)), + empty(), + renderComponent(player, applyFormatting(translatable("smod.menu.search.new", NamedTextColor.GOLD))) + )); + if (searchQuery != null){ + loreBuilder.addLine(renderComponent(player, applyFormatting(translatable("smod.menu.search.remove", NamedTextColor.GOLD)))); + } + stack.setData(DataComponentTypes.LORE, loreBuilder); return searchStack = stack; } @@ -294,8 +298,8 @@ public class SModMenu extends PageableCustomInventory { )); skullMeta.setPlayerProfile(profile); } - addPunishmentInfo(punishment, meta); }); + addPunishmentInfo(punishment, stack); return stack; }); } else { @@ -308,40 +312,41 @@ public class SModMenu extends PageableCustomInventory { LOGGER.warn("Player {} has a punishment but was never on this server!", punishment.to); } } - addPunishmentInfo(punishment, meta); }); + addPunishmentInfo(punishment, stack); return CompletableFuture.completedFuture(stack); } } - private void addPunishmentInfo(Punishment punishment, ItemMeta meta) { - meta.displayName(applyFormatting(text(punishment.type.name).color(NamedTextColor.RED).decorate(TextDecoration.BOLD))); - ArrayList lore = new ArrayList<>(); - lore.add(applyFormatting(text("Player: ").color(SECONDARY_COLOR).append(text(PlayerUtil.offlinePlayerName(punishment.to)).color(PRIMARY_COLOR)))); - lore.add(applyFormatting(text("Punished by: ").color(SECONDARY_COLOR).append(text(PlayerUtil.offlinePlayerName(punishment.by)).color(PRIMARY_COLOR)))); - lore.add(applyFormatting(text("Timestamp: ").color(SECONDARY_COLOR).append(text(TimeUtil.calendarTimestamp(punishment.time)).color(PRIMARY_COLOR)))); + private void addPunishmentInfo(Punishment punishment, ItemStack stack) { + stack.setData(DataComponentTypes.CUSTOM_NAME, renderComponent(player, applyFormatting(punishment.type.name.color(NamedTextColor.RED).decorate(TextDecoration.BOLD)))); + ItemLore.Builder lore = ItemLore.lore(); + + lore.addLine(renderComponent(player, applyFormatting(translatable("smod.menu.info.player", text(PlayerUtil.offlinePlayerName(punishment.to)))))); + lore.addLine(renderComponent(player, applyFormatting(translatable("smod.menu.info.punishedBy", text(PlayerUtil.offlinePlayerName(punishment.by)))))); + lore.addLine(renderComponent(player, applyFormatting(translatable("smod.menu.info.timestamp", TimeUtil.formatTimeLong(punishment.time))))); + if (punishment.type != PunishmentType.KICK){ - lore.add(applyFormatting(text("Duration: ").color(SECONDARY_COLOR).append(text(TimeUtil.formatTimeLong(punishment.until - punishment.time)).color(PRIMARY_COLOR)))); + lore.addLine(renderComponent(player, applyFormatting(translatable("smod.menu.info.duration", TimeUtil.formatTimeLong(punishment.until - punishment.time))))); long remainingTime = punishment.until - System.currentTimeMillis(); - final String expires; if (remainingTime > 0){ - expires = "in " + TimeUtil.formatTimeLong(remainingTime); + lore.addLine(renderComponent(player, applyFormatting(translatable("smod.menu.info.expiry.future", TimeUtil.formatTimeLong(remainingTime))))); } else { - remainingTime *= -1; - expires = TimeUtil.formatTimeLong(remainingTime) + " ago"; + lore.addLine(renderComponent(player, applyFormatting(translatable("smod.menu.info.expiry.past", TimeUtil.formatTimeLong(-remainingTime))))); } - lore.add(applyFormatting(text("Expires: ").color(SECONDARY_COLOR).append(text(expires).color(PRIMARY_COLOR)))); } - lore.add(applyFormatting(text("Reason: ").color(SECONDARY_COLOR).append(text(punishment.reason).color(PRIMARY_COLOR)))); + + lore.addLine(renderComponent(player, applyFormatting(translatable("smod.menu.info.reason", text(punishment.reason))))); + if (punishment.wasUndone()){ - lore.add(applyFormatting(text("Undone by: ").color(NamedTextColor.RED).append(text(PlayerUtil.offlinePlayerName(punishment.undoneBy())).color(NamedTextColor.GOLD)))); + lore.addLine(renderComponent(player, applyFormatting(translatable("smod.menu.info.undone", text(PlayerUtil.offlinePlayerName(punishment.undoneBy())))))); } else if (punishment.isActive()) { if ((punishment.type == PunishmentType.BAN && player.hasPermission("smod.unban")) || (punishment.type == PunishmentType.MUTE && player.hasPermission("smod.unmute"))){ - lore.add(Component.empty()); - lore.add(applyFormatting(text("\u00BB Click to undo punishment").color(NamedTextColor.GOLD))); + lore.addLine(empty()); + lore.addLine(renderComponent(player, applyFormatting(translatable("smod.menu.info.click", NamedTextColor.GOLD)))); } } - meta.lore(lore); + stack.setData(DataComponentTypes.LORE, lore); } private int rfId = 0; @@ -413,7 +418,7 @@ public class SModMenu extends PageableCustomInventory { if (timestamp != null) { final Punishment punishment = container.findByTimestamp(timestamp); if (punishment != null) { - new ConfirmationInventory(player, "Do you want to undo this punishment?", () -> { + new ConfirmationInventory(player, translatable("smod.menu.undoConfirmation"), () -> { punishment.undo(player.getUniqueId()); punishment.broadcastUndo(container); player.playSound(player, Sound.BLOCK_NOTE_BLOCK_PLING, 1f, 2f); diff --git a/src/main/java/de/shiewk/smoderation/paper/listener/SocialSpyListener.java b/src/main/java/de/shiewk/smoderation/paper/listener/SocialSpyListener.java index 57e9218..c2bae13 100644 --- a/src/main/java/de/shiewk/smoderation/paper/listener/SocialSpyListener.java +++ b/src/main/java/de/shiewk/smoderation/paper/listener/SocialSpyListener.java @@ -16,9 +16,8 @@ import org.bukkit.persistence.PersistentDataType; import java.util.List; -import static de.shiewk.smoderation.paper.SModerationPaper.PRIMARY_COLOR; -import static de.shiewk.smoderation.paper.SModerationPaper.SECONDARY_COLOR; import static net.kyori.adventure.text.Component.text; +import static net.kyori.adventure.text.Component.translatable; public class SocialSpyListener implements Listener { @@ -71,13 +70,7 @@ public class SocialSpyListener implements Listener { public static void command(Player player, String command){ for (CommandSender target : targets) { - target.sendMessage(text("[", PRIMARY_COLOR) - .append(text("SocialSpy", SECONDARY_COLOR)) - .append(text("] ")) - .append(player.displayName().colorIfAbsent(PRIMARY_COLOR)) - .append(text(": ", PRIMARY_COLOR)) - .append(text(command, SECONDARY_COLOR)) - ); + target.sendMessage(translatable("smod.socialspy.command", player.teamDisplayName(), text(command))); } } diff --git a/src/main/java/de/shiewk/smoderation/paper/listener/VanishListener.java b/src/main/java/de/shiewk/smoderation/paper/listener/VanishListener.java index 799f3df..8edbefa 100644 --- a/src/main/java/de/shiewk/smoderation/paper/listener/VanishListener.java +++ b/src/main/java/de/shiewk/smoderation/paper/listener/VanishListener.java @@ -6,7 +6,6 @@ import de.shiewk.smoderation.paper.util.SchedulerUtil; import net.kyori.adventure.key.Key; import net.kyori.adventure.sound.Sound; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.TextDecoration; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -19,6 +18,7 @@ import org.bukkit.persistence.PersistentDataType; import static de.shiewk.smoderation.paper.SModerationPaper.SECONDARY_COLOR; import static net.kyori.adventure.text.Component.text; +import static net.kyori.adventure.text.Component.translatable; public class VanishListener implements Listener { @@ -54,11 +54,7 @@ public class VanishListener implements Listener { broadcast(message.color(null)); } SchedulerUtil.scheduleForEntity(SModerationPaper.PLUGIN, player, () -> { - player.sendMessage(SModerationPaper.CHAT_PREFIX - .decorate(TextDecoration.BOLD) - .append( - text("You are still vanished!") - )); + player.sendMessage(translatable("smod.vanish.stillEnabled")); player.playSound(Sound.sound( Key.key("minecraft", "block.beacon.power_select"), Sound.Source.MASTER, @@ -92,7 +88,7 @@ public class VanishListener implements Listener { private static void broadcast(Component message) { Component result = PREFIX.append(message); - SModerationPaper.LOGGER.info(result); + Bukkit.getConsoleSender().sendMessage(result); for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { if (onlinePlayer.hasPermission("smod.vanish.see")){ onlinePlayer.sendMessage(result); diff --git a/src/main/java/de/shiewk/smoderation/paper/punishments/Punishment.java b/src/main/java/de/shiewk/smoderation/paper/punishments/Punishment.java index 6efe4b0..a68e8a5 100644 --- a/src/main/java/de/shiewk/smoderation/paper/punishments/Punishment.java +++ b/src/main/java/de/shiewk/smoderation/paper/punishments/Punishment.java @@ -16,6 +16,8 @@ import java.io.OutputStream; import java.util.UUID; import static de.shiewk.smoderation.paper.SModerationPaper.*; +import static net.kyori.adventure.text.Component.text; +import static net.kyori.adventure.text.Component.translatable; public class Punishment { public static final String DEFAULT_REASON = "No reason provided."; @@ -96,8 +98,6 @@ public class Punishment { return new Punishment(PunishmentType.KICK, time, time, by, to, reason); } - private static final int BUFFER_LENGTH = 56; - public void writeBytes(OutputStream stream) throws IOException { stream.write(ByteUtil.intToBytes(type.ordinal())); stream.write(ByteUtil.longToBytes(time)); @@ -114,18 +114,8 @@ public class Punishment { } private Component undoMessage(){ - String msg = ""; - switch (type){ - case MUTE -> msg = "unmuted"; - case KICK -> msg = "unkicked??"; - case BAN -> msg = "unbanned"; - } - return Component.text(PlayerUtil.offlinePlayerName(to)).color(SECONDARY_COLOR) - .append(Component.text(" was ").color(PRIMARY_COLOR)) - .append(Component.text(msg)) - .append(Component.text(" by ").color(PRIMARY_COLOR)) - .append(Component.text(PlayerUtil.offlinePlayerName(undoneBy))) - .append(Component.text(".").color(PRIMARY_COLOR)); + String key = "smod.punishment.undo." + type.name().toLowerCase(); + return translatable(key, text(PlayerUtil.offlinePlayerName(to)), text(PlayerUtil.offlinePlayerName(undoneBy))); } public void broadcastUndo(PunishmentContainer container){ @@ -156,37 +146,14 @@ public class Punishment { } private Component broadcastMessage(){ - final String toName = PlayerUtil.offlinePlayerName(to); - switch (type) { - case MUTE -> { - return Component.text(toName).color(SECONDARY_COLOR).append( - Component.text(" has been muted by ").color(PRIMARY_COLOR) - .append(Component.text(PlayerUtil.offlinePlayerName(this.by)).color(SECONDARY_COLOR)) - .append(Component.text(" for ")) - .append(Component.text(TimeUtil.formatTimeLong(this.until - this.time)).color(SECONDARY_COLOR)) - .append(Component.text(".\nReason: ")) - .append(Component.text(reason).color(SECONDARY_COLOR))); - } - case KICK -> { - return Component.text(toName).color(SECONDARY_COLOR).append( - Component.text(" has been kicked by ").color(PRIMARY_COLOR) - .append(Component.text(PlayerUtil.offlinePlayerName(this.by)).color(SECONDARY_COLOR)) - .append(Component.text(".\nReason: ")) - .append(Component.text(reason).color(SECONDARY_COLOR)) - .color(PRIMARY_COLOR)); - } - case BAN -> { - return Component.text(toName).color(SECONDARY_COLOR).append( - Component.text(" has been banned by ").color(PRIMARY_COLOR) - .append(Component.text(PlayerUtil.offlinePlayerName(this.by)).color(SECONDARY_COLOR)) - .append(Component.text(" for ")) - .append(Component.text(TimeUtil.formatTimeLong(this.until - this.time)).color(SECONDARY_COLOR)) - .append(Component.text(".\nReason: ")) - .append(Component.text(reason).color(SECONDARY_COLOR)) - .color(PRIMARY_COLOR)); - } - default -> throw new IllegalStateException("Unknown punishment type " + type); - } + String key = "smod.punishment.broadcast." + type.name().toLowerCase(); + return translatable( + key, + text(PlayerUtil.offlinePlayerName(to)), + text(PlayerUtil.offlinePlayerName(by)), + TimeUtil.formatTimeLong(this.until - this.time), + text(reason) + ); } private void broadcastIssue(PunishmentContainer container){ @@ -208,36 +175,13 @@ public class Punishment { } public Component playerMessage(){ - switch (type) { - case MUTE -> { - return Component.text("You have been muted by ") - .append(Component.text(PlayerUtil.offlinePlayerName(this.by)).color(SECONDARY_COLOR)) - .append(Component.text(".\nReason: ")) - .append(Component.text(reason).color(SECONDARY_COLOR)) - .append(Component.text("\nYour mute expires in ")) - .append(Component.text(TimeUtil.formatTimeLong(this.until - System.currentTimeMillis())).color(SECONDARY_COLOR)) - .append(Component.text(".")) - .color(PRIMARY_COLOR); - } - case KICK -> { - return Component.text("You have been kicked by ") - .append(Component.text(PlayerUtil.offlinePlayerName(this.by)).color(SECONDARY_COLOR)) - .append(Component.text(".\nReason: ")) - .append(Component.text(reason).color(SECONDARY_COLOR)) - .color(PRIMARY_COLOR); - } - case BAN -> { - return Component.text("You have been banned from this server by ") - .append(Component.text(PlayerUtil.offlinePlayerName(this.by)).color(SECONDARY_COLOR)) - .append(Component.text(".\nReason: ")) - .append(Component.text(reason).color(SECONDARY_COLOR)) - .append(Component.text("\nYour ban expires in ")) - .append(Component.text(TimeUtil.formatTimeLong(this.until - System.currentTimeMillis())).color(SECONDARY_COLOR)) - .append(Component.text(".")) - .color(PRIMARY_COLOR); - } - default -> throw new IllegalStateException("Unknown punishment type " + type); - } + String key = "smod.punishment.playerMessage." + type.name().toLowerCase(); + return translatable( + key, + text(PlayerUtil.offlinePlayerName(this.by)), + text(reason), + TimeUtil.formatTimeLong(this.until - System.currentTimeMillis()) + ); } public boolean matchesSearchQuery(String searchQuery) { diff --git a/src/main/java/de/shiewk/smoderation/paper/punishments/PunishmentType.java b/src/main/java/de/shiewk/smoderation/paper/punishments/PunishmentType.java index f51a1a8..968f7d4 100644 --- a/src/main/java/de/shiewk/smoderation/paper/punishments/PunishmentType.java +++ b/src/main/java/de/shiewk/smoderation/paper/punishments/PunishmentType.java @@ -1,13 +1,17 @@ package de.shiewk.smoderation.paper.punishments; +import net.kyori.adventure.text.Component; + +import static net.kyori.adventure.text.Component.translatable; + public enum PunishmentType { - MUTE("Mute"), - KICK("Kick"), - BAN("Ban"); + MUTE(translatable("smod.punishment.name.mute")), + KICK(translatable("smod.punishment.name.kick")), + BAN(translatable("smod.punishment.name.ban")); - public final String name; + public final Component name; - PunishmentType(String name) { + PunishmentType(Component name) { this.name = name; } } diff --git a/src/main/java/de/shiewk/smoderation/paper/translation/TranslatorManager.java b/src/main/java/de/shiewk/smoderation/paper/translation/TranslatorManager.java new file mode 100644 index 0000000..9170427 --- /dev/null +++ b/src/main/java/de/shiewk/smoderation/paper/translation/TranslatorManager.java @@ -0,0 +1,48 @@ +package de.shiewk.smoderation.paper.translation; + +import com.google.gson.JsonIOException; +import com.google.gson.JsonSyntaxException; +import com.google.gson.reflect.TypeToken; +import de.shiewk.smoderation.paper.SModerationPaper; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.minimessage.translation.MiniMessageTranslationStore; +import net.kyori.adventure.translation.GlobalTranslator; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Locale; +import java.util.Map; + +public class TranslatorManager { + + public final String resourcePath; + public final Locale[] availableLocales; + private final MiniMessageTranslationStore translationStore; + + public TranslatorManager(Key key, MiniMessage miniMessage, String resourcePath, Locale[] availableLocales) { + this.resourcePath = resourcePath; + this.availableLocales = availableLocales; + this.translationStore = MiniMessageTranslationStore.create(key, miniMessage); + } + + public void load(){ + for (Locale locale : availableLocales) { + String s = locale.getLanguage() + "_" + locale.getCountry().toLowerCase(); + try (InputStream stream = SModerationPaper.class.getClassLoader().getResourceAsStream(resourcePath + s + ".json")) { + if (stream == null) { + SModerationPaper.LOGGER.warn("Translations for {} not found or not accessible", locale); + continue; + } + + Map translationMap = SModerationPaper.gson.fromJson(new InputStreamReader(stream), new TypeToken<>(){}); + translationStore.registerAll(locale, translationMap); + + } catch (IOException | JsonSyntaxException | JsonIOException e) { + SModerationPaper.LOGGER.warn("Failed to load translations for {}", locale, e); + } + } + GlobalTranslator.translator().addSource(translationStore); + } +} diff --git a/src/main/java/de/shiewk/smoderation/paper/util/ByteUtil.java b/src/main/java/de/shiewk/smoderation/paper/util/ByteUtil.java index 56a48e0..ac9cc80 100644 --- a/src/main/java/de/shiewk/smoderation/paper/util/ByteUtil.java +++ b/src/main/java/de/shiewk/smoderation/paper/util/ByteUtil.java @@ -7,7 +7,7 @@ import java.util.UUID; /** * Utility class for byte-based saving of integers, longs and UUIDs */ -public abstract class ByteUtil { +public final class ByteUtil { private ByteUtil(){} public static byte[] longToBytes(long v){ diff --git a/src/main/java/de/shiewk/smoderation/paper/util/CommandUtil.java b/src/main/java/de/shiewk/smoderation/paper/util/CommandUtil.java index e199949..fd4f39b 100644 --- a/src/main/java/de/shiewk/smoderation/paper/util/CommandUtil.java +++ b/src/main/java/de/shiewk/smoderation/paper/util/CommandUtil.java @@ -12,13 +12,15 @@ import org.bukkit.command.ConsoleCommandSender; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; +import java.util.Arrays; import java.util.List; import java.util.UUID; import java.util.function.Predicate; import static de.shiewk.smoderation.paper.util.PlayerUtil.UUID_CONSOLE; +import static net.kyori.adventure.text.Component.translatable; -public abstract class CommandUtil { +public final class CommandUtil { private CommandUtil(){} public static Predicate requirePermission(String permission) { @@ -30,7 +32,7 @@ public abstract class CommandUtil { if (sender instanceof Player player) { return player; } else { - error("Only players can execute this command."); + errorTranslatable("smod.command.fail.players"); throw new UnknownError(); // can't happen } } @@ -42,7 +44,7 @@ public abstract class CommandUtil { } else if (sender instanceof ConsoleCommandSender){ return UUID_CONSOLE; } else { - error("Only players and the console can execute this command."); + errorTranslatable("smod.command.fail.playersConsole"); throw new UnknownError(); // can't happen } } @@ -50,17 +52,27 @@ public abstract class CommandUtil { public static Player getPlayerSingle(CommandContext context, String name) throws CommandSyntaxException { @NotNull List players = context.getArgument(name, PlayerSelectorArgumentResolver.class).resolve(context.getSource()); if (players.isEmpty()){ - CommandUtil.error("Please provide a valid player."); + errorTranslatable("smod.command.fail.invalidPlayer"); } return players.getFirst(); } - public static void error(String message) throws CommandSyntaxException { + public static void error(Component message) throws CommandSyntaxException { throw new CommandSyntaxException( new SimpleCommandExceptionType(null), - MessageComponentSerializer.message().serialize( - Component.text(message) - ) + MessageComponentSerializer.message().serialize(message) ); } + + public static void errorTranslatable(String key) throws CommandSyntaxException { + error(translatable(key)); + } + + public static void errorTranslatable(String key, Component...args) throws CommandSyntaxException { + error(translatable(key, args)); + } + + public static void errorTranslatable(String key, String...args) throws CommandSyntaxException { + errorTranslatable(key, Arrays.stream(args).map(Component::text).toArray(Component[]::new)); + } } diff --git a/src/main/java/de/shiewk/smoderation/paper/util/PlayerUtil.java b/src/main/java/de/shiewk/smoderation/paper/util/PlayerUtil.java index 7be7ff0..7a971b0 100644 --- a/src/main/java/de/shiewk/smoderation/paper/util/PlayerUtil.java +++ b/src/main/java/de/shiewk/smoderation/paper/util/PlayerUtil.java @@ -1,21 +1,14 @@ package de.shiewk.smoderation.paper.util; -import de.shiewk.smoderation.paper.SModerationPaper; -import de.shiewk.smoderation.paper.punishments.Punishment; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.util.StringUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; import java.util.UUID; -import java.util.function.Predicate; -public abstract class PlayerUtil { +public final class PlayerUtil { private PlayerUtil(){} public static final UUID UUID_CONSOLE = new UUID(0, 0); @@ -25,21 +18,7 @@ public abstract class PlayerUtil { return "CONSOLE"; } OfflinePlayer player = Bukkit.getOfflinePlayer(uuid); - return player.getName() == null ? "Unknown Player " + uuid : player.getName(); - } - - public static @Nullable UUID offlinePlayerUUIDByName(String name){ - final OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayerIfCached(name); // getOfflinePlayerIfCached(String) is safer (I have experience with getOfflinePlayer(String) returning wrong UUIDs) - if (offlinePlayer != null) { - return offlinePlayer.getUniqueId(); - } else { - // try to find uuid by searching through punishments - final Punishment punishment = SModerationPaper.container.find(p -> offlinePlayerName(p.to).equalsIgnoreCase(name)); - if (punishment != null) { - return punishment.to; - } - return null; - } + return player.getName() == null ? uuid.toString() : player.getName(); } public static @Nullable CommandSender senderByUUID(@NotNull UUID uid){ @@ -49,31 +28,4 @@ public abstract class PlayerUtil { return Bukkit.getPlayer(uid); } } - - public static @Nullable Player findOnlinePlayer(String name){ - for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { - if (onlinePlayer.getName().equalsIgnoreCase(name)){ - return onlinePlayer; - } - } - return null; - } - - public static List listPlayerNames(){ - return listPlayerNames(pl -> true); - } - - public static List listPlayerNames(final Predicate predicate) { - final ArrayList names = new ArrayList<>(); - for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { - if (predicate.test(onlinePlayer)){ - names.add(onlinePlayer.getName()); - } - } - return List.copyOf(names); - } - - public static List listPlayerNames(String search) { - return StringUtil.copyPartialMatches(search, listPlayerNames(), new ArrayList<>()); - } } diff --git a/src/main/java/de/shiewk/smoderation/paper/util/TimeUtil.java b/src/main/java/de/shiewk/smoderation/paper/util/TimeUtil.java index 97f2adf..3328a25 100644 --- a/src/main/java/de/shiewk/smoderation/paper/util/TimeUtil.java +++ b/src/main/java/de/shiewk/smoderation/paper/util/TimeUtil.java @@ -1,15 +1,19 @@ package de.shiewk.smoderation.paper.util; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; import org.jetbrains.annotations.Range; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; -public abstract class TimeUtil { +import static net.kyori.adventure.text.Component.*; + +public final class TimeUtil { private TimeUtil(){} - public static String formatTimeLong(long millis){ + public static Component formatTimeLong(long millis){ long seconds = millis / 1000; millis -= seconds * 1000; @@ -31,95 +35,65 @@ public abstract class TimeUtil { long weeks = days / 7; days -= weeks * 7; - StringBuilder builder = new StringBuilder(); + TextComponent.Builder builder = empty().toBuilder(); if (years > 0){ - if (!builder.isEmpty()){ - builder.append(" "); + if (!builder.children().isEmpty()){ + builder.appendSpace(); } - builder.append("%s years".formatted(years)); + builder.append(translatable("smod.time.years", text(years))); } if (months > 0){ - if (!builder.isEmpty()){ - builder.append(" "); + if (!builder.children().isEmpty()){ + builder.appendSpace(); } - builder.append("%s months".formatted(months)); + builder.append(translatable("smod.time.months", text(months))); } if (weeks > 0){ - if (!builder.isEmpty()){ - builder.append(" "); + if (!builder.children().isEmpty()){ + builder.appendSpace(); } - builder.append("%s weeks".formatted(weeks)); + builder.append(translatable("smod.time.weeks", text(weeks))); } if (days > 0){ - if (!builder.isEmpty()){ - builder.append(" "); + if (!builder.children().isEmpty()){ + builder.appendSpace(); } - builder.append("%s days".formatted(days)); + builder.append(translatable("smod.time.days", text(days))); } if (hours > 0){ - if (!builder.isEmpty()){ - builder.append(" "); + if (!builder.children().isEmpty()){ + builder.appendSpace(); } - builder.append("%s hours".formatted(hours)); + builder.append(translatable("smod.time.hours", text(hours))); } if (minutes > 0){ - if (!builder.isEmpty()){ - builder.append(" "); + if (!builder.children().isEmpty()){ + builder.appendSpace(); } - builder.append("%s minutes".formatted(minutes)); + builder.append(translatable("smod.time.minutes", text(minutes))); } if (seconds > 0){ - if (!builder.isEmpty()){ - builder.append(" "); + if (!builder.children().isEmpty()){ + builder.appendSpace(); } - builder.append("%s seconds".formatted(seconds)); + builder.append(translatable("smod.time.seconds", text(seconds))); } - if (builder.isEmpty()){ - builder.append("%s ms".formatted(millis)); + if (builder.children().isEmpty()){ + builder.append(translatable("smod.time.milliseconds", text(millis))); } - return builder.toString(); + return builder.build(); } - public static long parseDurationMillisSafely(String in){ - try { - return parseDurationMillis(in); - } catch (Throwable e){ - return -1; - } - } - - public static long parseDurationMillis(String in){ - if (in.endsWith("ms")){ - return Long.parseLong(in.substring(0, in.length()-2)); - } else if (in.endsWith("s")){ - return Long.parseLong(in.substring(0, in.length()-1)) * 1000L; - } else if (in.endsWith("min")){ - return Long.parseLong(in.substring(0, in.length()-3)) * 60000L; - } else if (in.endsWith("h")){ - return Long.parseLong(in.substring(0, in.length()-1)) * 3600000L; - } else if (in.endsWith("d")){ - return Long.parseLong(in.substring(0, in.length()-1)) * 86400000L; - } else if (in.endsWith("w")){ - return Long.parseLong(in.substring(0, in.length()-1)) * 604800000L; - } else if (in.endsWith("mo")){ - return Long.parseLong(in.substring(0, in.length()-2)) * 2592000000L; - } else if (in.endsWith("y")){ - return Long.parseLong(in.substring(0, in.length()-1)) * 31536000000L; - } else { - return -1; - } - } - - public static String calendarTimestamp(long time){ + public static Component calendarTimestamp(long time){ Date date = new Date(time); Calendar calendar = Calendar.getInstance(); calendar.setTime(date); @@ -129,73 +103,22 @@ public abstract class TimeUtil { int hour = calendar.get(Calendar.HOUR_OF_DAY); int year = calendar.get(Calendar.YEAR); - String day = numberWithSuffix(calendar.get(Calendar.DAY_OF_MONTH)); - String month = monthName(calendar.get(Calendar.MONTH)); - return "%s %s %s, %s:%s:%s %s".formatted( + int day = calendar.get(Calendar.DAY_OF_MONTH); + Component month = monthName(calendar.get(Calendar.MONTH)); + return translatable( + "smod.time.timestamp", + text(year), month, - day, - year, - hour < 10 ? "0" + hour : hour, - minute < 10 ? "0" + minute : minute, - second < 10 ? "0" + second : second, - zone.getDisplayName(false, TimeZone.SHORT) + text(day), + text((hour < 10 ? "0" : "") + hour), + text((minute < 10 ? "0" : "") + minute), + text((second < 10 ? "0" : "") + second), + text(zone.getDisplayName(zone.inDaylightTime(calendar.getTime()), TimeZone.SHORT)) ); } - private static String numberWithSuffix(int i){ - return i + numberSuffix(i); + public static Component monthName(@Range(from = 0, to = 11) int m){ + return translatable("smod.time.month." + m); } - private static String numberSuffix(int i){ - if ((i % 10) == 1 && i != 11){ - return "st"; - } else if ((i % 10) == 2 && i != 12){ - return "nd"; - } else if ((i % 10) == 3 && i != 13){ - return "rd"; - } - return "th"; - } - - public static String monthName(@Range(from = 0, to = 11) int m){ - switch (m){ - case 0 -> { - return "January"; - } - case 1 -> { - return "February"; - } - case 2 -> { - return "March"; - } - case 3 -> { - return "April"; - } - case 4 -> { - return "May"; - } - case 5 -> { - return "June"; - } - case 6 -> { - return "July"; - } - case 7 -> { - return "August"; - } - case 8 -> { - return "September"; - } - case 9 -> { - return "October"; - } - case 10 -> { - return "November"; - } - case 11 -> { - return "December"; - } - } - return "Unknown Month"; - } } diff --git a/src/main/resources/paper-plugin.yml b/src/main/resources/paper-plugin.yml index 7c8d652..6a6610c 100644 --- a/src/main/resources/paper-plugin.yml +++ b/src/main/resources/paper-plugin.yml @@ -2,7 +2,7 @@ name: SModeration version: '${version}' main: de.shiewk.smoderation.paper.SModerationPaper website: https://github.com/Shiewk/SModeration -api-version: '1.21.3' +api-version: '1.21.4' load: STARTUP authors: - Shiewk diff --git a/src/main/resources/smoderation/translations/en_us.json b/src/main/resources/smoderation/translations/en_us.json new file mode 100644 index 0000000..dabc9f8 --- /dev/null +++ b/src/main/resources/smoderation/translations/en_us.json @@ -0,0 +1,107 @@ +{ + "smod.command.ban.fail.tooShort": "You can't ban an offline player for less than 1ms.", + "smod.command.ban.fail.self": "You can't ban yourself.", + "smod.command.ban.fail.protect": "This player can't be banned.", + "smod.command.ban.fail.forceReason": "Please provide a reason.", + "smod.argument.duration.fail.invalid": "Invalid duration ''", + "smod.argument.duration.fail.pattern": "Please provide a valid duration, e.g. '1d6h30min'", + "smod.argument.offlinePlayer.fail.notCached": "That player is not cached.", + "smod.argument.uuid.fail.notCached": "That player is not cached. Try providing an UUID instead.", + "smod.command.ecsee.opening": "Opening ender chest of .", + "smod.command.invsee.fail.self": "You can't open your own inventory.", + "smod.command.invsee.opening": "Opening inventory of .", + "smod.command.kick.fail.forceReason": "Please provide a reason.", + "smod.command.kick.fail.self": "You can't kick yourself.", + "smod.command.kick.fail.protect": "This player can't be kicked.", + "smod.command.modlogs.heading": "Player ()", + "smod.command.modlogs.none": "- is not currently muted or banned.", + "smod.command.modlogs.mute": "- is muted until (in ). Reason: ", + "smod.command.modlogs.ban": "- is banned until (in ). Reason: ", + "smod.command.mute.fail.forceReason": "Please provide a reason.", + "smod.command.mute.fail.self": "You can't mute yourself.", + "smod.command.mute.fail.protect": "This player can't be muted.", + "smod.command.offlinetp.fail.unknown": "This player's location is unknown.", + "smod.command.offlinetp.teleporting": "Teleporting you to .", + "smod.command.socialspy.enabled": "SocialSpy enabled.", + "smod.command.socialspy.disabled": "SocialSpy disabled.", + "smod.command.unban.fail.notBanned": "That player is not banned.", + "smod.command.unmute.fail.notMuted": "That player is not muted.", + "smod.command.vanish.fail.noPlayersFound": "No player was found.", + "smod.command.vanish.broadcast.on": " vanished.", + "smod.command.vanish.broadcast.off": " re-appeared.", + "smod.command.vanish.toggle.on": "You are now vanished.", + "smod.command.vanish.toggle.off": "You are no longer vanished.", + "smod.command.vanish.list.none": "No players are currently vanished.", + "smod.command.vanish.list": "The following players are currently vanished: ", + "smod.chatInput.remainingTime": " seconds", + "smod.confirm.yes": "Yes", + "smod.confirm.no": "No", + "smod.socialspy.command": "[SocialSpy] : ", + "smod.vanish.stillEnabled": "You are still vanished!", + "smod.punishment.undo.mute": " was unmuted by .", + "smod.punishment.undo.ban": " was unbanned by .", + "smod.punishment.broadcast.mute": " was muted by for .Reason: ", + "smod.punishment.broadcast.ban": " was banned by for .Reason: ", + "smod.punishment.broadcast.kick": " was kicked by .Reason: ", + "smod.punishment.playerMessage.mute": "You have been muted by .Reason: Your mute expires in .", + "smod.punishment.playerMessage.ban": "You have been banned from this server by .Reason: Your ban expires in .", + "smod.punishment.playerMessage.kick": "You have been kicked by .Reason: ", + "smod.punishment.name.mute": "Mute", + "smod.punishment.name.kick": "Kick", + "smod.punishment.name.ban": "Ban", + "smod.command.fail.players": "Only players can execute this command.", + "smod.command.fail.playersConsole": "Only players and the console can execute this command.", + "smod.command.fail.invalidPlayer": "Please provide a valid player.", + "smod.time.month.0": "January", + "smod.time.month.1": "February", + "smod.time.month.2": "March", + "smod.time.month.3": "April", + "smod.time.month.4": "May", + "smod.time.month.5": "June", + "smod.time.month.6": "July", + "smod.time.month.7": "August", + "smod.time.month.8": "September", + "smod.time.month.9": "October", + "smod.time.month.10": "November", + "smod.time.month.11": "December", + "smod.time.timestamp": " :: ", + "smod.time.years": " years", + "smod.time.months": " months", + "smod.time.weeks": " weeks", + "smod.time.days": " days", + "smod.time.hours": " hours", + "smod.time.minutes": " minutes", + "smod.time.seconds": " seconds", + "smod.time.milliseconds": " milliseconds", + "smod.menu.undoConfirmation": "Are you sure that you want to undo this punishment?", + "smod.menu.filter.active": "Active punishments", + "smod.menu.filter.expired": "Expired punishments", + "smod.menu.filter.all": "All punishments", + "smod.menu.sort.expiry": "Expiry time", + "smod.menu.sort.time": "Time issued", + "smod.menu.sort.playerName": "Player name", + "smod.menu.sort.moderatorName": "Moderator name", + "smod.menu": "SMod Menu", + "smod.menu.search.query": "Enter your search query in the chat", + "smod.menu.filter": "Filter: ", + "smod.menu.filter.switch": "\u00BB Click to switch filter", + "smod.menu.type": "Type: ", + "smod.menu.type.all": "All", + "smod.menu.type.switch": "ยป Click to switch type", + "smod.menu.sort": "Sort by: ", + "smod.menu.sort.switch": "\u00BB Click to switch sorting option", + "smod.menu.search": "Search", + "smod.menu.search.current": "Current search query: ", + "smod.menu.search.none": "None", + "smod.menu.search.new": "\u00BB Click to enter new search query", + "smod.menu.search.remove": "\u00BB Right click to remove search query", + "smod.menu.info.player": "Player: ", + "smod.menu.info.punishedBy": "Punished by: ", + "smod.menu.info.timestamp": "Timestamp: ", + "smod.menu.info.duration": "Duration: ", + "smod.menu.info.expiry.future": "Expires: In ", + "smod.menu.info.expiry.past": "Expired: ago", + "smod.menu.info.reason": "Reason: ", + "smod.menu.info.undone": "Undone by: ", + "smod.menu.info.click": "\u00BB Click to undo punishment" +} \ No newline at end of file