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.client.*; 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(); private static final FixedSizeCache TRANSLATION_CACHE = new FixedSizeCache<>(126); @Override public void onInitialize() { ServerMessageDecoratorEvent.EVENT.register(ServerMessageDecoratorEvent.CONTENT_PHASE, (sender, message) -> { if (sender != null) // Client messages should first be translated to the server language message = translateIfNeeded(message, Direction.C2S, true); // All messages should be translated to the client language before sending message = translateIfNeeded(message, Direction.S2C, true); return CompletableFuture.completedFuture(message); }); } public static boolean isSelf(UUID sender) { MinecraftClient mc = MinecraftClient.getInstance(); return mc == null || mc.player == null || sender.equals(mc.player.getUuid()); } 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.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 want potential keys 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; TranslateService svc = (TranslateService) GoogleChat.TRANSLATE_SERVICE; // Generics bypass TLang sourceLang = svc.parseLang(direction == Direction.C2S ? GoogleChatConfig.clientLanguage : GoogleChatConfig.serverLanguage); TLang targetLang = svc.parseLang(direction == Direction.C2S ? GoogleChatConfig.serverLanguage : GoogleChatConfig.clientLanguage); CacheKey key = new CacheKey(source, sourceLang.getIdentifier(), targetLang.getIdentifier()); if (TRANSLATION_CACHE.containsKey(key)) return TRANSLATION_CACHE.get(key); try { String translated = svc.translate(source, sourceLang, targetLang); TRANSLATION_CACHE.put(key, translated); return translated; } 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) { if (!GoogleChatConfig.enabled) return true; Language clientLang = TRANSLATE_SERVICE.parseLang(GoogleChatConfig.clientLanguage); Language serverLang = TRANSLATE_SERVICE.parseLang(GoogleChatConfig.serverLanguage); if (direction == Direction.S2C && clientLang.getIdentifier().equals("auto")) return true; if (direction == Direction.C2S && serverLang.getIdentifier().equals("auto")) return true; return false; } public enum Direction { C2S, S2C } }