package io.gitlab.jfronny.googlechat; import io.gitlab.jfronny.commons.log.*; import io.gitlab.jfronny.libjf.translate.api.*; import net.fabricmc.api.*; import net.fabricmc.fabric.api.message.v1.*; import net.fabricmc.loader.api.*; import net.minecraft.text.*; import java.util.*; import java.util.concurrent.*; public class GoogleChat implements ModInitializer { public static final String MOD_ID = "google-chat"; public static final Logger LOGGER = Logger.forName(MOD_ID); public static final TranslateService TRANSLATE_SERVICE = TranslateService.getConfigured(); @Override public void onInitialize() { ServerMessageDecoratorEvent.EVENT.register(ServerMessageDecoratorEvent.CONTENT_PHASE, (sender, message) -> { Text original; if (sender != null) { // Client messages should first be translated to the server language if (hasTarget(Direction.C2S) && hasTarget(Direction.S2C)) // Do not translate back and forth return CompletableFuture.completedFuture(message); original = message; message = translateIfNeeded(message, Direction.C2S, true); if (GoogleChatConfig.debugLogs) LOGGER.info("Applied C2S translation from " + original + " to " + message); } // All messages should be translated to the client language before sending original = message; message = translateIfNeeded(message, Direction.S2C, true); if (GoogleChatConfig.debugLogs) LOGGER.info("Applied S2C translation from " + original + " to " + message); return CompletableFuture.completedFuture(message); }); } public static Text translateIfNeeded(Text source, Direction direction, boolean respectRegex) { if (shouldSkipOutright(direction)) return source; String sourceString = toString(source); if (respectRegex && failsRegex(sourceString, direction)) return source; if (GoogleChatConfig.desugar) { MutableText translatedText = Text.literal(translateIfNeeded(sourceString, direction, true)); if (GoogleChatConfig.debugLogs) LOGGER.info("Translated " + sourceString + " to " + toString(translatedText)); if (GoogleChatConfig.translationTooltip) return source.copy().setStyle(addHover(Style.EMPTY, Text.literal("Translated: ").append(translatedText))); else return translatedText.setStyle(addHover(Style.EMPTY, Text.literal("Original: ").append(source))); } MutableText translated; if (source.getContent() instanceof TranslatableTextContent tx) { Object[] args = tx.getArgs(); args = Arrays.copyOf(args, args.length); // We're not translating TranslatableText, but are translating arguments for (int i = 0; i < args.length; i++) { args[i] = args[i] instanceof Text tx1 ? translateIfNeeded(tx1, direction, false) : args[i] instanceof String tx1 ? translateIfNeeded(tx1, direction, false) : args[i]; } translated = Text.translatable(tx.getKey(), args); } else if (source.getContent() instanceof LiteralTextContent tx) { translated = Text.literal(translateIfNeeded(tx.string(), direction, false)).setStyle(source.getStyle()); } else { //LOGGER.info("Unhandled text type: " + source.getClass() + " (" + source + ")"); translated = source.copy(); } if (GoogleChatConfig.translationTooltip) return source.copy().styled(style -> addHover(style, translated)); else return translated; } private static String toString(Text text) { StringBuilder sb = new StringBuilder(); text.asOrderedText().accept((index, style, codePoint) -> { sb.append((char)codePoint); return true; }); return sb.toString(); } private static Style addHover(Style style, Text hoverText) { return style.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, hoverText)); } public static String translateIfNeeded(String source, Direction direction, boolean respectRegex) { if (shouldSkipOutright(direction)) return source; if (respectRegex && failsRegex(source, direction)) return source; // Ignore generics since this is apparently not something java supports @SuppressWarnings("rawtypes") TranslateService svc = GoogleChat.TRANSLATE_SERVICE; Language sourceLang = svc.parseLang(direction == Direction.C2S ? GoogleChatConfig.clientLanguage : GoogleChatConfig.serverLanguage); Language targetLang = svc.parseLang(direction == Direction.C2S ? GoogleChatConfig.serverLanguage : GoogleChatConfig.clientLanguage); try { //noinspection unchecked return svc.translate(source, sourceLang, targetLang); } catch (TranslateException e) { LOGGER.error("Could not translate text: " + source, e); return source; } } private static boolean failsRegex(String text, Direction direction) { boolean isSender = (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) == (direction == Direction.C2S); if (isSender) return text.matches(GoogleChatConfig.sendingRegex) == GoogleChatConfig.sendingRegexIsBlacklist; else return text.matches(GoogleChatConfig.receivingRegex) == GoogleChatConfig.receivingRegexIsBlacklist; } private static boolean shouldSkipOutright(Direction direction) { return !GoogleChatConfig.enabled || !hasTarget(direction); } private static boolean hasTarget(Direction direction) { return !TRANSLATE_SERVICE.parseLang(switch (direction) { case C2S -> GoogleChatConfig.serverLanguage; case S2C -> GoogleChatConfig.clientLanguage; }).getIdentifier().equals("auto"); } public enum Direction { C2S, S2C } }