From 98ac42a6c6f5168b691fe88cdd1b50afa62a31f2 Mon Sep 17 00:00:00 2001 From: AnimeGitB Date: Sun, 9 Oct 2022 12:21:05 +1030 Subject: [PATCH] Flatten language translation keys in-memory --- .../java/emu/grasscutter/utils/Language.java | 62 ++++++++----------- 1 file changed, 25 insertions(+), 37 deletions(-) diff --git a/src/main/java/emu/grasscutter/utils/Language.java b/src/main/java/emu/grasscutter/utils/Language.java index 93c06ead2..d24ef3280 100644 --- a/src/main/java/emu/grasscutter/utils/Language.java +++ b/src/main/java/emu/grasscutter/utils/Language.java @@ -14,8 +14,6 @@ import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import lombok.EqualsAndHashCode; -import javax.annotation.Nullable; - import static emu.grasscutter.config.Configuration.*; import static emu.grasscutter.utils.FileUtils.getResourcePath; @@ -46,9 +44,8 @@ import java.util.Map; public final class Language { private static final Map cachedLanguages = new ConcurrentHashMap<>(); - private final JsonObject languageData; private final String languageCode; - private final Map cachedTranslations = new ConcurrentHashMap<>(); + private final Map translations = new ConcurrentHashMap<>(); /** * Creates a language instance from a code. @@ -139,20 +136,36 @@ public final class Language { return languageCode; } + /** + * Recursive helper function to flatten a Json tree + * Converts input like {"foo": {"bar": "baz"}} to {"foo.bar": "baz"} + * @param map The map to insert the keys into + * @param key The flattened key of the current element + * @param element The current element + */ + private static void putFlattenedKey(Map map, String key, JsonElement element) { + if (element.isJsonObject()) { + element.getAsJsonObject().entrySet().forEach(entry -> { + String keyPrefix = key.isEmpty() ? "" : key + "."; + putFlattenedKey(map, keyPrefix + entry.getKey(), entry.getValue()); + }); + } else { + map.put(key, element.getAsString()); + } + } + /** * Reads a file and creates a language instance. */ private Language(LanguageStreamDescription description) { - @Nullable JsonObject languageData = null; languageCode = description.getLanguageCode(); try { - languageData = JsonUtils.decode(Utils.readFromInputStream(description.getLanguageFile()), JsonObject.class); + var object = JsonUtils.decode(Utils.readFromInputStream(description.getLanguageFile()), JsonObject.class); + object.entrySet().forEach(entry -> putFlattenedKey(translations, entry.getKey(), entry.getValue())); } catch (Exception exception) { Grasscutter.getLogger().warn("Failed to load language file: " + description.getLanguageCode(), exception); } - - this.languageData = languageData; } /** @@ -199,41 +212,16 @@ public final class Language { * @return The value (as a string) from a nested key. */ public String get(String key) { - if (this.cachedTranslations.containsKey(key)) { - return this.cachedTranslations.get(key); - } - - String[] keys = key.split("\\."); - JsonObject object = this.languageData; - - int index = 0; + if (translations.containsKey(key)) return translations.get(key); String valueNotFoundPattern = "This value does not exist. Please report this to the Discord: "; String result = valueNotFoundPattern + key; - boolean isValueFound = false; - - while (true) { - if (index == keys.length) break; - - String currentKey = keys[index++]; - if (object.has(currentKey)) { - JsonElement element = object.get(currentKey); - if (element.isJsonObject()) - object = element.getAsJsonObject(); - else { - isValueFound = true; - result = element.getAsString(); break; - } - } else break; - } - - if (!isValueFound && !languageCode.equals("en-US")) { - var englishValue = getLanguage("en-US").get(key); + if (!languageCode.equals("en-US")) { + String englishValue = getLanguage("en-US").get(key); if (!englishValue.contains(valueNotFoundPattern)) { result += "\nhere is english version:\n" + englishValue; } } - - this.cachedTranslations.put(key, result); return result; + return result; } private static class LanguageStreamDescription {