From cfdd93a3450e23dc2f7a4637afe8330ef39496ec Mon Sep 17 00:00:00 2001 From: BBOY2024 Date: Sun, 28 Dec 2025 17:26:49 +0800 Subject: [PATCH] Add internationalization (i18n) and localization support, including Chinese translation and in-game language switch commands --- pom.xml | 3 +- src/main/java/pk/ajneb97/MainCommand.java | 65 ++++++++++- .../pk/ajneb97/configs/ConfigsManager.java | 10 +- .../pk/ajneb97/configs/MainConfigManager.java | 14 +++ .../configs/MessagesConfigManager.java | 110 ++++++++++++++---- src/main/resources/messages_zh.yml | 44 +++++++ 6 files changed, 216 insertions(+), 30 deletions(-) create mode 100644 src/main/resources/messages_zh.yml diff --git a/pom.xml b/pom.xml index 7c98403..c3986fe 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,7 @@ + papermc https://repo.papermc.io/repository/maven-public/ @@ -51,7 +52,7 @@ provided - me.clip + com.github.placeholderapi placeholderapi 2.11.1 provided diff --git a/src/main/java/pk/ajneb97/MainCommand.java b/src/main/java/pk/ajneb97/MainCommand.java index 6453b45..416f59f 100644 --- a/src/main/java/pk/ajneb97/MainCommand.java +++ b/src/main/java/pk/ajneb97/MainCommand.java @@ -17,8 +17,11 @@ import pk.ajneb97.model.inventory.KitInventory; import pk.ajneb97.utils.PlayerUtils; +import java.io.File; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; public class MainCommand implements CommandExecutor, TabCompleter { @@ -82,7 +85,11 @@ public boolean onCommand(CommandSender sender, Command command, String label, St }else if(args[0].equalsIgnoreCase("open")){ open(sender,args,messagesConfig,msgManager); } - else{ + else if(args[0].equalsIgnoreCase("setlanguage")){ + setLanguage(sender,args,messagesConfig,msgManager); + }else if(args[0].equalsIgnoreCase("listlanguages")){ + listLanguages(sender,msgManager,messagesConfig); + }else{ // /kit (short command) if(claimKitShortCommand){ claimKitShortCommand(player,messagesConfig,msgManager,args[0]); @@ -428,6 +435,7 @@ public List onTabComplete(CommandSender sender, Command command, String commands.add("give");commands.add("delete");commands.add("create"); commands.add("reload");commands.add("reset");commands.add("edit"); commands.add("verify");commands.add("migrate");commands.add("open"); + commands.add("setlanguage");commands.add("listlanguages"); } for(String c : commands) { if(args[0].isEmpty() || c.toLowerCase().startsWith(args[0].toLowerCase())) { @@ -520,4 +528,59 @@ public List getInventoryCompletions(String[] args,int argInvPos){ } return completions; } + + public void listLanguages(CommandSender sender, MessagesManager msgManager, FileConfiguration messagesConfig){ + if(!PlayerUtils.isPlayerKitsAdmin(sender)){ + msgManager.sendMessage(sender,messagesConfig.getString("noPermissions"),true); + return; + } + // Dynamically list languages based on files present in the plugin data folder + Set langs = new HashSet<>(); + // always include default english + langs.add("en"); + File dataFolder = plugin.getDataFolder(); + if(dataFolder != null && dataFolder.exists()){ + File[] files = dataFolder.listFiles(); + if(files != null){ + for(File f : files){ + String name = f.getName(); + if(name.startsWith("messages_") && name.endsWith(".yml")){ + String code = name.substring("messages_".length(), name.length()-4); + if(!code.isEmpty()) langs.add(code); + } + } + } + } + + sender.sendMessage(MessagesManager.getLegacyColoredMessage("&6Available languages:")); + for(String l : langs){ + if(l.equals("en")){ + sender.sendMessage(MessagesManager.getLegacyColoredMessage("&e"+l+" &7- English (default)")); + }else{ + sender.sendMessage(MessagesManager.getLegacyColoredMessage("&e"+l+" &7- messages_"+l+".yml")); + } + } + } + + public void setLanguage(CommandSender sender,String[] args,FileConfiguration messagesConfig,MessagesManager msgManager){ + if(!PlayerUtils.isPlayerKitsAdmin(sender)){ + msgManager.sendMessage(sender,messagesConfig.getString("noPermissions"),true); + return; + } + if(args.length < 2){ + msgManager.sendMessage(sender,messagesConfig.getString("commandDoesNotExists"),true); + return; + } + String lang = args[1].toLowerCase(); + // simple validation: only accept 'en' or 'zh' for now + if(!lang.equals("en") && !lang.equals("zh")){ + msgManager.sendMessage(sender, MessagesManager.getLegacyColoredMessage("&cUnsupported language. Use /kit listlanguages"), true); + return; + } + + plugin.getConfigsManager().getMainConfigManager().setLanguage(lang); + // reload messages + plugin.getConfigsManager().reload(); + msgManager.sendMessage(sender, MessagesManager.getLegacyColoredMessage("&aLanguage set to &e"+lang+"&a. Reloaded configs."), true); + } } diff --git a/src/main/java/pk/ajneb97/configs/ConfigsManager.java b/src/main/java/pk/ajneb97/configs/ConfigsManager.java index 7a6ae5f..59d0f6b 100644 --- a/src/main/java/pk/ajneb97/configs/ConfigsManager.java +++ b/src/main/java/pk/ajneb97/configs/ConfigsManager.java @@ -22,9 +22,10 @@ public ConfigsManager(PlayerKits2 plugin){ } public void configure(){ - this.kitsConfigManager.configure(); - this.messagesConfigManager.configure(); + // Load main config first so 'language' and other options are available for other config managers this.mainConfigManager.configure(); + this.messagesConfigManager.configure(); + this.kitsConfigManager.configure(); if(!mainConfigManager.isMySQL()){ this.playersConfigManager.configure(); } @@ -52,10 +53,11 @@ public InventoryConfigManager getInventoryConfigManager() { } public boolean reload(){ - if(!messagesConfigManager.reloadConfig()){ + // Reload main config first so messages can be reloaded based on language + if(!mainConfigManager.reloadConfig()){ return false; } - if(!mainConfigManager.reloadConfig()){ + if(!messagesConfigManager.reloadConfig()){ return false; } if(!inventoryConfigManager.reloadConfig()){ diff --git a/src/main/java/pk/ajneb97/configs/MainConfigManager.java b/src/main/java/pk/ajneb97/configs/MainConfigManager.java index a51a679..75a7c1a 100644 --- a/src/main/java/pk/ajneb97/configs/MainConfigManager.java +++ b/src/main/java/pk/ajneb97/configs/MainConfigManager.java @@ -81,6 +81,11 @@ public void checkUpdate(){ getConfig().set("new_kit_default_save_mode_original", true); configFile.saveConfig(); } + // Add language option defaulting to english (en) + if(!text.contains("language:")){ + getConfig().set("language","en"); + configFile.saveConfig(); + } }catch(IOException e){ e.printStackTrace(); } @@ -129,4 +134,13 @@ public boolean isNewKitDefaultSaveModeOriginal() { public boolean isUseMiniMessage() { return useMiniMessage; } + + // Allow changing the language at runtime and persist it to the main config + public void setLanguage(String lang){ + if(lang == null) return; + getConfig().set("language", lang); + configFile.saveConfig(); + // reload internal fields + configure(); + } } diff --git a/src/main/java/pk/ajneb97/configs/MessagesConfigManager.java b/src/main/java/pk/ajneb97/configs/MessagesConfigManager.java index 77ea39e..d19217f 100644 --- a/src/main/java/pk/ajneb97/configs/MessagesConfigManager.java +++ b/src/main/java/pk/ajneb97/configs/MessagesConfigManager.java @@ -23,18 +23,48 @@ public MessagesConfigManager(PlayerKits2 plugin){ } public void configure(){ - FileConfiguration config = configFile.getConfig(); + // Load default messages (messages.yml) to use as fallback + CommonConfig defaultConfigFile = new CommonConfig("messages.yml", plugin, null, false); + defaultConfigFile.registerConfig(); + FileConfiguration defaultConfig = defaultConfigFile.getConfig(); - //Configure messages + // Determine language from main config (default to en) + String lang = "en"; + try{ + if(this.plugin.getConfigsManager() != null && this.plugin.getConfigsManager().getMainConfigManager() != null) { + FileConfiguration mainConfig = this.plugin.getConfigsManager().getMainConfigManager().getConfig(); + if(mainConfig != null && mainConfig.contains("language")){ + lang = mainConfig.getString("language","en"); + } + } + }catch(Exception e){ + lang = "en"; + } + + // If language is not english, try to load messages_.yml, otherwise use default + if(lang != null && !lang.equalsIgnoreCase("en")){ + CommonConfig langConfig = new CommonConfig("messages_"+lang+".yml", plugin, null, false); + langConfig.registerConfig(); + this.configFile = langConfig; + }else{ + this.configFile = defaultConfigFile; + } + + FileConfiguration config = this.configFile.getConfig(); + + //Configure messages with fallback to default messages.yml MessagesManager msgManager = new MessagesManager(); - msgManager.setTimeSeconds(config.getString("seconds")); - msgManager.setTimeMinutes(config.getString("minutes")); - msgManager.setTimeHours(config.getString("hours")); - msgManager.setTimeDays(config.getString("days")); - msgManager.setPrefix(config.getString("prefix")); - msgManager.setRequirementsMessageStatusSymbolTrue(config.getString("requirementsMessageStatusSymbolTrue")); - msgManager.setRequirementsMessageStatusSymbolFalse(config.getString("requirementsMessageStatusSymbolFalse")); - msgManager.setCooldownPlaceholderReady(config.getString("cooldownPlaceholderReady")); + msgManager.setTimeSeconds(config.contains("seconds") ? config.getString("seconds") : defaultConfig.getString("seconds")); + msgManager.setTimeMinutes(config.contains("minutes") ? config.getString("minutes") : defaultConfig.getString("minutes")); + msgManager.setTimeHours(config.contains("hours") ? config.getString("hours") : defaultConfig.getString("hours")); + msgManager.setTimeDays(config.contains("days") ? config.getString("days") : defaultConfig.getString("days")); + msgManager.setPrefix(config.contains("prefix") ? config.getString("prefix") : defaultConfig.getString("prefix")); + msgManager.setRequirementsMessageStatusSymbolTrue(config.contains("requirementsMessageStatusSymbolTrue") ? + config.getString("requirementsMessageStatusSymbolTrue") : defaultConfig.getString("requirementsMessageStatusSymbolTrue")); + msgManager.setRequirementsMessageStatusSymbolFalse(config.contains("requirementsMessageStatusSymbolFalse") ? + config.getString("requirementsMessageStatusSymbolFalse") : defaultConfig.getString("requirementsMessageStatusSymbolFalse")); + msgManager.setCooldownPlaceholderReady(config.contains("cooldownPlaceholderReady") ? + config.getString("cooldownPlaceholderReady") : defaultConfig.getString("cooldownPlaceholderReady")); this.plugin.setMessagesManager(msgManager); } @@ -44,6 +74,27 @@ public void saveConfig(){ } public boolean reloadConfig(){ + // Reload main config first may be needed by caller; here we just select the appropriate messages file + String lang = "en"; + try{ + if(this.plugin.getConfigsManager() != null && this.plugin.getConfigsManager().getMainConfigManager() != null){ + FileConfiguration mainConfig = this.plugin.getConfigsManager().getMainConfigManager().getConfig(); + if(mainConfig != null && mainConfig.contains("language")){ + lang = mainConfig.getString("language","en"); + } + } + }catch(Exception e){ + lang = "en"; + } + + if(lang != null && !lang.equalsIgnoreCase("en")){ + this.configFile = new CommonConfig("messages_"+lang+".yml", plugin, null, false); + this.configFile.registerConfig(); + }else{ + this.configFile = new CommonConfig("messages.yml", plugin, null, false); + this.configFile.registerConfig(); + } + if(!configFile.reloadConfig()){ return false; } @@ -56,31 +107,42 @@ public FileConfiguration getConfig(){ } public void checkUpdate(){ - Path pathConfig = Paths.get(configFile.getRoute()); + // Always check and update the default messages.yml file (do not modify language-specific files) + Path pathConfig = Paths.get(plugin.getDataFolder().getPath(), "messages.yml"); try{ String text = new String(Files.readAllBytes(pathConfig)); + // Ensure default messages file contains new keys, add them if missing + CommonConfig defaultCfg = new CommonConfig("messages.yml", plugin, null, false); + defaultCfg.registerConfig(); + FileConfiguration cfg = defaultCfg.getConfig(); + + boolean changed = false; if(!text.contains("commandPreviewOtherCorrect:")){ - getConfig().set("onlyPlayerCommand", "&cOnly a player can use this command."); - getConfig().set("commandPreviewOtherCorrect", "&aPreviewing kit &7%kit% &ato &e%player%&a."); - saveConfig(); + cfg.set("onlyPlayerCommand", "&cOnly a player can use this command."); + cfg.set("commandPreviewOtherCorrect", "&aPreviewing kit &7%kit% &ato &e%player%&a."); + changed = true; } if(!text.contains("kitResetCorrectAll:")){ - getConfig().set("kitResetCorrectAll", "&aKit &7%kit% &areset for &7all players&a!"); - saveConfig(); + cfg.set("kitResetCorrectAll", "&aKit &7%kit% &areset for &7all players&a!"); + changed = true; } if(!text.contains("commandOpenError:")){ - getConfig().set("commandOpenError", "&cYou need to use: &7/kit open "); - getConfig().set("inventoryNotExists", "&cThat inventory doesn't exists."); - saveConfig(); + cfg.set("commandOpenError", "&cYou need to use: &7/kit open "); + cfg.set("inventoryNotExists", "&cThat inventory doesn't exists."); + changed = true; } if(!text.contains("commandPreviewError:")){ - getConfig().set("commandPreviewError", "&cYou need to use: &7/kit preview "); - getConfig().set("kitPreviewDisabled", "&cKit preview is disabled."); - saveConfig(); + cfg.set("commandPreviewError", "&cYou need to use: &7/kit preview "); + cfg.set("kitPreviewDisabled", "&cKit preview is disabled."); + changed = true; } if(!text.contains("pluginCriticalErrors:")){ - getConfig().set("pluginCriticalErrors", "&cThe plugin has detected some errors. Check them using &7/kit verify"); - saveConfig(); + cfg.set("pluginCriticalErrors", "&cThe plugin has detected some errors. Check them using &7/kit verify"); + changed = true; + } + + if(changed){ + defaultCfg.saveConfig(); } }catch(IOException e){ diff --git a/src/main/resources/messages_zh.yml b/src/main/resources/messages_zh.yml new file mode 100644 index 0000000..fa3b2b7 --- /dev/null +++ b/src/main/resources/messages_zh.yml @@ -0,0 +1,44 @@ +prefix: "&8[&bPlayerKits&a²&8] " +kitDoesNotExists: "&c套装 &7%kit% &c不存在。" +kitNoPermissions: "&c你没有权限领取此套装。" +noSpaceError: "&c你的物品栏没有足够的空间。" +commandReload: "&a配置已重载。" +commandGiveError: "&c你需要使用: &7/kit give " +commandGiveError2: "&c发放套装时出错: &7%error%" +commandGiveCorrect: "&a套装 &7%kit% &a已给予 &e%player%&a!" +commandClaimError: "&c你需要使用: &7/kit claim " +commandCreateError: "&c你需要使用: &7/kit create (可选)original/configurable" +commandDeleteError: "&c你需要使用: &7/kit delete " +commandResetError: "&c你需要使用: &7/kit reset " +commandEditError: "&c你需要使用: &7/kit edit " +commandPreviewError: "&c你需要使用: &7/kit preview (可选)" +commandOpenError: "&c你需要使用: &7/kit open " +commandDoesNotExists: "&c该命令不存在。" +playerNotOnline: "&c玩家 &7%player% &c不在线。" +oneTimeError: "&c你不能再次领取此套装。" +cooldownError: "&c你需要等待 &7%time% &c才能再次领取此套装。" +kitReceived: "&a已领取套装 &7%kit% &a。" +kitAlreadyExists: "&c配置中已存在套装 &7%kit% &c。" +inventoryEmpty: "&c你的物品栏至少需要1个物品来创建套装。" +kitCreated: "&a套装 &7%kit% &a创建成功。" +kitNotAddedToInventory: "&e无法自动将套装添加到物品栏。没有足够的空间。" +kitAddedToInventory: "&e套装物品已添加到默认物品栏 &7%inventory% &e的槽位 &7%slot%&e!" +noPermissions: "&c你没有权限执行该操作。" +kitDeleted: "&a套装 &7%kit% &a已删除。" +playerDataNotFound: "&c未找到玩家 &7%player% &c的数据。" +kitResetCorrect: "&a套装 &7%kit% &a已为玩家 &7%player% &a重置!" +kitResetCorrectAll: "&a套装 &7%kit% &a已为所有玩家重置!" +cantPreviewError: "&c你无法预览此套装。" +requirementsMessageStatusSymbolTrue: "&a&l✔" +requirementsMessageStatusSymbolFalse: "&c&l✖" +requirementsError: "&c你没有购买所需的条件!" +seconds: 's' +minutes: 'm' +hours: 'h' +days: 'd' +cooldownPlaceholderReady: "&a&l已就绪!" +pluginCriticalErrors: "&c插件检测到一些错误。使用 &7/kit verify &c检查它们" +kitPreviewDisabled: "&c套装预览被禁用。" +inventoryNotExists: "&c该物品栏不存在。" +onlyPlayerCommand: "&c只有玩家可以使用此命令。" +commandPreviewOtherCorrect: "&a正在为 &e%player% &a预览套装 &7%kit%&a。"