From bf53c54e6fa637e78f04dfddcebd97be0b55e31d Mon Sep 17 00:00:00 2001 From: BugTeaON <99161186+BUGTeas@users.noreply.github.com> Date: Sun, 8 Jun 2025 07:54:08 +0800 Subject: [PATCH] Escape curly braces correctly in translation strings; remove default empty translation fallback in the text component (#5559) * Escape curly braces correctly in translation strings * Default translation fallback should not appear in the text component * Only left braces need to be escaped * Some adjustments * Move the escapeBraces method to the MessageTranslator class * Improve code readability * Use complied static Pattern instead of Pattern.matches * Improve some code comments * Use regular expression complelety to escape braces instead of the inefficient, low-readablity escapeBraces method * Add some tests about escaping curly braces in translatable strings * Check instance availability to avoid exception during testing --- .../geysermc/geyser/text/MinecraftLocale.java | 21 ++++++++++++------- .../text/MinecraftTranslationRegistry.java | 18 ++++++++++------ .../translator/text/MessageTranslator.java | 2 +- .../chat/MessageTranslatorTest.java | 6 ++++++ 4 files changed, 32 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/text/MinecraftLocale.java b/core/src/main/java/org/geysermc/geyser/text/MinecraftLocale.java index 94d8b254f..74ab1e7e6 100644 --- a/core/src/main/java/org/geysermc/geyser/text/MinecraftLocale.java +++ b/core/src/main/java/org/geysermc/geyser/text/MinecraftLocale.java @@ -47,15 +47,20 @@ public class MinecraftLocale { public static final Map> LOCALE_MAPPINGS = new HashMap<>(); - private static final Path LOCALE_FOLDER = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("locales"); + // Check instance availability to avoid exception during testing + private static final boolean IN_INSTANCE = GeyserImpl.getInstance() != null; + + private static final Path LOCALE_FOLDER = (IN_INSTANCE) ? GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("locales") : null; static { - try { - // Create the locales folder - Files.createDirectories(LOCALE_FOLDER); - Files.createDirectories(LOCALE_FOLDER.resolve("overrides")); - } catch (IOException exception) { - throw new RuntimeException("Unable to create locale folders! " + exception.getMessage()); + if (IN_INSTANCE) { + try { + // Create the locales folder + Files.createDirectories(LOCALE_FOLDER); + Files.createDirectories(LOCALE_FOLDER.resolve("overrides")); + } catch (IOException exception) { + throw new RuntimeException("Unable to create locale folders! " + exception.getMessage()); + } } } @@ -266,4 +271,4 @@ public class MinecraftLocale { } return result.toString(); } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/geysermc/geyser/text/MinecraftTranslationRegistry.java b/core/src/main/java/org/geysermc/geyser/text/MinecraftTranslationRegistry.java index 67654360d..03b017c9c 100644 --- a/core/src/main/java/org/geysermc/geyser/text/MinecraftTranslationRegistry.java +++ b/core/src/main/java/org/geysermc/geyser/text/MinecraftTranslationRegistry.java @@ -41,6 +41,7 @@ import java.util.regex.Pattern; public class MinecraftTranslationRegistry extends TranslatableComponentRenderer { private final Pattern stringReplacement = Pattern.compile("%s"); private final Pattern positionalStringReplacement = Pattern.compile("%([0-9]+)\\$s"); + private final Pattern escapeBraces = Pattern.compile("\\{+['{]+\\{+|\\{+"); // Exists to maintain compatibility with Velocity's older Adventure version @Override @@ -66,14 +67,19 @@ public class MinecraftTranslationRegistry extends TranslatableComponentRenderer< // replace single quote instances which get lost in MessageFormat otherwise localeString = localeString.replace("'", "''"); - // Wrap all curly brackets with single quote inserts - fixes https://github.com/GeyserMC/Geyser/issues/4662 - localeString = localeString.replace("{", "'{") - .replace("}", "'}"); - - // Replace the `%s` with numbered inserts `{0}` - Pattern p = stringReplacement; + // Escape all left curly brackets with single quote - fixes https://github.com/GeyserMC/Geyser/issues/4662 + Pattern p = escapeBraces; Matcher m = p.matcher(localeString); StringBuilder sb = new StringBuilder(); + while (m.find()) { + m.appendReplacement(sb, "'" + m.group() + "'"); + } + m.appendTail(sb); + + // Replace the `%s` with numbered inserts `{0}` + p = stringReplacement; + m = p.matcher(sb.toString()); + sb = new StringBuilder(); int i = 0; while (m.find()) { m.appendReplacement(sb, "{" + (i++) + "}"); diff --git a/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java index 62a6aa00b..341bbd921 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java @@ -500,7 +500,7 @@ public class MessageTranslator { } else { String translateKey = map.getString("translate", null); if (translateKey != null) { - String fallback = map.getString("fallback", ""); + String fallback = map.getString("fallback", null); List args = new ArrayList<>(); Object with = map.get("with"); diff --git a/core/src/test/java/org/geysermc/geyser/network/translators/chat/MessageTranslatorTest.java b/core/src/test/java/org/geysermc/geyser/network/translators/chat/MessageTranslatorTest.java index 1b1ef485a..3af4f0a11 100644 --- a/core/src/test/java/org/geysermc/geyser/network/translators/chat/MessageTranslatorTest.java +++ b/core/src/test/java/org/geysermc/geyser/network/translators/chat/MessageTranslatorTest.java @@ -69,6 +69,12 @@ public class MessageTranslatorTest { "§e All participants will receive a reward\n" + "§e and the top 3 will get extra bonus prizes!"); + // Escape curly braces in translatable strings (make MessageFormat ignore them) + messages.put("{\"translate\":\"tt{tt%stt}tt\",\"with\":[\"AA\"]}", "tt{ttAAtt}tt"); + messages.put("{\"translate\":\"tt{'tt%stt'{tt\",\"with\":[\"AA\"]}", "tt{'ttAAtt'{tt"); + messages.put("{\"translate\":\"tt{''{tt\"}", "tt{''{tt"); + messages.put("{\"translate\":\"tt{{''}}tt\"}", "tt{{''}}tt"); + MessageTranslator.init(); }