Allow loading extra languages on Bukkit

This commit is contained in:
Vankka 2023-05-30 19:22:34 +03:00
parent ad01ee5748
commit 3e3438f0d2
No known key found for this signature in database
GPG Key ID: 6E50CB7A29B96AD0
4 changed files with 173 additions and 58 deletions

View File

@ -274,6 +274,9 @@ public interface DiscordSRVApi {
MODULES(false),
DISCORD_COMMANDS(false),
// Bukkit only
TRANSLATIONS(false)
;
public static final Set<ReloadFlag> ALL = Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(values())));

View File

@ -20,6 +20,7 @@ package com.discordsrv.bukkit;
import com.discordsrv.api.DiscordSRVApi;
import com.discordsrv.bukkit.command.game.handler.AbstractBukkitCommandHandler;
import com.discordsrv.bukkit.component.translation.BukkitTranslationLoader;
import com.discordsrv.bukkit.config.connection.BukkitConnectionConfig;
import com.discordsrv.bukkit.config.main.BukkitConfig;
import com.discordsrv.bukkit.config.manager.BukkitConfigManager;
@ -39,30 +40,25 @@ import com.discordsrv.bukkit.scheduler.FoliaScheduler;
import com.discordsrv.bukkit.scheduler.IBukkitScheduler;
import com.discordsrv.common.ServerDiscordSRV;
import com.discordsrv.common.command.game.handler.ICommandHandler;
import com.discordsrv.common.component.translation.Translation;
import com.discordsrv.common.config.manager.ConnectionConfigManager;
import com.discordsrv.common.config.manager.MainConfigManager;
import com.discordsrv.common.debug.data.OnlineMode;
import com.discordsrv.common.messageforwarding.game.minecrafttodiscord.MinecraftToDiscordChatModule;
import com.discordsrv.common.plugin.PluginManager;
import com.fasterxml.jackson.databind.JsonNode;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import org.bukkit.Server;
import org.bukkit.plugin.ServicePriority;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.List;
import java.util.Set;
public class BukkitDiscordSRV extends ServerDiscordSRV<DiscordSRVBukkitBootstrap, BukkitConfig, BukkitConnectionConfig> {
private BukkitAudiences audiences;
private BukkitTranslationLoader translationLoader;
private final IBukkitScheduler scheduler;
private final BukkitConsole console;
@ -171,55 +167,6 @@ public class BukkitDiscordSRV extends ServerDiscordSRV<DiscordSRVBukkitBootstrap
return configManager;
}
private URL findResource(String name) {
ClassLoader classLoader = getClass().getClassLoader();
URL url = null;
while (classLoader != null && url == null) {
url = classLoader.getResource(name);
classLoader = classLoader.getParent();
}
return url;
}
private void loadMCTranslations() {
Map<String, Translation> translations = new HashMap<>();
try {
URL enUS = findResource("assets/minecraft/lang/en_US.lang");
if (enUS == null) {
enUS = findResource("assets/minecraft/lang/en_us.lang");
}
if (enUS != null) {
Properties properties = new Properties();
try (InputStream inputStream = enUS.openStream()) {
properties.load(inputStream);
}
properties.forEach((k, v) -> translations.put((String) k, Translation.stringFormat((String) v)));
}
} catch (Throwable t) {
logger().debug("Failed to load locale", t);
}
try {
URL enUS = findResource("assets/minecraft/lang/en_us.json");
if (enUS != null) {
JsonNode node = json().readTree(enUS);
node.fields().forEachRemaining(entry -> translations.put(
entry.getKey(),
Translation.stringFormat(entry.getValue().textValue()))
);
}
} catch (Throwable t) {
logger().debug("Failed to load locale", t);
}
if (translations.isEmpty()) {
logger().warning("No Minecraft translations were found, some components may not render correctly");
} else {
componentFactory().translationRegistry().register(Locale.US, translations);
logger().debug("Found " + translations.size() + " Minecraft translations for en_us");
}
}
@Override
protected void enable() throws Throwable {
// Service provider
@ -227,7 +174,7 @@ public class BukkitDiscordSRV extends ServerDiscordSRV<DiscordSRVBukkitBootstrap
// Adventure related stuff
this.audiences = BukkitAudiences.create(bootstrap.getPlugin());
loadMCTranslations();
this.translationLoader = new BukkitTranslationLoader(this);
// Command handler
commandHandler = AbstractBukkitCommandHandler.get(this);
@ -260,10 +207,22 @@ public class BukkitDiscordSRV extends ServerDiscordSRV<DiscordSRVBukkitBootstrap
server().getPluginManager().registerEvents(new BukkitConnectionListener(this), plugin());
}
@Override
protected List<ReloadResult> reload(Set<ReloadFlag> flags, boolean initial) throws Throwable {
List<ReloadResult> results = super.reload(flags, initial);
if (flags.contains(ReloadFlag.TRANSLATIONS)) {
translationLoader.reload();
}
return results;
}
@Override
protected void disable() {
super.disable();
requiredLinkingListener.disable();
audiences.close();
}
}

View File

@ -0,0 +1,149 @@
package com.discordsrv.bukkit.component.translation;
import com.discordsrv.bukkit.BukkitDiscordSRV;
import com.discordsrv.common.component.translation.Translation;
import com.discordsrv.common.component.translation.TranslationRegistry;
import com.discordsrv.common.logging.NamedLogger;
import com.fasterxml.jackson.databind.JsonNode;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Stream;
public class BukkitTranslationLoader {
private final BukkitDiscordSRV discordSRV;
private final NamedLogger logger;
public BukkitTranslationLoader(BukkitDiscordSRV discordSRV) {
this.discordSRV = discordSRV;
this.logger = new NamedLogger(discordSRV, "TRANSLATION_LOADER");
}
public void reload() {
try {
TranslationRegistry registry = discordSRV.componentFactory().translationRegistry();
registry.clear();
AtomicBoolean any = new AtomicBoolean(false);
Path languages = discordSRV.dataDirectory().resolve("game_languages");
if (Files.exists(languages)) {
loadFromFiles(languages, registry, any);
}
loadMCTranslations(any);
if (!any.get()) {
logger.warning("No Minecraft translations were found, some components may not render correctly");
}
} catch (Throwable t) {
logger.error("Failed to reload languages", t);
}
}
private void loadFromFiles(Path folder, TranslationRegistry registry, AtomicBoolean any) throws IOException {
try (Stream<Path> paths = Files.list(folder)) {
paths.forEach(path -> {
String fileName = path.getFileName().toString();
int lastDot = fileName.lastIndexOf("\\.");
String extension = lastDot == -1 ? null : fileName.substring(lastDot + 1);
if (extension == null || !(extension.equals("json") || extension.equals("lang"))) {
discordSRV.logger().warning("Unexpected file in game_languages: " + fileName);
return;
}
try {
String language = fileName.substring(0, lastDot);
Locale locale = Locale.forLanguageTag(language);
URL url = path.toUri().toURL();
Map<String, Translation> translations = null;
if (path.endsWith(".json")) {
translations = getFromJson(url);
} else if (path.endsWith(".lang")) {
translations = getFromProperties(url);
}
if (translations != null && !translations.isEmpty()) {
registry.register(locale, translations);
logger.debug("Loaded " + translations.size() + " translations for " + locale);
any.set(true);
}
} catch (Throwable t) {
logger.warning("Failed to read language file " + fileName, t);
}
});
}
}
private Map<String, Translation> getFromProperties(URL url) throws IOException {
Map<String, Translation> translations = new HashMap<>();
Properties properties = new Properties();
try (InputStream inputStream = url.openStream()) {
properties.load(inputStream);
}
properties.forEach((k, v) -> translations.put((String) k, Translation.stringFormat((String) v)));
return translations;
}
private Map<String, Translation> getFromJson(URL url) throws IOException {
Map<String, Translation> translations = new HashMap<>();
JsonNode node = discordSRV.json().readTree(url);
node.fields().forEachRemaining(entry -> translations.put(
entry.getKey(),
Translation.stringFormat(entry.getValue().textValue()))
);
return translations;
}
private URL findResource(String name) {
ClassLoader classLoader = getClass().getClassLoader();
URL url = null;
while (classLoader != null && url == null) {
url = classLoader.getResource(name);
classLoader = classLoader.getParent();
}
return url;
}
private void loadMCTranslations(AtomicBoolean any) {
Map<String, Translation> translations = new HashMap<>();
try {
URL enUS = findResource("assets/minecraft/lang/en_US.lang");
if (enUS == null) {
enUS = findResource("assets/minecraft/lang/en_us.lang");
}
if (enUS != null) {
translations = getFromProperties(enUS);
}
} catch (Throwable t) {
logger.debug("Failed to load locale", t);
}
try {
URL enUS = findResource("assets/minecraft/lang/en_us.json");
if (enUS != null) {
translations = getFromJson(enUS);
}
} catch (Throwable t) {
logger.debug("Failed to load locale", t);
}
if (!translations.isEmpty()) {
discordSRV.componentFactory().translationRegistry().register(Locale.US, translations);
logger.debug("Found " + translations.size() + " Minecraft translations for en_us");
any.set(true);
}
}
}

View File

@ -55,4 +55,8 @@ public class TranslationRegistry {
).get(key);
}
}
public void clear() {
translations.clear();
}
}