diff --git a/src/main/java/net/knarcraft/paidsigns/PaidSigns.java b/src/main/java/net/knarcraft/paidsigns/PaidSigns.java index 33866df..4a649df 100644 --- a/src/main/java/net/knarcraft/paidsigns/PaidSigns.java +++ b/src/main/java/net/knarcraft/paidsigns/PaidSigns.java @@ -10,6 +10,7 @@ import net.knarcraft.paidsigns.command.ReloadTabCommand; import net.knarcraft.paidsigns.command.RemoveConditionCommand; import net.knarcraft.paidsigns.command.RemoveConditionTabCompleter; import net.knarcraft.paidsigns.command.RemoveTabCommand; +import net.knarcraft.paidsigns.formatting.Translator; import net.knarcraft.paidsigns.listener.BlockBreakListener; import net.knarcraft.paidsigns.listener.SignListener; import net.knarcraft.paidsigns.manager.EconomyManager; @@ -33,6 +34,7 @@ public final class PaidSigns extends JavaPlugin { private static PaidSigns paidSigns; private PaidSignManager signManager; + private String language; private boolean ignoreCase; private boolean ignoreColor; private boolean enableRefunds; @@ -58,9 +60,10 @@ public final class PaidSigns extends JavaPlugin { @Override public void onEnable() { setupVault(); + loadConfig(); + Translator.loadLanguages(language); signManager = new PaidSignManager(PaidSignManager.loadSigns()); TrackedSignManager.loadTrackedSigns(); - loadConfig(); PluginManager pluginManager = getServer().getPluginManager(); pluginManager.registerEvents(new SignListener(), this); @@ -79,6 +82,7 @@ public final class PaidSigns extends JavaPlugin { public void reload() { this.reloadConfig(); loadConfig(); + Translator.loadLanguages(language); signManager = new PaidSignManager(PaidSignManager.loadSigns()); TrackedSignManager.loadTrackedSigns(); } @@ -174,6 +178,7 @@ public final class PaidSigns extends JavaPlugin { ignoreColor = config.getBoolean("ignoreColor", false); enableRefunds = config.getBoolean("enableRefunds", true); refundPercentage = config.getInt("refundPercentage", 100); + language = config.getString("language", "en"); } /** diff --git a/src/main/java/net/knarcraft/paidsigns/formatting/StringFormatter.java b/src/main/java/net/knarcraft/paidsigns/formatting/StringFormatter.java new file mode 100644 index 0000000..89bcecc --- /dev/null +++ b/src/main/java/net/knarcraft/paidsigns/formatting/StringFormatter.java @@ -0,0 +1,125 @@ +package net.knarcraft.paidsigns.formatting; + +import net.md_5.bungee.api.ChatColor; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A formatter for formatting displayed messages + */ +public final class StringFormatter { + + private StringFormatter() { + + } + + /** + * Translates the given boolean value + * + * @param booleanValue

The boolean value to translate

+ * @return

The translation of the boolean value

+ */ + public static String translateBoolean(boolean booleanValue) { + if (booleanValue) { + return Translator.getTranslatedMessage(TranslatableMessage.BOOLEAN_TRUE); + } else { + return Translator.getTranslatedMessage(TranslatableMessage.BOOLEAN_FALSE); + } + } + + /** + * 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; + } + + /** + * Gets a translated and formatted info message + * + * @param translatableMessage

The translatable message to translate and format

+ * @return

The translated and formatted message

+ */ + public static String getTranslatedInfoMessage(TranslatableMessage translatableMessage) { + return formatInfoMessage(Translator.getTranslatedMessage(translatableMessage)); + } + + /** + * Gets a translated and formatted error message + * + * @param translatableMessage

The translatable message to translate and format

+ * @return

The translated and formatted message

+ */ + public static String getTranslatedErrorMessage(TranslatableMessage translatableMessage) { + return formatErrorMessage(Translator.getTranslatedMessage(translatableMessage)); + } + + /** + * Formats an information message by adding the prefix and text color + * + * @param message

The message to format

+ * @return

The formatted message

+ */ + public static String formatInfoMessage(String message) { + return ChatColor.DARK_GREEN + formatMessage(message); + } + + /** + * Formats an error message by adding the prefix and text color + * + * @param message

The message to format

+ * @return

The formatted message

+ */ + public static String formatErrorMessage(String message) { + return ChatColor.DARK_RED + formatMessage(message); + } + + /** + * 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; + } + + /** + * Formats a message by adding the prefix and text color + * + * @param message

The message to format

+ * @return

The formatted message

+ */ + private static String formatMessage(String message) { + return Translator.getTranslatedMessage(TranslatableMessage.PREFIX) + " " + + ChatColor.RESET + message; + } + +} diff --git a/src/main/java/net/knarcraft/paidsigns/formatting/TranslatableMessage.java b/src/main/java/net/knarcraft/paidsigns/formatting/TranslatableMessage.java new file mode 100644 index 0000000..db8ad79 --- /dev/null +++ b/src/main/java/net/knarcraft/paidsigns/formatting/TranslatableMessage.java @@ -0,0 +1,78 @@ +package net.knarcraft.paidsigns.formatting; + +/** + * An enum representing all translatable messages + */ +public enum TranslatableMessage { + + /** + * The prefix to display in messages + */ + PREFIX, + + /** + * The message to display when a paid sign is successfully added + */ + SUCCESS_ADDED_PAID_SIGN, + + /** + * The message to display when a paid sign condition is successfully added + */ + SUCCESS_ADDED_PAID_SIGN_CONDITION, + + /** + * The info text used to display information about a paid sign + */ + PAID_SIGN_INFO, + + /** + * The info text used to display information about a paid sign condition + */ + PAID_SIGN_CONDITION_INFO, + + /** + * The format used for displaying one of a paid sign's conditions + */ + PAID_SIGN_INFO_CONDITION_FORMAT, + + /** + * The message to display for a true boolean value + */ + BOOLEAN_TRUE, + + /** + * The message to display for a false boolean value + */ + BOOLEAN_FALSE, + + /** + * The error to display when a command argument contains an invalid number + */ + ERROR_INVALID_NUMBER, + + /** + * The error to display if a paid sign name duplicate is encountered + */ + ERROR_NAME_DUPLICATE, + + /** + * The error to display if a severe exception occurs + */ + ERROR_EXCEPTION_OCCURRED, + + /** + * The error to display if some input is invalid + */ + ERROR_INVALID_INPUT, + + /** + * The error to display if a specified paid sign is not found + */ + ERROR_PAID_SIGN_NOT_FOUND, + + /** + * The error to display if a paid sign condition is specified, but does not exist + */ + ERROR_NO_SUCH_CONDITION, + +} diff --git a/src/main/java/net/knarcraft/paidsigns/formatting/Translator.java b/src/main/java/net/knarcraft/paidsigns/formatting/Translator.java new file mode 100644 index 0000000..43acf89 --- /dev/null +++ b/src/main/java/net/knarcraft/paidsigns/formatting/Translator.java @@ -0,0 +1,118 @@ +package net.knarcraft.paidsigns.formatting; + +import net.knarcraft.paidsigns.PaidSigns; +import net.knarcraft.paidsigns.utility.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) { + PaidSigns.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) { + File strings = new File(PaidSigns.getInstance().getDataFolder(), "strings.yml"); + if (!strings.exists()) { + PaidSigns.getInstance().getLogger().log(Level.FINEST, "Strings file not found"); + return null; + } + + try { + PaidSigns.getInstance().getLogger().log(Level.WARNING, "Loading custom strings..."); + return loadTranslatableMessages(language, new BufferedReader(new InputStreamReader(new FileInputStream(strings)))); + } catch (FileNotFoundException e) { + PaidSigns.getInstance().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/paidsigns/utility/FileHelper.java b/src/main/java/net/knarcraft/paidsigns/utility/FileHelper.java new file mode 100644 index 0000000..ece2d2c --- /dev/null +++ b/src/main/java/net/knarcraft/paidsigns/utility/FileHelper.java @@ -0,0 +1,32 @@ +package net.knarcraft.paidsigns.utility; + +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/resources/config.yml b/src/main/resources/config.yml index de76417..6e25191 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,3 +1,6 @@ +# The currently enabled language. More languages can be added to the language file +language: en + # Whether to ignore the case (lowercase/uppercase) of the paid sign text. The option can be set on a per-sign basis, but # this value is used if not specified. The correct value depends on whether the plugin signs it should match are # case-sensitive or not.