Compare commits

...

21 Commits

Author SHA1 Message Date
73521f1ab2
chore: update to 1.21
All checks were successful
ci/woodpecker/push/jfmod Pipeline was successful
ci/woodpecker/tag/jfmod Pipeline was successful
2024-06-14 11:12:40 +02:00
11e367a747
chore: bump to 1.20.6
All checks were successful
ci/woodpecker/push/jfmod Pipeline was successful
ci/woodpecker/tag/jfmod Pipeline was successful
2024-05-11 22:11:45 +02:00
8251ac6ab3
chore: extend name cache slightly again 2024-05-11 22:08:30 +02:00
d5b494bbee
style: make idea happy
All checks were successful
ci/woodpecker/push/jfmod Pipeline was successful
2024-05-11 19:07:34 +02:00
f0a479d7c5
fix: add client-side compatibility with nucleoid serverside translation
Some checks are pending
ci/woodpecker/push/jfmod Pipeline is pending
2024-05-11 18:53:57 +02:00
acaf46b835
chore: add some new entries to name cache 2024-05-11 18:52:29 +02:00
60ae06b581
chore: clean up logging in LanguageMixin 2024-05-11 17:15:42 +02:00
32d3d59f1d
fix: add Fail transformer when detecting noop backend 2024-05-11 17:14:46 +02:00
5507375bdf
fix: don't store partial transformations
All checks were successful
ci/woodpecker/push/jfmod Pipeline was successful
2024-05-11 13:11:58 +02:00
e2d5de3c62
fix: handle null result in TranslatingTransformer
All checks were successful
ci/woodpecker/push/jfmod Pipeline was successful
2024-05-10 14:28:40 +02:00
6ede586866
chore: clean up a bit 2024-05-10 14:28:10 +02:00
fa4fa75944
fix: don't discard initial work if it doesn't get finished 2024-05-10 14:27:23 +02:00
6a0cfdb26a
chore: split visualization mode config entry into separate 2024-05-10 14:26:51 +02:00
c93a60965f
fix: respect useDefaultCache 2024-05-10 13:25:20 +02:00
60cfd016d6
chore: update to 1.20.5
All checks were successful
ci/woodpecker/push/jfmod Pipeline was successful
ci/woodpecker/tag/jfmod Pipeline was successful
2024-04-25 20:55:25 +02:00
62f4415b28
chore: update to 1.20.4
All checks were successful
ci/woodpecker/push/jfmod Pipeline was successful
2023-12-07 19:56:32 +01:00
5873dff9ec
chore: bump deps
All checks were successful
ci/woodpecker/push/jfmod Pipeline was successful
2023-11-19 15:10:52 +01:00
27ccde0bec
fix: additional null check
All checks were successful
ci/woodpecker/push/jfmod Pipeline was successful
2023-11-19 14:07:10 +01:00
2a371d627f
fix: patch Language instances created by Language.create() 2023-11-19 14:06:59 +01:00
f82ded7e5d
perf: parallel translation
All checks were successful
ci/woodpecker/push/jfmod Pipeline was successful
2023-10-05 16:46:41 +02:00
dfea11cafd
fix: use new default translater cache entries on existing translations 2023-10-05 15:53:29 +02:00
15 changed files with 54079 additions and 31342 deletions

View File

@ -1,16 +1,17 @@
plugins { plugins {
id("jfmod") version "1.5-SNAPSHOT" id("jfmod") version "1.6-SNAPSHOT"
} }
allprojects { group = "io.gitlab.jfronny" } allprojects { group = "io.gitlab.jfronny" }
base.archivesName = "translater" base.archivesName = "translater"
val modmenuVersion = "8.0.0-beta.2" val modmenuVersion = "11.0.0-beta.1"
jfMod { jfMod {
minecraftVersion = "1.20.2" minecraftVersion = "1.21"
yarn("build.1") yarn("build.1")
loaderVersion = "0.14.22" loaderVersion = "0.15.11"
libJfVersion = "3.13.1" libJfVersion = "3.16.0"
fabricApiVersion = "0.100.1+1.21"
modrinth { modrinth {
projectId = "translater" projectId = "translater"
@ -25,11 +26,15 @@ jfMod {
} }
dependencies { dependencies {
modImplementation("io.gitlab.jfronny.libjf:libjf-config-core-v2:${jfMod.libJfVersion.get()}") modImplementation("io.gitlab.jfronny.libjf:libjf-config-core-v2")
modImplementation("io.gitlab.jfronny.libjf:libjf-translate-v1:${jfMod.libJfVersion.get()}") modImplementation("io.gitlab.jfronny.libjf:libjf-translate-v1")
// Dev env // Dev env
modLocalRuntime("io.gitlab.jfronny.libjf:libjf-config-ui-tiny:${jfMod.libJfVersion.get()}") modLocalRuntime("io.gitlab.jfronny.libjf:libjf-config-ui-tiny")
modLocalRuntime("io.gitlab.jfronny.libjf:libjf-devutil:${jfMod.libJfVersion.get()}") modLocalRuntime("io.gitlab.jfronny.libjf:libjf-devutil")
modLocalRuntime("com.terraformersmc:modmenu:$modmenuVersion") modLocalRuntime("com.terraformersmc:modmenu:$modmenuVersion")
// for modmenu
modLocalRuntime("net.fabricmc.fabric-api:fabric-resource-loader-v0")
modLocalRuntime("net.fabricmc.fabric-api:fabric-screen-api-v1")
modLocalRuntime("net.fabricmc.fabric-api:fabric-key-binding-api-v1")
} }

View File

@ -3,13 +3,16 @@ package io.gitlab.jfronny.translater;
import io.gitlab.jfronny.libjf.config.api.v2.Entry; import io.gitlab.jfronny.libjf.config.api.v2.Entry;
import io.gitlab.jfronny.libjf.config.api.v2.JfConfig; import io.gitlab.jfronny.libjf.config.api.v2.JfConfig;
@JfConfig(referencedConfigs = "libjf-translate-v1") @JfConfig(referencedConfigs = "libjf-translate-v1", tweaker = CfgMigration.class)
public class Cfg { public class Cfg {
@Entry public static int rounds = 5; @Entry public static int rounds = 5;
@Entry public static boolean breakFully = false; @Entry public static boolean breakFully = false;
@Entry public static String targetLanguage = "en"; @Entry public static String targetLanguage = "en";
@Entry public static ProgressMode renderProgress = ProgressMode.None; @Entry public static boolean progressGui = false;
@Entry public static boolean progressConsole = true;
@Entry public static boolean detailedProgress = false;
@Entry public static boolean forceRegenerate = false; @Entry public static boolean forceRegenerate = false;
@Entry public static boolean useDefaultCache = true;
static { static {
JFC_Cfg.ensureInitialized(); JFC_Cfg.ensureInitialized();

View File

@ -0,0 +1,26 @@
package io.gitlab.jfronny.translater;
import io.gitlab.jfronny.libjf.config.api.v2.dsl.ConfigBuilder;
import io.gitlab.jfronny.libjf.config.api.v2.dsl.Migration;
public class CfgMigration {
@SuppressWarnings("UnstableApiUsage")
public static ConfigBuilder<?> tweak(ConfigBuilder<?> cb) {
return cb.addMigration("renderProgress", Migration.of(reader -> {
String renderMode = reader.nextString();
if (renderMode == null) return;
switch (renderMode.toLowerCase()) {
case "full" -> Cfg.progressGui = Cfg.progressConsole = Cfg.detailedProgress = true;
case "gui" -> {
Cfg.progressConsole = Cfg.detailedProgress = false;
Cfg.progressGui = true;
}
case "console" -> {
Cfg.progressGui = Cfg.detailedProgress = false;
Cfg.progressConsole = true;
}
case "none" -> Cfg.progressGui = Cfg.progressConsole = Cfg.detailedProgress = false;
}
}));
}
}

View File

@ -0,0 +1,26 @@
package io.gitlab.jfronny.translater;
import io.gitlab.jfronny.translater.mixin.MinecraftClientAccessor;
import net.minecraft.client.MinecraftClient;
import java.util.concurrent.atomic.AtomicBoolean;
public class RenderScheduler {
private final AtomicBoolean shouldRun = new AtomicBoolean(false);
public void scheduleRender() {
shouldRun.set(true);
MinecraftClient mc = MinecraftClient.getInstance();
if (mc != null && mc.isOnThread()) performTask();
}
public void deschedule() {
shouldRun.set(false);
}
private void performTask() {
do {
((MinecraftClientAccessor) MinecraftClient.getInstance()).invokeRender(false);
} while (shouldRun.compareAndSet(true, false));
}
}

View File

@ -2,6 +2,7 @@ package io.gitlab.jfronny.translater;
import io.gitlab.jfronny.libjf.translate.api.TranslateService; import io.gitlab.jfronny.libjf.translate.api.TranslateService;
import io.gitlab.jfronny.translater.transformer.CachingTransformer; import io.gitlab.jfronny.translater.transformer.CachingTransformer;
import io.gitlab.jfronny.translater.transformer.FailTransformer;
import io.gitlab.jfronny.translater.transformer.TransformingMap; import io.gitlab.jfronny.translater.transformer.TransformingMap;
import io.gitlab.jfronny.translater.transformer.TranslatingTransformer; import io.gitlab.jfronny.translater.transformer.TranslatingTransformer;
import net.fabricmc.api.EnvType; import net.fabricmc.api.EnvType;
@ -17,13 +18,16 @@ import java.util.Map;
public class Translater { public class Translater {
public static final String MOD_ID = "translater"; public static final String MOD_ID = "translater";
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID); public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
private static final TransformingMap map = new TransformingMap(new CachingTransformer(new TranslatingTransformer<>(TranslateService.getConfigured()))); private static final TransformingMap map;
public static boolean progressUIEnabled() { static {
return Cfg.renderProgress == ProgressMode.Full || Cfg.renderProgress == ProgressMode.Gui; TranslateService<?> ts = TranslateService.getConfigured();
} if (ts == null || ts.getName().equals("Noop")) {
public static boolean progressLogsEnabled() { LOGGER.error("No translation service found! Please configure a translation service in the libjf-translate config and restart the game!\nThis means NO NEW TRANSLATIONS WILL BE GENERATED!");
return Cfg.renderProgress == ProgressMode.Full || Cfg.renderProgress == ProgressMode.Console; map = new TransformingMap(new CachingTransformer(new FailTransformer()));
} else {
map = new TransformingMap(new CachingTransformer(new TranslatingTransformer<>(ts)));
}
} }
public static @NotNull TransformingMap getMap(@Nullable Map<String, String> base) { public static @NotNull TransformingMap getMap(@Nullable Map<String, String> base) {

View File

@ -4,18 +4,29 @@ import io.gitlab.jfronny.translater.Translater;
import net.minecraft.client.resource.language.TranslationStorage; import net.minecraft.client.resource.language.TranslationStorage;
import net.minecraft.util.Language; import net.minecraft.util.Language;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.*;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Language.class) import java.util.Map;
@Mixin(value = Language.class, priority = 900)
public class LanguageMixin { public class LanguageMixin {
@Inject(at = @At("HEAD"), method = "setInstance") @ModifyVariable(at = @At("HEAD"), method = "setInstance", argsOnly = true)
private static void languageSetInstance(Language language, CallbackInfo ci) { private static Language languageSetInstance(Language language) {
if (language instanceof TranslationStorage t) { if (language instanceof TranslationStorage t) {
TranslationStorageAccessor ta = (TranslationStorageAccessor) t; TranslationStorageAccessor ta = (TranslationStorageAccessor) t;
ta.setTranslations(Translater.getMap(ta.getTranslations())); ta.setTranslations(Translater.getMap(ta.getTranslations()));
Translater.LOGGER.info("Set translater backend"); Translater.LOGGER.info("Set translater backend 2");
} } else Translater.LOGGER.error("Unsupported language configured: {}", language);
return language;
}
@ModifyArg(
at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Language$1;<init>(Ljava/util/Map;)V"),
method = "create()Lnet/minecraft/util/Language;",
index = 0
)
private static Map<String, String> postCreate(Map<String, String> par1) {
Translater.LOGGER.info("Set translater backend 1");
return Translater.getMap(par1);
} }
} }

View File

@ -1,7 +1,7 @@
package io.gitlab.jfronny.translater.mixin; package io.gitlab.jfronny.translater.mixin;
import io.gitlab.jfronny.libjf.LibJf; import io.gitlab.jfronny.libjf.LibJf;
import io.gitlab.jfronny.translater.Translater; import io.gitlab.jfronny.translater.Cfg;
import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.ClassNode;
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
import org.spongepowered.asm.mixin.extensibility.IMixinInfo; import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
@ -28,9 +28,9 @@ public class Plugin implements IMixinConfigPlugin {
|| Objects.equals(mixinClassName, TranslationStorageAccessor.class.getName())) || Objects.equals(mixinClassName, TranslationStorageAccessor.class.getName()))
return true; return true;
else if (Objects.equals(mixinClassName, MinecraftClientAccessor.class.getName())) else if (Objects.equals(mixinClassName, MinecraftClientAccessor.class.getName()))
return Translater.progressUIEnabled(); return Cfg.progressGui;
else if (Objects.equals(mixinClassName, SplashScreenMixin.class.getName())) else if (Objects.equals(mixinClassName, SplashScreenMixin.class.getName()))
return Translater.progressUIEnabled(); return Cfg.progressGui;
else else
throw new IllegalStateException("Unrecognized mixin! This should never happen"); throw new IllegalStateException("Unrecognized mixin! This should never happen");
} }

View File

@ -17,12 +17,13 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.Set;
@Mixin(SplashOverlay.class) @Mixin(SplashOverlay.class)
public abstract class SplashScreenMixin extends Overlay { public abstract class SplashScreenMixin extends Overlay {
@Shadow @Final private MinecraftClient client; @Shadow @Final private MinecraftClient client;
private TextRenderer translater$textRenderer; @Unique private TextRenderer translater$textRenderer;
@Inject(at = @At("RETURN"), method = "renderProgressBar") @Inject(at = @At("RETURN"), method = "renderProgressBar")
private void renderTranslationProgress(DrawContext context, int minX, int minY, int maxX, int maxY, float opacity, CallbackInfo ci) { private void renderTranslationProgress(DrawContext context, int minX, int minY, int maxX, int maxY, float opacity, CallbackInfo ci) {
@ -34,7 +35,7 @@ public abstract class SplashScreenMixin extends Overlay {
@Inject(method = "<init>", at = @At("RETURN")) @Inject(method = "<init>", at = @At("RETURN"))
public void setup(CallbackInfo ci) throws IOException { public void setup(CallbackInfo ci) throws IOException {
final FontStorage fontStorage = new FontStorage(client.getTextureManager(), new Identifier("loading")); final FontStorage fontStorage = new FontStorage(client.getTextureManager(), Identifier.ofVanilla("loading"));
Font font = JsonOps.INSTANCE.getMap(JsonParser.parseString(""" Font font = JsonOps.INSTANCE.getMap(JsonParser.parseString("""
{ {
"type": "bitmap", "type": "bitmap",
@ -60,10 +61,9 @@ public abstract class SplashScreenMixin extends Overlay {
] ]
}""").getAsJsonObject()) }""").getAsJsonObject())
.flatMap(ml -> FontType.BITMAP.getLoaderCodec().decode(JsonOps.INSTANCE, ml)) .flatMap(ml -> FontType.BITMAP.getLoaderCodec().decode(JsonOps.INSTANCE, ml))
.map(fl -> fl.build().left().orElseThrow()).get() .map(fl -> fl.build().left().orElseThrow()).getOrThrow()
.left().orElseThrow()
.load(client.getResourceManager()); .load(client.getResourceManager());
fontStorage.setFonts(Collections.singletonList(font)); fontStorage.setFonts(Collections.singletonList(new Font.FontFilterPair(font, FontFilterType.FilterMap.NO_FILTER)), Set.of());
translater$textRenderer = new TextRenderer(id -> fontStorage, false); translater$textRenderer = new TextRenderer(id -> fontStorage, false);
} }
} }

View File

@ -1,17 +1,22 @@
package io.gitlab.jfronny.translater.transformer; package io.gitlab.jfronny.translater.transformer;
import io.gitlab.jfronny.commons.concurrent.AsyncRequestState;
import io.gitlab.jfronny.libjf.config.api.v2.ConfigHolder; import io.gitlab.jfronny.libjf.config.api.v2.ConfigHolder;
import io.gitlab.jfronny.translater.Cfg; import io.gitlab.jfronny.translater.Cfg;
import io.gitlab.jfronny.translater.Translater; import io.gitlab.jfronny.translater.Translater;
import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.FabricLoader;
import org.jetbrains.annotations.Nullable;
import java.io.*; import java.io.*;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
public class CachingTransformer implements Transformer { public class CachingTransformer implements Transformer {
private static final Path CACHE_FILE = FabricLoader.getInstance().getConfigDir().resolve(Translater.MOD_ID + ".cache"); private static final Path CACHE_FILE = FabricLoader.getInstance().getConfigDir().resolve(Translater.MOD_ID + ".cache");
private final @Nullable Properties defaultCache;
private final Properties cache = new Properties(); private final Properties cache = new Properties();
private final Transformer transformer; private final Transformer transformer;
@ -21,18 +26,47 @@ public class CachingTransformer implements Transformer {
return null; return null;
//Transform and cache if not present //Transform and cache if not present
if (!cache.containsKey(str)) { if (!cache.containsKey(str)) {
if (defaultCache != null && defaultCache.containsKey(str)) return (String) defaultCache.get(str);
String transformed = transformer.transform(str); String transformed = transformer.transform(str);
if (transformed == null) { if (transformed == null) {
// The transformer failed // The transformer failed
return str; return str;
} }
cache.put(str, transformed); cache.put(str, transformed);
save(); scheduleSave();
} }
//Return cached result //Return cached result
return (String) cache.get(str); return (String) cache.get(str);
} }
@Override
public void transformMultiple(Stream<? extends String> strings, ResultConsumer results) {
AtomicInteger count = new AtomicInteger(0);
transformer.transformMultiple(strings.filter(s -> {
String translation = (String) cache.get(s);
if (translation != null) {
results.accept(s, translation);
return false;
}
if (defaultCache != null) {
translation = (String) defaultCache.get(s);
if (translation != null) {
results.accept(s, translation);
return false;
}
}
return true;
}), (str, translation) -> {
cache.put(str, translation);
results.accept(str, translation);
if (count.incrementAndGet() == 50) {
count.addAndGet(-50);
scheduleSave();
}
});
if (count.get() > 0) scheduleSave();
}
public CachingTransformer(Transformer baseTransformer) { public CachingTransformer(Transformer baseTransformer) {
transformer = baseTransformer; transformer = baseTransformer;
if (Cfg.forceRegenerate) { if (Cfg.forceRegenerate) {
@ -47,32 +81,37 @@ public class CachingTransformer implements Transformer {
} catch (IOException e) { } catch (IOException e) {
Translater.LOGGER.error("Could not load translater cache", e); Translater.LOGGER.error("Could not load translater cache", e);
} }
} else {
//Save default cache if parameters are default
if (!Cfg.breakFully && Cfg.rounds == 5) {
Translater.LOGGER.info("Initializing default cache");
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream inS = classLoader.getResourceAsStream("namecache.ini");
if (inS != null) {
try {
cache.load(inS);
inS.close();
save();
} catch (IOException e) {
Translater.LOGGER.error("Could not initialize default translater cache");
}
}
}
} }
} }
save(); //Save default cache if parameters are default
if (Cfg.useDefaultCache && !Cfg.breakFully && Cfg.rounds == 5 && "en".equals(Cfg.targetLanguage) && !Cfg.forceRegenerate) {
Translater.LOGGER.info("Initializing default cache");
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Properties p = new Properties();
try (InputStream inS = classLoader.getResourceAsStream("namecache.ini")) {
if (inS == null) p = null;
else p.load(inS);
} catch (IOException e) {
p = null;
Translater.LOGGER.error("Could not initialize default translater cache", e);
}
defaultCache = p;
} else defaultCache = null;
scheduleSave();
} }
private void save() { private final AsyncRequestState state = new AsyncRequestState();
try (OutputStream outS = Files.newOutputStream(CACHE_FILE)) { private void scheduleSave() {
cache.store(outS, "---Lang---"); if (state.request().shouldStart()) {
} catch (IOException e) { do {
Translater.LOGGER.error("Could not save translater cache"); if (!cache.isEmpty()) {
try (OutputStream outS = Files.newOutputStream(CACHE_FILE)) {
cache.store(outS, "---Lang---");
} catch (IOException e) {
Translater.LOGGER.error("Could not save translater cache");
}
}
} while (state.emitFinished().shouldContinue());
} }
} }
} }

View File

@ -0,0 +1,14 @@
package io.gitlab.jfronny.translater.transformer;
import java.util.stream.Stream;
public class FailTransformer implements Transformer {
@Override
public String transform(String str) {
return null;
}
@Override
public void transformMultiple(Stream<? extends String> strings, ResultConsumer results) {
}
}

View File

@ -1,5 +1,13 @@
package io.gitlab.jfronny.translater.transformer; package io.gitlab.jfronny.translater.transformer;
import java.util.stream.Stream;
public interface Transformer { public interface Transformer {
String transform(String str); String transform(String str);
void transformMultiple(Stream<? extends String> strings, ResultConsumer results);
@FunctionalInterface
interface ResultConsumer {
void accept(String str, String translation);
}
} }

View File

@ -1,18 +1,21 @@
package io.gitlab.jfronny.translater.transformer; package io.gitlab.jfronny.translater.transformer;
import io.gitlab.jfronny.translater.Cfg;
import io.gitlab.jfronny.translater.RenderScheduler;
import io.gitlab.jfronny.translater.Translater; import io.gitlab.jfronny.translater.Translater;
import io.gitlab.jfronny.translater.mixin.MinecraftClientAccessor;
import net.minecraft.client.MinecraftClient;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
public class TransformingMap implements Map<String, String> { public class TransformingMap implements Map<String, String> {
private final Transformer transformer; private final Transformer transformer;
private final RenderScheduler renderScheduler = new RenderScheduler();
private Map<String, String> backer = null; private Map<String, String> backer = null;
public boolean initializing = false; public boolean initializing = false;
private String initProgress = null; private AtomicInteger initProgress = null;
private int maxProgress = 0;
public TransformingMap(Transformer t) { public TransformingMap(Transformer t) {
transformer = t; transformer = t;
@ -23,24 +26,28 @@ public class TransformingMap implements Map<String, String> {
this.backer = backer; this.backer = backer;
Collection<String> strings = backer.values(); Collection<String> strings = backer.values();
initializing = true; initializing = true;
final int max = strings.size(); generateTranslations(strings);
int i = 0;
for (String value : strings) {
initProgress = "Transforming " + i++ + "/" + max;
if (Translater.progressLogsEnabled())
Translater.LOGGER.info(initProgress);
if (Translater.progressUIEnabled() && i % 10 == 0)
((MinecraftClientAccessor) MinecraftClient.getInstance()).invokeRender(false);
initProgress = null;
transformer.transform(value);
}
initializing = false; initializing = false;
} }
} }
private void generateTranslations(Collection<? extends String> strings) {
maxProgress = strings.size();
initProgress = new AtomicInteger();
transformer.transformMultiple(strings.parallelStream(), (str, translation) -> {
int i = initProgress.incrementAndGet();
if (i % 10 == 0 || Cfg.detailedProgress) {
if (Cfg.progressConsole) Translater.LOGGER.info(getInitProgress());
if (Cfg.progressGui && i % 10 == 0) renderScheduler.scheduleRender();
}
});
renderScheduler.deschedule();
initProgress = null;
}
public String getInitProgress() { public String getInitProgress() {
if (initProgress == null || !initializing) throw new IllegalStateException("Tried to get init progress while not initializing"); if (initProgress == null || !initializing) throw new IllegalStateException("Tried to get init progress while not initializing");
return initProgress; return "Transforming %d/%d".formatted(initProgress.get(), maxProgress);
} }
@Override @Override
@ -65,8 +72,7 @@ public class TransformingMap implements Map<String, String> {
@Override @Override
public String get(Object o) { public String get(Object o) {
if (o instanceof String && ((String)o).startsWith("translater.")) if (o instanceof String s && s.startsWith("translater.")) return backer.get(s);
return backer.get(o);
return transformer.transform(backer.get(o)); return transformer.transform(backer.get(o));
} }
@ -83,9 +89,7 @@ public class TransformingMap implements Map<String, String> {
@Override @Override
public void putAll(Map<? extends String, ? extends String> map) { public void putAll(Map<? extends String, ? extends String> map) {
for (String value : map.values()) { generateTranslations(map.values());
transformer.transform(value);
}
backer.putAll(map); backer.putAll(map);
} }
@ -101,11 +105,11 @@ public class TransformingMap implements Map<String, String> {
@Override @Override
public Collection<String> values() { public Collection<String> values() {
return backer.values(); return backer.values(); // This does not support updates, but I don't care
} }
@Override @Override
public Set<Entry<String, String>> entrySet() { public Set<Entry<String, String>> entrySet() {
return backer.entrySet(); return backer.entrySet(); // This does not support updates, but I don't care
} }
} }

View File

@ -7,10 +7,13 @@ import io.gitlab.jfronny.translater.Cfg;
import io.gitlab.jfronny.translater.Translater; import io.gitlab.jfronny.translater.Translater;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.util.List; import java.util.*;
import java.util.Random; import java.util.function.Function;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Stream;
import static java.util.function.Function.identity;
public class TranslatingTransformer<T extends Language> implements Transformer { public class TranslatingTransformer<T extends Language> implements Transformer {
public TranslatingTransformer(TranslateService<T> ts) { public TranslatingTransformer(TranslateService<T> ts) {
@ -31,56 +34,72 @@ public class TranslatingTransformer<T extends Language> implements Transformer {
@Override @Override
public String transform(String str) { public String transform(String str) {
try { try {
if (str.contains("%")) { return tryTransform(str);
StringBuilder res = new StringBuilder(); } catch (Exception e) {
boolean f = true; Translater.LOGGER.warn("Failed to transform: \"{}\" ({} characters)! Please report this bug with the mod containing the lang file! ({})", str, str.length(), e.getLocalizedMessage());
for (String s : str.split("%")) {
if (!f) {
res.append("%");
if (s.length() > 0)
res.append(s.charAt(0)).append(transform(s.substring(1)).replace("%", ""));
} else
res.append(transform(s));
f = false;
}
return res.toString();
} else if (str.contains("$")) {
StringBuilder res = new StringBuilder();
boolean f = true;
for (String s : str.split("\\$")) {
if (!f) {
res.append("$").append(s.charAt(0)).append(transform(s.substring(1)).replace("$", ""));
} else
res.append(transform(s));
f = false;
}
return res.toString();
} else if (str.contains("§")) {
StringBuilder res = new StringBuilder();
boolean f = true;
for (String s : str.split("§")) {
if (!f) {
res.append("§").append(s.charAt(0)).append(transform(s.substring(1)).replace("§", ""));
} else
res.append(transform(s));
f = false;
}
return res.toString();
} else {
return translateMultiple(str);
}
}
catch (Exception e) {
Translater.LOGGER.warn("Failed to transform: \"" + str + "\" (" + str.length() + " characters)");
Translater.LOGGER.warn("Please report this bug with the mod containing the lang file");
return null; return null;
} }
} }
private String tryTransform(String str) throws TranslateException {
if (str.contains("%")) {
StringBuilder res = new StringBuilder();
boolean f = true;
for (String s : str.split("%")) {
if (!f) {
res.append("%");
if (!s.isEmpty()) {
appendTransformed(res.append(s.charAt(0)), s.substring(1), x -> x.replace("%", ""));
}
} else appendTransformed(res, s, identity());
f = false;
}
return res.toString();
} else if (str.contains("$")) {
StringBuilder res = new StringBuilder();
boolean f = true;
for (String s : str.split("\\$")) {
if (f) appendTransformed(res, s, identity());
else appendTransformed(res.append("$").append(s.charAt(0)), s.substring(1), x -> x.replace("$", ""));
f = false;
}
return res.toString();
} else if (str.contains("§")) {
StringBuilder res = new StringBuilder();
boolean f = true;
for (String s : str.split("§")) {
if (f) appendTransformed(res, s, identity());
else appendTransformed(res.append("§").append(s.charAt(0)), s.substring(1), x -> x.replace("§", ""));
f = false;
}
return res.toString();
} else {
return translateMultiple(str);
}
}
private void appendTransformed(StringBuilder sb, String transformable, Function<String, String> fix) throws TranslateException {
if (transformable.isEmpty()) return;
sb.append(fix.apply(tryTransform(transformable)));
}
@Override
public void transformMultiple(Stream<? extends String> strings, ResultConsumer results) {
strings.flatMap(s -> {
try {
return Stream.of(new Translation(s, tryTransform(s)));
} catch (Exception e) {
return Stream.empty();
}
}).forEach(t -> results.accept(t.from, t.to));
}
private record Translation(String from, String to) {}
private String translateMultiple(String str) throws TranslateException { private String translateMultiple(String str) throws TranslateException {
Matcher m = SURROUNDING_SPACE_PATTERN.matcher(str); Matcher m = SURROUNDING_SPACE_PATTERN.matcher(str);
if (!m.find()) { if (!m.find()) {
Translater.LOGGER.info("Skipping translation of \"" + str + "\""); if (Cfg.detailedProgress) Translater.LOGGER.info("Skipping translation of \"{}\"", str);
return str; return str;
} }
try { try {
@ -96,10 +115,11 @@ public class TranslatingTransformer<T extends Language> implements Transformer {
} }
currentState = ts.translate(currentState, currentLang, startLang == languageAuto ? languageEnglish : startLang); // Translate to starting language currentState = ts.translate(currentState, currentLang, startLang == languageAuto ? languageEnglish : startLang); // Translate to starting language
currentState = m.group(1) + currentState + m.group(3); // Add back surrounding white space currentState = m.group(1) + currentState + m.group(3); // Add back surrounding white space
Translater.LOGGER.info("Transformed: \"" + str + "\" to: \"" + currentState + "\""); if (Cfg.detailedProgress) Translater.LOGGER.info("Transformed: \"{}\" to: \"{}\"", str, currentState);
return currentState; return currentState;
} catch (Exception e) { } catch (Exception e) {
Translater.LOGGER.warn("Failed to break: \"" + m.group(2) + "\" (" + m.group(2).length() + " characters). Is your API key valid?"); String s = m.group(2);
Translater.LOGGER.warn("Failed to break: \"{}\" ({} characters). Is your API key valid? ({})", s, s.length(), e.getLocalizedMessage());
throw e; throw e;
} }
} }

View File

@ -6,12 +6,14 @@
"translater.jfconfig.breakFully.tooltip": "Whether to fully break the texts content by translating from the wrong language (enable for complete breaking)", "translater.jfconfig.breakFully.tooltip": "Whether to fully break the texts content by translating from the wrong language (enable for complete breaking)",
"translater.jfconfig.targetLanguage": "Target Language", "translater.jfconfig.targetLanguage": "Target Language",
"translater.jfconfig.targetLanguage.tooltip": "The language to translate to - Leave empty for auto-detection (might break text even more)", "translater.jfconfig.targetLanguage.tooltip": "The language to translate to - Leave empty for auto-detection (might break text even more)",
"translater.jfconfig.renderProgress": "Progress Renderer", "translater.jfconfig.progressGui": "Progress GUI",
"translater.jfconfig.renderProgress.tooltip": "Significantly slows down the loading time but gives a visual of the progress. Values: Full, Console, None", "translater.jfconfig.progressGui.tooltip": "Significantly slows down the loading time but gives an in-game visual of the progress",
"translater.jfconfig.progressConsole": "Progress Console",
"translater.jfconfig.progressConsole.tooltip": "Slightly slows down the loading time but logs the progress",
"translater.jfconfig.detailedProgress": "Detailed Progress",
"translater.jfconfig.renderProgress.tooltip": "Significantly slows down the loading time but provides information about individual translations",
"translater.jfconfig.forceRegenerate": "Force Regenerate", "translater.jfconfig.forceRegenerate": "Force Regenerate",
"translater.jfconfig.forceRegenerate.tooltip": "Use this if something is broken. This initiates the regeneration of the cache", "translater.jfconfig.forceRegenerate.tooltip": "Use this if something is broken. This initiates the regeneration of the cache",
"translater.jfconfig.enum.ProgressMode.Full": "Full", "translater.jfconfig.useDefaultCache": "Use default Cache",
"translater.jfconfig.enum.ProgressMode.Gui": "Gui", "translater.jfconfig.useDefaultCache.tooltip": "Use pre-generated translations shipped with the mod if the config permits to save time"
"translater.jfconfig.enum.ProgressMode.Console": "Console",
"translater.jfconfig.enum.ProgressMode.None": "None"
} }

File diff suppressed because it is too large Load Diff