Adds some code required for custom formatting and translation

This commit is contained in:
Kristian Knarvik 2022-11-05 17:55:56 +01:00
parent cfff5485b2
commit 5b02a094e8
8 changed files with 340 additions and 6 deletions

View File

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

View File

@ -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 <p>The command sender to display the message to</p>
* @param message <p>The translatable message to display</p>
*/
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 <p>The command sender to display the message to</p>
* @param message <p>The raw message to display</p>
*/
public static void displaySuccessMessage(CommandSender sender, String message) {
sender.sendMessage(ChatColor.GREEN + getFormattedMessage(message));
}
/**
* Displays a message signifying an unsuccessful action
*
* @param sender <p>The command sender to display the message to</p>
* @param message <p>The translatable message to display</p>
*/
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 <p>The message to format</p>
* @return <p>The formatted message</p>
*/
private static String getFormattedMessage(String message) {
return "[" + pluginName + "] " + ChatColor.RESET + translateColors(message);
}
/**
* Translates & color codes to proper colors
*
* @param input <p>The input string to translate colors for</p>
* @return <p>The input with color codes translated</p>
*/
private static String translateColors(String 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,7 @@
package net.knarcraft.dynmapcitizens.formatting;
public enum TranslatableMessage {
SENTINEL_DETAILS
}

View File

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

@ -47,12 +47,14 @@ public class SentinelHandler extends AbstractTraitHandler {
description += "<br><b>Squad:</b> " + trait.squad;
}
if (settings.displaySentinelStats()) {
description += "<br><b>Invincible:</b> " + trait.invincible + "<br><b>Armor:</b> " +
trait.armor + "<br><b>Health:</b> " + trait.health + "<br><b>Accuracy:</b> " + trait.accuracy +
"<br><b>Damage:</b> " + trait.damage + "<br><b>Allow knockback:</b> " + trait.allowKnockback;
description += "<br><b>Invincible:</b> " + trait.invincible + "<br><b>Armor:</b> " + trait.armor;
description += "<br><b>Health:</b> " + trait.health + "<br><b>Accuracy:</b> " + trait.accuracy;
description += "<br><b>Damage:</b> " + trait.damage + "<br><b>Speed:</b> " + trait.speed;
description += "<br><b>Allow knockback:</b> " + trait.allowKnockback;
description += "<br><b>Range:</b> " + trait.range + "<br><b>Reach:</b> " + trait.reach;
description += "<br><b>Targets:</b> " + trait.allTargets.toAllInOneString() + "<br><b>Avoids:</b> " +
trait.allAvoids.toAllInOneString() + "<br><b>Ignores:</b> " + trait.allIgnores.toAllInOneString();
description += "<br><b>Targets:</b> " + trait.allTargets.toAllInOneString();
description += "<br><b>Avoids:</b> " + trait.allAvoids.toAllInOneString();
description += "<br><b>Ignores:</b> " + trait.allIgnores.toAllInOneString();
}
addNPCMarker(npc.getUniqueId(), "Sentinel NPC: ", description,
DynmapCitizens.getInstance().getGlobalSettings().getMarkerIcons().get(Icon.SENTINEL), super.markerSet);

View File

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

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

View File

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