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:
Ursprung
f1e7ef92f4
Commit
d63a70daa9
@ -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
|
||||||
*
|
*
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren