Adds possibility for message customization and translation #5

This commit is contained in:
Kristian Knarvik 2022-10-03 18:15:38 +02:00
parent 3d333c6406
commit 30bddfa9c8
20 changed files with 406 additions and 188 deletions

View File

@ -6,6 +6,7 @@ import net.knarcraft.blacksmith.command.BlackSmithConfigTabCompleter;
import net.knarcraft.blacksmith.command.BlackSmithEditCommand; import net.knarcraft.blacksmith.command.BlackSmithEditCommand;
import net.knarcraft.blacksmith.command.BlackSmithEditTabCompleter; import net.knarcraft.blacksmith.command.BlackSmithEditTabCompleter;
import net.knarcraft.blacksmith.config.GlobalSettings; import net.knarcraft.blacksmith.config.GlobalSettings;
import net.knarcraft.blacksmith.formatting.Translator;
import net.knarcraft.blacksmith.listener.NPCClickListener; import net.knarcraft.blacksmith.listener.NPCClickListener;
import net.knarcraft.blacksmith.listener.PlayerListener; import net.knarcraft.blacksmith.listener.PlayerListener;
import net.knarcraft.blacksmith.manager.EconomyManager; import net.knarcraft.blacksmith.manager.EconomyManager;
@ -70,6 +71,8 @@ public class BlacksmithPlugin extends JavaPlugin {
config = new GlobalSettings(this); config = new GlobalSettings(this);
config.load(); config.load();
Translator.loadLanguages("en");
//Set up Vault integration //Set up Vault integration
if (!setUpVault()) { if (!setUpVault()) {
return; return;

View File

@ -5,8 +5,9 @@ import net.knarcraft.blacksmith.config.GlobalSetting;
import net.knarcraft.blacksmith.config.GlobalSettings; import net.knarcraft.blacksmith.config.GlobalSettings;
import net.knarcraft.blacksmith.config.NPCSetting; import net.knarcraft.blacksmith.config.NPCSetting;
import net.knarcraft.blacksmith.config.SettingValueType; import net.knarcraft.blacksmith.config.SettingValueType;
import net.knarcraft.blacksmith.manager.ItemType; import net.knarcraft.blacksmith.formatting.ItemType;
import net.knarcraft.blacksmith.manager.Message; import net.knarcraft.blacksmith.formatting.TranslatableMessage;
import net.knarcraft.blacksmith.formatting.Translator;
import net.knarcraft.blacksmith.util.InputParsingHelper; import net.knarcraft.blacksmith.util.InputParsingHelper;
import net.knarcraft.blacksmith.util.TypeValidationHelper; import net.knarcraft.blacksmith.util.TypeValidationHelper;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
@ -18,8 +19,8 @@ import org.bukkit.command.CommandSender;
import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.Enchantment;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import static net.knarcraft.blacksmith.util.MessageFormatter.displayErrorMessage; import static net.knarcraft.blacksmith.formatting.StringFormatter.displayErrorMessage;
import static net.knarcraft.blacksmith.util.MessageFormatter.displaySuccessMessage; import static net.knarcraft.blacksmith.formatting.StringFormatter.displaySuccessMessage;
/** /**
* The command used for changing global configuration options * The command used for changing global configuration options
@ -41,9 +42,8 @@ public class BlackSmithConfigCommand implements CommandExecutor {
//Changing reforge-able items' default isn't recommended //Changing reforge-able items' default isn't recommended
if (commandName.equalsIgnoreCase(NPCSetting.REFORGE_ABLE_ITEMS.getCommandName())) { if (commandName.equalsIgnoreCase(NPCSetting.REFORGE_ABLE_ITEMS.getCommandName())) {
displayErrorMessage(sender, "Changing reforge-able items globally will make every new " + displayErrorMessage(sender,
"blacksmith unable to reforge anything not in the list, unless it's changed for the " + Translator.getTranslatedMessage(TranslatableMessage.DEFAULT_REFORGE_ABLE_ITEMS_UNCHANGEABLE));
"individual NPC. If you really want to change this, change it manually.");
return false; return false;
} }
@ -82,9 +82,10 @@ public class BlackSmithConfigCommand implements CommandExecutor {
} else { } else {
return false; return false;
} }
displaySuccessMessage(sender, Message.getCurrentValueMessage(correctCommandName, rawValue)); displaySuccessMessage(sender, TranslatableMessage.getCurrentValueMessage(correctCommandName, rawValue));
if (printRawValue) { if (printRawValue) {
sender.sendMessage("Raw value: " + rawValue.replace(ChatColor.COLOR_CHAR, '&')); sender.sendMessage(TranslatableMessage.getRawValueMessage(
rawValue.replace(ChatColor.COLOR_CHAR, '&')));
} }
return true; return true;
} else if (args.length == 2 && isSpecialCase(commandName)) { } else if (args.length == 2 && isSpecialCase(commandName)) {
@ -100,11 +101,11 @@ public class BlackSmithConfigCommand implements CommandExecutor {
String newValue = args[1]; String newValue = args[1];
if (detectedGlobalSetting != null) { if (detectedGlobalSetting != null) {
settings.changeValue(detectedGlobalSetting, newValue); settings.changeValue(detectedGlobalSetting, newValue);
displaySuccessMessage(sender, Message.getValueChangedMessage(detectedGlobalSetting.getCommandName(), newValue)); displaySuccessMessage(sender, TranslatableMessage.getValueChangedMessage(detectedGlobalSetting.getCommandName(), newValue));
return true; return true;
} else if (detectedNPCSetting != null) { } else if (detectedNPCSetting != null) {
settings.changeValue(detectedNPCSetting, newValue); settings.changeValue(detectedNPCSetting, newValue);
displaySuccessMessage(sender, Message.getValueChangedMessage(detectedNPCSetting.getNodeName(), newValue)); displaySuccessMessage(sender, TranslatableMessage.getValueChangedMessage(detectedNPCSetting.getNodeName(), newValue));
return true; return true;
} }
return false; return false;
@ -132,7 +133,7 @@ public class BlackSmithConfigCommand implements CommandExecutor {
} else { } else {
currentValue = String.valueOf(settings.getPricePerDurabilityPoint(material)); currentValue = String.valueOf(settings.getPricePerDurabilityPoint(material));
} }
displaySuccessMessage(sender, Message.getItemCurrentValueMessage(setting.getCommandName(), ItemType.MATERIAL, displaySuccessMessage(sender, TranslatableMessage.getItemCurrentValueMessage(setting.getCommandName(), ItemType.MATERIAL,
material.name(), currentValue)); material.name(), currentValue));
return true; return true;
} else if (setting == GlobalSetting.ENCHANTMENT_COST) { } else if (setting == GlobalSetting.ENCHANTMENT_COST) {
@ -140,7 +141,7 @@ public class BlackSmithConfigCommand implements CommandExecutor {
if (enchantment == null) { if (enchantment == null) {
return false; return false;
} }
displaySuccessMessage(sender, Message.getItemCurrentValueMessage(setting.getCommandName(), ItemType.ENCHANTMENT, displaySuccessMessage(sender, TranslatableMessage.getItemCurrentValueMessage(setting.getCommandName(), ItemType.ENCHANTMENT,
enchantment.toString(), String.valueOf(settings.getEnchantmentCost(enchantment)))); enchantment.toString(), String.valueOf(settings.getEnchantmentCost(enchantment))));
return true; return true;
} else { } else {
@ -204,7 +205,7 @@ public class BlackSmithConfigCommand implements CommandExecutor {
return false; return false;
} }
displaySuccessMessage(sender, Message.getItemValueChangedMessage(detectedGlobalSetting.getCommandName(), displaySuccessMessage(sender, TranslatableMessage.getItemValueChangedMessage(detectedGlobalSetting.getCommandName(),
itemType, itemChanged, newValue)); itemType, itemChanged, newValue));
return true; return true;
} }

View File

@ -24,11 +24,11 @@ public class BlackSmithConfigTabCompleter implements TabCompleter {
@Override @Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] args) { @NotNull String[] args) {
if (args.length == 1) { if (!sender.hasPermission("blacksmith.admin")) {
if (!sender.hasPermission("blacksmith.admin")) { return new ArrayList<>();
return new ArrayList<>(); }
}
if (args.length == 1) {
List<String> availableCommands = new ArrayList<>(); List<String> availableCommands = new ArrayList<>();
availableCommands.add("reload"); availableCommands.add("reload");
for (NPCSetting setting : NPCSetting.values()) { for (NPCSetting setting : NPCSetting.values()) {

View File

@ -3,9 +3,10 @@ package net.knarcraft.blacksmith.command;
import net.citizensnpcs.api.CitizensAPI; import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.npc.NPC;
import net.knarcraft.blacksmith.config.NPCSetting; import net.knarcraft.blacksmith.config.NPCSetting;
import net.knarcraft.blacksmith.manager.Message; import net.knarcraft.blacksmith.formatting.StringFormatter;
import net.knarcraft.blacksmith.formatting.TranslatableMessage;
import net.knarcraft.blacksmith.formatting.Translator;
import net.knarcraft.blacksmith.trait.BlacksmithTrait; import net.knarcraft.blacksmith.trait.BlacksmithTrait;
import net.knarcraft.blacksmith.util.MessageFormatter;
import net.knarcraft.blacksmith.util.TypeValidationHelper; import net.knarcraft.blacksmith.util.TypeValidationHelper;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import org.bukkit.command.Command; import org.bukkit.command.Command;
@ -13,7 +14,7 @@ import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import static net.knarcraft.blacksmith.util.MessageFormatter.displaySuccessMessage; import static net.knarcraft.blacksmith.formatting.StringFormatter.displaySuccessMessage;
/** /**
* The main command used for blacksmith editing * The main command used for blacksmith editing
@ -25,7 +26,8 @@ public class BlackSmithEditCommand implements CommandExecutor {
@NotNull String[] args) { @NotNull String[] args) {
NPC npc = CitizensAPI.getDefaultNPCSelector().getSelected(sender); NPC npc = CitizensAPI.getDefaultNPCSelector().getSelected(sender);
if (npc == null || !npc.hasTrait(BlacksmithTrait.class)) { if (npc == null || !npc.hasTrait(BlacksmithTrait.class)) {
MessageFormatter.displayErrorMessage(sender, "You must select an NPC before running this command"); StringFormatter.displayErrorMessage(sender,
Translator.getTranslatedMessage(TranslatableMessage.NO_NPC_SELECTED));
return true; return true;
} }
@ -59,18 +61,20 @@ public class BlackSmithEditCommand implements CommandExecutor {
if (newValue == null) { if (newValue == null) {
//Display the current value of the setting //Display the current value of the setting
String rawValue = String.valueOf(blacksmithTrait.getSettings().getRawValue(npcSetting)); String rawValue = String.valueOf(blacksmithTrait.getSettings().getRawValue(npcSetting));
displaySuccessMessage(sender, Message.getCurrentValueMessage(npcSetting.getCommandName(), rawValue)); displaySuccessMessage(sender, TranslatableMessage.getCurrentValueMessage(npcSetting.getCommandName(), rawValue));
if (npcSetting.getPath().startsWith("defaults.messages")) { if (npcSetting.getPath().startsWith("defaults.messages")) {
sender.sendMessage("Raw value: " + rawValue.replace(ChatColor.COLOR_CHAR, '&')); sender.sendMessage(TranslatableMessage.getRawValueMessage(
rawValue.replace(ChatColor.COLOR_CHAR, '&')));
} }
return true; return true;
} else { } else {
boolean isValidType = TypeValidationHelper.isValid(npcSetting.getValueType(), newValue, sender); boolean isValidType = TypeValidationHelper.isValid(npcSetting.getValueType(), newValue, sender);
if (isValidType) { if (isValidType) {
//Change the setting //Change the setting
blacksmithTrait.getSettings().changeSetting(npcSetting, Object nullCheckedValue = newValue.equalsIgnoreCase("null") ? null :
ChatColor.translateAlternateColorCodes('&', newValue)); ChatColor.translateAlternateColorCodes('&', newValue);
displaySuccessMessage(sender, Message.getValueChangedMessage(npcSetting.getNodeName(), newValue)); blacksmithTrait.getSettings().changeSetting(npcSetting, nullCheckedValue);
displaySuccessMessage(sender, TranslatableMessage.getValueChangedMessage(npcSetting.getNodeName(), newValue));
return true; return true;
} else { } else {
return false; return false;

View File

@ -21,15 +21,16 @@ public class BlackSmithEditTabCompleter implements TabCompleter {
@Override @Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] args) { @NotNull String[] args) {
if (!sender.hasPermission("blacksmith.edit")) {
return new ArrayList<>();
}
List<String> npcSettings = new ArrayList<>(); List<String> npcSettings = new ArrayList<>();
for (NPCSetting setting : NPCSetting.values()) { for (NPCSetting setting : NPCSetting.values()) {
npcSettings.add(setting.getCommandName()); npcSettings.add(setting.getCommandName());
} }
if (args.length == 1) { if (args.length == 1) {
if (!sender.hasPermission("blacksmith.edit")) {
return new ArrayList<>();
}
return TabCompletionHelper.filterMatchingContains(npcSettings, args[0]); return TabCompletionHelper.filterMatchingContains(npcSettings, args[0]);
} else { } else {
if (npcSettings.contains(args[0]) && args.length <= 2) { if (npcSettings.contains(args[0]) && args.length <= 2) {

View File

@ -1,6 +1,8 @@
package net.knarcraft.blacksmith.command; package net.knarcraft.blacksmith.command;
import net.knarcraft.blacksmith.BlacksmithPlugin; import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.formatting.TranslatableMessage;
import net.knarcraft.blacksmith.formatting.Translator;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor; import org.bukkit.command.TabExecutor;
@ -10,7 +12,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static net.knarcraft.blacksmith.util.MessageFormatter.displaySuccessMessage; import static net.knarcraft.blacksmith.formatting.StringFormatter.displaySuccessMessage;
/** /**
* The command for re-loading the plugin * The command for re-loading the plugin
@ -21,7 +23,7 @@ public class ReloadCommand implements TabExecutor {
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] args) { @NotNull String[] args) {
BlacksmithPlugin.getInstance().reload(); BlacksmithPlugin.getInstance().reload();
displaySuccessMessage(sender, "Blacksmith config reloaded!"); displaySuccessMessage(sender, Translator.getTranslatedMessage(TranslatableMessage.PLUGIN_RELOADED));
return true; return true;
} }

View File

@ -62,11 +62,9 @@ public class NPCSettings {
* @param newValue <p>The new value of the setting</p> * @param newValue <p>The new value of the setting</p>
*/ */
public void changeSetting(NPCSetting setting, Object newValue) { public void changeSetting(NPCSetting setting, Object newValue) {
currentValues.put(setting, newValue);
if (setting == NPCSetting.REFORGE_ABLE_ITEMS) { if (setting == NPCSetting.REFORGE_ABLE_ITEMS) {
currentValues.put(setting, newValue);
updateReforgeAbleItems(); updateReforgeAbleItems();
} else {
currentValues.put(setting, newValue);
} }
} }
@ -338,13 +336,13 @@ public class NPCSettings {
String[] list = string.split(","); String[] list = string.split(",");
List<String> replaced = new ArrayList<>(list.length); List<String> replaced = new ArrayList<>(list.length);
for (String item : list) { for (String item : list) {
replaced.add(SmithPreset.replacePlaceholder(item)); replaced.add(SmithPreset.replacePreset(item));
} }
return String.join(",", replaced); return String.join(",", replaced);
} else if (value instanceof String[] stringList) { } else if (value instanceof String[] stringList) {
List<String> replaced = new ArrayList<>(stringList.length); List<String> replaced = new ArrayList<>(stringList.length);
for (String item : stringList) { for (String item : stringList) {
replaced.add(SmithPreset.replacePlaceholder(item)); replaced.add(SmithPreset.replacePreset(item));
} }
return replaced.toArray(); return replaced.toArray();
} else { } else {

View File

@ -36,19 +36,19 @@ public enum SmithPreset {
RANGED_SMITH; RANGED_SMITH;
/** /**
* Replaces the given string if it's a smith type placeholder * Replaces the given string if it's a smith type preset
* *
* @param possiblePlaceholder <p>The string that might be a placeholder</p> * @param possiblePreset <p>The string that might be a preset</p>
* @return <p>The string, possibly with the placeholder replaced</p> * @return <p>The string, possibly with the preset replaced</p>
*/ */
public static String replacePlaceholder(String possiblePlaceholder) { public static String replacePreset(String possiblePreset) {
for (SmithPreset smithPreset : SmithPreset.values()) { for (SmithPreset smithPreset : SmithPreset.values()) {
if (possiblePlaceholder.replace('-', '_').equalsIgnoreCase("preset:" + if (possiblePreset.replace('-', '_').equalsIgnoreCase("preset:" +
smithPreset.name())) { smithPreset.name())) {
return String.join(",", smithPreset.getMaterialNames()); return String.join(",", smithPreset.getMaterialNames());
} }
} }
return possiblePlaceholder; return possiblePreset;
} }
/** /**

View File

@ -0,0 +1,23 @@
package net.knarcraft.blacksmith.formatting;
/**
* An enum representing all item types used in messages
*/
public enum ItemType {
ENCHANTMENT,
MATERIAL;
/**
* Gets the name of this item type
*
* @return <p>The name of this item type</p>
*/
public String getItemTypeName() {
return switch (this) {
case MATERIAL -> Translator.getTranslatedMessage(TranslatableMessage.ITEM_TYPE_MATERIAL);
case ENCHANTMENT -> Translator.getTranslatedMessage(TranslatableMessage.ITEM_TYPE_ENCHANTMENT);
};
}
}

View File

@ -1,4 +1,4 @@
package net.knarcraft.blacksmith.util; package net.knarcraft.blacksmith.formatting;
import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.npc.NPC;
import net.knarcraft.blacksmith.BlacksmithPlugin; import net.knarcraft.blacksmith.BlacksmithPlugin;
@ -6,14 +6,17 @@ import net.md_5.bungee.api.ChatColor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* A message formatter to ensure uniform colors and format of chat messages * A formatter for formatting displayed messages
*/ */
public final class MessageFormatter { public final class StringFormatter {
private final static String pluginName = BlacksmithPlugin.getInstance().getDescription().getName(); private final static String pluginName = BlacksmithPlugin.getInstance().getDescription().getName();
private MessageFormatter() { private StringFormatter() {
} }
@ -69,4 +72,47 @@ public final class MessageFormatter {
return ChatColor.translateAlternateColorCodes('&', input); return ChatColor.translateAlternateColorCodes('&', input);
} }
/**
* Replaces a placeholder in a string
*
* @param input <p>The input string to replace in</p>
* @param placeholder <p>The placeholder to replace</p>
* @param replacement <p>The replacement value</p>
* @return <p>The input string with the placeholder replaced</p>
*/
public static String replacePlaceholder(String input, String placeholder, String replacement) {
return input.replace(placeholder, replacement);
}
/**
* Replaces placeholders in a string
*
* @param input <p>The input string to replace in</p>
* @param placeholders <p>The placeholders to replace</p>
* @param replacements <p>The replacement values</p>
* @return <p>The input string with placeholders replaced</p>
*/
public static String replacePlaceholders(String input, String[] placeholders, String[] replacements) {
for (int i = 0; i < Math.min(placeholders.length, replacements.length); i++) {
input = replacePlaceholder(input, placeholders[i], replacements[i]);
}
return input;
}
/**
* Translates all found color codes to formatting in a string
*
* @param message <p>The string to search for color codes</p>
* @return <p>The message with color codes translated</p>
*/
public static String translateAllColorCodes(String message) {
message = ChatColor.translateAlternateColorCodes('&', message);
Pattern pattern = Pattern.compile("(#[a-fA-F0-9]{6})");
Matcher matcher = pattern.matcher(message);
while (matcher.find()) {
message = message.replace(matcher.group(), "" + ChatColor.of(matcher.group()));
}
return message;
}
} }

View File

@ -0,0 +1,95 @@
package net.knarcraft.blacksmith.formatting;
import static net.knarcraft.blacksmith.formatting.StringFormatter.replacePlaceholders;
/**
* An enum containing all translatable global messages
*
* <p>This does not include NPC messages as they are configurable per-npc, and can be translated by changing the
* default message values in the main config file.</p>
*/
public enum TranslatableMessage {
VALUE_CHANGED,
VALUE_FOR_ITEM_CHANGED,
CURRENT_VALUE,
CURRENT_VALUE_FOR_ITEM,
ITEM_TYPE_ENCHANTMENT,
ITEM_TYPE_MATERIAL,
RAW_VALUE,
NO_NPC_SELECTED,
DEFAULT_REFORGE_ABLE_ITEMS_UNCHANGEABLE,
INPUT_STRING_LIST_REQUIRED,
INPUT_PERCENTAGE_REQUIRED,
INPUT_STRING_REQUIRED,
INPUT_POSITIVE_DOUBLE_REQUIRED,
INPUT_POSITIVE_INTEGER_REQUIRED,
PERMISSION_DENIED,
PLUGIN_RELOADED;
/**
* Gets the message to display when displaying the raw value of messages
*
* @param rawValue <p>The raw value to display</p>
* @return <p>The message to display</p>
*/
public static String getRawValueMessage(String rawValue) {
return StringFormatter.replacePlaceholder(Translator.getTranslatedMessage(TranslatableMessage.RAW_VALUE),
"{rawValue}", rawValue);
}
/**
* Gets the message to display when a value has been changed
*
* @param setting <p>The setting whose value has been changed</p>
* @param newValue <p>The new value of the setting</p>
* @return <p>The string to display to a user</p>
*/
public static String getValueChangedMessage(String setting, String newValue) {
return replacePlaceholders(Translator.getTranslatedMessage(TranslatableMessage.VALUE_CHANGED),
new String[]{"{setting}", "{newValue}"}, new String[]{setting, newValue});
}
/**
* Gets the message to display when a value has been changed for an item
*
* @param setting <p>The setting whose value has been changed</p>
* @param itemType <p>The type of item changed ("material" or "enchantment")</p>
* @param item <p>The item the setting was changed for (a material or an enchantment name)</p>
* @param newValue <p>The new value of the setting</p>
* @return <p>The string to display to a user</p>
*/
public static String getItemValueChangedMessage(String setting, ItemType itemType, String item, String newValue) {
return replacePlaceholders(Translator.getTranslatedMessage(TranslatableMessage.VALUE_FOR_ITEM_CHANGED),
new String[]{"{setting}", "{itemType}", "{item}", "{newValue}"},
new String[]{setting, itemType.getItemTypeName(), item, newValue});
}
/**
* Gets the message to display when displaying a setting's current value
*
* @param setting <p>The setting whose value is shown</p>
* @param currentValue <p>The current value of the setting</p>
* @return <p>The string to display to a user</p>
*/
public static String getCurrentValueMessage(String setting, String currentValue) {
return replacePlaceholders(Translator.getTranslatedMessage(TranslatableMessage.CURRENT_VALUE),
new String[]{"{setting}", "{currentValue}"}, new String[]{setting, currentValue});
}
/**
* Gets the message to display when displaying a setting's current value for an item
*
* @param setting <p>The setting whose value is shown</p>
* @param itemType <p>The type of item shown ("material" or "enchantment")</p>
* @param item <p>The item the setting is shown for (a material or an enchantment name)</p>
* @param currentValue <p>The current value of the setting</p>
* @return <p>The string to display to a user</p>
*/
public static String getItemCurrentValueMessage(String setting, ItemType itemType, String item, String currentValue) {
return replacePlaceholders(Translator.getTranslatedMessage(TranslatableMessage.CURRENT_VALUE_FOR_ITEM),
new String[]{"{setting}", "{itemType}", "{item}", "{currentValue}"},
new String[]{setting, itemType.getItemTypeName(), item, currentValue});
}
}

View File

@ -0,0 +1,120 @@
package net.knarcraft.blacksmith.formatting;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.util.FileHelper;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
/**
* A tool to get strings translated to the correct language
*/
public final class Translator {
private static Map<TranslatableMessage, String> translatedMessages;
private static Map<TranslatableMessage, String> backupTranslatedMessages;
private Translator() {
}
/**
* Loads the languages used by this translator
*/
public static void loadLanguages(String selectedLanguage) {
backupTranslatedMessages = loadTranslatedMessages("en");
translatedMessages = loadCustomTranslatedMessages(selectedLanguage);
if (translatedMessages == null) {
translatedMessages = loadTranslatedMessages(selectedLanguage);
}
}
/**
* Gets a translated version of the given translatable message
*
* @param translatableMessage <p>The message to translate</p>
* @return <p>The translated message</p>
*/
public static String getTranslatedMessage(TranslatableMessage translatableMessage) {
if (translatedMessages == null) {
return "Translated strings not loaded";
}
String translatedMessage;
if (translatedMessages.containsKey(translatableMessage)) {
translatedMessage = translatedMessages.get(translatableMessage);
} else if (backupTranslatedMessages.containsKey(translatableMessage)) {
translatedMessage = backupTranslatedMessages.get(translatableMessage);
} else {
translatedMessage = translatableMessage.toString();
}
return StringFormatter.translateAllColorCodes(translatedMessage);
}
/**
* Loads all translated messages for the given language
*
* @param language <p>The language chosen by the user</p>
* @return <p>A mapping of all strings for the given language</p>
*/
public static Map<TranslatableMessage, String> loadTranslatedMessages(String language) {
try {
BufferedReader reader = FileHelper.getBufferedReaderForInternalFile("/strings.yml");
return loadTranslatableMessages(language, reader);
} catch (FileNotFoundException e) {
BlacksmithPlugin.getInstance().getLogger().log(Level.SEVERE, "Unable to load translated messages");
return null;
}
}
/**
* Tries to load translated messages from a custom strings.yml file
*
* @param language <p>The selected language</p>
* @return <p>The loaded translated strings, or null if no custom language file exists</p>
*/
public static Map<TranslatableMessage, String> loadCustomTranslatedMessages(String language) {
BlacksmithPlugin instance = BlacksmithPlugin.getInstance();
File strings = new File(instance.getDataFolder(), "strings.yml");
if (!strings.exists()) {
instance.getLogger().log(Level.FINEST, "Strings file not found");
return null;
}
try {
instance.getLogger().log(Level.INFO, "Loading custom strings...");
return loadTranslatableMessages(language, new BufferedReader(new InputStreamReader(new FileInputStream(strings))));
} catch (FileNotFoundException e) {
instance.getLogger().log(Level.WARNING, "Unable to load custom messages");
return null;
}
}
/**
* Loads translatable messages from the given reader
*
* @param language <p>The selected language</p>
* @param reader <p>The buffered reader to read from</p>
* @return <p>The loaded translated strings</p>
*/
private static Map<TranslatableMessage, String> loadTranslatableMessages(String language, BufferedReader reader) {
Map<TranslatableMessage, String> translatedMessages = new HashMap<>();
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(reader);
for (TranslatableMessage message : TranslatableMessage.values()) {
String translated = configuration.getString(language + "." + message.toString());
if (translated != null) {
translatedMessages.put(message, translated);
}
}
return translatedMessages;
}
}

View File

@ -1,7 +1,9 @@
package net.knarcraft.blacksmith.listener; package net.knarcraft.blacksmith.listener;
import net.knarcraft.blacksmith.formatting.StringFormatter;
import net.knarcraft.blacksmith.formatting.TranslatableMessage;
import net.knarcraft.blacksmith.formatting.Translator;
import net.knarcraft.blacksmith.trait.BlacksmithTrait; import net.knarcraft.blacksmith.trait.BlacksmithTrait;
import net.knarcraft.blacksmith.util.MessageFormatter;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
@ -24,7 +26,8 @@ public class NPCClickListener implements Listener {
//Permission check //Permission check
if (!player.hasPermission("blacksmith.use")) { if (!player.hasPermission("blacksmith.use")) {
MessageFormatter.displayErrorMessage(player, "You lack the necessary permission"); StringFormatter.displayErrorMessage(player,
Translator.getTranslatedMessage(TranslatableMessage.PERMISSION_DENIED));
return; return;
} }

View File

@ -1,31 +0,0 @@
package net.knarcraft.blacksmith.manager;
/**
* An enum representing all item types used in messages
*/
public enum ItemType {
ENCHANTMENT("enchantment"),
MATERIAL("material");
private final String itemTypeName;
/**
* Instantiates an item type
*
* @param itemTypeName <p>The name shown in messages</p>
*/
ItemType(String itemTypeName) {
this.itemTypeName = itemTypeName;
}
/**
* Gets the name of this item type
*
* @return <p>The name of this item type</p>
*/
public String getItemTypeName() {
return this.itemTypeName;
}
}

View File

@ -1,100 +0,0 @@
package net.knarcraft.blacksmith.manager;
public enum Message {
VALUE_CHANGED("&7{setting} set to &6{newValue}"),
VALUE_FOR_ITEM_CHANGED("&7{setting} for {itemType} {item} set to &6{newValue}"),
CURRENT_VALUE("&7Current value of {setting}:&r {currentValue}"),
CURRENT_VALUE_FOR_ITEM("&7Current value of {setting} for {itemType} {item}:&r {currentValue}");
private final String messageString;
/**
* Instantiates a new message
*
* @param messageString <p>The string value of this message</p>
*/
Message(String messageString) {
this.messageString = messageString;
}
/**
* Gets the message to display when a value has been changed
*
* @param setting <p>The setting whose value has been changed</p>
* @param newValue <p>The new value of the setting</p>
* @return <p>The string to display to a user</p>
*/
public static String getValueChangedMessage(String setting, String newValue) {
return replacePlaceholders(VALUE_CHANGED.messageString, new String[]{"{setting}", "{newValue}"},
new String[]{setting, newValue});
}
/**
* Gets the message to display when a value has been changed for an item
*
* @param setting <p>The setting whose value has been changed</p>
* @param itemType <p>The type of item changed ("material" or "enchantment")</p>
* @param item <p>The item the setting was changed for (a material or an enchantment name)</p>
* @param newValue <p>The new value of the setting</p>
* @return <p>The string to display to a user</p>
*/
public static String getItemValueChangedMessage(String setting, ItemType itemType, String item, String newValue) {
return replacePlaceholders(VALUE_FOR_ITEM_CHANGED.messageString, new String[]{"{setting}", "{itemType}",
"{item}", "{newValue}"}, new String[]{setting, itemType.getItemTypeName(), item, newValue});
}
/**
* Gets the message to display when displaying a setting's current value
*
* @param setting <p>The setting whose value is shown</p>
* @param currentValue <p>The current value of the setting</p>
* @return <p>The string to display to a user</p>
*/
public static String getCurrentValueMessage(String setting, String currentValue) {
return replacePlaceholders(CURRENT_VALUE.messageString, new String[]{"{setting}", "{currentValue}"},
new String[]{setting, currentValue});
}
/**
* Gets the message to display when displaying a setting's current value for an item
*
* @param setting <p>The setting whose value is shown</p>
* @param itemType <p>The type of item shown ("material" or "enchantment")</p>
* @param item <p>The item the setting is shown for (a material or an enchantment name)</p>
* @param currentValue <p>The current value of the setting</p>
* @return <p>The string to display to a user</p>
*/
public static String getItemCurrentValueMessage(String setting, ItemType itemType, String item, String currentValue) {
return replacePlaceholders(CURRENT_VALUE_FOR_ITEM.messageString, new String[]{"{setting}", "{itemType}",
"{item}", "{currentValue}"}, new String[]{setting, itemType.getItemTypeName(), item, currentValue});
}
/**
* Replaces a placeholder in a string
*
* @param input <p>The input string to replace in</p>
* @param placeholder <p>The placeholder to replace</p>
* @param replacement <p>The replacement value</p>
* @return <p>The input string with the placeholder replaced</p>
*/
private static String replacePlaceholder(String input, String placeholder, String replacement) {
return input.replace(placeholder, replacement);
}
/**
* Replaces placeholders in a string
*
* @param input <p>The input string to replace in</p>
* @param placeholders <p>The placeholders to replace</p>
* @param replacements <p>The replacement values</p>
* @return <p>The input string with placeholders replaced</p>
*/
private static String replacePlaceholders(String input, String[] placeholders, String[] replacements) {
for (int i = 0; i < Math.min(placeholders.length, replacements.length); i++) {
input = replacePlaceholder(input, placeholders[i], replacements[i]);
}
return input;
}
}

View File

@ -22,7 +22,7 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.UUID; import java.util.UUID;
import static net.knarcraft.blacksmith.util.MessageFormatter.sendNPCMessage; import static net.knarcraft.blacksmith.formatting.StringFormatter.sendNPCMessage;
/** /**
* The class representing the Blacksmith NPC trait * The class representing the Blacksmith NPC trait

View File

@ -20,7 +20,7 @@ import java.util.Objects;
import java.util.Random; import java.util.Random;
import java.util.logging.Level; import java.util.logging.Level;
import static net.knarcraft.blacksmith.util.MessageFormatter.sendNPCMessage; import static net.knarcraft.blacksmith.formatting.StringFormatter.sendNPCMessage;
/** /**
* A representation of the session between a player and a blacksmith * A representation of the session between a player and a blacksmith

View File

@ -0,0 +1,32 @@
package net.knarcraft.blacksmith.util;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
/**
* A helper class for dealing with files
*/
public final class FileHelper {
private FileHelper() {
}
/**
* Gets a buffered reader for
*
* @return <p>A buffered read for reading the file</p>
* @throws FileNotFoundException <p>If unable to get an input stream for the given file</p>
*/
public static BufferedReader getBufferedReaderForInternalFile(String file) throws FileNotFoundException {
InputStream inputStream = FileHelper.class.getResourceAsStream(file);
if (inputStream == null) {
throw new FileNotFoundException("Unable to read the given file");
}
return new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
}
}

View File

@ -1,9 +1,11 @@
package net.knarcraft.blacksmith.util; package net.knarcraft.blacksmith.util;
import net.knarcraft.blacksmith.config.SettingValueType; import net.knarcraft.blacksmith.config.SettingValueType;
import net.knarcraft.blacksmith.formatting.TranslatableMessage;
import net.knarcraft.blacksmith.formatting.Translator;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import static net.knarcraft.blacksmith.util.MessageFormatter.displayErrorMessage; import static net.knarcraft.blacksmith.formatting.StringFormatter.displayErrorMessage;
/** /**
* A helper class for validating a value's type * A helper class for validating a value's type
@ -49,7 +51,7 @@ public final class TypeValidationHelper {
private static boolean isStringList(Object value, CommandSender sender) { private static boolean isStringList(Object value, CommandSender sender) {
boolean isStringList = value instanceof String[] || value instanceof String; boolean isStringList = value instanceof String[] || value instanceof String;
if (!isStringList && sender != null) { if (!isStringList && sender != null) {
displayErrorMessage(sender, "A string list is required!"); displayErrorMessage(sender, Translator.getTranslatedMessage(TranslatableMessage.INPUT_STRING_LIST_REQUIRED));
} }
return isStringList; return isStringList;
} }
@ -67,7 +69,7 @@ public final class TypeValidationHelper {
return intValue > 0 && intValue <= 100; return intValue > 0 && intValue <= 100;
} catch (NumberFormatException | NullPointerException exception) { } catch (NumberFormatException | NullPointerException exception) {
if (sender != null) { if (sender != null) {
displayErrorMessage(sender, "You specified a value which isn't between 0 and 100!"); displayErrorMessage(sender, Translator.getTranslatedMessage(TranslatableMessage.INPUT_PERCENTAGE_REQUIRED));
} }
return false; return false;
} }
@ -83,7 +85,7 @@ public final class TypeValidationHelper {
private static boolean isNonEmptyString(Object value, CommandSender sender) { private static boolean isNonEmptyString(Object value, CommandSender sender) {
boolean isString = value instanceof String string && !string.strip().isEmpty(); boolean isString = value instanceof String string && !string.strip().isEmpty();
if (!isString && sender != null) { if (!isString && sender != null) {
displayErrorMessage(sender, "A non-empty string is required!"); displayErrorMessage(sender, Translator.getTranslatedMessage(TranslatableMessage.INPUT_STRING_REQUIRED));
} }
return isString; return isString;
} }
@ -100,7 +102,8 @@ public final class TypeValidationHelper {
return ConfigHelper.asDouble(value) > 0.0; return ConfigHelper.asDouble(value) > 0.0;
} catch (NumberFormatException | NullPointerException exception) { } catch (NumberFormatException | NullPointerException exception) {
if (sender != null) { if (sender != null) {
displayErrorMessage(sender, "You specified a value which isn't a positive double!"); displayErrorMessage(sender,
Translator.getTranslatedMessage(TranslatableMessage.INPUT_POSITIVE_DOUBLE_REQUIRED));
} }
return false; return false;
} }
@ -118,7 +121,8 @@ public final class TypeValidationHelper {
return ConfigHelper.asInt(value) > 0; return ConfigHelper.asInt(value) > 0;
} catch (NumberFormatException | NullPointerException exception) { } catch (NumberFormatException | NullPointerException exception) {
if (sender != null) { if (sender != null) {
displayErrorMessage(sender, "You specified a value which isn't a positive integer!"); displayErrorMessage(sender,
Translator.getTranslatedMessage(TranslatableMessage.INPUT_POSITIVE_INTEGER_REQUIRED));
} }
return false; return false;
} }

View File

@ -0,0 +1,17 @@
en:
VALUE_CHANGED: "&7{setting} set to &6{newValue}"
VALUE_FOR_ITEM_CHANGED: "&7{setting} for {itemType} {item} set to &6{newValue}"
CURRENT_VALUE: "&7Current value of {setting}:&r {currentValue}"
CURRENT_VALUE_FOR_ITEM: "&7Current value of {setting} for {itemType} {item}:&r {currentValue}"
ITEM_TYPE_ENCHANTMENT: "enchantment"
ITEM_TYPE_MATERIAL: "material"
RAW_VALUE: "Raw value: {rawValue}"
NO_NPC_SELECTED: "You must select an NPC before running this command"
DEFAULT_REFORGE_ABLE_ITEMS_UNCHANGEABLE: "Changing reforge-able items globally will make every new blacksmith unable to reforge anything not in the list, unless it's changed for the individual NPC. If you really want to change this, change it manually."
INPUT_STRING_LIST_REQUIRED: "A string list is required!"
INPUT_PERCENTAGE_REQUIRED: "You specified a value which isn't between 0 and 100!"
INPUT_STRING_REQUIRED: "A non-empty string is required!"
INPUT_POSITIVE_DOUBLE_REQUIRED: "You specified a value which isn't a positive double!"
INPUT_POSITIVE_INTEGER_REQUIRED: "You specified a value which isn't a positive integer!"
PERMISSION_DENIED: "You lack the necessary permission"
PLUGIN_RELOADED: "Blacksmith config reloaded!"