3
0
Mirror von https://github.com/GeyserMC/Geyser.git synchronisiert 2024-11-20 06:50:09 +01:00

Add support for adding custom translations. (#4047)

* Add support for loading locale overwrites. Any lang files in this new folder will be appended to the main lang file when loaded.
* A locale will no longer attempt to be downloaded and loaded if it already is loaded. Previously a lang file was reloaded everytime a player joins.
* Switch some io bits to nio
* formatting fixes
* Update core/src/main/java/org/geysermc/geyser/text/MinecraftLocale.java
* Rename isLocalLoaded to isLocaleLoaded
* Rename overwrites to overrides
* Catch separate exceptions when parsing locale file. Similar to previous implementation
* Add //no-op to try/catch
* Apply suggestions to fix issues that might arise with the Norwegian locale
* Properly resolve override locale path for nb_no
* Yeet temporary fix - addresses @Camotoy's review
* Catch IOException properly

---------
Co-authored-by: onebeastchris <github@onechris.mozmail.com>
Dieser Commit ist enthalten in:
kyrptonaught 2023-12-11 18:20:25 -05:00 committet von GitHub
Ursprung f1e7ef92f4
Commit d63a70daa9
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 4AEE18F83AFDEB23

Datei anzeigen

@ -35,6 +35,7 @@ import org.geysermc.geyser.util.WebUtils;
import java.io.*; import java.io.*;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.Locale; import java.util.Locale;
@ -44,16 +45,15 @@ public class MinecraftLocale {
public static final Map<String, Map<String, String>> LOCALE_MAPPINGS = new HashMap<>(); public static final Map<String, Map<String, String>> LOCALE_MAPPINGS = new HashMap<>();
static { private static final Path LOCALE_FOLDER = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("locales");
// Create the locales folder
File localesFolder = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("locales").toFile();
//noinspection ResultOfMethodCallIgnored
localesFolder.mkdir();
// FIXME TEMPORARY static {
try { try {
Files.delete(localesFolder.toPath().resolve("en_us.hash")); // Create the locales folder
} catch (IOException ignored) { Files.createDirectories(LOCALE_FOLDER);
Files.createDirectories(LOCALE_FOLDER.resolve("overrides"));
} catch (IOException exception) {
throw new RuntimeException("Unable to create locale folders! " + exception.getMessage());
} }
} }
@ -69,12 +69,18 @@ public class MinecraftLocale {
} }
/** /**
* Downloads a locale from Mojang if its not already loaded * Downloads a locale from Mojang if it's not already loaded
* *
* @param locale Locale to download and load * @param locale Locale to download and load
*/ */
public static void downloadAndLoadLocale(String locale) { public static void downloadAndLoadLocale(String locale) {
locale = locale.toLowerCase(Locale.ROOT); locale = locale.toLowerCase(Locale.ROOT);
if (isLocaleLoaded(locale)) {
GeyserImpl.getInstance().getLogger().debug("Locale already loaded: " + locale);
return;
}
if (locale.equals("nb_no")) { if (locale.equals("nb_no")) {
// Different locale code - https://minecraft.wiki/w/Language // Different locale code - https://minecraft.wiki/w/Language
locale = "no_no"; locale = "no_no";
@ -99,7 +105,7 @@ public class MinecraftLocale {
} }
/** /**
* Downloads the specified locale if its not already downloaded * Downloads the specified locale if it's not already downloaded
* *
* @param locale Locale to download * @param locale Locale to download
*/ */
@ -132,34 +138,56 @@ public class MinecraftLocale {
} }
private static Path getPath(String locale) { private static Path getPath(String locale) {
return GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("locales/" + locale + ".json"); return LOCALE_FOLDER.resolve(locale + ".json");
} }
/** /**
* Loads a locale already downloaded, if the file doesn't exist it just logs a warning * Loads a locale already downloaded, if the file doesn't exist it just logs a warning
* *
* @param locale Locale to load * @param locale Bedrock locale to load
*/ */
private static boolean loadLocale(String locale) { private static boolean loadLocale(String locale) {
File localeFile = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("locales/" + locale + ".json").toFile(); String lowercaseLocale = locale.toLowerCase(Locale.ROOT);
// Load the locale // Need to grab this before we change the locale - downloaded/override locales are stored under the Java locale name
if (localeFile.exists()) { Path localeFile = getPath(lowercaseLocale);
Path localeOverride = getPath("overrides/" + lowercaseLocale);
if (lowercaseLocale.equals("no_no")) {
// Store this locale under the Bedrock locale, so we don't need to do this check over and over
lowercaseLocale = "nb_no";
}
Map<String, String> langMap = new HashMap<>();
if (Files.exists(localeFile) && Files.isReadable(localeFile)) {
langMap.putAll(parseLangFile(localeFile, lowercaseLocale));
}
// Load the locale overwrites
if (Files.exists(localeOverride) && Files.isReadable(localeOverride)) {
langMap.putAll(parseLangFile(localeOverride, lowercaseLocale));
}
if (!langMap.isEmpty()) {
LOCALE_MAPPINGS.put(lowercaseLocale, langMap);
return true;
} else {
return false;
}
}
/**
* Load and parse a json lang file.
*
* @param localeFile Path of locale file
* @param locale Locale to load
* @return a Map of the loaded translations
*/
public static Map<String, String> parseLangFile(Path localeFile, String locale) {
// Read the localefile // Read the localefile
InputStream localeStream; try (InputStream localeStream = Files.newInputStream(localeFile, StandardOpenOption.READ)) {
try {
localeStream = new FileInputStream(localeFile);
} catch (FileNotFoundException e) {
throw new AssertionError(GeyserLocale.getLocaleStringLog("geyser.locale.fail.file", locale, e.getMessage()));
}
// Parse the file as json // Parse the file as json
JsonNode localeObj; JsonNode localeObj = GeyserImpl.JSON_MAPPER.readTree(localeStream);
try {
localeObj = GeyserImpl.JSON_MAPPER.readTree(localeStream);
} catch (Exception e) {
throw new AssertionError(GeyserLocale.getLocaleStringLog("geyser.locale.fail.json", locale), e);
}
// Parse all the locale fields // Parse all the locale fields
Iterator<Map.Entry<String, JsonNode>> localeIterator = localeObj.fields(); Iterator<Map.Entry<String, JsonNode>> localeIterator = localeObj.fields();
@ -168,24 +196,12 @@ public class MinecraftLocale {
Map.Entry<String, JsonNode> entry = localeIterator.next(); Map.Entry<String, JsonNode> entry = localeIterator.next();
langMap.put(entry.getKey(), entry.getValue().asText()); langMap.put(entry.getKey(), entry.getValue().asText());
} }
String bedrockLocale = locale.toLowerCase(Locale.ROOT);
if (bedrockLocale.equals("no_no")) {
// Store this locale under the Bedrock locale so we don't need to do this check over and over
bedrockLocale = "nb_no";
}
// Insert the locale into the mappings
LOCALE_MAPPINGS.put(bedrockLocale, langMap);
try {
localeStream.close(); localeStream.close();
} catch (IOException e) { return langMap;
} catch (FileNotFoundException e){
throw new AssertionError(GeyserLocale.getLocaleStringLog("geyser.locale.fail.file", locale, e.getMessage())); throw new AssertionError(GeyserLocale.getLocaleStringLog("geyser.locale.fail.file", locale, e.getMessage()));
} } catch (Exception e) {
return true; throw new AssertionError(GeyserLocale.getLocaleStringLog("geyser.locale.fail.json", locale), e);
} else {
return false;
} }
} }
@ -226,6 +242,16 @@ public class MinecraftLocale {
return null; return null;
} }
/**
* Checks if a locale has been loaded.
*
* @param locale Locale to check
* @return true if the locale has been loaded
*/
public static boolean isLocaleLoaded(String locale) {
return LOCALE_MAPPINGS.containsKey(locale.toLowerCase(Locale.ROOT));
}
/** /**
* Convert a byte array into a hex string * Convert a byte array into a hex string
* *