package net.knarcraft.stargate; import org.bukkit.ChatColor; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; /* * stargate - A portal plugin for Bukkit * Copyright (C) 2011, 2012 Steven "Drakia" Scott * Copyright (C) 2021 Kristian Knarvik *

* This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. *

* This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. *

* You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ /** * This class is responsible for loading all strings which are translated into several languages */ public class LanguageLoader { // Variables private final String languageFolder; private String chosenLanguage; private Map loadedStringTranslations; private final Map loadedBackupStrings; /** * Instantiates a new language loader * @param languageFolder

The folder containing the language files

* @param chosenLanguage

The chosen plugin language

*/ public LanguageLoader(String languageFolder, String chosenLanguage) { this.chosenLanguage = chosenLanguage; this.languageFolder = languageFolder; File tmp = new File(languageFolder, chosenLanguage + ".txt"); if (!tmp.exists()) { tmp.getParentFile().mkdirs(); } updateLanguage(chosenLanguage); loadedStringTranslations = load(chosenLanguage); // We have a default hashMap used for when new text is added. InputStream inputStream = getClass().getResourceAsStream("/lang/" + chosenLanguage + ".txt"); if (inputStream != null) { loadedBackupStrings = load("en", inputStream); } else { loadedBackupStrings = null; Stargate.log.severe("[stargate] Error loading backup language. There may be missing text in-game"); } } /** * Reloads languages from the files on disk */ public void reload() { // This extracts/updates the language as needed updateLanguage(chosenLanguage); loadedStringTranslations = load(chosenLanguage); } /** * Gets the string to display given its name/key * @param name

The name/key of the string to display

* @return

The string in the user's preferred language

*/ public String getString(String name) { String val = loadedStringTranslations.get(name); if (val == null && loadedBackupStrings != null) val = loadedBackupStrings.get(name); if (val == null) return ""; return val; } /** * Sets the chosen plugin language * @param chosenLanguage

The new plugin language

*/ public void setChosenLanguage(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(String language) { // Load the current language file ArrayList keyList = new ArrayList<>(); ArrayList valueList = new ArrayList<>(); Map currentLanguageValues = load(language); InputStream inputStream = getClass().getResourceAsStream("/lang/" + language + ".txt"); if (inputStream == null) return; boolean updated = false; FileOutputStream fileOutputStream = null; try { if (readChangedLanguageStrings(inputStream, keyList, valueList, currentLanguageValues)) { updated = true; } // Save file fileOutputStream = new FileOutputStream(languageFolder + language + ".txt"); OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, StandardCharsets.UTF_8); BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter); // Output normal Language data for (int i = 0; i < keyList.size(); i++) { bufferedWriter.write(keyList.get(i) + valueList.get(i)); bufferedWriter.newLine(); } bufferedWriter.newLine(); // Output any custom language strings the user had if (currentLanguageValues != null) { for (String key : currentLanguageValues.keySet()) { bufferedWriter.write(key + "=" + currentLanguageValues.get(key)); bufferedWriter.newLine(); } } bufferedWriter.close(); } catch (IOException ex) { ex.printStackTrace(); } finally { if (fileOutputStream != null) { try { fileOutputStream.close(); } catch (Exception e) { //Ignored } } } if (updated) { Stargate.log.info("[stargate] Your language file (" + language + ".txt) has been updated"); } } /** * Reads language strings * @param inputStream

The input stream to read from

* @param keyList

The key list to add keys to

* @param valueList

The value list to add values to

* @param currentLanguageValues

The current values of the loaded/processed language

* @return

True if at least one line was updated

* @throws IOException

if unable to read a language file

*/ private boolean readChangedLanguageStrings(InputStream inputStream, List keyList, List valueList, Map currentLanguageValues) throws IOException { boolean updated = false; // Input stuff InputStreamReader inputStreamReader = new InputStreamReader(inputStream); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String line = bufferedReader.readLine(); boolean firstLine = true; while (line != null) { // Strip UTF BOM if (firstLine) { line = removeUTF8BOM(line); firstLine = false; } // Split at first "=" int equalSignIndex = line.indexOf('='); if (equalSignIndex == -1) { keyList.add(""); valueList.add(""); line = bufferedReader.readLine(); continue; } String key = line.substring(0, equalSignIndex); String value = line.substring(equalSignIndex); if (currentLanguageValues == null || currentLanguageValues.get(key) == null) { keyList.add(key); valueList.add(value); updated = true; } else { keyList.add(key); valueList.add("=" + currentLanguageValues.get(key).replace('\u00A7', '&')); currentLanguageValues.remove(key); } line = bufferedReader.readLine(); } bufferedReader.close(); return updated; } /** * 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(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(String lang, InputStream inputStream) { Map strings = new HashMap<>(); FileInputStream fileInputStream = null; InputStreamReader inputStreamReader; try { if (inputStream == null) { fileInputStream = new FileInputStream(languageFolder + lang + ".txt"); inputStreamReader = new InputStreamReader(fileInputStream, StandardCharsets.UTF_8); } else { inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); } readLanguageFile(inputStreamReader, strings); } catch (Exception e) { return null; } finally { if (fileInputStream != null) { try { fileInputStream.close(); } catch (IOException e) { //Ignored } } } return strings; } /** * Reads a language file given its input stream * @param inputStreamReader

The input stream reader to read from

* @param strings

The loaded string pairs

* @throws IOException

If unable to read the file

*/ private void readLanguageFile(InputStreamReader inputStreamReader, Map strings) throws IOException { BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String line = bufferedReader.readLine(); boolean firstLine = true; while (line != null) { // Strip UTF BOM if (firstLine) { line = removeUTF8BOM(line); firstLine = false; } // Split at first "=" int equalSignIndex = line.indexOf('='); if (equalSignIndex == -1) { line = bufferedReader.readLine(); continue; } String key = line.substring(0, equalSignIndex); String val = ChatColor.translateAlternateColorCodes('&', line.substring(equalSignIndex + 1)); strings.put(key, val); line = bufferedReader.readLine(); } } /** * Prints debug output to the console for checking of loading language strings/translations */ public void debug() { Set keys = loadedStringTranslations.keySet(); for (String key : keys) { Stargate.debug("LanguageLoader::Debug::loadedStringTranslations", key + " => " + loadedStringTranslations.get(key)); } if (loadedBackupStrings == null) return; keys = loadedBackupStrings.keySet(); for (String key : keys) { Stargate.debug("LanguageLoader::Debug::loadedBackupStrings", key + " => " + loadedBackupStrings.get(key)); } } /** * Removes the UTF-8 Byte Order Mark if present * @param string

The string to remove the BOM from

* @return

A string guaranteed without a BOM

*/ private String removeUTF8BOM(String string) { String UTF8_BOM = "\uFEFF"; if (string.startsWith(UTF8_BOM)) { string = string.substring(1); } return string; } }