Adds code to be able to translate plugin messages

This commit is contained in:
Kristian Knarvik 2022-03-01 16:03:08 +01:00
parent 483ffaec2b
commit eb0e06f193
6 changed files with 362 additions and 1 deletions

View File

@ -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");
}
/**

View File

@ -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 <p>The boolean value to translate</p>
* @return <p>The translation of the boolean value</p>
*/
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 <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;
}
/**
* Gets a translated and formatted info message
*
* @param translatableMessage <p>The translatable message to translate and format</p>
* @return <p>The translated and formatted message</p>
*/
public static String getTranslatedInfoMessage(TranslatableMessage translatableMessage) {
return formatInfoMessage(Translator.getTranslatedMessage(translatableMessage));
}
/**
* Gets a translated and formatted error message
*
* @param translatableMessage <p>The translatable message to translate and format</p>
* @return <p>The translated and formatted message</p>
*/
public static String getTranslatedErrorMessage(TranslatableMessage translatableMessage) {
return formatErrorMessage(Translator.getTranslatedMessage(translatableMessage));
}
/**
* Formats an information message by adding the prefix and text color
*
* @param message <p>The message to format</p>
* @return <p>The formatted message</p>
*/
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 <p>The message to format</p>
* @return <p>The formatted message</p>
*/
public static String formatErrorMessage(String message) {
return ChatColor.DARK_RED + formatMessage(message);
}
/**
* 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;
}
/**
* Formats a message by adding the prefix and text color
*
* @param message <p>The message to format</p>
* @return <p>The formatted message</p>
*/
private static String formatMessage(String message) {
return Translator.getTranslatedMessage(TranslatableMessage.PREFIX) + " " +
ChatColor.RESET + message;
}
}

View File

@ -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,
}

View File

@ -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<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) {
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 <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) {
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 <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

@ -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 <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,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.