From 5b02a094e8992ce88808dcd86ed1c154b46e4648 Mon Sep 17 00:00:00 2001 From: EpicKnarvik97 Date: Sat, 5 Nov 2022 17:55:56 +0100 Subject: [PATCH] Adds some code required for custom formatting and translation --- .../dynmapcitizens/DynmapCitizens.java | 4 + .../formatting/StringFormatter.java | 114 +++++++++++++++++ .../formatting/TranslatableMessage.java | 7 + .../dynmapcitizens/formatting/Translator.java | 120 ++++++++++++++++++ .../handler/trait/SentinelHandler.java | 12 +- .../dynmapcitizens/util/FileHelper.java | 32 +++++ .../dynmapcitizens/util/TimeFormatter.java | 2 +- src/main/resources/strings.yml | 55 ++++++++ 8 files changed, 340 insertions(+), 6 deletions(-) create mode 100644 src/main/java/net/knarcraft/dynmapcitizens/formatting/StringFormatter.java create mode 100644 src/main/java/net/knarcraft/dynmapcitizens/formatting/TranslatableMessage.java create mode 100644 src/main/java/net/knarcraft/dynmapcitizens/formatting/Translator.java create mode 100644 src/main/java/net/knarcraft/dynmapcitizens/util/FileHelper.java create mode 100644 src/main/resources/strings.yml diff --git a/src/main/java/net/knarcraft/dynmapcitizens/DynmapCitizens.java b/src/main/java/net/knarcraft/dynmapcitizens/DynmapCitizens.java index 44a9cb6..6cfee42 100644 --- a/src/main/java/net/knarcraft/dynmapcitizens/DynmapCitizens.java +++ b/src/main/java/net/knarcraft/dynmapcitizens/DynmapCitizens.java @@ -1,5 +1,6 @@ package net.knarcraft.dynmapcitizens; +import net.knarcraft.dynmapcitizens.formatting.Translator; import net.knarcraft.dynmapcitizens.handler.VaultHandler; import net.knarcraft.dynmapcitizens.handler.trait.BlacksmithHandler; import net.knarcraft.dynmapcitizens.handler.trait.CitizensTraitHandler; @@ -49,6 +50,9 @@ public final class DynmapCitizens extends JavaPlugin { configuration = this.getConfig(); this.globalSettings.load(configuration); + //Load all messages + Translator.loadLanguages("en"); + //Initialize all enabled traits initializeTraitHandlers(configuration); diff --git a/src/main/java/net/knarcraft/dynmapcitizens/formatting/StringFormatter.java b/src/main/java/net/knarcraft/dynmapcitizens/formatting/StringFormatter.java new file mode 100644 index 0000000..ce14013 --- /dev/null +++ b/src/main/java/net/knarcraft/dynmapcitizens/formatting/StringFormatter.java @@ -0,0 +1,114 @@ +package net.knarcraft.dynmapcitizens.formatting; + +import net.knarcraft.dynmapcitizens.DynmapCitizens; +import net.md_5.bungee.api.ChatColor; +import org.bukkit.command.CommandSender; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A formatter for formatting displayed messages + */ +public final class StringFormatter { + + private final static String pluginName = DynmapCitizens.getInstance().getDescription().getName(); + + private StringFormatter() { + + } + + /** + * Displays a message signifying a successful action + * + * @param sender

The command sender to display the message to

+ * @param message

The translatable message to display

+ */ + public static void displaySuccessMessage(CommandSender sender, TranslatableMessage message) { + sender.sendMessage(ChatColor.GREEN + getFormattedMessage(Translator.getTranslatedMessage(message))); + } + + /** + * Displays a message signifying a successful action + * + * @param sender

The command sender to display the message to

+ * @param message

The raw message to display

+ */ + public static void displaySuccessMessage(CommandSender sender, String message) { + sender.sendMessage(ChatColor.GREEN + getFormattedMessage(message)); + } + + /** + * Displays a message signifying an unsuccessful action + * + * @param sender

The command sender to display the message to

+ * @param message

The translatable message to display

+ */ + public static void displayErrorMessage(CommandSender sender, TranslatableMessage message) { + sender.sendMessage(ChatColor.DARK_RED + getFormattedMessage(Translator.getTranslatedMessage(message))); + } + + /** + * Gets the formatted version of any chat message + * + * @param message

The message to format

+ * @return

The formatted message

+ */ + private static String getFormattedMessage(String message) { + return "[" + pluginName + "] " + ChatColor.RESET + translateColors(message); + } + + /** + * Translates & color codes to proper colors + * + * @param input

The input string to translate colors for

+ * @return

The input with color codes translated

+ */ + private static String translateColors(String input) { + return ChatColor.translateAlternateColorCodes('&', input); + } + + /** + * Replaces a placeholder in a string + * + * @param input

The input string to replace in

+ * @param placeholder

The placeholder to replace

+ * @param replacement

The replacement value

+ * @return

The input string with the placeholder replaced

+ */ + public static String replacePlaceholder(String input, String placeholder, String replacement) { + return input.replace(placeholder, replacement); + } + + /** + * Replaces placeholders in a string + * + * @param input

The input string to replace in

+ * @param placeholders

The placeholders to replace

+ * @param replacements

The replacement values

+ * @return

The input string with placeholders replaced

+ */ + 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

The string to search for color codes

+ * @return

The message with color codes translated

+ */ + 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; + } + +} diff --git a/src/main/java/net/knarcraft/dynmapcitizens/formatting/TranslatableMessage.java b/src/main/java/net/knarcraft/dynmapcitizens/formatting/TranslatableMessage.java new file mode 100644 index 0000000..46ebefe --- /dev/null +++ b/src/main/java/net/knarcraft/dynmapcitizens/formatting/TranslatableMessage.java @@ -0,0 +1,7 @@ +package net.knarcraft.dynmapcitizens.formatting; + +public enum TranslatableMessage { + + SENTINEL_DETAILS + +} diff --git a/src/main/java/net/knarcraft/dynmapcitizens/formatting/Translator.java b/src/main/java/net/knarcraft/dynmapcitizens/formatting/Translator.java new file mode 100644 index 0000000..85c3716 --- /dev/null +++ b/src/main/java/net/knarcraft/dynmapcitizens/formatting/Translator.java @@ -0,0 +1,120 @@ +package net.knarcraft.dynmapcitizens.formatting; + +import net.knarcraft.dynmapcitizens.DynmapCitizens; +import net.knarcraft.dynmapcitizens.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 translatedMessages; + private static Map 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

The message to translate

+ * @return

The translated message

+ */ + 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

The language chosen by the user

+ * @return

A mapping of all strings for the given language

+ */ + public static Map loadTranslatedMessages(String language) { + try { + BufferedReader reader = FileHelper.getBufferedReaderForInternalFile("/strings.yml"); + return loadTranslatableMessages(language, reader); + } catch (FileNotFoundException e) { + DynmapCitizens.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

The selected language

+ * @return

The loaded translated strings, or null if no custom language file exists

+ */ + public static Map loadCustomTranslatedMessages(String language) { + DynmapCitizens instance = DynmapCitizens.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

The selected language

+ * @param reader

The buffered reader to read from

+ * @return

The loaded translated strings

+ */ + private static Map loadTranslatableMessages(String language, BufferedReader reader) { + Map 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; + } + +} \ No newline at end of file diff --git a/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/SentinelHandler.java b/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/SentinelHandler.java index 28a7993..dc0938e 100644 --- a/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/SentinelHandler.java +++ b/src/main/java/net/knarcraft/dynmapcitizens/handler/trait/SentinelHandler.java @@ -47,12 +47,14 @@ public class SentinelHandler extends AbstractTraitHandler { description += "
Squad: " + trait.squad; } if (settings.displaySentinelStats()) { - description += "
Invincible: " + trait.invincible + "
Armor: " + - trait.armor + "
Health: " + trait.health + "
Accuracy: " + trait.accuracy + - "
Damage: " + trait.damage + "
Allow knockback: " + trait.allowKnockback; + description += "
Invincible: " + trait.invincible + "
Armor: " + trait.armor; + description += "
Health: " + trait.health + "
Accuracy: " + trait.accuracy; + description += "
Damage: " + trait.damage + "
Speed: " + trait.speed; + description += "
Allow knockback: " + trait.allowKnockback; description += "
Range: " + trait.range + "
Reach: " + trait.reach; - description += "
Targets: " + trait.allTargets.toAllInOneString() + "
Avoids: " + - trait.allAvoids.toAllInOneString() + "
Ignores: " + trait.allIgnores.toAllInOneString(); + description += "
Targets: " + trait.allTargets.toAllInOneString(); + description += "
Avoids: " + trait.allAvoids.toAllInOneString(); + description += "
Ignores: " + trait.allIgnores.toAllInOneString(); } addNPCMarker(npc.getUniqueId(), "Sentinel NPC: ", description, DynmapCitizens.getInstance().getGlobalSettings().getMarkerIcons().get(Icon.SENTINEL), super.markerSet); diff --git a/src/main/java/net/knarcraft/dynmapcitizens/util/FileHelper.java b/src/main/java/net/knarcraft/dynmapcitizens/util/FileHelper.java new file mode 100644 index 0000000..a5d13cc --- /dev/null +++ b/src/main/java/net/knarcraft/dynmapcitizens/util/FileHelper.java @@ -0,0 +1,32 @@ +package net.knarcraft.dynmapcitizens.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

A buffered read for reading the file

+ * @throws FileNotFoundException

If unable to get an input stream for the given file

+ */ + 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)); + } + +} diff --git a/src/main/java/net/knarcraft/dynmapcitizens/util/TimeFormatter.java b/src/main/java/net/knarcraft/dynmapcitizens/util/TimeFormatter.java index 5ebc8ba..30c0753 100644 --- a/src/main/java/net/knarcraft/dynmapcitizens/util/TimeFormatter.java +++ b/src/main/java/net/knarcraft/dynmapcitizens/util/TimeFormatter.java @@ -9,7 +9,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import static net.knarcraft.blacksmith.formatting.StringFormatter.replacePlaceholder; +import static net.knarcraft.dynmapcitizens.formatting.StringFormatter.replacePlaceholder; /** * A helper class for time formatting diff --git a/src/main/resources/strings.yml b/src/main/resources/strings.yml new file mode 100644 index 0000000..05aec0f --- /dev/null +++ b/src/main/resources/strings.yml @@ -0,0 +1,55 @@ +en: + SENTINEL_DESCRIPTION: | +

name

+
Squad: {squad} + {sentinelDetails} + SENTINEL_DETAILS: | +
    +
  • Invincible: {invincible}
  • +
  • Armor: {armor}
  • +
  • Health: {health}
  • +
  • Accuracy: {accuracy}
  • +
  • Damage: {damage}
  • +
  • Speed: {speed}
  • +
  • Allow knockback: {allowKnockback}
  • +
  • Range: {range}
  • +
  • Reach: {reach}
  • +
  • Targets: {targets}
  • +
  • Avoids: {avoids}
  • +
  • Ignores: {ignores}
  • +
+ QUESTS_PLANNER_DESCRIPTION: | + Planner:
    + {questCoolDown} + {questFrom} + {questUntil} + {questRepeat} +
+ QUESTS_PLANNER_COOL_DOWN: "
  • Quest repeatable after: {coolDown}
  • " + QUESTS_PLANNER_UNREPEATABLE: "
  • Quest cannot be repeated!
  • " + QUESTS_PLANNER_FROM: "
  • Quest available from {startDate}
  • " + QUESTS_PLANNER_UNTIL: "
  • Quest available until {endDate}
  • " + QUEST_PLANNER_REPEAT: "
  • Quest will become available again after {repeatDelay}
  • " + QUESTS_REQUIREMENTS_FORMAT: | + Requirements:
      + {requirementQuestPoints} + {requirementExp} + {requirementBlockedByQuests} + {requirementRequiredQuests} + {requirementRequiredItems} + {requirementMCMMOSkills} + {requirementPermissions} + {requirementCustom} +
    + QUESTS_REQUIREMENTS_QUEST_POINTS: "
  • {questPoints} quest points
  • " + QUESTS_REQUIREMENTS_EXP: "
  • {exp} exp
  • " + QUESTS_REQUIREMENTS_BLOCKED_BY_QUEST_FORMAT: "
  • Blocked by quests:
      {blockingQuests}
  • " + QUESTS_REQUIREMENTS_BLOCKED_BY_QUEST_ITEM: "
  • {questName}
  • " + QUESTS_REQUIREMENTS_REQUIRED_QUEST_FORMAT: "
  • Required quests:
      {requiredQuests}
  • " + QUESTS_REQUIREMENTS_REQUIRED_QUEST_ITEM: "
  • {questName}
  • " + QUESTS_REQUIREMENTS_REQUIRED_ITEM_FORMAT: "
  • Required items:
      {requiredItems}
  • " + QUESTS_REQUIREMENTS_REQUIRED_ITEM_ITEM: "
  • {itemName}
  • " + QUESTS_REQUIREMENTS_MCMMO_SKILL: "
  • Requires mcMMO skill {skill} at level {level}
  • " + QUESTS_REQUIREMENTS_REQUIRED_PERMISSION_FORMAT: "
  • Required permissions:
      {permissions}
  • " + QUESTS_REQUIREMENTS_REQUIRED_PERMISSION_ITEM: "
  • {permission}
  • " + \ No newline at end of file