Skip to content

Commit 848b19e

Browse files
committed
Initial localization support
1 parent 6e7de77 commit 848b19e

File tree

5 files changed

+177
-11
lines changed

5 files changed

+177
-11
lines changed

src/main/java/net/fabricmc/mappingio/format/MappingFormat.java

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616

1717
package net.fabricmc.mappingio.format;
1818

19+
import java.util.Locale;
20+
21+
import net.fabricmc.mappingio.i18n.I18n;
22+
import net.fabricmc.mappingio.i18n.MioLocale;
23+
1924
/**
2025
* Represents a supported mapping format. Feature comparison table:
2126
* <table>
@@ -98,55 +103,59 @@ public enum MappingFormat {
98103
/**
99104
* The {@code Tiny} mapping format, as specified <a href="https://fabricmc.net/wiki/documentation:tiny">here</a>.
100105
*/
101-
TINY_FILE("Tiny file", "tiny", true, true, false, false, false),
106+
TINY_FILE("tiny", true, true, false, false, false),
102107

103108
/**
104109
* The {@code Tiny v2} mapping format, as specified <a href="https://fabricmc.net/wiki/documentation:tiny2">here</a>.
105110
*/
106-
TINY_2_FILE("Tiny v2 file", "tiny", true, true, true, true, true),
111+
TINY_2_FILE("tiny", true, true, true, true, true),
107112

108113
/**
109114
* Enigma's mapping format, as specified <a href="https://fabricmc.net/wiki/documentation:enigma_mappings">here</a>.
110115
*/
111-
ENIGMA_FILE("Enigma file", "mapping", false, true, true, true, false),
116+
ENIGMA_FILE("mapping", false, true, true, true, false),
112117

113118
/**
114119
* Enigma's mapping format (in directory form), as specified <a href="https://fabricmc.net/wiki/documentation:enigma_mappings">here</a>.
115120
*/
116-
ENIGMA_DIR("Enigma directory", null, false, true, true, true, false),
121+
ENIGMA_DIR(null, false, true, true, true, false),
117122

118123
/**
119124
* The {@code SRG} ({@code Searge RetroGuard}) mapping format, as specified <a href="https://github.com/MinecraftForge/SrgUtils/blob/67f30647ece29f18256ca89a23cda6216d6bd21e/src/main/java/net/minecraftforge/srgutils/InternalUtils.java#L69-L81">here</a>.
120125
*/
121-
SRG_FILE("SRG file", "srg", false, false, false, false, false),
126+
SRG_FILE("srg", false, false, false, false, false),
122127

123128
/**
124129
* The {@code TSRG} ({@code Tiny SRG}, since it saves disk space over SRG) mapping format, as specified <a href="https://github.com/MinecraftForge/SrgUtils/blob/67f30647ece29f18256ca89a23cda6216d6bd21e/src/main/java/net/minecraftforge/srgutils/InternalUtils.java#L196-L213">here</a>.
125130
*/
126-
TSRG_FILE("TSRG file", "tsrg", false, false, false, false, false),
131+
TSRG_FILE("tsrg", false, false, false, false, false),
127132

128133
/**
129134
* The {@code TSRG v2} mapping format, as specified <a href="https://github.com/MinecraftForge/SrgUtils/blob/67f30647ece29f18256ca89a23cda6216d6bd21e/src/main/java/net/minecraftforge/srgutils/InternalUtils.java#L262-L285">here</a>.
130135
*/
131-
TSRG_2_FILE("TSRG2 file", "tsrg", true, true, false, true, false),
136+
TSRG_2_FILE("tsrg", true, true, false, true, false),
132137

133138
/**
134139
* ProGuard's mapping format, as specified <a href="https://www.guardsquare.com/manual/tools/retrace">here</a>.
135140
*/
136-
PROGUARD_FILE("ProGuard file", "txt", false, true, false, false, false);
141+
PROGUARD_FILE("txt", false, true, false, false, false);
137142

138-
MappingFormat(String name, String fileExt,
139-
boolean hasNamespaces, boolean hasFieldDescriptors,
143+
MappingFormat(String fileExt, boolean hasNamespaces, boolean hasFieldDescriptors,
140144
boolean supportsComments, boolean supportsArgs, boolean supportsLocals) {
141-
this.name = name;
142145
this.fileExt = fileExt;
146+
this.translationKey = "format." + name().toLowerCase(Locale.ROOT);
147+
this.name = getName(MioLocale.EN_US);
143148
this.hasNamespaces = hasNamespaces;
144149
this.hasFieldDescriptors = hasFieldDescriptors;
145150
this.supportsComments = supportsComments;
146151
this.supportsArgs = supportsArgs;
147152
this.supportsLocals = supportsLocals;
148153
}
149154

155+
public String getName(MioLocale locale) {
156+
return I18n.translate(translationKey, locale);
157+
}
158+
150159
public boolean hasSingleFile() {
151160
return fileExt != null;
152161
}
@@ -157,11 +166,13 @@ public String getGlobPattern() {
157166
return "*."+fileExt;
158167
}
159168

169+
/** @deprecated Use {@link #getName()} instead. */
160170
public final String name;
161171
public final String fileExt;
162172
public final boolean hasNamespaces;
163173
public final boolean hasFieldDescriptors;
164174
public final boolean supportsComments;
165175
public final boolean supportsArgs;
166176
public final boolean supportsLocals;
177+
private final String translationKey;
167178
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright (c) 2023 FabricMC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package net.fabricmc.mappingio.i18n;
18+
19+
import java.io.IOException;
20+
import java.io.InputStreamReader;
21+
import java.io.Reader;
22+
import java.net.URL;
23+
import java.nio.charset.StandardCharsets;
24+
import java.util.HashMap;
25+
import java.util.Map;
26+
import java.util.PropertyResourceBundle;
27+
import java.util.ResourceBundle;
28+
29+
import org.jetbrains.annotations.ApiStatus;
30+
31+
@ApiStatus.Internal
32+
public class I18n {
33+
private I18n() {
34+
}
35+
36+
public static String translate(String key, MioLocale locale, Object... args) {
37+
return String.format(translate(key, locale), args);
38+
}
39+
40+
public static String translate(String key, MioLocale locale) {
41+
try {
42+
return messageBundles.getOrDefault(locale, load(locale)).getString(key);
43+
} catch (Exception e) {
44+
System.err.println("Exception while translating key " + key + " to locale " + locale.id + ": " + e.getMessage());
45+
if (locale == fallbackLocale) return key;
46+
47+
try {
48+
return messageBundles.getOrDefault(fallbackLocale, load(fallbackLocale)).getString(key);
49+
} catch (Exception e2) {
50+
System.err.println("Exception while translating key " + key + " to fallback locale: " + e2.getMessage());
51+
return key;
52+
}
53+
}
54+
}
55+
56+
private static ResourceBundle load(MioLocale locale) {
57+
ResourceBundle resBundle;
58+
String resName = String.format("/i18n/%s.properties", locale.id);
59+
URL resUrl = I18n.class.getResource(resName);
60+
61+
if (resUrl == null) {
62+
throw new RuntimeException("Locale resource not found: " + resName);
63+
}
64+
65+
try (Reader reader = new InputStreamReader(resUrl.openStream(), StandardCharsets.UTF_8)) {
66+
resBundle = new PropertyResourceBundle(reader);
67+
messageBundles.put(locale, resBundle);
68+
return resBundle;
69+
} catch (IOException e) {
70+
throw new RuntimeException("Failed to load " + resName, e);
71+
}
72+
}
73+
74+
private static final MioLocale fallbackLocale = MioLocale.EN_US;
75+
private static final Map<MioLocale, ResourceBundle> messageBundles = new HashMap<>();
76+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright (c) 2023 FabricMC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package net.fabricmc.mappingio.i18n;
18+
19+
public enum MioLocale {
20+
EN_US("en_us");
21+
22+
MioLocale(String id) {
23+
this.id = id;
24+
}
25+
26+
final String id;
27+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
format.tiny_file = Tiny File
2+
format.tiny_2_file = Tiny v2 File
3+
format.enigma_file = Enigma File
4+
format.enigma_dir = Enigma Directory
5+
format.mcp_dir = MCP Directory
6+
format.srg_file = SRG File
7+
format.tsrg_file = TSRG File
8+
format.tsrg_2_file = TSRG2 File
9+
format.proguard_file = ProGuard File
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright (c) 2023 FabricMC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package net.fabricmc.mappingio.i18n;
18+
19+
import static org.junit.jupiter.api.Assertions.assertFalse;
20+
import static org.junit.jupiter.api.Assertions.assertTrue;
21+
22+
import org.junit.jupiter.api.Test;
23+
24+
import net.fabricmc.mappingio.format.MappingFormat;
25+
26+
public class I18nTest {
27+
@Test
28+
public void mappingFormats() {
29+
for (MappingFormat format : MappingFormat.values()) {
30+
String enUsName = format.getName(MioLocale.EN_US);
31+
assertTrue(format.name.equals(enUsName));
32+
33+
for (MioLocale locale : MioLocale.values()) {
34+
String translatedName = format.getName(locale);
35+
assertFalse(translatedName.startsWith("format."));
36+
37+
if (locale != MioLocale.EN_US) {
38+
assertFalse(translatedName.equals(enUsName));
39+
}
40+
}
41+
}
42+
}
43+
}

0 commit comments

Comments
 (0)