Skip to content

Commit 3c153c5

Browse files
authored
Fix 1.20.6 translation support (#147)
2 parents 203344d + 35577c3 commit 3c153c5

File tree

5 files changed

+252
-21
lines changed

5 files changed

+252
-21
lines changed

src/main/java/net/onelitefeather/antiredstoneclockremastered/AntiRedstoneClockRemastered.java

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
package net.onelitefeather.antiredstoneclockremastered;
22

3-
import net.kyori.adventure.key.Key;
3+
import io.papermc.paper.ServerBuildInfo;
44
import net.kyori.adventure.text.Component;
55
import net.kyori.adventure.text.minimessage.MiniMessage;
6-
import net.kyori.adventure.text.minimessage.translation.MiniMessageTranslationStore;
7-
import net.kyori.adventure.translation.GlobalTranslator;
86
import net.kyori.adventure.util.UTF8ResourceBundleControl;
97
import net.onelitefeather.antiredstoneclockremastered.api.PlotsquaredSupport;
108
import net.onelitefeather.antiredstoneclockremastered.api.WorldGuardSupport;
@@ -16,6 +14,9 @@
1614
import net.onelitefeather.antiredstoneclockremastered.plotsquared.v7.PlotSquaredModernSupport;
1715
import net.onelitefeather.antiredstoneclockremastered.service.RedstoneClockService;
1816
import net.onelitefeather.antiredstoneclockremastered.service.UpdateService;
17+
import net.onelitefeather.antiredstoneclockremastered.service.api.TranslationService;
18+
import net.onelitefeather.antiredstoneclockremastered.service.impl.LegacyTranslationService;
19+
import net.onelitefeather.antiredstoneclockremastered.service.impl.ModernTranslationService;
1920
import net.onelitefeather.antiredstoneclockremastered.utils.CheckTPS;
2021
import net.onelitefeather.antiredstoneclockremastered.worldguard.v6.WorldGuardLegacySupport;
2122
import net.onelitefeather.antiredstoneclockremastered.worldguard.v7.WorldGuardModernSupport;
@@ -44,6 +45,7 @@
4445
import static org.incendo.cloud.parser.standard.StringParser.greedyStringParser;
4546

4647
public final class AntiRedstoneClockRemastered extends JavaPlugin {
48+
public static final String RESOURCE_BUNDLE_NAME = "antiredstoneclockremasterd";
4749
private CheckTPS tps;
4850

4951
private RedstoneClockService redstoneClockService;
@@ -67,27 +69,30 @@ public void onLoad() {
6769

6870
@Override
6971
public void onEnable() {
70-
final MiniMessageTranslationStore miniMessageTranslationStore = MiniMessageTranslationStore.create(Key.key("antiredstoneclockremastered", "translations"));
71-
miniMessageTranslationStore.defaultLocale(Locale.US);
72+
TranslationService translationService;
73+
ServerBuildInfo buildInfo = ServerBuildInfo.buildInfo();
74+
if (buildInfo.minecraftVersionId().startsWith("1.20")) {
75+
translationService = new LegacyTranslationService();
76+
getSLF4JLogger().info("Using legacy translation service");
77+
} else {
78+
translationService = new ModernTranslationService();
79+
getSLF4JLogger().info("Using modern translation service");
80+
}
7281
Path langFolder = getDataFolder().toPath().resolve("lang");
73-
var languages = new HashSet<>(getConfig().getStringList("translations"));
74-
languages.add("en-US");
75-
if (Files.exists(langFolder)) {
76-
try (var urlClassLoader = new URLClassLoader(new URL[]{langFolder.toUri().toURL()})) {
77-
languages.stream().map(Locale::forLanguageTag).forEach(r -> {
78-
var bundle = ResourceBundle.getBundle("antiredstoneclockremasterd", r, urlClassLoader, UTF8ResourceBundleControl.get());
79-
miniMessageTranslationStore.registerAll(r, bundle, false);
80-
});
82+
if (Files.notExists(langFolder)) {
83+
try {
84+
Files.createDirectories(langFolder);
8185
} catch (IOException e) {
82-
throw new RuntimeException(e);
86+
getSLF4JLogger().error("An error occurred while creating lang folder");
87+
return;
8388
}
84-
} else {
85-
languages.stream().map(Locale::forLanguageTag).forEach(r -> {
86-
var bundle = ResourceBundle.getBundle("antiredstoneclockremasterd", r, UTF8ResourceBundleControl.get());
87-
miniMessageTranslationStore.registerAll(r, bundle, false);
88-
});
8989
}
90-
GlobalTranslator.translator().addSource(miniMessageTranslationStore);
90+
var languages = new HashSet<>(getConfig().getStringList("translations"));
91+
languages.add("en-US");
92+
languages.stream()
93+
.map(Locale::forLanguageTag)
94+
.forEach(locale -> loadAndRegisterTranslation(locale, langFolder, translationService));
95+
translationService.registerGlobal();
9196
donationInformation();
9297
updateService();
9398
enableCommandFramework();
@@ -107,7 +112,9 @@ private void updateService() {
107112

108113
@Override
109114
public void onDisable() {
110-
this.updateService.shutdown();
115+
if (this.updateService != null) {
116+
this.updateService.shutdown();
117+
}
111118
}
112119

113120
private void donationInformation() {
@@ -276,6 +283,29 @@ private String bstatsWorldGuardVersion() {
276283
return "unknown";
277284
}
278285

286+
private void loadAndRegisterTranslation(Locale locale, Path langFolder, TranslationService translationService) {
287+
try {
288+
ResourceBundle bundle = loadResourceBundle(locale, langFolder);
289+
if (bundle != null) {
290+
translationService.registerAll(locale, bundle, false);
291+
}
292+
} catch (Exception e) {
293+
getSLF4JLogger().error("An error occurred while loading language file for locale {}", locale, e);
294+
}
295+
}
296+
297+
private ResourceBundle loadResourceBundle(Locale locale, Path langFolder) throws Exception {
298+
Path langFile = langFolder.resolve(RESOURCE_BUNDLE_NAME + "_" + locale.toLanguageTag() + ".properties");
299+
300+
if (Files.exists(langFile)) {
301+
try (URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{langFolder.toUri().toURL()})) {
302+
return ResourceBundle.getBundle(RESOURCE_BUNDLE_NAME, locale, urlClassLoader, UTF8ResourceBundleControl.get());
303+
}
304+
} else {
305+
return ResourceBundle.getBundle(RESOURCE_BUNDLE_NAME, locale, UTF8ResourceBundleControl.get());
306+
}
307+
}
308+
279309
public CheckTPS getTps() {
280310
return tps;
281311
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package net.onelitefeather.antiredstoneclockremastered.service.api;
2+
3+
import org.jetbrains.annotations.NotNull;
4+
5+
import java.util.Locale;
6+
import java.util.ResourceBundle;
7+
8+
public interface TranslationService {
9+
10+
void registerAll(final @NotNull Locale locale, final @NotNull ResourceBundle bundle, final boolean escapeSingleQuotes);
11+
12+
void registerGlobal();
13+
14+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package net.onelitefeather.antiredstoneclockremastered.service.impl;
2+
3+
import net.kyori.adventure.key.Key;
4+
import net.kyori.adventure.translation.GlobalTranslator;
5+
import net.kyori.adventure.translation.TranslationRegistry;
6+
import net.onelitefeather.antiredstoneclockremastered.service.api.TranslationService;
7+
import net.onelitefeather.antiredstoneclockremastered.translations.PluginTranslationRegistry;
8+
import org.jetbrains.annotations.NotNull;
9+
10+
import java.util.Locale;
11+
import java.util.ResourceBundle;
12+
13+
@Deprecated(since = "2.0.13", forRemoval = true)
14+
public final class LegacyTranslationService implements TranslationService {
15+
16+
private final TranslationRegistry translationRegistry;
17+
18+
public LegacyTranslationService() {
19+
this.translationRegistry = new PluginTranslationRegistry(TranslationRegistry.create(Key.key("antiredstoneclockremastered", "translations")));
20+
this.translationRegistry.defaultLocale(Locale.US);
21+
}
22+
23+
@Override
24+
public void registerAll(@NotNull Locale locale, @NotNull ResourceBundle bundle, boolean escapeSingleQuotes) {
25+
this.translationRegistry.registerAll(locale, bundle, escapeSingleQuotes);
26+
}
27+
28+
@Override
29+
public void registerGlobal() {
30+
GlobalTranslator.translator().addSource(this.translationRegistry);
31+
}
32+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package net.onelitefeather.antiredstoneclockremastered.service.impl;
2+
3+
import net.kyori.adventure.key.Key;
4+
import net.kyori.adventure.text.minimessage.translation.MiniMessageTranslationStore;
5+
import net.kyori.adventure.translation.GlobalTranslator;
6+
import net.onelitefeather.antiredstoneclockremastered.service.api.TranslationService;
7+
import org.jetbrains.annotations.NotNull;
8+
9+
import java.util.Locale;
10+
import java.util.ResourceBundle;
11+
12+
public final class ModernTranslationService implements TranslationService {
13+
14+
private final MiniMessageTranslationStore miniMessageTranslationStore;
15+
16+
public ModernTranslationService() {
17+
this.miniMessageTranslationStore = MiniMessageTranslationStore.create(Key.key("antiredstoneclockremastered", "translations"));
18+
this.miniMessageTranslationStore.defaultLocale(Locale.US);
19+
}
20+
21+
@Override
22+
public void registerAll(@NotNull Locale locale, @NotNull ResourceBundle bundle, boolean escapeSingleQuotes) {
23+
this.miniMessageTranslationStore.registerAll(locale, bundle, escapeSingleQuotes);
24+
}
25+
26+
@Override
27+
public void registerGlobal() {
28+
GlobalTranslator.translator().addSource(this.miniMessageTranslationStore);
29+
}
30+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package net.onelitefeather.antiredstoneclockremastered.translations;
2+
3+
import net.kyori.adventure.key.Key;
4+
import net.kyori.adventure.text.Component;
5+
import net.kyori.adventure.text.ComponentLike;
6+
import net.kyori.adventure.text.TranslatableComponent;
7+
import net.kyori.adventure.text.minimessage.Context;
8+
import net.kyori.adventure.text.minimessage.MiniMessage;
9+
import net.kyori.adventure.text.minimessage.ParsingException;
10+
import net.kyori.adventure.text.minimessage.tag.Tag;
11+
import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue;
12+
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
13+
import net.kyori.adventure.translation.TranslationRegistry;
14+
import org.jetbrains.annotations.NotNull;
15+
import org.jetbrains.annotations.Nullable;
16+
17+
import java.text.MessageFormat;
18+
import java.util.List;
19+
import java.util.Locale;
20+
import java.util.Objects;
21+
22+
public final class PluginTranslationRegistry implements TranslationRegistry {
23+
24+
private final TranslationRegistry backedRegistry;
25+
26+
public PluginTranslationRegistry(TranslationRegistry backedRegistry) {
27+
this.backedRegistry = backedRegistry;
28+
}
29+
30+
@Override
31+
public boolean contains(@NotNull String key) {
32+
return backedRegistry.contains(key);
33+
}
34+
35+
@Override
36+
public boolean contains(@NotNull String key, @NotNull Locale locale) {
37+
return backedRegistry.contains(key, locale);
38+
}
39+
40+
@Override
41+
public @NotNull Key name() {
42+
return backedRegistry.name();
43+
}
44+
45+
@Override
46+
public @Nullable MessageFormat translate(@NotNull String key, @NotNull Locale locale) {
47+
return null;
48+
}
49+
50+
@Override
51+
public @Nullable Component translate(
52+
@NotNull TranslatableComponent component,
53+
@NotNull Locale locale
54+
) {
55+
final MessageFormat translationFormat = backedRegistry.translate(component.key(), locale);
56+
57+
if (translationFormat == null) {
58+
return null;
59+
}
60+
61+
final String miniMessageString = translationFormat.toPattern();
62+
63+
final Component resultingComponent;
64+
65+
if (component.arguments().isEmpty()) {
66+
resultingComponent = MiniMessage.miniMessage().deserialize(miniMessageString);
67+
} else {
68+
resultingComponent = MiniMessage.miniMessage().deserialize(miniMessageString,
69+
new ArgumentTag(component.arguments()));
70+
}
71+
72+
if (component.children().isEmpty()) {
73+
return resultingComponent;
74+
} else {
75+
return resultingComponent.children(component.children());
76+
}
77+
}
78+
79+
@Override
80+
public void defaultLocale(@NotNull Locale locale) {
81+
backedRegistry.defaultLocale(locale);
82+
}
83+
84+
@Override
85+
public void register(@NotNull String key, @NotNull Locale locale, @NotNull MessageFormat format) {
86+
backedRegistry.register(key, locale, format);
87+
}
88+
89+
@Override
90+
public void unregister(@NotNull String key) {
91+
backedRegistry.unregister(key);
92+
}
93+
94+
private static final class ArgumentTag implements TagResolver {
95+
private static final String NAME = "argument";
96+
private static final String NAME_1 = "arg";
97+
98+
private final List<? extends ComponentLike> argumentComponents;
99+
100+
private ArgumentTag(final @NotNull List<? extends ComponentLike> argumentComponents) {
101+
this.argumentComponents = Objects.requireNonNull(argumentComponents, "argumentComponents");
102+
}
103+
104+
@Override
105+
public Tag resolve(
106+
final @NotNull String name,
107+
final @NotNull ArgumentQueue arguments,
108+
final @NotNull Context ctx
109+
) throws ParsingException {
110+
final int index = arguments.popOr("No argument number provided")
111+
.asInt().orElseThrow(() -> ctx.newException("Invalid argument number", arguments));
112+
113+
if (index < 0 || index >= argumentComponents.size()) {
114+
throw ctx.newException("Invalid argument number", arguments);
115+
}
116+
117+
return Tag.inserting(argumentComponents.get(index));
118+
}
119+
120+
@Override
121+
public boolean has(final @NotNull String name) {
122+
return name.equals(NAME) || name.equals(NAME_1);
123+
}
124+
}
125+
}

0 commit comments

Comments
 (0)