Add code
This commit is contained in:
commit
63659703b5
|
@ -0,0 +1,118 @@
|
|||
# User-specific stuff
|
||||
.idea/
|
||||
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
*~
|
||||
|
||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||
.fuse_hidden*
|
||||
|
||||
# KDE directory preferences
|
||||
.directory
|
||||
|
||||
# Linux trash folder which might appear on any partition or disk
|
||||
.Trash-*
|
||||
|
||||
# .nfs files are created when an open file is removed but is still being accessed
|
||||
.nfs*
|
||||
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
Thumbs.db:encryptable
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
|
||||
# Dump file
|
||||
*.stackdump
|
||||
|
||||
# Folder config file
|
||||
[Dd]esktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
.gradle
|
||||
build/
|
||||
|
||||
# Ignore Gradle GUI config
|
||||
gradle-app.setting
|
||||
|
||||
# Cache of project
|
||||
.gradletasknamecache
|
||||
|
||||
**/build/
|
||||
|
||||
# Common working directory
|
||||
run/
|
||||
|
||||
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||
!gradle-wrapper.jar
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2023 JFronny
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -0,0 +1,15 @@
|
|||
import io.gitlab.jfronny.scripts.*
|
||||
|
||||
plugins {
|
||||
id("jfmod") version "1.3-SNAPSHOT"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
modImplementation("io.gitlab.jfronny.libjf:libjf-config-core-v1:${prop("libjf_version")}")
|
||||
modImplementation("io.gitlab.jfronny.libjf:libjf-translate-v1:${prop("libjf_version")}")
|
||||
|
||||
// Dev env
|
||||
modLocalRuntime("io.gitlab.jfronny.libjf:libjf-config-ui-tiny-v1:${prop("libjf_version")}")
|
||||
modLocalRuntime("io.gitlab.jfronny.libjf:libjf-devutil:${prop("libjf_version")}")
|
||||
modLocalRuntime("com.terraformersmc:modmenu:5.0.2")
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
# https://fabricmc.net/develop/
|
||||
minecraft_version=1.19.3
|
||||
yarn_mappings=build.5
|
||||
loader_version=0.14.12
|
||||
|
||||
maven_group=io.gitlab.jfronny
|
||||
archives_base_name=globalization
|
||||
|
||||
modrinth_id=globalization
|
||||
modrinth_required_dependencies=libjf
|
||||
modrinth_optional_dependencies=modmenu
|
||||
|
||||
libjf_version=3.5.0
|
||||
fabric_version=0.70.0+1.19.3
|
|
@ -0,0 +1,9 @@
|
|||
pluginManagement {
|
||||
repositories {
|
||||
maven("https://maven.fabricmc.net/") // FabricMC
|
||||
maven("https://maven.frohnmeyer-wds.de/artifacts") // scripts
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "globalization"
|
|
@ -0,0 +1,37 @@
|
|||
package io.gitlab.jfronny.globalization.mixin;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.gitlab.jfronny.globalization.DelegateHashMap;
|
||||
import io.gitlab.jfronny.globalization.GlobalizationMap;
|
||||
import net.minecraft.client.resource.language.LanguageDefinition;
|
||||
import net.minecraft.client.resource.language.TranslationStorage;
|
||||
import net.minecraft.resource.ResourceManager;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.*;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Mixin(TranslationStorage.class)
|
||||
public class TranslationStorageMixin {
|
||||
@ModifyVariable(method = "load(Lnet/minecraft/resource/ResourceManager;Ljava/util/List;)Lnet/minecraft/client/resource/language/TranslationStorage;", at = @At(value = "INVOKE_ASSIGN", target = "Lcom/google/common/collect/Maps;newHashMap()Ljava/util/HashMap;"), index = 2)
|
||||
private static Map<String, String> globalization$createCustomMap(Map<String, String> original) {
|
||||
if (!original.isEmpty()) throw new IllegalStateException("Non-empty original");
|
||||
return new GlobalizationMap();
|
||||
}
|
||||
|
||||
@Inject(method = "load(Lnet/minecraft/resource/ResourceManager;Ljava/util/List;)Lnet/minecraft/client/resource/language/TranslationStorage;", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resource/language/LanguageDefinition;isRightToLeft()Z"), locals = LocalCapture.CAPTURE_FAILHARD)
|
||||
private static void globalization$forkCustomMap(ResourceManager resourceManager, List<LanguageDefinition> definitions, CallbackInfoReturnable<TranslationStorage> cir, Map<String, String> map) {
|
||||
globalization$getMap(map).fork();
|
||||
}
|
||||
|
||||
@Redirect(method = "load(Lnet/minecraft/resource/ResourceManager;Ljava/util/List;)Lnet/minecraft/client/resource/language/TranslationStorage;", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/ImmutableMap;copyOf(Ljava/util/Map;)Lcom/google/common/collect/ImmutableMap;"))
|
||||
private static ImmutableMap<String, String> globalization$generateMissing(Map<String, String> source) {
|
||||
return ImmutableMap.copyOf(globalization$getMap(source).generateMissing());
|
||||
}
|
||||
|
||||
private static GlobalizationMap globalization$getMap(Map<String, String> map) {
|
||||
return (GlobalizationMap) map;
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 151 KiB |
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"globalization.jfconfig.title": "LibJF Translate v1",
|
||||
"globalization.jfconfig.targetCode": "Target Code",
|
||||
"globalization.jfconfig.targetCode.tooltip": "The language code to translate missing translations to.\nShould be the same as your Minecraft language. Source language is detected automatically."
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "io.gitlab.jfronny.globalization.mixin",
|
||||
"compatibilityLevel": "JAVA_17",
|
||||
"client": [
|
||||
"TranslationStorageMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
package io.gitlab.jfronny.globalization;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.*;
|
||||
|
||||
public class DelegateHashMap<M extends Map<K, V>, K, V> extends HashMap<K, V> {
|
||||
private final M delegate;
|
||||
|
||||
public DelegateHashMap(M delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
public M getDelegate() {
|
||||
if (!super.isEmpty()) throw new IllegalStateException("Something accesses this maps internal state");
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return delegate.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return delegate.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(Object key) {
|
||||
return delegate.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return delegate.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V put(K key, V value) {
|
||||
return delegate.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends K, ? extends V> m) {
|
||||
delegate.putAll(m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(Object key) {
|
||||
return delegate.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
delegate.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return delegate.containsValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
return delegate.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> values() {
|
||||
return delegate.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<K, V>> entrySet() {
|
||||
return delegate.entrySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getOrDefault(Object key, V defaultValue) {
|
||||
return delegate.getOrDefault(key, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V putIfAbsent(K key, V value) {
|
||||
return delegate.putIfAbsent(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object key, Object value) {
|
||||
return delegate.remove(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replace(K key, V oldValue, V newValue) {
|
||||
return delegate.replace(key, oldValue, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V replace(K key, V value) {
|
||||
return delegate.replace(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
|
||||
return delegate.computeIfAbsent(key, mappingFunction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
|
||||
return delegate.computeIfPresent(key, remappingFunction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
|
||||
return delegate.compute(key, remappingFunction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
|
||||
return delegate.merge(key, value, remappingFunction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(BiConsumer<? super K, ? super V> action) {
|
||||
delegate.forEach(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
|
||||
delegate.replaceAll(function);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return delegate.equals(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return delegate.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return delegate.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package io.gitlab.jfronny.globalization;
|
||||
|
||||
import io.gitlab.jfronny.commons.log.Logger;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
|
||||
public class Globalization implements ModInitializer {
|
||||
public static final Logger LOG = Logger.forName("globalization");
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package io.gitlab.jfronny.globalization;
|
||||
|
||||
import io.gitlab.jfronny.libjf.config.api.v1.Entry;
|
||||
import io.gitlab.jfronny.libjf.config.api.v1.JfConfig;
|
||||
|
||||
@JfConfig(referencedConfigs = "libjf-translate-v1")
|
||||
public class GlobalizationConfig {
|
||||
@Entry public static String targetCode = "en";
|
||||
|
||||
static {
|
||||
JFC_GlobalizationConfig.ensureInitialized();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
package io.gitlab.jfronny.globalization;
|
||||
|
||||
import io.gitlab.jfronny.libjf.translate.api.*;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class GlobalizationMap extends AbstractMap<String, String> {
|
||||
private static final Path targetPath = FabricLoader.getInstance().getConfigDir().resolve("globalization");
|
||||
private List<Map<String, String>> pools = new LinkedList<>();
|
||||
|
||||
public void fork() {
|
||||
if (pools.isEmpty() || !getCurrentPool().isEmpty()) pools.add(new LinkedHashMap<>());
|
||||
}
|
||||
|
||||
public Map<String, String> generateMissing() {
|
||||
if (pools.isEmpty()) return Map.of();
|
||||
if (pools.size() == 1) return pools.get(0);
|
||||
return generateMissing(pools.get(pools.size() - 1), pools.subList(0, pools.size() - 1), TranslateService.getConfigured());
|
||||
}
|
||||
|
||||
private static <T extends Language, S extends TranslateService<T>> Map<String, String> generateMissing(Map<String, String> origin, List<Map<String, String>> other, S service) {
|
||||
Map<String, String> target = new LinkedHashMap<>(origin);
|
||||
Map<String, String> toAdd = other
|
||||
.stream()
|
||||
.map(Map::entrySet)
|
||||
.flatMap(Collection::stream)
|
||||
.filter(s -> !target.containsKey(s.getKey()))
|
||||
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
|
||||
Properties cache = new Properties();
|
||||
T targetLang = service.parseLang(GlobalizationConfig.targetCode);
|
||||
Path cacheFile = targetPath.resolve(targetLang.getIdentifier() + ".cache");
|
||||
try {
|
||||
Files.createDirectories(targetPath);
|
||||
if (Files.exists(cacheFile)) {
|
||||
try (Reader r = Files.newBufferedReader(cacheFile)) {
|
||||
cache.load(r);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Globalization.LOG.error("Could not read cache", e);
|
||||
}
|
||||
toAdd.forEach((k, v) -> {
|
||||
try {
|
||||
if (cache.containsKey(v)) target.put(k, cache.get(v).toString());
|
||||
else {
|
||||
String s = service.translate(v, service.detect(v), targetLang);
|
||||
target.put(k, s);
|
||||
cache.put(v, s);
|
||||
Globalization.LOG.info("Translated " + v + " to " + s);
|
||||
}
|
||||
} catch (TranslateException e) {
|
||||
Globalization.LOG.error("Could not translate " + v, e);
|
||||
}
|
||||
});
|
||||
try {
|
||||
Files.createDirectories(targetPath);
|
||||
try (Writer w = Files.newBufferedWriter(cacheFile)) {
|
||||
cache.store(w, "Cache for translations to " + targetLang.getDisplayName());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Globalization.LOG.error("Could not write cache", e);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
private Map<String, String> getCurrentPool() {
|
||||
if (pools.isEmpty()) throw new IllegalStateException("Called method on non-forked globalization map");
|
||||
return pools.get(pools.size() - 1);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String put(String s, String s2) {
|
||||
String s3 = getCurrentPool().put(s, s2);
|
||||
if (s3 == null) {
|
||||
for (Map<String, String> map : pools.subList(0, pools.size() - 1)) {
|
||||
s3 = map.getOrDefault(s, s3);
|
||||
}
|
||||
}
|
||||
return s3;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<Entry<String, String>> entrySet() {
|
||||
return pools.stream()
|
||||
.map(Map::entrySet)
|
||||
.flatMap(Collection::stream)
|
||||
.collect(Collectors.groupingBy(Entry::getKey, Collectors.toList()))
|
||||
.entrySet()
|
||||
.stream()
|
||||
.map(s -> new MapEntry<>(s.getKey(), s.getValue().get(s.getValue().size() - 1).getValue()))
|
||||
.collect(Collectors.toUnmodifiableSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return pools.isEmpty() || (pools.size() == 1 && pools.get(0).isEmpty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object o) {
|
||||
return pools.stream().anyMatch(s -> s.containsKey(o));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object o) {
|
||||
return pools.stream().anyMatch(s -> s.containsValue(o));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(Object o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String remove(Object o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
return pools.stream().flatMap(s -> s.keySet().stream()).collect(Collectors.toUnmodifiableSet());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package io.gitlab.jfronny.globalization;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public record MapEntry<K, V>(K key, V value) implements Map.Entry<K, V> {
|
||||
@Override
|
||||
public K getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V setValue(V v) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "globalization",
|
||||
"name": "Globalization",
|
||||
"version": "${version}",
|
||||
"description": "Generate missing translations",
|
||||
"authors": ["JFronny"],
|
||||
"contact": {
|
||||
"email": "projects.contact@frohnmeyer-wds.de",
|
||||
"homepage": "https://jfronny.gitlab.io",
|
||||
"issues": "https://git.frohnmeyer-wds.de/Johannes/Globalization/issues",
|
||||
"sources": "https://git.frohnmeyer-wds.de/Johannes/Globalization"
|
||||
},
|
||||
"license": "MIT",
|
||||
"icon": "assets/globalization/icon.png",
|
||||
"environment": "client",
|
||||
"entrypoints": {
|
||||
"libjf:config": ["io.gitlab.jfronny.globalization.JFC_GlobalizationConfig"],
|
||||
"main": ["io.gitlab.jfronny.globalization.Globalization"]
|
||||
},
|
||||
"mixins": [
|
||||
{
|
||||
"config": "globalization.client.mixins.json",
|
||||
"environment": "client"
|
||||
}
|
||||
],
|
||||
"depends": {
|
||||
"fabricloader": ">=0.14.12",
|
||||
"minecraft": "*",
|
||||
"libjf-config-core-v1": "*",
|
||||
"libjf-translate-v1": "*"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue