Backport Messages from BigDoors v2
- Backported the Messages system from BigDoors v2. This new system does not overwrite the default language file anymore (nor does it make it read-only). Instead, it reads as many translations from whatever file is selected and takes any missing translations from the translation file inside the jar (and lets the admin know that a translation is missing in the console). This makes the plugin a bit friendlier to use for people that cannot be bothered to read the disclaimer at the top of the file.
This commit is contained in:
		| @@ -9,8 +9,9 @@ import nl.pim16aap2.armoredElytra.nbtEditor.NBTEditor; | ||||
| import nl.pim16aap2.armoredElytra.util.ArmorTier; | ||||
| import nl.pim16aap2.armoredElytra.util.ArmorTierName; | ||||
| import nl.pim16aap2.armoredElytra.util.ConfigLoader; | ||||
| import nl.pim16aap2.armoredElytra.util.Messages; | ||||
| import nl.pim16aap2.armoredElytra.util.UpdateManager; | ||||
| import nl.pim16aap2.armoredElytra.util.messages.Message; | ||||
| import nl.pim16aap2.armoredElytra.util.messages.Messages; | ||||
| import org.bstats.bukkit.Metrics; | ||||
| import org.bukkit.Bukkit; | ||||
| import org.bukkit.ChatColor; | ||||
| @@ -21,9 +22,7 @@ import org.bukkit.plugin.java.JavaPlugin; | ||||
|  | ||||
| import java.util.EnumMap; | ||||
| import java.util.Map; | ||||
| import java.util.Objects; | ||||
| import java.util.logging.Level; | ||||
| import java.util.regex.Pattern; | ||||
|  | ||||
| // TODO: Figure out if the config really does read the list of enchantments accurately. A bug report with a customized config seemed to load the default settings... | ||||
| // TODO: Verify enchantments on startup. Remove them from the list if they're invalid. | ||||
| @@ -37,9 +36,6 @@ public class ArmoredElytra extends JavaPlugin implements Listener | ||||
|     private ConfigLoader config; | ||||
|  | ||||
|     private final Map<ArmorTier, ArmorTierName> armorTierNames = new EnumMap<>(ArmorTier.class); | ||||
|     private String elytraReceivedMessage; | ||||
|     private String usageDeniedMessage; | ||||
|     private String elytraLore; | ||||
|     private boolean upToDate; | ||||
|     private boolean is1_9; | ||||
|     private UpdateManager updateManager; | ||||
| @@ -112,35 +108,19 @@ public class ArmoredElytra extends JavaPlugin implements Listener | ||||
|         return messages; | ||||
|     } | ||||
|  | ||||
|     private final String getColorCodedStringFromConfig(final String configEntry) | ||||
|     { | ||||
|         return getMyMessages().getString(configEntry).replaceAll("&((?i)[0-9a-fk-or])", "\u00A7$1"); | ||||
|     } | ||||
|  | ||||
|     private void readMessages() | ||||
|     { | ||||
|         // Replace color codes by the corresponding colors. | ||||
|         usageDeniedMessage = getColorCodedStringFromConfig("MESSAGES.UsageDenied"); | ||||
|         elytraReceivedMessage = getColorCodedStringFromConfig("MESSAGES.ElytraReceived"); | ||||
|         elytraLore = getColorCodedStringFromConfig("MESSAGES.Lore"); | ||||
|  | ||||
|         armorTierNames.put(ArmorTier.NONE, new ArmorTierName("NONE", "NONE")); // Shouldn't be used. | ||||
|         armorTierNames.put(ArmorTier.LEATHER, new ArmorTierName(getColorCodedStringFromConfig("TIER.Leather"), | ||||
|                                                                 getColorCodedStringFromConfig("TIER.SHORT.Leather"))); | ||||
|         armorTierNames.put(ArmorTier.GOLD, new ArmorTierName(getColorCodedStringFromConfig("TIER.Gold"), | ||||
|                                                              getColorCodedStringFromConfig("TIER.SHORT.Gold"))); | ||||
|         armorTierNames.put(ArmorTier.CHAIN, new ArmorTierName(getColorCodedStringFromConfig("TIER.Chain"), | ||||
|                                                               getColorCodedStringFromConfig("TIER.SHORT.Chain"))); | ||||
|         armorTierNames.put(ArmorTier.IRON, new ArmorTierName(getColorCodedStringFromConfig("TIER.Iron"), | ||||
|                                                              getColorCodedStringFromConfig("TIER.SHORT.Iron"))); | ||||
|         armorTierNames.put(ArmorTier.DIAMOND, new ArmorTierName(getColorCodedStringFromConfig("TIER.Diamond"), | ||||
|                                                                 getColorCodedStringFromConfig("TIER.SHORT.Diamond"))); | ||||
|  | ||||
|         // Change the string to null if it says "NONE". | ||||
|         usageDeniedMessage = (Objects.equals(usageDeniedMessage, new String("NONE")) ? null : usageDeniedMessage); | ||||
|         elytraReceivedMessage = (Objects.equals(elytraReceivedMessage, new String("NONE")) ? null : | ||||
|                                  elytraReceivedMessage); | ||||
|         elytraLore = (Objects.equals(elytraLore, new String("NONE")) ? null : elytraLore); | ||||
|         armorTierNames.put(ArmorTier.LEATHER, new ArmorTierName(messages.getString(Message.TIER_LEATHER), | ||||
|                                                                 messages.getString(Message.TIER_SHORT_LEATHER))); | ||||
|         armorTierNames.put(ArmorTier.GOLD, new ArmorTierName(messages.getString(Message.TIER_GOLD), | ||||
|                                                              messages.getString(Message.TIER_SHORT_GOLD))); | ||||
|         armorTierNames.put(ArmorTier.CHAIN, new ArmorTierName(messages.getString(Message.TIER_CHAIN), | ||||
|                                                               messages.getString(Message.TIER_SHORT_CHAIN))); | ||||
|         armorTierNames.put(ArmorTier.IRON, new ArmorTierName(messages.getString(Message.TIER_IRON), | ||||
|                                                              messages.getString(Message.TIER_SHORT_IRON))); | ||||
|         armorTierNames.put(ArmorTier.DIAMOND, new ArmorTierName(messages.getString(Message.TIER_DIAMOND), | ||||
|                                                                 messages.getString(Message.TIER_SHORT_DIAMOND))); | ||||
|     } | ||||
|  | ||||
|     public boolean playerHasCraftPerm(Player player, ArmorTier armorTier) | ||||
| @@ -185,41 +165,41 @@ public class ArmoredElytra extends JavaPlugin implements Listener | ||||
|         messagePlayer(player, ChatColor.WHITE, str); | ||||
|     } | ||||
|  | ||||
|     private String getMessageWithTierNames(final Message message, final ArmorTier armorTier) | ||||
|     { | ||||
|         ArmorTierName tierName = armorTierNames.get(armorTier); | ||||
|         return getMyMessages().getString(message, | ||||
|                                          tierName.getLongName(), | ||||
|                                          tierName.getShortName()); | ||||
|     } | ||||
|  | ||||
|     // Send the usageDeniedMessage message to the player. | ||||
|     public void usageDeniedMessage(Player player, ArmorTier armorTier) | ||||
|     { | ||||
|         if (usageDeniedMessage != null) | ||||
|         { | ||||
|             final String message = fillInArmorTierInStringNoColor(usageDeniedMessage, armorTier); | ||||
|         final String message = getMessageWithTierNames(Message.MESSAGES_USAGEDENIED, armorTier); | ||||
|         if (!message.equals("NONE")) | ||||
|             messagePlayer(player, ChatColor.RED, message); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Send the elytraReceivedMessage message to the player. | ||||
|     public void elytraReceivedMessage(Player player, ArmorTier armorTier) | ||||
|     { | ||||
|         if (elytraReceivedMessage != null) | ||||
|         { | ||||
|             final String message = fillInArmorTierInStringNoColor(elytraReceivedMessage, armorTier); | ||||
|         final String message = getMessageWithTierNames(Message.MESSAGES_ELYTRARECEIVED, armorTier); | ||||
|         if (!message.equals("NONE")) | ||||
|             messagePlayer(player, ChatColor.GREEN, message); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static final Pattern ARMOR_TIER = Pattern.compile("%ARMOR_TIER%"); | ||||
|     private static final Pattern ARMOR_TIER_SHORT = Pattern.compile("%ARMOR_TIER_SHORT%"); | ||||
|  | ||||
|     // Replace %ARMOR_TIER% by the name of that armor tier in a string, but strip %ARMOR_TIER% of its color. | ||||
|     public String fillInArmorTierInStringNoColor(String string, ArmorTier armorTier) | ||||
|     public void sendNoGivePermissionMessage(Player player, ArmorTier armorTier) | ||||
|     { | ||||
|         if (armorTier == null) | ||||
|         { | ||||
|             getLogger().log(Level.INFO, "ArmorTier was null! Failed to obtain proper string!"); | ||||
|             return string; | ||||
|         } | ||||
|         final ArmorTierName tierName = armorTierNames.get(armorTier); | ||||
|         return ARMOR_TIER_SHORT | ||||
|             .matcher(ARMOR_TIER.matcher(string).replaceAll(ChatColor.stripColor(tierName.getLongName()))) | ||||
|             .replaceAll(ChatColor.stripColor(tierName.getShortName())); | ||||
|         final String message = getMessageWithTierNames(Message.MESSAGES_NOGIVEPERMISSION, armorTier); | ||||
|         messagePlayer(player, ChatColor.RED, message); | ||||
|     } | ||||
|  | ||||
|     public String getElytraLore(ArmorTier armorTier) | ||||
|     { | ||||
|         final String message = getMessageWithTierNames(Message.MESSAGES_LORE, armorTier); | ||||
|         Bukkit.broadcastMessage(message); | ||||
|         return message.equals("NONE") ? null : message; | ||||
|     } | ||||
|  | ||||
|     // Print a string to the log. | ||||
| @@ -258,11 +238,6 @@ public class ArmoredElytra extends JavaPlugin implements Listener | ||||
|         return instance; | ||||
|     } | ||||
|  | ||||
|     public String getElytraLore() | ||||
|     { | ||||
|         return elytraLore; | ||||
|     } | ||||
|  | ||||
|     public String getArmoredElytraName(ArmorTier tier) | ||||
|     { | ||||
|         if (tier == null) | ||||
|   | ||||
| @@ -3,6 +3,7 @@ package nl.pim16aap2.armoredElytra.handlers; | ||||
| import nl.pim16aap2.armoredElytra.ArmoredElytra; | ||||
| import nl.pim16aap2.armoredElytra.nbtEditor.NBTEditor; | ||||
| import nl.pim16aap2.armoredElytra.util.ArmorTier; | ||||
| import nl.pim16aap2.armoredElytra.util.messages.Message; | ||||
| import org.bukkit.Bukkit; | ||||
| import org.bukkit.ChatColor; | ||||
| import org.bukkit.Material; | ||||
| @@ -34,7 +35,7 @@ public class CommandHandler implements CommandExecutor | ||||
|  | ||||
|             if (plugin.getConfigLoader().uninstallMode()) | ||||
|             { | ||||
|                 plugin.messagePlayer(player, plugin.getMyMessages().getString("MESSAGES.UninstallMode")); | ||||
|                 plugin.messagePlayer(player, plugin.getMyMessages().getString(Message.MESSAGES_UNINSTALLMODE)); | ||||
|                 return true; | ||||
|             } | ||||
|  | ||||
| @@ -67,7 +68,8 @@ public class CommandHandler implements CommandExecutor | ||||
|                         allowed = player.hasPermission("armoredelytra.give." + ArmorTier.getName(armorTier)); | ||||
|                     else | ||||
|                     { | ||||
|                         plugin.messagePlayer(player, plugin.getMyMessages().getString("MESSAGES.UnsupportedTier")); | ||||
|                         plugin.messagePlayer(player, plugin.getMyMessages() | ||||
|                                                            .getString(Message.MESSAGES_UNSUPPORTEDTIER)); | ||||
|                         return false; | ||||
|                     } | ||||
|  | ||||
| @@ -79,8 +81,7 @@ public class CommandHandler implements CommandExecutor | ||||
|                         plugin.giveArmoredElytraToPlayer(receiver, newElytra); | ||||
|                     } | ||||
|                     else | ||||
|                         plugin.messagePlayer(player, plugin.fillInArmorTierInStringNoColor( | ||||
|                             plugin.getMyMessages().getString("MESSAGES.NoGivePermission"), armorTier)); | ||||
|                         plugin.sendNoGivePermissionMessage(player, armorTier); | ||||
|                     return true; | ||||
|                 } | ||||
|         } | ||||
| @@ -94,7 +95,7 @@ public class CommandHandler implements CommandExecutor | ||||
|  | ||||
|             if (args.length == 2) | ||||
|             { | ||||
|                 ItemStack newElytra = null; | ||||
|                 ItemStack newElytra; | ||||
|                 final String tier = args[1]; | ||||
|                 if (Bukkit.getPlayer(args[0]) != null) | ||||
|                 { | ||||
|   | ||||
| @@ -11,6 +11,7 @@ import nl.pim16aap2.armoredElytra.util.AllowedToWearEnum; | ||||
| import nl.pim16aap2.armoredElytra.util.ArmorTier; | ||||
| import nl.pim16aap2.armoredElytra.util.Util; | ||||
| import nl.pim16aap2.armoredElytra.util.XMaterial; | ||||
| import nl.pim16aap2.armoredElytra.util.messages.Message; | ||||
| import org.bukkit.Bukkit; | ||||
| import org.bukkit.Material; | ||||
| import org.bukkit.enchantments.Enchantment; | ||||
| @@ -525,7 +526,7 @@ public class EventHandlers implements Listener | ||||
|             case ALLOWED: | ||||
|                 break; | ||||
|             case BROKEN: | ||||
|                 plugin.messagePlayer(e.getPlayer(), plugin.getMyMessages().getString("MESSAGES.RepairNeeded")); | ||||
|                 plugin.messagePlayer(e.getPlayer(), plugin.getMyMessages().getString(Message.MESSAGES_REPAIRNEEDED)); | ||||
|                 e.setCancelled(true); | ||||
|                 break; | ||||
|             case NOPERMISSION: | ||||
|   | ||||
| @@ -3,6 +3,7 @@ package nl.pim16aap2.armoredElytra.nbtEditor; | ||||
| import nl.pim16aap2.armoredElytra.ArmoredElytra; | ||||
| import nl.pim16aap2.armoredElytra.util.ArmorTier; | ||||
| import org.bukkit.Bukkit; | ||||
| import org.bukkit.ChatColor; | ||||
| import org.bukkit.inventory.ItemStack; | ||||
| import org.bukkit.inventory.meta.ItemMeta; | ||||
|  | ||||
| @@ -145,10 +146,9 @@ public class NBTEditor | ||||
|             int armorToughness = ArmorTier.getToughness(armorTier); | ||||
|  | ||||
|             itemmeta.setDisplayName(ArmoredElytra.getInstance().getArmoredElytraName(armorTier)); | ||||
|             if (ArmoredElytra.getInstance().getElytraLore() != null) | ||||
|                 itemmeta | ||||
|                     .setLore(Arrays.asList(ArmoredElytra.getInstance().fillInArmorTierInStringNoColor( | ||||
|                         ArmoredElytra.getInstance().getElytraLore(), armorTier))); | ||||
|             final String message = ChatColor.stripColor(ArmoredElytra.getInstance().getElytraLore(armorTier)); | ||||
|             if (message != null) | ||||
|                 itemmeta.setLore(Arrays.asList(message)); | ||||
|  | ||||
|             item.setItemMeta(itemmeta); | ||||
|  | ||||
|   | ||||
| @@ -1,97 +0,0 @@ | ||||
| package nl.pim16aap2.armoredElytra.util; | ||||
|  | ||||
| import java.io.BufferedReader; | ||||
| import java.io.File; | ||||
| import java.io.FileNotFoundException; | ||||
| import java.io.FileReader; | ||||
| import java.io.IOException; | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
| import java.util.logging.Level; | ||||
|  | ||||
| import nl.pim16aap2.armoredElytra.ArmoredElytra; | ||||
|  | ||||
| public class Messages | ||||
| { | ||||
|     private Map<String, String> messageMap = new HashMap<>(); | ||||
|     private ArmoredElytra plugin; | ||||
|     private File textFile; | ||||
|  | ||||
|     public Messages(ArmoredElytra plugin) | ||||
|     { | ||||
|         this.plugin = plugin; | ||||
|         textFile = new File(plugin.getDataFolder(), plugin.getConfigLoader().languageFile() + ".txt"); | ||||
|         if (!textFile.exists()) | ||||
|         { | ||||
|             plugin.myLogger(Level.WARNING, "Failed to load language file: \"" + textFile + "\": File not found! Using default file instead!"); | ||||
|             textFile = new File(plugin.getDataFolder(), "en_US.txt"); | ||||
|         } | ||||
|         readFile(); | ||||
|     } | ||||
|  | ||||
|     private void writeDefaultFile() | ||||
|     { | ||||
|         File defaultFile = new File(plugin.getDataFolder(), "en_US.txt"); | ||||
|         if (!defaultFile.setWritable(true)) | ||||
|             plugin.myLogger(Level.SEVERE, "Failed to make file \"" + defaultFile + "\" writable!"); | ||||
|  | ||||
|         // Load the default en_US from the resources. | ||||
|         plugin.saveResource("en_US.txt", true); | ||||
|         if (!defaultFile.setWritable(false)) | ||||
|         { | ||||
|             plugin.myLogger(Level.WARNING, "Could not set default language file to read-only!"); | ||||
|             plugin.myLogger(Level.WARNING, "This is not a big problem. Just remember not to edit the file!"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Read locale file. | ||||
|     private void readFile() | ||||
|     { | ||||
|         writeDefaultFile(); | ||||
|  | ||||
|         try (BufferedReader br = new BufferedReader(new FileReader(textFile))) | ||||
|         { | ||||
|             String sCurrentLine; | ||||
|  | ||||
|             while ((sCurrentLine = br.readLine()) != null) | ||||
|             { | ||||
|                 // Ignore comments. | ||||
|                 if (sCurrentLine.startsWith("#") || sCurrentLine.isEmpty()) | ||||
|                     continue; | ||||
|                 String key, value; | ||||
|                 String[] parts = sCurrentLine.split("=", 2); | ||||
|                 key = parts[0]; | ||||
|                 value = parts[1].replaceAll("&((?i)[0-9a-fk-or])", "\u00A7$1"); | ||||
|                 String[] newLineSplitter = value.split("\\\\n"); | ||||
|  | ||||
|                 String values = newLineSplitter[0]; | ||||
|  | ||||
|                 for (int idx = 1; idx < newLineSplitter.length; ++idx) | ||||
|                     values += "\n" + newLineSplitter[idx]; | ||||
|  | ||||
|                 messageMap.put(key, values); | ||||
|             } | ||||
|             br.close(); | ||||
|         } | ||||
|         catch (FileNotFoundException e) | ||||
|         { | ||||
|             plugin.myLogger(Level.SEVERE, "Locale file \"" + textFile + "\" does not exist!"); | ||||
|         } | ||||
|         catch (IOException e) | ||||
|         { | ||||
|             plugin.myLogger(Level.SEVERE, "Could not read locale file: \"" + textFile + "\""); | ||||
|             e.printStackTrace(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Get a string from a key. Returns "null" if null. | ||||
|     public String getString(String key) | ||||
|     { | ||||
|         String value = messageMap.get(key); | ||||
|         if (value != null) | ||||
|             return value; | ||||
|  | ||||
|         plugin.myLogger(Level.WARNING, "Failed to get the translation for key " + key); | ||||
|         return "Translation for key \"" + key + "\" not found! Contact server admin!"; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,12 @@ | ||||
| package nl.pim16aap2.armoredElytra.util.messages; | ||||
|  | ||||
| /** | ||||
|  * Represents supported variables in localizable messages. | ||||
|  * | ||||
|  * @author Pim | ||||
|  */ | ||||
| public interface IMessageVariable | ||||
| { | ||||
|     String VAR_TIER_NAME = "%ARMOR_TIER%"; | ||||
|     String VAR_TIER_NAME_SHORT = "%ARMOR_TIER_SHORT%"; | ||||
| } | ||||
| @@ -0,0 +1,85 @@ | ||||
| package nl.pim16aap2.armoredElytra.util.messages; | ||||
|  | ||||
| /** | ||||
|  * Represents a localizable message. | ||||
|  * | ||||
|  * @author Pim | ||||
|  */ | ||||
| public enum Message implements IMessageVariable | ||||
| { | ||||
|     EMPTY(), | ||||
|  | ||||
|     TIER_LEATHER(), | ||||
|     TIER_GOLD(), | ||||
|     TIER_CHAIN(), | ||||
|     TIER_IRON(), | ||||
|     TIER_DIAMOND(), | ||||
|  | ||||
|     TIER_SHORT_LEATHER(), | ||||
|     TIER_SHORT_GOLD(), | ||||
|     TIER_SHORT_CHAIN(), | ||||
|     TIER_SHORT_IRON(), | ||||
|     TIER_SHORT_DIAMOND(), | ||||
|  | ||||
|     MESSAGES_UNINSTALLMODE(), | ||||
|     MESSAGES_UNSUPPORTEDTIER(), | ||||
|  | ||||
|     MESSAGES_REPAIRNEEDED(), | ||||
|     MESSAGES_LORE(VAR_TIER_NAME, VAR_TIER_NAME_SHORT), | ||||
|     MESSAGES_NOGIVEPERMISSION(VAR_TIER_NAME, VAR_TIER_NAME_SHORT), | ||||
|     MESSAGES_USAGEDENIED(VAR_TIER_NAME, VAR_TIER_NAME_SHORT), | ||||
|     MESSAGES_ELYTRARECEIVED(VAR_TIER_NAME, VAR_TIER_NAME_SHORT), | ||||
|  | ||||
|     ; | ||||
|  | ||||
|     /** | ||||
|      * The list of names that can be used as variables in this message. | ||||
|      * <p> | ||||
|      * For example: "This door will move %BLOCKSTOMOVE% blocks." Would contain at least "%BLOCKSTOMOVE%". | ||||
|      */ | ||||
|     private final String[] variableNames; | ||||
|  | ||||
|     /** | ||||
|      * Constructs a message. | ||||
|      * | ||||
|      * @param variableNames The names of the variables in the value that can be replaced. | ||||
|      */ | ||||
|     Message(final String... variableNames) | ||||
|     { | ||||
|         this.variableNames = variableNames; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the name of the variable at the given position for the given message. | ||||
|      * | ||||
|      * @param msg The message for which to retrieve the variable name. | ||||
|      * @param idx The index of the variable name. | ||||
|      * @return The name of the variable at the given position of this message. | ||||
|      */ | ||||
|     public static String getVariableName(final Message msg, final int idx) | ||||
|     { | ||||
|         return msg.variableNames[idx]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the names of the variables for the given message.. | ||||
|      * | ||||
|      * @param msg The message for which to retrieve the variable names. | ||||
|      * @return The names of the variables of this message. | ||||
|      */ | ||||
|     public static String[] getVariableNames(final Message msg) | ||||
|     { | ||||
|         return msg.variableNames; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the number of variables in this message that can be substituted. | ||||
|      * | ||||
|      * @param msg The message to retrieve the variable count for. | ||||
|      * @return The number of variables in this message that can be substituted. | ||||
|      */ | ||||
|     public static int getVariableCount(final Message msg) | ||||
|     { | ||||
|         return msg.variableNames.length; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,250 @@ | ||||
| package nl.pim16aap2.armoredElytra.util.messages; | ||||
|  | ||||
| import nl.pim16aap2.armoredElytra.ArmoredElytra; | ||||
|  | ||||
| import java.io.BufferedReader; | ||||
| import java.io.File; | ||||
| import java.io.FileNotFoundException; | ||||
| import java.io.FileReader; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.io.InputStreamReader; | ||||
| import java.net.URL; | ||||
| import java.net.URLConnection; | ||||
| import java.nio.file.StandardCopyOption; | ||||
| import java.util.EnumMap; | ||||
| import java.util.Map; | ||||
| import java.util.Objects; | ||||
| import java.util.function.BiConsumer; | ||||
| import java.util.logging.Level; | ||||
| import java.util.regex.Pattern; | ||||
|  | ||||
| public class Messages | ||||
| { | ||||
|     private static final String DEFAULTFILENAME = "en_US.txt"; | ||||
|  | ||||
|     /** | ||||
|      * The map of all messages. | ||||
|      * <p> | ||||
|      * Key: The {@link Message} enum entry. | ||||
|      * <p> | ||||
|      * Value: The translated message. | ||||
|      */ | ||||
|     private Map<Message, String> messageMap = new EnumMap<>(Message.class); | ||||
|  | ||||
|     private final ArmoredElytra plugin; | ||||
|     private File textFile; | ||||
|  | ||||
|     private static final Pattern matchDots = Pattern.compile("\\."); | ||||
|     private static final Pattern matchNewLines = Pattern.compile("\\\\n"); | ||||
|     private static final Pattern matchColorCodes = Pattern.compile("&((?i)[0-9a-fk-or])"); | ||||
|  | ||||
|     public Messages(final ArmoredElytra plugin) | ||||
|     { | ||||
|         this.plugin = plugin; | ||||
|         textFile = new File(plugin.getDataFolder(), plugin.getConfigLoader().languageFile() + ".txt"); | ||||
|         if (!textFile.exists()) | ||||
|         { | ||||
|             plugin.myLogger(Level.WARNING, "Failed to load language file: \"" + textFile + | ||||
|                 "\": File not found! Using default file instead!"); | ||||
|             textFile = new File(plugin.getDataFolder(), DEFAULTFILENAME); | ||||
|         } | ||||
|         populateMessageMap(); | ||||
|     } | ||||
|  | ||||
|     private void writeDefaultFile() | ||||
|     { | ||||
|         File defaultFile = new File(plugin.getDataFolder(), DEFAULTFILENAME); | ||||
|  | ||||
|         InputStream in = null; | ||||
|         try | ||||
|         { | ||||
|             URL url = getClass().getClassLoader().getResource(DEFAULTFILENAME); | ||||
|             if (url == null) | ||||
|                 plugin.myLogger(Level.SEVERE, "Failed to read resources file from the jar! " + | ||||
|                     "The default translation file cannot be generated! Please contact pim16aap2"); | ||||
|             else | ||||
|             { | ||||
|                 URLConnection connection = url.openConnection(); | ||||
|                 connection.setUseCaches(false); | ||||
|                 in = connection.getInputStream(); | ||||
|                 java.nio.file.Files.copy(in, defaultFile.toPath(), StandardCopyOption.REPLACE_EXISTING); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception e) | ||||
|         { | ||||
|             plugin.myLogger(Level.SEVERE, "Failed to write default file to \"" + textFile + "\"."); | ||||
|             e.printStackTrace(); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 if (in != null) | ||||
|                     in.close(); | ||||
|             } | ||||
|             catch (IOException e) | ||||
|             { | ||||
|                 e.printStackTrace(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Processes the contents of a file. Each valid line will be split up in the message key and the message value. It | ||||
|      * then | ||||
|      * | ||||
|      * @param br     The {@link BufferedReader} that supplies the text. | ||||
|      * @param action The action to take for every message and value combination that is encountered. | ||||
|      * @throws IOException | ||||
|      */ | ||||
|     private void processFile(final BufferedReader br, final BiConsumer<Message, String> action) | ||||
|         throws IOException | ||||
|     { | ||||
|         String sCurrentLine; | ||||
|  | ||||
|         while ((sCurrentLine = br.readLine()) != null) | ||||
|         { | ||||
|             // Ignore comments. | ||||
|             if (sCurrentLine.startsWith("#") || sCurrentLine.isEmpty()) | ||||
|                 continue; | ||||
|  | ||||
|             String[] parts = sCurrentLine.split("=", 2); | ||||
|             try | ||||
|             { | ||||
|                 final Message msg = Message.valueOf(matchDots.matcher(parts[0]).replaceAll("_").toUpperCase()); | ||||
|                 final String value = matchNewLines.matcher(matchColorCodes.matcher(parts[1]).replaceAll("\u00A7$1")) | ||||
|                                                   .replaceAll("\n"); | ||||
|                 action.accept(msg, value); | ||||
|             } | ||||
|             catch (IllegalArgumentException e) | ||||
|             { | ||||
|                 plugin.myLogger(Level.WARNING, "Failed to identify Message corresponding to key: \"" + parts[0] + | ||||
|                     "\". Its value will be ignored!"); | ||||
|  | ||||
|                 System.out.println( | ||||
|                     "Trying to find enum value of: " + matchDots.matcher(parts[0]).replaceAll("_").toUpperCase()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Adds a message to the {@link #messageMap}. | ||||
|      * | ||||
|      * @param message The {@link Message}. | ||||
|      * @param value   The value of the message. | ||||
|      */ | ||||
|     private void addMessage(final Message message, final String value) | ||||
|     { | ||||
|         messageMap.put(message, value); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Adds a message to the {@link #messageMap} if it isn't on the map already. | ||||
|      * | ||||
|      * @param message The {@link Message}. | ||||
|      * @param value   The value of the message. | ||||
|      */ | ||||
|     private void addBackupMessage(final Message message, final String value) | ||||
|     { | ||||
|         if (messageMap.containsKey(message)) | ||||
|             return; | ||||
|  | ||||
|         plugin.myLogger(Level.WARNING, | ||||
|                         "Could not find translation of key: \"" + message.name() + "\". Using default value instead!"); | ||||
|         addMessage(message, value); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Reads the translations from the provided translations file. | ||||
|      * <p> | ||||
|      * Missing translations will use their default value. | ||||
|      */ | ||||
|     private void populateMessageMap() | ||||
|     { | ||||
|         try (BufferedReader br = new BufferedReader(new FileReader(textFile))) | ||||
|         { | ||||
|             processFile(br, this::addMessage); | ||||
|         } | ||||
|         catch (FileNotFoundException e) | ||||
|         { | ||||
|             plugin.myLogger(Level.SEVERE, "Locale file \"" + textFile + "\" does not exist!"); | ||||
|             e.printStackTrace(); | ||||
|         } | ||||
|         catch (IOException e) | ||||
|         { | ||||
|             plugin.myLogger(Level.SEVERE, "Could not read locale file! \"" + textFile + "\""); | ||||
|             e.printStackTrace(); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         try (BufferedReader br = new BufferedReader( | ||||
|             new InputStreamReader( | ||||
|                 Objects.requireNonNull(getClass().getClassLoader().getResource(DEFAULTFILENAME)).openStream()))) | ||||
|         { | ||||
|             processFile(br, this::addBackupMessage); | ||||
|         } | ||||
|         catch (FileNotFoundException e) | ||||
|         { | ||||
|             plugin.myLogger(Level.SEVERE, "Failed to load internal locale file!"); | ||||
|             e.printStackTrace(); | ||||
|         } | ||||
|         catch (IOException e) | ||||
|         { | ||||
|             plugin.myLogger(Level.SEVERE, "Could not read internal locale file!"); | ||||
|             e.printStackTrace(); | ||||
|         } | ||||
|  | ||||
|         for (final Message msg : Message.values()) | ||||
|             if (!msg.equals(Message.EMPTY) && !messageMap.containsKey(msg)) | ||||
|             { | ||||
|                 plugin.myLogger(Level.WARNING, "Could not find translation of key: " + msg.name()); | ||||
|                 messageMap.put(msg, getFailureString(msg.name())); | ||||
|             } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the default String to return in case a value could not be found for a given String. | ||||
|      * | ||||
|      * @param key The key that could not be resolved. | ||||
|      * @return The default String to return in case a value could not be found for a given String. | ||||
|      */ | ||||
|     private String getFailureString(final String key) | ||||
|     { | ||||
|         return "Translation for key \"" + key + "\" not found! Contact server admin!"; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the translated message of the provided {@link Message} and substitutes its variables for the provided | ||||
|      * values. | ||||
|      * | ||||
|      * @param msg    The {@link Message} to translate. | ||||
|      * @param values The values to substitute for the variables in the message. | ||||
|      * @return The translated message of the provided {@link Message} and substitutes its variables for the provided | ||||
|      * values. | ||||
|      */ | ||||
|     public String getString(final Message msg, final String... values) | ||||
|     { | ||||
|         if (msg.equals(Message.EMPTY)) | ||||
|             return ""; | ||||
|  | ||||
|         if (values.length != Message.getVariableCount(msg)) | ||||
|         { | ||||
|             plugin.myLogger(Level.SEVERE, | ||||
|                             "Expected " + Message.getVariableCount(msg) + " variables for key " + msg.name() + | ||||
|                                 " but only got " + values.length + ". This is a bug. Please contact pim16aap2!"); | ||||
|             return getFailureString(msg.name()); | ||||
|         } | ||||
|  | ||||
|         String value = messageMap.get(msg); | ||||
|         if (value != null) | ||||
|         { | ||||
|             for (int idx = 0; idx != values.length; ++idx) | ||||
|                 value = value.replaceAll(Message.getVariableName(msg, idx), values[idx]); | ||||
|             return value; | ||||
|         } | ||||
|  | ||||
|         plugin.myLogger(Level.WARNING, "Failed to get the translation for key " + msg.name()); | ||||
|         return getFailureString(msg.name()); | ||||
|     } | ||||
| } | ||||
| @@ -1,5 +1,4 @@ | ||||
| # This file contains all the (partial) sentences used in this plugin. | ||||
| # If you want to modify anything, make sure to do so in a copy, as this file will be regenerated on startup! | ||||
| # You can change which file will be used in the config.yml.  | ||||
| # The format is "key=value" (without quotation marks). You can modify the values, but not the keys.  | ||||
| # Order doesn't matter and you can use comments if you so desire. | ||||
| @@ -25,4 +24,4 @@ MESSAGES.RepairNeeded=&cYou cannot equip this elytra! Please repair it in an anv | ||||
| MESSAGES.Lore=Elytra with %ARMOR_TIER_SHORT% level protection. | ||||
| MESSAGES.NoGivePermission=&cYou do not have the required permission node to give a(n) %ARMOR_TIER%s. | ||||
| MESSAGES.UsageDenied=You do not have the required permissions to wear a(n) %ARMOR_TIER%! | ||||
| MESSAGES.ElytraReceived=&2A(n) %ARMOR_TIER% has been bestowed upon you! | ||||
| MESSAGES.ElytraReceived=&2A(n) %ARMOR_TIER% has been bestowed upon you! | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 
				 Pim van der Loos
					Pim van der Loos