From 848b19ed78ca59ae2b184159ae8300c7533bf215 Mon Sep 17 00:00:00 2001 From: NebelNidas Date: Mon, 16 Oct 2023 13:05:26 +0200 Subject: [PATCH 01/12] Initial localization support --- .../mappingio/format/MappingFormat.java | 33 +++++--- .../net/fabricmc/mappingio/i18n/I18n.java | 76 +++++++++++++++++++ .../fabricmc/mappingio/i18n/MioLocale.java | 27 +++++++ src/main/resources/i18n/en_us.properties | 9 +++ .../net/fabricmc/mappingio/i18n/I18nTest.java | 43 +++++++++++ 5 files changed, 177 insertions(+), 11 deletions(-) create mode 100644 src/main/java/net/fabricmc/mappingio/i18n/I18n.java create mode 100644 src/main/java/net/fabricmc/mappingio/i18n/MioLocale.java create mode 100644 src/main/resources/i18n/en_us.properties create mode 100644 src/test/java/net/fabricmc/mappingio/i18n/I18nTest.java diff --git a/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java b/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java index e79e48a7..a14aa963 100644 --- a/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java +++ b/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java @@ -16,6 +16,11 @@ package net.fabricmc.mappingio.format; +import java.util.Locale; + +import net.fabricmc.mappingio.i18n.I18n; +import net.fabricmc.mappingio.i18n.MioLocale; + /** * Represents a supported mapping format. Feature comparison table: * @@ -98,48 +103,48 @@ public enum MappingFormat { /** * The {@code Tiny} mapping format, as specified here. */ - TINY_FILE("Tiny file", "tiny", true, true, false, false, false), + TINY_FILE("tiny", true, true, false, false, false), /** * The {@code Tiny v2} mapping format, as specified here. */ - TINY_2_FILE("Tiny v2 file", "tiny", true, true, true, true, true), + TINY_2_FILE("tiny", true, true, true, true, true), /** * Enigma's mapping format, as specified here. */ - ENIGMA_FILE("Enigma file", "mapping", false, true, true, true, false), + ENIGMA_FILE("mapping", false, true, true, true, false), /** * Enigma's mapping format (in directory form), as specified here. */ - ENIGMA_DIR("Enigma directory", null, false, true, true, true, false), + ENIGMA_DIR(null, false, true, true, true, false), /** * The {@code SRG} ({@code Searge RetroGuard}) mapping format, as specified here. */ - SRG_FILE("SRG file", "srg", false, false, false, false, false), + SRG_FILE("srg", false, false, false, false, false), /** * The {@code TSRG} ({@code Tiny SRG}, since it saves disk space over SRG) mapping format, as specified here. */ - TSRG_FILE("TSRG file", "tsrg", false, false, false, false, false), + TSRG_FILE("tsrg", false, false, false, false, false), /** * The {@code TSRG v2} mapping format, as specified here. */ - TSRG_2_FILE("TSRG2 file", "tsrg", true, true, false, true, false), + TSRG_2_FILE("tsrg", true, true, false, true, false), /** * ProGuard's mapping format, as specified here. */ - PROGUARD_FILE("ProGuard file", "txt", false, true, false, false, false); + PROGUARD_FILE("txt", false, true, false, false, false); - MappingFormat(String name, String fileExt, - boolean hasNamespaces, boolean hasFieldDescriptors, + MappingFormat(String fileExt, boolean hasNamespaces, boolean hasFieldDescriptors, boolean supportsComments, boolean supportsArgs, boolean supportsLocals) { - this.name = name; this.fileExt = fileExt; + this.translationKey = "format." + name().toLowerCase(Locale.ROOT); + this.name = getName(MioLocale.EN_US); this.hasNamespaces = hasNamespaces; this.hasFieldDescriptors = hasFieldDescriptors; this.supportsComments = supportsComments; @@ -147,6 +152,10 @@ public enum MappingFormat { this.supportsLocals = supportsLocals; } + public String getName(MioLocale locale) { + return I18n.translate(translationKey, locale); + } + public boolean hasSingleFile() { return fileExt != null; } @@ -157,6 +166,7 @@ public String getGlobPattern() { return "*."+fileExt; } + /** @deprecated Use {@link #getName()} instead. */ public final String name; public final String fileExt; public final boolean hasNamespaces; @@ -164,4 +174,5 @@ public String getGlobPattern() { public final boolean supportsComments; public final boolean supportsArgs; public final boolean supportsLocals; + private final String translationKey; } diff --git a/src/main/java/net/fabricmc/mappingio/i18n/I18n.java b/src/main/java/net/fabricmc/mappingio/i18n/I18n.java new file mode 100644 index 00000000..41e11095 --- /dev/null +++ b/src/main/java/net/fabricmc/mappingio/i18n/I18n.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2023 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.mappingio.i18n; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; +import java.util.PropertyResourceBundle; +import java.util.ResourceBundle; + +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Internal +public class I18n { + private I18n() { + } + + public static String translate(String key, MioLocale locale, Object... args) { + return String.format(translate(key, locale), args); + } + + public static String translate(String key, MioLocale locale) { + try { + return messageBundles.getOrDefault(locale, load(locale)).getString(key); + } catch (Exception e) { + System.err.println("Exception while translating key " + key + " to locale " + locale.id + ": " + e.getMessage()); + if (locale == fallbackLocale) return key; + + try { + return messageBundles.getOrDefault(fallbackLocale, load(fallbackLocale)).getString(key); + } catch (Exception e2) { + System.err.println("Exception while translating key " + key + " to fallback locale: " + e2.getMessage()); + return key; + } + } + } + + private static ResourceBundle load(MioLocale locale) { + ResourceBundle resBundle; + String resName = String.format("/i18n/%s.properties", locale.id); + URL resUrl = I18n.class.getResource(resName); + + if (resUrl == null) { + throw new RuntimeException("Locale resource not found: " + resName); + } + + try (Reader reader = new InputStreamReader(resUrl.openStream(), StandardCharsets.UTF_8)) { + resBundle = new PropertyResourceBundle(reader); + messageBundles.put(locale, resBundle); + return resBundle; + } catch (IOException e) { + throw new RuntimeException("Failed to load " + resName, e); + } + } + + private static final MioLocale fallbackLocale = MioLocale.EN_US; + private static final Map messageBundles = new HashMap<>(); +} diff --git a/src/main/java/net/fabricmc/mappingio/i18n/MioLocale.java b/src/main/java/net/fabricmc/mappingio/i18n/MioLocale.java new file mode 100644 index 00000000..0a535a9a --- /dev/null +++ b/src/main/java/net/fabricmc/mappingio/i18n/MioLocale.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.mappingio.i18n; + +public enum MioLocale { + EN_US("en_us"); + + MioLocale(String id) { + this.id = id; + } + + final String id; +} diff --git a/src/main/resources/i18n/en_us.properties b/src/main/resources/i18n/en_us.properties new file mode 100644 index 00000000..244556f0 --- /dev/null +++ b/src/main/resources/i18n/en_us.properties @@ -0,0 +1,9 @@ +format.tiny_file = Tiny File +format.tiny_2_file = Tiny v2 File +format.enigma_file = Enigma File +format.enigma_dir = Enigma Directory +format.mcp_dir = MCP Directory +format.srg_file = SRG File +format.tsrg_file = TSRG File +format.tsrg_2_file = TSRG2 File +format.proguard_file = ProGuard File diff --git a/src/test/java/net/fabricmc/mappingio/i18n/I18nTest.java b/src/test/java/net/fabricmc/mappingio/i18n/I18nTest.java new file mode 100644 index 00000000..e79f9e70 --- /dev/null +++ b/src/test/java/net/fabricmc/mappingio/i18n/I18nTest.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.mappingio.i18n; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import net.fabricmc.mappingio.format.MappingFormat; + +public class I18nTest { + @Test + public void mappingFormats() { + for (MappingFormat format : MappingFormat.values()) { + String enUsName = format.getName(MioLocale.EN_US); + assertTrue(format.name.equals(enUsName)); + + for (MioLocale locale : MioLocale.values()) { + String translatedName = format.getName(locale); + assertFalse(translatedName.startsWith("format.")); + + if (locale != MioLocale.EN_US) { + assertFalse(translatedName.equals(enUsName)); + } + } + } + } +} From c3071eb74cec03f266263750bd5b70797b2ac9e7 Mon Sep 17 00:00:00 2001 From: NebelNidas Date: Mon, 16 Oct 2023 13:38:09 +0200 Subject: [PATCH 02/12] Remove MCP dir from translation strings --- src/main/resources/i18n/en_us.properties | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/resources/i18n/en_us.properties b/src/main/resources/i18n/en_us.properties index 244556f0..5e3cc738 100644 --- a/src/main/resources/i18n/en_us.properties +++ b/src/main/resources/i18n/en_us.properties @@ -2,7 +2,6 @@ format.tiny_file = Tiny File format.tiny_2_file = Tiny v2 File format.enigma_file = Enigma File format.enigma_dir = Enigma Directory -format.mcp_dir = MCP Directory format.srg_file = SRG File format.tsrg_file = TSRG File format.tsrg_2_file = TSRG2 File From 0ee988f3d4e69b2b450d767afe9f8336cde7b4ca Mon Sep 17 00:00:00 2001 From: NebelNidas Date: Fri, 19 Apr 2024 17:10:55 +0200 Subject: [PATCH 03/12] Return a `Translatable` instead of translating implicitly --- .../mappingio/format/MappingFormat.java | 18 +++++----- .../net/fabricmc/mappingio/i18n/I18n.java | 15 ++++---- .../{MioLocale.java => Translatable.java} | 16 +++++---- .../mappingio/i18n/TranslatableImpl.java | 35 +++++++++++++++++++ .../net/fabricmc/mappingio/i18n/I18nTest.java | 15 ++++---- 5 files changed, 69 insertions(+), 30 deletions(-) rename src/main/java/net/fabricmc/mappingio/i18n/{MioLocale.java => Translatable.java} (68%) create mode 100644 src/main/java/net/fabricmc/mappingio/i18n/TranslatableImpl.java diff --git a/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java b/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java index a14aa963..fcee2218 100644 --- a/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java +++ b/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java @@ -18,8 +18,8 @@ import java.util.Locale; -import net.fabricmc.mappingio.i18n.I18n; -import net.fabricmc.mappingio.i18n.MioLocale; +import net.fabricmc.mappingio.i18n.Translatable; +import net.fabricmc.mappingio.i18n.TranslatableImpl; /** * Represents a supported mapping format. Feature comparison table: @@ -144,7 +144,7 @@ public enum MappingFormat { boolean supportsComments, boolean supportsArgs, boolean supportsLocals) { this.fileExt = fileExt; this.translationKey = "format." + name().toLowerCase(Locale.ROOT); - this.name = getName(MioLocale.EN_US); + this.name = getName().translate(Locale.US); this.hasNamespaces = hasNamespaces; this.hasFieldDescriptors = hasFieldDescriptors; this.supportsComments = supportsComments; @@ -152,10 +152,6 @@ public enum MappingFormat { this.supportsLocals = supportsLocals; } - public String getName(MioLocale locale) { - return I18n.translate(translationKey, locale); - } - public boolean hasSingleFile() { return fileExt != null; } @@ -166,7 +162,13 @@ public String getGlobPattern() { return "*."+fileExt; } - /** @deprecated Use {@link #getName()} instead. */ + public Translatable getName() { + return new TranslatableImpl(translationKey); + } + + /** + * @deprecated Use {@link #getName()} instead. + */ public final String name; public final String fileExt; public final boolean hasNamespaces; diff --git a/src/main/java/net/fabricmc/mappingio/i18n/I18n.java b/src/main/java/net/fabricmc/mappingio/i18n/I18n.java index 41e11095..09e11037 100644 --- a/src/main/java/net/fabricmc/mappingio/i18n/I18n.java +++ b/src/main/java/net/fabricmc/mappingio/i18n/I18n.java @@ -22,6 +22,7 @@ import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.PropertyResourceBundle; import java.util.ResourceBundle; @@ -33,15 +34,15 @@ public class I18n { private I18n() { } - public static String translate(String key, MioLocale locale, Object... args) { + public static String translate(String key, Locale locale, Object... args) { return String.format(translate(key, locale), args); } - public static String translate(String key, MioLocale locale) { + public static String translate(String key, Locale locale) { try { return messageBundles.getOrDefault(locale, load(locale)).getString(key); } catch (Exception e) { - System.err.println("Exception while translating key " + key + " to locale " + locale.id + ": " + e.getMessage()); + System.err.println("Exception while translating key " + key + " to locale " + locale.toLanguageTag() + ": " + e.getMessage()); if (locale == fallbackLocale) return key; try { @@ -53,9 +54,9 @@ public static String translate(String key, MioLocale locale) { } } - private static ResourceBundle load(MioLocale locale) { + private static ResourceBundle load(Locale locale) { ResourceBundle resBundle; - String resName = String.format("/i18n/%s.properties", locale.id); + String resName = String.format("/i18n/%s.properties", locale.toLanguageTag().replace('-', '_').toLowerCase(Locale.ROOT)); URL resUrl = I18n.class.getResource(resName); if (resUrl == null) { @@ -71,6 +72,6 @@ private static ResourceBundle load(MioLocale locale) { } } - private static final MioLocale fallbackLocale = MioLocale.EN_US; - private static final Map messageBundles = new HashMap<>(); + private static final Locale fallbackLocale = Locale.US; + private static final Map messageBundles = new HashMap<>(); } diff --git a/src/main/java/net/fabricmc/mappingio/i18n/MioLocale.java b/src/main/java/net/fabricmc/mappingio/i18n/Translatable.java similarity index 68% rename from src/main/java/net/fabricmc/mappingio/i18n/MioLocale.java rename to src/main/java/net/fabricmc/mappingio/i18n/Translatable.java index 0a535a9a..542deb46 100644 --- a/src/main/java/net/fabricmc/mappingio/i18n/MioLocale.java +++ b/src/main/java/net/fabricmc/mappingio/i18n/Translatable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 FabricMC + * Copyright (c) 2024 FabricMC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,14 @@ package net.fabricmc.mappingio.i18n; -public enum MioLocale { - EN_US("en_us"); +import java.util.Locale; - MioLocale(String id) { - this.id = id; - } +import org.jetbrains.annotations.ApiStatus; - final String id; +@ApiStatus.NonExtendable +public interface Translatable { + /** + * Translates this translatable to the specified locale, with a fallback to en_US. + */ + String translate(Locale locale); } diff --git a/src/main/java/net/fabricmc/mappingio/i18n/TranslatableImpl.java b/src/main/java/net/fabricmc/mappingio/i18n/TranslatableImpl.java new file mode 100644 index 00000000..9b3d6ea6 --- /dev/null +++ b/src/main/java/net/fabricmc/mappingio/i18n/TranslatableImpl.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.mappingio.i18n; + +import java.util.Locale; + +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Internal +public final class TranslatableImpl implements Translatable { + public TranslatableImpl(String translationKey) { + this.translationKey = translationKey; + } + + @Override + public String translate(Locale locale) { + return I18n.translate(translationKey, locale); + } + + private final String translationKey; +} diff --git a/src/test/java/net/fabricmc/mappingio/i18n/I18nTest.java b/src/test/java/net/fabricmc/mappingio/i18n/I18nTest.java index e79f9e70..3ec79432 100644 --- a/src/test/java/net/fabricmc/mappingio/i18n/I18nTest.java +++ b/src/test/java/net/fabricmc/mappingio/i18n/I18nTest.java @@ -19,24 +19,23 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.Locale; + import org.junit.jupiter.api.Test; import net.fabricmc.mappingio.format.MappingFormat; public class I18nTest { @Test - public void mappingFormats() { + @SuppressWarnings("deprecation") + public void mappingFormatNames() { for (MappingFormat format : MappingFormat.values()) { - String enUsName = format.getName(MioLocale.EN_US); + String enUsName = format.getName().translate(Locale.US); assertTrue(format.name.equals(enUsName)); - for (MioLocale locale : MioLocale.values()) { - String translatedName = format.getName(locale); + for (Locale locale : Locale.getAvailableLocales()) { + String translatedName = format.getName().translate(locale); assertFalse(translatedName.startsWith("format.")); - - if (locale != MioLocale.EN_US) { - assertFalse(translatedName.equals(enUsName)); - } } } } From e533fc1199d8f5ccc21b32ed90d8e849596456e2 Mon Sep 17 00:00:00 2001 From: NebelNidas Date: Fri, 19 Apr 2024 17:58:06 +0200 Subject: [PATCH 04/12] Only test supported languages --- .../net/fabricmc/mappingio/i18n/I18nTest.java | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/test/java/net/fabricmc/mappingio/i18n/I18nTest.java b/src/test/java/net/fabricmc/mappingio/i18n/I18nTest.java index 3ec79432..9332cb10 100644 --- a/src/test/java/net/fabricmc/mappingio/i18n/I18nTest.java +++ b/src/test/java/net/fabricmc/mappingio/i18n/I18nTest.java @@ -19,13 +19,37 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.File; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; import java.util.Locale; +import java.util.stream.Collectors; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import net.fabricmc.mappingio.format.MappingFormat; public class I18nTest { + private static List supportedLocales; + + @BeforeAll + public static void init() throws Exception { + URL i18nUrl = I18nTest.class.getResource("/i18n/"); + Path path = Paths.get(i18nUrl.toURI()); + + supportedLocales = Files.walk(path) + .map(Path::toAbsolutePath) + .map(Path::toString) + .filter(name -> name.endsWith(".properties")) + .map(name -> name.substring(Math.max(0, name.lastIndexOf(File.separatorChar) + 1), name.length() - ".properties".length())) + .map(tag -> Locale.forLanguageTag(tag)) + .collect(Collectors.toList()); + } + @Test @SuppressWarnings("deprecation") public void mappingFormatNames() { @@ -33,9 +57,9 @@ public void mappingFormatNames() { String enUsName = format.getName().translate(Locale.US); assertTrue(format.name.equals(enUsName)); - for (Locale locale : Locale.getAvailableLocales()) { + for (Locale locale : supportedLocales) { String translatedName = format.getName().translate(locale); - assertFalse(translatedName.startsWith("format.")); + assertFalse(translatedName.startsWith("format."), "Translated name for " + format + " in " + locale + " is missing"); } } } From b83edf9e8e8d2c5c18e805f0eda2e3d3e315276b Mon Sep 17 00:00:00 2001 From: NebelNidas Date: Fri, 19 Apr 2024 18:01:39 +0200 Subject: [PATCH 05/12] Replace `assertTrue` with `assertEquals` --- src/test/java/net/fabricmc/mappingio/i18n/I18nTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/net/fabricmc/mappingio/i18n/I18nTest.java b/src/test/java/net/fabricmc/mappingio/i18n/I18nTest.java index 9332cb10..460c7ca5 100644 --- a/src/test/java/net/fabricmc/mappingio/i18n/I18nTest.java +++ b/src/test/java/net/fabricmc/mappingio/i18n/I18nTest.java @@ -16,8 +16,8 @@ package net.fabricmc.mappingio.i18n; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.net.URL; @@ -55,7 +55,7 @@ public static void init() throws Exception { public void mappingFormatNames() { for (MappingFormat format : MappingFormat.values()) { String enUsName = format.getName().translate(Locale.US); - assertTrue(format.name.equals(enUsName)); + assertEquals(enUsName, format.name); for (Locale locale : supportedLocales) { String translatedName = format.getName().translate(locale); From 5ebad578e99a8e844aa3e299fbf3c4073d56a0cd Mon Sep 17 00:00:00 2001 From: NebelNidas Date: Sat, 20 Apr 2024 07:04:52 +0200 Subject: [PATCH 06/12] Move lang files into `mappingio` directory --- src/main/java/net/fabricmc/mappingio/i18n/I18n.java | 2 +- src/main/resources/{i18n => mappingio/lang}/en_us.properties | 0 src/test/java/net/fabricmc/mappingio/i18n/I18nTest.java | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename src/main/resources/{i18n => mappingio/lang}/en_us.properties (100%) diff --git a/src/main/java/net/fabricmc/mappingio/i18n/I18n.java b/src/main/java/net/fabricmc/mappingio/i18n/I18n.java index 09e11037..f49368f2 100644 --- a/src/main/java/net/fabricmc/mappingio/i18n/I18n.java +++ b/src/main/java/net/fabricmc/mappingio/i18n/I18n.java @@ -56,7 +56,7 @@ public static String translate(String key, Locale locale) { private static ResourceBundle load(Locale locale) { ResourceBundle resBundle; - String resName = String.format("/i18n/%s.properties", locale.toLanguageTag().replace('-', '_').toLowerCase(Locale.ROOT)); + String resName = String.format("/mappingio/lang/%s.properties", locale.toLanguageTag().replace('-', '_').toLowerCase(Locale.ROOT)); URL resUrl = I18n.class.getResource(resName); if (resUrl == null) { diff --git a/src/main/resources/i18n/en_us.properties b/src/main/resources/mappingio/lang/en_us.properties similarity index 100% rename from src/main/resources/i18n/en_us.properties rename to src/main/resources/mappingio/lang/en_us.properties diff --git a/src/test/java/net/fabricmc/mappingio/i18n/I18nTest.java b/src/test/java/net/fabricmc/mappingio/i18n/I18nTest.java index 460c7ca5..0696ebee 100644 --- a/src/test/java/net/fabricmc/mappingio/i18n/I18nTest.java +++ b/src/test/java/net/fabricmc/mappingio/i18n/I18nTest.java @@ -38,7 +38,7 @@ public class I18nTest { @BeforeAll public static void init() throws Exception { - URL i18nUrl = I18nTest.class.getResource("/i18n/"); + URL i18nUrl = I18nTest.class.getResource("/mappingio/lang/"); Path path = Paths.get(i18nUrl.toURI()); supportedLocales = Files.walk(path) From 8b4431b3602c32f55075f041bece46cb7c8b82d9 Mon Sep 17 00:00:00 2001 From: NebelNidas Date: Sat, 20 Apr 2024 07:23:28 +0200 Subject: [PATCH 07/12] Optimize bundle loading --- .../net/fabricmc/mappingio/i18n/I18n.java | 42 +++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/fabricmc/mappingio/i18n/I18n.java b/src/main/java/net/fabricmc/mappingio/i18n/I18n.java index f49368f2..45f137d6 100644 --- a/src/main/java/net/fabricmc/mappingio/i18n/I18n.java +++ b/src/main/java/net/fabricmc/mappingio/i18n/I18n.java @@ -18,7 +18,9 @@ import java.io.IOException; import java.io.InputStreamReader; +import java.io.PrintWriter; import java.io.Reader; +import java.io.StringWriter; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.HashMap; @@ -26,6 +28,8 @@ import java.util.Map; import java.util.PropertyResourceBundle; import java.util.ResourceBundle; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import org.jetbrains.annotations.ApiStatus; @@ -40,21 +44,41 @@ public static String translate(String key, Locale locale, Object... args) { public static String translate(String key, Locale locale) { try { - return messageBundles.getOrDefault(locale, load(locale)).getString(key); + return load(locale).getString(key); } catch (Exception e) { - System.err.println("Exception while translating key " + key + " to locale " + locale.toLanguageTag() + ": " + e.getMessage()); + System.err.println("Exception while translating key " + key + " to locale " + locale.toLanguageTag() + ": " + getStackTrace(e)); if (locale == fallbackLocale) return key; try { - return messageBundles.getOrDefault(fallbackLocale, load(fallbackLocale)).getString(key); + return load(fallbackLocale).getString(key); } catch (Exception e2) { - System.err.println("Exception while translating key " + key + " to fallback locale: " + e2.getMessage()); + System.err.println("Exception while translating key " + key + " to fallback locale: " + getStackTrace(e2)); return key; } } } private static ResourceBundle load(Locale locale) { + ResourceBundle bundle = messageBundles.get(locale); + + if (bundle != null) { + return bundle; + } + + bundlesLock.lock(); + + try { + if ((bundle = messageBundles.get(locale)) != null) { + return bundle; + } + + return load0(locale); + } finally { + bundlesLock.unlock(); + } + } + + private static ResourceBundle load0(Locale locale) { ResourceBundle resBundle; String resName = String.format("/mappingio/lang/%s.properties", locale.toLanguageTag().replace('-', '_').toLowerCase(Locale.ROOT)); URL resUrl = I18n.class.getResource(resName); @@ -72,6 +96,16 @@ private static ResourceBundle load(Locale locale) { } } + private static String getStackTrace(Throwable t) { + if (t == null) return null; + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + t.printStackTrace(pw); + return sw.toString(); + } + + private static final Lock bundlesLock = new ReentrantLock(); private static final Locale fallbackLocale = Locale.US; private static final Map messageBundles = new HashMap<>(); } From 3f67d1c4a1ce142130858bbd1d83542d6c7970e1 Mon Sep 17 00:00:00 2001 From: NebelNidas Date: Fri, 17 Jan 2025 03:50:40 +0100 Subject: [PATCH 08/12] Some API changes --- CHANGELOG.md | 2 +- .../net/fabricmc/mappingio/format/MappingFormat.java | 9 ++++----- .../java/net/fabricmc/mappingio/i18n/Translatable.java | 5 +++++ .../net/fabricmc/mappingio/i18n/TranslatableImpl.java | 7 ++----- .../net/fabricmc/mappingio/test/tests/i18n/I18nTest.java | 4 ++-- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1735ebe3..0772569b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Made `OuterClassNamePropagator` configurable - Made Enigma writer always output destination names if visited explicitly, establishing consistency across all writers - Added a simplified `MappingNsCompleter` constructor for completing all destination names with the source names -- Added translatable `MappingFormat#getName()` +- Added `MappingFormat#translatableName()` ## [0.7.1] - 2025-01-07 - Restored the ability to read source-namespace-only mapping files, even if not spec-compliant diff --git a/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java b/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java index 2c20ff64..cb0060f6 100644 --- a/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java +++ b/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java @@ -24,7 +24,6 @@ import net.fabricmc.mappingio.format.FeatureSet.FeaturePresence; import net.fabricmc.mappingio.format.FeatureSet.MetadataSupport; import net.fabricmc.mappingio.i18n.Translatable; -import net.fabricmc.mappingio.i18n.TranslatableImpl; /** * Represents a supported mapping format. Every format can be assumed to have an associated reader available. @@ -281,7 +280,7 @@ public enum MappingFormat { MappingFormat(@Nullable String fileExt, boolean hasWriter, FeatureSetBuilder featureBuilder) { this.translationKey = "format." + name().toLowerCase(Locale.ROOT); - this.name = getName().translate(Locale.US); + this.name = translatableName().translate(Locale.US); this.fileExt = fileExt; this.hasWriter = hasWriter; this.features = featureBuilder.build(); @@ -292,8 +291,8 @@ public enum MappingFormat { this.supportsLocals = features.supportsVars(); } - public Translatable getName() { - return new TranslatableImpl(translationKey); + public Translatable translatableName() { + return Translatable.of(translationKey); } public FeatureSet features() { @@ -314,7 +313,7 @@ public String getGlobPattern() { private final String translationKey; private final FeatureSet features; /** - * @deprecated Use {@link #getName()} instead. + * @deprecated Use {@link #translatableName()} instead. */ @Deprecated public final String name; diff --git a/src/main/java/net/fabricmc/mappingio/i18n/Translatable.java b/src/main/java/net/fabricmc/mappingio/i18n/Translatable.java index 542deb46..5f5f0d07 100644 --- a/src/main/java/net/fabricmc/mappingio/i18n/Translatable.java +++ b/src/main/java/net/fabricmc/mappingio/i18n/Translatable.java @@ -22,6 +22,11 @@ @ApiStatus.NonExtendable public interface Translatable { + @ApiStatus.Internal + static Translatable of(String translationKey) { + return new TranslatableImpl(translationKey); + } + /** * Translates this translatable to the specified locale, with a fallback to en_US. */ diff --git a/src/main/java/net/fabricmc/mappingio/i18n/TranslatableImpl.java b/src/main/java/net/fabricmc/mappingio/i18n/TranslatableImpl.java index 9b3d6ea6..29e7dc81 100644 --- a/src/main/java/net/fabricmc/mappingio/i18n/TranslatableImpl.java +++ b/src/main/java/net/fabricmc/mappingio/i18n/TranslatableImpl.java @@ -18,11 +18,8 @@ import java.util.Locale; -import org.jetbrains.annotations.ApiStatus; - -@ApiStatus.Internal -public final class TranslatableImpl implements Translatable { - public TranslatableImpl(String translationKey) { +final class TranslatableImpl implements Translatable { + TranslatableImpl(String translationKey) { this.translationKey = translationKey; } diff --git a/src/test/java/net/fabricmc/mappingio/test/tests/i18n/I18nTest.java b/src/test/java/net/fabricmc/mappingio/test/tests/i18n/I18nTest.java index 5da0060d..9b837532 100644 --- a/src/test/java/net/fabricmc/mappingio/test/tests/i18n/I18nTest.java +++ b/src/test/java/net/fabricmc/mappingio/test/tests/i18n/I18nTest.java @@ -54,11 +54,11 @@ public static void init() throws Exception { @SuppressWarnings("deprecation") public void mappingFormatNames() { for (MappingFormat format : MappingFormat.values()) { - String enUsName = format.getName().translate(Locale.US); + String enUsName = format.translatableName().translate(Locale.US); assertEquals(enUsName, format.name); for (Locale locale : supportedLocales) { - String translatedName = format.getName().translate(locale); + String translatedName = format.translatableName().translate(locale); assertFalse(translatedName.startsWith("format."), "Translated name for " + format + " in " + locale + " is missing"); } } From f1d5555aed44c721e2e357314fa2b0f0b5d646d5 Mon Sep 17 00:00:00 2001 From: NebelNidas Date: Sat, 18 Jan 2025 03:36:12 +0100 Subject: [PATCH 09/12] Add translation key getter --- .../java/net/fabricmc/mappingio/i18n/Translatable.java | 7 +++++++ .../java/net/fabricmc/mappingio/i18n/TranslatableImpl.java | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/src/main/java/net/fabricmc/mappingio/i18n/Translatable.java b/src/main/java/net/fabricmc/mappingio/i18n/Translatable.java index 5f5f0d07..30016bfd 100644 --- a/src/main/java/net/fabricmc/mappingio/i18n/Translatable.java +++ b/src/main/java/net/fabricmc/mappingio/i18n/Translatable.java @@ -31,4 +31,11 @@ static Translatable of(String translationKey) { * Translates this translatable to the specified locale, with a fallback to en_US. */ String translate(Locale locale); + + /** + * Returns the translation key of this translatable, allowing consumers provide a custom translation. + * + *

No guarantees about the stability of this value across major library versions are made. + */ + String translationKey(); } diff --git a/src/main/java/net/fabricmc/mappingio/i18n/TranslatableImpl.java b/src/main/java/net/fabricmc/mappingio/i18n/TranslatableImpl.java index 29e7dc81..78d0e2e2 100644 --- a/src/main/java/net/fabricmc/mappingio/i18n/TranslatableImpl.java +++ b/src/main/java/net/fabricmc/mappingio/i18n/TranslatableImpl.java @@ -28,5 +28,10 @@ public String translate(Locale locale) { return I18n.translate(translationKey, locale); } + @Override + public String translationKey() { + return translationKey; + } + private final String translationKey; } From 9d81e04cd2980595eaa6ca9cdc257b4113d4f677 Mon Sep 17 00:00:00 2001 From: NebelNidas Date: Sat, 18 Jan 2025 03:38:56 +0100 Subject: [PATCH 10/12] Adjust Javadoc --- src/main/java/net/fabricmc/mappingio/i18n/Translatable.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/fabricmc/mappingio/i18n/Translatable.java b/src/main/java/net/fabricmc/mappingio/i18n/Translatable.java index 30016bfd..f4879782 100644 --- a/src/main/java/net/fabricmc/mappingio/i18n/Translatable.java +++ b/src/main/java/net/fabricmc/mappingio/i18n/Translatable.java @@ -33,7 +33,8 @@ static Translatable of(String translationKey) { String translate(Locale locale); /** - * Returns the translation key of this translatable, allowing consumers provide a custom translation. + * Returns the translation key of this translatable, allowing consumers to provide their own translations + * via custom translation facilities. * *

No guarantees about the stability of this value across major library versions are made. */ From 60e0c7c5050cc88f9e2646c993b358258cef77b0 Mon Sep 17 00:00:00 2001 From: NebelNidas Date: Sat, 18 Jan 2025 03:41:02 +0100 Subject: [PATCH 11/12] Remove note about translation key stability guarantees --- src/main/java/net/fabricmc/mappingio/i18n/Translatable.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/net/fabricmc/mappingio/i18n/Translatable.java b/src/main/java/net/fabricmc/mappingio/i18n/Translatable.java index f4879782..10ede414 100644 --- a/src/main/java/net/fabricmc/mappingio/i18n/Translatable.java +++ b/src/main/java/net/fabricmc/mappingio/i18n/Translatable.java @@ -35,8 +35,6 @@ static Translatable of(String translationKey) { /** * Returns the translation key of this translatable, allowing consumers to provide their own translations * via custom translation facilities. - * - *

No guarantees about the stability of this value across major library versions are made. */ String translationKey(); } From c218104c6b31a1d77f357fcddac25d4757ff4c6e Mon Sep 17 00:00:00 2001 From: NebelNidas Date: Sat, 18 Jan 2025 13:15:47 +0100 Subject: [PATCH 12/12] Change field order --- src/main/java/net/fabricmc/mappingio/format/MappingFormat.java | 3 ++- src/main/java/net/fabricmc/mappingio/i18n/Translatable.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java b/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java index cb0060f6..9b7c0682 100644 --- a/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java +++ b/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java @@ -309,15 +309,16 @@ public String getGlobPattern() { return "*."+fileExt; } - public final boolean hasWriter; private final String translationKey; private final FeatureSet features; + /** * @deprecated Use {@link #translatableName()} instead. */ @Deprecated public final String name; + public final boolean hasWriter; @Nullable public final String fileExt; diff --git a/src/main/java/net/fabricmc/mappingio/i18n/Translatable.java b/src/main/java/net/fabricmc/mappingio/i18n/Translatable.java index 10ede414..e1029734 100644 --- a/src/main/java/net/fabricmc/mappingio/i18n/Translatable.java +++ b/src/main/java/net/fabricmc/mappingio/i18n/Translatable.java @@ -34,7 +34,7 @@ static Translatable of(String translationKey) { /** * Returns the translation key of this translatable, allowing consumers to provide their own translations - * via custom translation facilities. + * via custom localization facilities. */ String translationKey(); }