otterlib language system

This commit is contained in:
Oth3r 2025-06-16 20:36:32 -05:00
commit 85cc76a022
9 changed files with 69 additions and 172 deletions

View file

@ -7,9 +7,9 @@ import one.oth3r.otterlib.client.screen.ConfigScreen;
import one.oth3r.otterlib.client.screen.utl.CustomImage; import one.oth3r.otterlib.client.screen.utl.CustomImage;
import one.oth3r.otterlib.client.screen.utl.SimpleButton; import one.oth3r.otterlib.client.screen.utl.SimpleButton;
import one.oth3r.sit.file.FileData; import one.oth3r.sit.file.FileData;
import one.oth3r.sit.utl.Chat;
import one.oth3r.sit.utl.Data; import one.oth3r.sit.utl.Data;
import one.oth3r.sit.utl.Events; import one.oth3r.sit.utl.Events;
import one.oth3r.sit.utl.Utl;
import java.net.URI; import java.net.URI;
import java.util.List; import java.util.List;
@ -23,17 +23,17 @@ public class SitClient implements ClientModInitializer {
} }
public static Screen getConfigScreen(Screen parent) { public static Screen getConfigScreen(Screen parent) {
return new ConfigScreen(parent, Utl.lang("sit!.screen.config"), return new ConfigScreen(parent, Chat.lang("sit!.screen.config"),
new CustomImage(Identifier.of(Data.MOD_ID, "textures/gui/banner.png"),128, 72), new CustomImage(Identifier.of(Data.MOD_ID, "textures/gui/banner.png"),128, 72),
List.of( List.of(
SimpleButton.Templates.fileEditor(Utl.lang("config.server"), FileData.getServerConfig(), new CustomImage(Identifier.of(Data.MOD_ID, "server_button"),246,26)).build(), SimpleButton.Templates.fileEditor(Chat.lang("config.server"), FileData.getServerConfig(), new CustomImage(Identifier.of(Data.MOD_ID, "server_button"),246,26)).build(),
SimpleButton.Templates.fileEditor(Utl.lang("config.sitting"), FileData.getSittingConfig(), new CustomImage(Identifier.of(Data.MOD_ID, "sitting_button"), 246, 26)).build() SimpleButton.Templates.fileEditor(Chat.lang("config.sitting"), FileData.getSittingConfig(), new CustomImage(Identifier.of(Data.MOD_ID, "sitting_button"), 246, 26)).build()
), ),
List.of( List.of(
SimpleButton.Templates.warning(Utl.lang("sit!.gui.button.issues")).openLink("https://github.com/Oth3r/Sit/issues").build(), SimpleButton.Templates.warning(Chat.lang("sit!.gui.button.issues")).openLink("https://github.com/Oth3r/Sit/issues").build(),
new SimpleButton.Builder(Utl.lang("sit!.gui.button.website")).openLink("https://modrinth.com/mod/sit!").build(), new SimpleButton.Builder(Chat.lang("sit!.gui.button.website")).openLink("https://modrinth.com/mod/sit!").build(),
SimpleButton.Templates.done(Utl.lang("gui.done")).build(), SimpleButton.Templates.done(Chat.lang("gui.done")).build(),
SimpleButton.Templates.donate(Utl.lang("sit!.gui.button.donate")).openLink(URI.create("https://ko-fi.com/oth3r")).build() SimpleButton.Templates.donate(Chat.lang("sit!.gui.button.donate")).openLink(URI.create("https://ko-fi.com/oth3r")).build()
)); ));
} }
} }

View file

@ -8,8 +8,8 @@ import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Formatting;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import one.oth3r.sit.utl.Chat;
import one.oth3r.sit.utl.Data; import one.oth3r.sit.utl.Data;
import one.oth3r.sit.utl.Logic; import one.oth3r.sit.utl.Logic;
import one.oth3r.sit.utl.Utl; import one.oth3r.sit.utl.Utl;
@ -50,7 +50,7 @@ public class SitCommand {
if (player == null) { if (player == null) {
if (args[0].equalsIgnoreCase("reload")) { if (args[0].equalsIgnoreCase("reload")) {
Logic.reload(); Logic.reload();
Data.LOGGER.info(Utl.lang("sit!.chat.reloaded").toString()); Data.LOGGER.info(Chat.lang("sit!.chat.reloaded").toString());
} }
return 1; return 1;
} }
@ -76,7 +76,7 @@ public class SitCommand {
if (args[0].equalsIgnoreCase("reload")) { if (args[0].equalsIgnoreCase("reload")) {
Logic.reload(); Logic.reload();
player.sendMessage(Utl.messageTag().append(Utl.lang("sit!.chat.reloaded").color(Color.GREEN)).b()); player.sendMessage(Chat.tag().append(Chat.lang("sit!.chat.reloaded").color(Color.GREEN)).b());
} }
if (args[0].equalsIgnoreCase("purgeChairEntities")) Utl.Entity.purge(player,true); if (args[0].equalsIgnoreCase("purgeChairEntities")) Utl.Entity.purge(player,true);

View file

@ -1,9 +1,15 @@
package one.oth3r.sit.file; package one.oth3r.sit.file;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import one.oth3r.otterlib.file.LanguageReader;
import one.oth3r.sit.Sit;
import one.oth3r.sit.utl.Data; import one.oth3r.sit.utl.Data;
import one.oth3r.sit.utl.Utl; import one.oth3r.sit.utl.Utl;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
@ -56,11 +62,21 @@ public class FileData {
return playerSettings.getOrDefault(player, sittingConfig); return playerSettings.getOrDefault(player, sittingConfig);
} }
/// the language / text system for the mod
private static final LanguageReader langReader = new LanguageReader(
getLangPath(),Path.of(Data.CONFIG_DIR),"en_us","en_us");
public static LanguageReader getLangReader() {
return langReader;
}
/** /**
* loads all config files to memory * loads all config files to memory
*/ */
public static void loadFiles() { public static void loadFiles() {
getServerConfig().load(); getServerConfig().load();
// load the language reader
langReader.updateLanguage(getServerConfig().getLang());
getSittingConfig().load(); getSittingConfig().load();
// if loading file and is on supported server on client, send the new settings over // if loading file and is on supported server on client, send the new settings over
@ -77,6 +93,20 @@ public class FileData {
getServerConfig().save(); getServerConfig().save();
} }
private static Path getLangPath() {
ClassLoader classLoader = Sit.class.getClassLoader();
URL resource = classLoader.getResource("assets/sit-oth3r/lang/");
if (resource == null) {
throw new RuntimeException("Language file not found.");
}
try {
return Paths.get(resource.toURI());
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
public static class Defaults { public static class Defaults {
public static final ArrayList<SittingBlock> SITTING_BLOCKS = new ArrayList<>(Arrays.asList( public static final ArrayList<SittingBlock> SITTING_BLOCKS = new ArrayList<>(Arrays.asList(
new SittingBlock(new ArrayList<>(),new ArrayList<>(Arrays.asList("#minecraft:campfires")), new ArrayList<>(Arrays.asList("lit=false")),.437), new SittingBlock(new ArrayList<>(),new ArrayList<>(Arrays.asList("#minecraft:campfires")), new ArrayList<>(Arrays.asList("lit=false")),.437),

View file

@ -1,124 +0,0 @@
package one.oth3r.sit.file;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import one.oth3r.otterlib.chat.CTxT;
import one.oth3r.sit.Sit;
import one.oth3r.sit.utl.Data;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class LangReader {
private static final Map<String, String> defaultLangMap = new HashMap<>();
private static final Map<String, String> languageMap = new HashMap<>();
private final String translationKey;
private final Object[] placeholders;
public LangReader(String translationKey, Object... placeholders) {
this.translationKey = translationKey;
this.placeholders = placeholders;
}
public CTxT getTxT() {
String translated = getLanguageValue(translationKey);
if (placeholders != null && placeholders.length > 0) {
//removed all double \\ and replaces with \
translated = translated.replaceAll("\\\\\\\\", "\\\\");
String regex = "%\\d*\\$?[dfs]";
Matcher anyMatch = Pattern.compile(regex).matcher(translated);
Matcher endMatch = Pattern.compile(regex+"$").matcher(translated);
// Arraylist with all the %(#$)[dfs]
ArrayList<String> matches = new ArrayList<>();
while (anyMatch.find()) {
String match = anyMatch.group();
matches.add(match);
}
//SPLITS the text at each regex and remove the regex
String[] parts = translated.split(regex);
//if the last element of the array ends with regex, remove it and add an empty string to the end of the array
if (endMatch.find()) {
String[] newParts = Arrays.copyOf(parts, parts.length + 1);
newParts[parts.length] = "";
parts = newParts;
}
//if there are placeholders specified, and the split is more than 1, it will replace %(dfs) with the placeholder objects
if (parts.length > 1) {
CTxT txt = new CTxT("");
int i = 0;
for (String match : matches) {
int get = i;
//if the match is numbered, change GET to the number it wants
if (match.contains("$")) {
match = match.substring(1,match.indexOf('$'));
get = Integer.parseInt(match)-1;
}
if (parts.length != i) txt.append(parts[i]);
//convert the obj into txt
txt.append(getTxTFromObj(placeholders[get]));
i++;
}
if (parts.length != i) txt.append(parts[i]);
return new CTxT(txt);
}
}
return new CTxT(translated);
}
private CTxT getTxTFromObj(Object obj) {
if (obj instanceof CTxT) return (((CTxT) obj));
else if (obj instanceof Text) return new CTxT((MutableText) obj);
else return new CTxT(String.valueOf(obj));
}
public static LangReader of(String translationKey, Object... placeholders) {
return new LangReader(translationKey, placeholders);
}
public static void loadLanguageFile() {
Type tToken = new TypeToken<Map<String, String>>(){}.getType();
try {
// load the config language
Reader selectionReader = new InputStreamReader(getInputStream(false), StandardCharsets.UTF_8);
languageMap.putAll(new Gson().fromJson(selectionReader, tToken));
// load the default language as well (fallback)
Reader defaultReader = new InputStreamReader(getInputStream(true), StandardCharsets.UTF_8);
defaultLangMap.putAll(new Gson().fromJson(defaultReader, tToken));
} catch (Exception e) {
Data.LOGGER.error("ERROR WITH LANGUAGE FILE - PLEASE REPORT WITH THE ERROR LOG");
Data.LOGGER.error(e.getMessage());
}
}
private static InputStream getInputStream(boolean english) {
ClassLoader classLoader = Sit.class.getClassLoader();
InputStream inputStream = classLoader.getResourceAsStream("assets/sit-oth3r/lang/"+FileData.getServerConfig().getLang()+".json");
// make null if english
if (english) inputStream = null;
// if it cant read (null), try again, but with the english file
if (inputStream == null) inputStream = classLoader.getResourceAsStream("assets/sit-oth3r/lang/"+new ServerConfig().getLang()+".json");
// if null after that, throw an exception
if (inputStream == null) throw new IllegalArgumentException("CANT LOAD THE LANGUAGE FILE. SIT! WILL BREAK.");
return inputStream;
}
public static String getLanguageValue(String key) {
return languageMap.getOrDefault(key, defaultLangMap.getOrDefault(key, key));
}
}

View file

@ -5,10 +5,9 @@ import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ReloadCommand; import net.minecraft.server.command.ReloadCommand;
import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Formatting; import one.oth3r.sit.utl.Chat;
import one.oth3r.sit.utl.Data; import one.oth3r.sit.utl.Data;
import one.oth3r.sit.utl.Logic; import one.oth3r.sit.utl.Logic;
import one.oth3r.sit.utl.Utl;
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.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
@ -29,7 +28,7 @@ public class ReloadCommandMixin {
// send a reloaded message to all players with permissions // send a reloaded message to all players with permissions
for (ServerPlayerEntity player : server.getPlayerManager().getPlayerList()) { for (ServerPlayerEntity player : server.getPlayerManager().getPlayerList()) {
if (player.isCreativeLevelTwoOp()) { if (player.isCreativeLevelTwoOp()) {
player.sendMessage(Utl.messageTag().append(Utl.lang("sit!.chat.reloaded").color(Color.GREEN)).b()); player.sendMessage(Chat.tag().append(Chat.lang("sit!.chat.reloaded").color(Color.GREEN)).b());
} }
} }
} }

View file

@ -0,0 +1,16 @@
package one.oth3r.sit.utl;
import one.oth3r.otterlib.chat.CTxT;
import one.oth3r.sit.file.FileData;
import java.awt.*;
public class Chat {
public static CTxT tag() {
return new CTxT("Sit!").btn(true).color(Color.decode("#c400ff")).append(" ");
}
public static CTxT lang(String key, Object... args) {
return FileData.getLangReader().dynamicTranslatable(key, args);
}
}

View file

@ -23,7 +23,6 @@ import net.minecraft.util.ActionResult;
import one.oth3r.sit.SitClient; import one.oth3r.sit.SitClient;
import one.oth3r.sit.command.SitCommand; import one.oth3r.sit.command.SitCommand;
import one.oth3r.sit.file.FileData; import one.oth3r.sit.file.FileData;
import one.oth3r.sit.file.LangReader;
import one.oth3r.sit.file.SittingConfig; import one.oth3r.sit.file.SittingConfig;
import one.oth3r.sit.packet.SitPayloads; import one.oth3r.sit.packet.SitPayloads;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
@ -78,7 +77,7 @@ public class Events {
player.networkHandler.sendCommand("sit"); player.networkHandler.sendCommand("sit");
} else { } else {
// unsupported server message if not in a Sit! server // unsupported server message if not in a Sit! server
player.sendMessage(Utl.lang("sit!.chat.unsupported") player.sendMessage(Chat.lang("sit!.chat.unsupported")
.color(Color.RED).b(), true); .color(Color.RED).b(), true);
} }
} }
@ -104,7 +103,7 @@ public class Events {
ServerPlayNetworking.send(context.player(),new SitPayloads.ResponsePayload(SitPayloads.ResponsePayload.VERSION)); ServerPlayNetworking.send(context.player(),new SitPayloads.ResponsePayload(SitPayloads.ResponsePayload.VERSION));
// log the receiving of the packet from the player // log the receiving of the packet from the player
Data.LOGGER.info(Utl.lang("sit!.console.player_settings",context.player().getName().getString()).toString()); Data.LOGGER.info(Chat.lang("sit!.console.player_settings",context.player().getName().getString()).toString());
}))); })));
} }
@ -114,7 +113,7 @@ public class Events {
// only update when needed // only update when needed
if (!Data.isSupportedServer()) { if (!Data.isSupportedServer()) {
Data.setSupportedServer(true); Data.setSupportedServer(true);
Data.LOGGER.info(Utl.lang("sit!.console.connected",payload.value()).toString()); Data.LOGGER.info(Chat.lang("sit!.console.connected",payload.value()).toString());
} }
})); }));
} }
@ -172,7 +171,6 @@ public class Events {
private static void serverLifecycle() { private static void serverLifecycle() {
ServerLifecycleEvents.SERVER_STARTED.register(s -> { ServerLifecycleEvents.SERVER_STARTED.register(s -> {
Data.setServer(s); Data.setServer(s);
LangReader.loadLanguageFile();
// right click on block event // right click on block event
UseBlockCallback.EVENT.register((pl, world, hand, hitResult) -> { UseBlockCallback.EVENT.register((pl, world, hand, hitResult) -> {

View file

@ -201,7 +201,6 @@ public class Logic {
public static void reload() { public static void reload() {
FileData.loadFiles(); FileData.loadFiles();
FileData.saveFiles(); FileData.saveFiles();
LangReader.loadLanguageFile();
} }
/** /**
@ -228,11 +227,11 @@ public class Logic {
Formatting messageColor = config.getEnabled()?Formatting.GREEN:Formatting.RED; Formatting messageColor = config.getEnabled()?Formatting.GREEN:Formatting.RED;
// send the player the actionbar message // send the player the actionbar message
return Utl.lang("sit!.chat.toggle_sit", return Chat.lang("sit!.chat.toggle_sit",
Utl.lang(messageKey).color(config.getEnabled()? Color.GREEN : Color.RED)).b(); Chat.lang(messageKey).color(config.getEnabled()? Color.GREEN : Color.RED)).b();
} else { } else {
// unsupported server message if not in a Sit! server // unsupported server message if not in a Sit! server
return Utl.lang("sit!.chat.unsupported") return Chat.lang("sit!.chat.unsupported")
.color(Color.RED).b(); .color(Color.RED).b();
} }
} }

View file

@ -281,35 +281,14 @@ public class Utl {
// send a message if needed // send a message if needed
if (message) { if (message) {
player.sendMessage(messageTag() player.sendMessage(Chat.tag()
.append(lang("sit!.chat.purged",lang("sit!.chat.purged.total",count).color(Color.gray).b()).color(Color.GREEN)).b()); .append(Chat.lang("sit!.chat.purged",
Chat.lang("sit!.chat.purged.total",count).color(Color.gray).b()
).color(Color.GREEN)).b());
} }
} }
} }
public static CTxT messageTag() {
return new CTxT("Sit!").btn(true).color(Color.decode("#c400ff")).append(" ");
}
/**
* gets a MutableText using the language key, if on server, using the custom lang reader
*/
public static CTxT lang(String key, Object... args) {
if (Data.isClient()) {
// we have to first convert all the CTxT's to the built version because minecraft lang reader doesn't know how to process it
// make a array with the same size of the args
Object[] fixedArgs = new Object[args.length];
// for every arg, build & add if CTxT or just add if not
for (var i = 0; i < args.length; i++) {
if (args[i] instanceof CTxT) fixedArgs[i] = ((CTxT) args[i]).b();
else fixedArgs[i] = args[i];
}
// return the translated text
return new CTxT(Text.translatable(key,fixedArgs));
}
else return LangReader.of(key, args).getTxT();
}
/** /**
* sends the settings packets to the server, if client & in game * sends the settings packets to the server, if client & in game
*/ */