package net.knarcraft.stargate.config; import net.knarcraft.knarlib.property.ColorConversion; import net.knarcraft.knarlib.util.FileHelper; import net.knarcraft.stargate.Stargate; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.EnumMap; import java.util.Map; /** * This class is responsible for loading all strings which are translated into several languages */ public final class LanguageLoader { private final String languageFolder; private final Map loadedBackupStrings; private String chosenLanguage; private Map loadedStringTranslations; /** * Instantiates a new language loader * *

This will only load the backup language. Set the chosen language and reload afterwards.

* * @param languageFolder

The folder containing the language files

*/ public LanguageLoader(@NotNull String languageFolder) { this.languageFolder = languageFolder; File testFile = new File(languageFolder, "en.txt"); if (!testFile.exists()) { if (testFile.getParentFile().mkdirs()) { Stargate.debug("LanguageLoader", "Created language folder"); } } //Load english as backup language in case the chosen language is missing newly added text strings InputStream inputStream = FileHelper.getInputStreamForInternalFile("/lang/en.txt"); if (inputStream != null) { loadedBackupStrings = load("en", inputStream); } else { loadedBackupStrings = null; Stargate.logSevere("Error loading backup language. " + "There may be missing text in-game"); } } /** * Reloads languages from the files on disk */ public void reload() { //Extracts/Updates the language as needed updateLanguage(chosenLanguage); loadedStringTranslations = load(chosenLanguage); } /** * Gets the string to display given its message key * * @param message

The message to display

* @return

The message in the user's preferred language

*/ @NotNull public String getString(@NotNull Message message) { String value = null; if (loadedStringTranslations != null) { value = loadedStringTranslations.get(message); } if (value == null) { value = getBackupString(message); } return value; } /** * Gets the string to display given its message key * * @param message

The message to display

* @return

The string in the backup language (English)

*/ @NotNull public String getBackupString(@NotNull Message message) { String value = null; if (loadedBackupStrings != null) { value = loadedBackupStrings.get(message); } if (value == null) { return ""; } return value; } /** * Sets the chosen plugin language * * @param chosenLanguage

The new plugin language

*/ public void setChosenLanguage(@NotNull String chosenLanguage) { this.chosenLanguage = chosenLanguage; } /** * Updates files in the plugin directory with contents from the compiled .jar * * @param language

The language to update

*/ private void updateLanguage(@NotNull String language) { Map currentLanguageValues = load(language); InputStream inputStream = getClass().getResourceAsStream("/lang/" + language + ".txt"); if (inputStream == null) { Stargate.logInfo(String.format("The language %s is not available. Falling back to english, You can add a " + "custom language by creating a new text file in the lang directory.", language)); Stargate.debug("LanguageLoader::updateLanguage", String.format("Unable to load /lang/%s.txt", language)); return; } try { readChangedLanguageStrings(inputStream, language, currentLanguageValues); } catch (IOException exception) { Stargate.logSevere("Unable to read language strings! Message: " + exception.getMessage()); } } /** * Reads language strings * * @param inputStream

The input stream to read from

* @param language

The selected language

* @param currentLanguageValues

The current values of the loaded/processed language

* @throws IOException

if unable to read a language file

*/ private void readChangedLanguageStrings(@NotNull InputStream inputStream, @NotNull String language, @Nullable Map currentLanguageValues) throws IOException { //Get language values BufferedReader bufferedReader = FileHelper.getBufferedReaderFromInputStream(inputStream); Map internalLanguageValues = fromStringMap(FileHelper.readKeyValuePairs(bufferedReader, "=", ColorConversion.NORMAL)); //If currentLanguageValues is null; the chosen language has not been used before if (currentLanguageValues == null) { updateLanguageFile(language, internalLanguageValues, null); Stargate.logInfo(String.format("Language (%s) has been loaded", language)); return; } //If a key is not found in the language file, add the one in the internal file. Must update the external file if (!internalLanguageValues.keySet().equals(currentLanguageValues.keySet())) { Map newLanguageValues = new EnumMap<>(Message.class); boolean updateNecessary = false; for (Map.Entry entry : internalLanguageValues.entrySet()) { Message key = entry.getKey(); String value = entry.getValue(); if (currentLanguageValues.get(key) == null) { newLanguageValues.put(key, value); //Found at least one value in the internal file not in the external file. Need to update updateNecessary = true; } else { newLanguageValues.put(key, currentLanguageValues.get(key)); currentLanguageValues.remove(key); } } //Update the file itself if (updateNecessary) { updateLanguageFile(language, newLanguageValues, currentLanguageValues); Stargate.logInfo(String.format("Your language file (%s.txt) has been updated", language)); } } } /** * Updates the language file for a given language * * @param language

The language to update

* @param languageStrings

The updated language strings

* @param customLanguageStrings

Any custom language strings not recognized

* @throws IOException

If unable to write to the language file

*/ private void updateLanguageFile(@NotNull String language, @NotNull Map languageStrings, @Nullable Map customLanguageStrings) throws IOException { BufferedWriter bufferedWriter = FileHelper.getBufferedWriterFromString(languageFolder + language + ".txt"); //Output normal Language data for (Map.Entry entry : languageStrings.entrySet()) { bufferedWriter.write(entry.getKey() + "=" + entry.getValue()); bufferedWriter.newLine(); } bufferedWriter.newLine(); //Output any custom language strings the user had if (customLanguageStrings != null) { for (Map.Entry entry : customLanguageStrings.entrySet()) { bufferedWriter.write(entry.getKey() + "=" + entry.getValue()); bufferedWriter.newLine(); } } bufferedWriter.close(); } /** * Loads the given language * * @param lang

The language to load

* @return

A mapping between loaded string indexes and the strings to display

*/ private Map load(@NotNull String lang) { return load(lang, null); } /** * Loads the given language * * @param lang

The language to load

* @param inputStream

An optional input stream to use. Defaults to using a file input stream

* @return

A mapping between loaded string indexes and the strings to display

*/ private Map load(@NotNull String lang, @Nullable InputStream inputStream) { BufferedReader bufferedReader; try { if (inputStream == null) { bufferedReader = FileHelper.getBufferedReaderFromString(languageFolder + lang + ".txt"); } else { bufferedReader = FileHelper.getBufferedReaderFromInputStream(inputStream); } return fromStringMap(FileHelper.readKeyValuePairs(bufferedReader, "=", ColorConversion.NORMAL)); } catch (Exception exception) { if (Stargate.getStargateConfig().isDebuggingEnabled()) { Stargate.logInfo("Unable to load language " + lang); } return null; } } /** * Prints debug output to the console for checking loaded language strings/translations */ public void debug() { if (loadedStringTranslations != null) { for (Map.Entry entry : loadedStringTranslations.entrySet()) { Stargate.debug("LanguageLoader::Debug::loadedStringTranslations", entry.getKey() + " => " + entry.getValue()); } } if (loadedBackupStrings == null) { return; } for (Map.Entry entry : loadedBackupStrings.entrySet()) { Stargate.debug("LanguageLoader::Debug::loadedBackupStrings", entry.getKey() + " => " + entry.getValue()); } } @NotNull private Map fromStringMap(@NotNull Map configurationStrings) { Map output = new EnumMap<>(Message.class); for (Map.Entry entry : configurationStrings.entrySet()) { Message message = Message.getFromKey(entry.getKey()); if (message == null) { Stargate.logWarning("Found unrecognized language key " + entry.getKey()); continue; } output.put(message, entry.getValue()); } return output; } }