From 69a2f9f95381322168a91c83035f772d169f5c54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=8D=9A=7ENex?= <87421482+NexIsDumb@users.noreply.github.com> Date: Thu, 3 Oct 2024 01:12:49 +0200 Subject: [PATCH] 2 men,1 pregnant WIP 1 we're so back Co-Authored-By: Ne_Eo --- source/funkin/backend/assets/ModsFolder.hx | 3 + .../backend/assets/TranslatedAssetLibrary.hx | 2 +- source/funkin/backend/system/Flags.hx | 19 +++ source/funkin/backend/system/MainState.hx | 2 +- .../funkin/backend/utils/TranslationUtil.hx | 108 ++++++++++++------ .../backend/utils/translations/FormatUtil.hx | 2 +- source/funkin/menus/PauseSubState.hx | 2 +- source/funkin/options/Options.hx | 2 +- .../funkin/options/categories/MiscOptions.hx | 2 +- 9 files changed, 98 insertions(+), 44 deletions(-) diff --git a/source/funkin/backend/assets/ModsFolder.hx b/source/funkin/backend/assets/ModsFolder.hx index 9b3bf5f1..4b0d8a25 100644 --- a/source/funkin/backend/assets/ModsFolder.hx +++ b/source/funkin/backend/assets/ModsFolder.hx @@ -60,6 +60,9 @@ class ModsFolder { public static function switchMod(mod:String) { Options.lastLoadedMod = currentModFolder = mod; reloadMods(); + if(mod == null) { + mod = "(default)"; + } Logs.traceColored([ Logs.logText('Switched to mod: '), Logs.logText(mod, GREEN) diff --git a/source/funkin/backend/assets/TranslatedAssetLibrary.hx b/source/funkin/backend/assets/TranslatedAssetLibrary.hx index 8e9211f9..a90da269 100644 --- a/source/funkin/backend/assets/TranslatedAssetLibrary.hx +++ b/source/funkin/backend/assets/TranslatedAssetLibrary.hx @@ -20,7 +20,7 @@ class TranslatedAssetLibrary extends AssetLibrary implements IModsAssetLibrary { return libName; } @:noCompletion private inline function set_langFolder(value:String):String { - basePath = prefix + (libName = modName = (value != null ? value : TranslationUtil.DEFAULT_LANGUAGE)) + "/"; + basePath = prefix + (libName = modName = (value != null ? value : Flags.DEFAULT_LANGUAGE)) + "/"; return libName; } diff --git a/source/funkin/backend/system/Flags.hx b/source/funkin/backend/system/Flags.hx index 25d13ebf..3b03b561 100644 --- a/source/funkin/backend/system/Flags.hx +++ b/source/funkin/backend/system/Flags.hx @@ -14,6 +14,20 @@ class Flags { public static var SONGS_LIST_MOD_MODE:Allow<"prepend", "override", "append"> = "override"; public static var WEEKS_LIST_MOD_MODE:Allow<"prepend", "override", "append"> = "override"; + // Translations system // + public static var DEFAULT_LANGUAGE:String = "en"; + public static var DEFAULT_LANGUAGE_NAME:String = "English"; + /** + * **NOTICE:** This will only contain the id of the language, not the full name. + * If you blacklist the default language, you will need to change DEFAULT_LANGUAGE and DEFAULT_LANGUAGE_NAME. + */ + public static var BLACKLISTED_LANGUAGES:Array = []; + /** + * **NOTICE:** This will only contain the id of the language, not the full name. + * If this list is not empty, the languages listed will be the only ones able to be used. + */ + public static var WHITELISTED_LANGUAGES:Array = []; + // Internal stuff @:bypass public static var customFlags:Map = []; @@ -24,6 +38,11 @@ class Flags { final flagsPath = Paths.getPath("flags.ini"); for(lib in Paths.assetsTree.libraries) { + #if TRANSLATIONS_SUPPORT + // skip translations since it would be useless, if you wanna modify it set the flags inside of global.hx + if(lib is funkin.backend.assets.TranslatedAssetLibrary) continue; + #end + if(lib.exists(flagsPath, AssetType.TEXT)) { var data:String = lib.getAsset(flagsPath, AssetType.TEXT); var trimmed:String; diff --git a/source/funkin/backend/system/MainState.hx b/source/funkin/backend/system/MainState.hx index 3d2358c2..83311c8d 100644 --- a/source/funkin/backend/system/MainState.hx +++ b/source/funkin/backend/system/MainState.hx @@ -56,9 +56,9 @@ class MainState extends FlxState { Main.refreshAssets(); ModsFolder.onModSwitch.dispatch(ModsFolder.currentModFolder); + Flags.load(); TranslationUtil.findAllLanguages(); TranslationUtil.setLanguage(); - Flags.load(); DiscordUtil.init(); EventsData.reloadEvents(); TitleState.initialized = false; diff --git a/source/funkin/backend/utils/TranslationUtil.hx b/source/funkin/backend/utils/TranslationUtil.hx index 13f72a8f..00fa2bc6 100644 --- a/source/funkin/backend/utils/TranslationUtil.hx +++ b/source/funkin/backend/utils/TranslationUtil.hx @@ -12,26 +12,28 @@ import funkin.backend.utils.translations.FormatUtil; /** * The class used for translations based on the XMLs inside the translations folders. * - * Made by @NexIsDumb originally for the Poldhub mod. + * Made by @NexIsDumb originally for the Poldhub mod and improved thanks to him and @NeeEoo. */ @:allow(funkin.backend.assets.TranslatedAssetLibrary) final class TranslationUtil { /** - * The current language selected translation map; If the current language it's english, this map will be empty. + * The current language selected translation map. * * It'll never be `null`. */ public static var stringMap(default, set):Map = []; /** - * The default language folder used inside of the source code. - */ - public static var DEFAULT_LANGUAGE:String = 'en'; // no inline so psychopathic mods can edit it - Nex - /** - * The default language used inside of the source code. - */ - public static var DEFAULT_LANGUAGE_NAME:String = 'English'; + * The alternative language selected translation map. + * It is filled with the default language. + * + * This is filled in if the current language is not the same as the default language. + * Its used when showMissingIds is false. + * + * It'll never be `null`. + **/ + public static var alternativeStringMap(default, set):Map = []; /** * Returns the current language config. @@ -68,10 +70,10 @@ final class TranslationUtil private static var langConfigs:Map = []; private static var nameMap:Map = []; private static inline function getDefaultNameMap():Map { - return [DEFAULT_LANGUAGE => DEFAULT_LANGUAGE_NAME]; + return [Flags.DEFAULT_LANGUAGE => Flags.DEFAULT_LANGUAGE_NAME]; } private static inline function getDefaultLangConfigs():Map { - return [DEFAULT_LANGUAGE => getDefaultConfig(DEFAULT_LANGUAGE)]; + return [Flags.DEFAULT_LANGUAGE => getDefaultConfig(Flags.DEFAULT_LANGUAGE)]; } @:noUsing private static inline function getDefaultConfig(name:String):IniMap { return ["name" => getLanguageName(name), "credits" => "", "version" => "1.0.0"]; @@ -84,15 +86,29 @@ final class TranslationUtil * If `name` is `null`, its gonna use the current language. * If `name` is not `null`, it will load the translations for the given language. */ - public static function setLanguage(?name:String) { + public static function setLanguage(?name:String):Void { #if TRANSLATIONS_SUPPORT if(name == null) name = curLanguage; + if(!langConfigs.exists(name)) name = Flags.DEFAULT_LANGUAGE; + if(!langConfigs.exists(name)) name = foundLanguages[0]; - Logs.traceColored([ - Logs.getPrefix("Language"), - Logs.logText("Setting Language To: "), - Logs.logText('${getLanguageName(name)} ($name)', GREEN) - ], VERBOSE); + if(curLanguage != name) { + Logs.traceColored([ + Logs.getPrefix("Language"), + Logs.logText("Changing saved language to: "), + Logs.logText('${getLanguageName(name)} ($name)', GREEN) + ], INFO); + // todo: make this into a notification system for the user + // notification would be in previous language + // but only if the user didnt manually change it. + curLanguage = name; + } else { + Logs.traceColored([ + Logs.getPrefix("Language"), + Logs.logText("Setting Language To: "), + Logs.logText('${getLanguageName(name)} ($name)', GREEN) + ], VERBOSE); + } for(mod in ModsFolder.getLoadedModsLibs(false)) if(mod is TranslatedAssetLibrary) @@ -120,11 +136,7 @@ final class TranslationUtil return get("diff." + id.toLowerCase(), params, id); public static inline function exists(id:String):Bool - #if TRANSLATIONS_SUPPORT - return stringMap.exists(id); - #else - return false; - #end + return #if TRANSLATIONS_SUPPORT stringMap.exists(id) #else false #end; public static function getRaw(id:String, ?def:String):IFormatInfo { @@ -137,7 +149,7 @@ final class TranslationUtil return FormatUtil.getStr("{" + id + "}"); - /*if(curLanguage == DEFAULT_LANGUAGE) { + /*if(curLanguage == Flags.DEFAULT_LANGUAGE) { return FormatUtil.getStr("{" + id + "}"); } else { @@ -153,15 +165,25 @@ final class TranslationUtil */ public static function raw2Id(str:String):String { - var result:String = ""; - for(i => s in str.split(" ")) result += (i == 0 ? s.charAt(0).toLowerCase() : s.charAt(0).toUpperCase()) + s.substr(1).toLowerCase(); - return result.length == 0 ? str : result; + str = str.toLowerCase(); + return [for(i => s in str.split(" ")) + i != 0 ? s.charAt(0).toUpperCase() + s.substr(1) : s + ].join(""); } + public static inline function isBlacklisted(lang:String):Bool + return Flags.BLACKLISTED_LANGUAGES.contains(lang); + + public static inline function isWhitelisted(lang:String):Bool + return Flags.WHITELISTED_LANGUAGES.length == 0 || Flags.WHITELISTED_LANGUAGES.contains(lang); + + public static inline function isAllowed(lang:String):Bool + return isWhitelisted(lang) && !isBlacklisted(lang); + /** * Returns an array that specifies which languages were found. */ - public static function findAllLanguages() + public static function findAllLanguages():Void { #if TRANSLATIONS_SUPPORT foundLanguages = []; @@ -170,8 +192,9 @@ final class TranslationUtil var mainPath:String = translationsMain(""); var langName:String = null; for (lang in Paths.assetsTree.getFolders("assets/" + mainPath)) { - var path:String = Path.join([mainPath, lang, "config.ini"]); + if (!isAllowed(lang)) continue; + var path:String = Path.join([mainPath, lang, "config.ini"]); var config = getDefaultConfig(lang); if(Assets.exists(path)) { @@ -191,11 +214,11 @@ final class TranslationUtil } // Ensure that the default language is always first - var englishName = DEFAULT_LANGUAGE + "/" + getLanguageName(DEFAULT_LANGUAGE); - if(foundLanguages.contains(englishName)) foundLanguages.remove(englishName); - foundLanguages.insert(0, englishName); + var defaultName = Flags.DEFAULT_LANGUAGE + "/" + getLanguageName(Flags.DEFAULT_LANGUAGE); + if(foundLanguages.contains(defaultName)) foundLanguages.remove(defaultName); + foundLanguages.insert(0, defaultName); - if(!nameMap.exists(curLanguage)) curLanguage = DEFAULT_LANGUAGE; + if(!nameMap.exists(curLanguage)) curLanguage = Flags.DEFAULT_LANGUAGE; Logs.trace("Found languages: " + foundLanguages.join(", "), "Language"); #end } @@ -210,11 +233,13 @@ final class TranslationUtil var mainPath:String = translationsMain(lang); var leMap:Map = []; var translations:Array = []; + + final NODE_NAMES = ["text", "trans", "lang", "string", "str"]; function parseXml(xml:Access, prefix:String = "") { for(node in xml.elements) { if (node.name == "group") // Cosmetic name parseXml(node, prefix + (node.has.prefix ? node.att.prefix : "")); - else if(["text", "trans", "lang", "string", "str"].contains(node.name)) + else if(NODE_NAMES.contains(node.name)) translations.push({ prefix: prefix, node: node @@ -222,6 +247,8 @@ final class TranslationUtil } } + // todo make it load the default languages in a second string map + for(mod in ModsFolder.getLoadedModsLibs(true)) for(file in mod.getFiles("assets/" + mainPath).sortAlphabetically().map((v)->'$mainPath/$v')) { if(Path.extension(file).toLowerCase() != "xml") continue; @@ -273,11 +300,11 @@ final class TranslationUtil // Utils - public static function getLanguageName(lang:String) { + public static function getLanguageName(lang:String):String { return nameMap.exists(lang) ? nameMap.get(lang) : lang; } - public static function getLanguageFromName(name:String) { + public static function getLanguageFromName(name:String):String { var reverseMap = new Map(); for(key => val in nameMap) reverseMap.set(val, key); return reverseMap.exists(name) ? reverseMap.get(name) : name; @@ -287,10 +314,10 @@ final class TranslationUtil return langConfigs.exists(lang) ? langConfigs.get(lang) : getDefaultConfig(lang); } - public static inline function translationsMain(key:String) + public static inline function translationsMain(key:String):String return '$LANG_FOLDER/$key'; - public static inline function translations(key:String) + public static inline function translations(key:String):String return translationsMain('$curLanguage/$key'); // getters & setters @@ -300,6 +327,11 @@ final class TranslationUtil return stringMap = value; } + @:noCompletion private static function set_alternativeStringMap(value:Map):Map { + if (value == null) value = []; + return alternativeStringMap = value; + } + @:noCompletion private static function get_curLanguage():String { return Options.language; } @@ -315,7 +347,7 @@ final class TranslationUtil } @:noCompletion private static function get_isDefaultLanguage():Bool - return Options.language == DEFAULT_LANGUAGE; + return Options.language == Flags.DEFAULT_LANGUAGE; @:noCompletion private static function get_isLanguageLoaded():Bool return Lambda.count(stringMap) > 0; diff --git a/source/funkin/backend/utils/translations/FormatUtil.hx b/source/funkin/backend/utils/translations/FormatUtil.hx index 9c22d4e7..cb601fc7 100644 --- a/source/funkin/backend/utils/translations/FormatUtil.hx +++ b/source/funkin/backend/utils/translations/FormatUtil.hx @@ -28,7 +28,7 @@ class FormatUtil { return fi; } - public inline static function clear() { + public inline static function clear():Void { cache.clear(); cacheStr.clear(); } diff --git a/source/funkin/menus/PauseSubState.hx b/source/funkin/menus/PauseSubState.hx index 1cd7b809..a0b187c4 100644 --- a/source/funkin/menus/PauseSubState.hx +++ b/source/funkin/menus/PauseSubState.hx @@ -79,7 +79,7 @@ class PauseSubState extends MusicBeatSubstate var levelDifficulty:FunkinText = new FunkinText(20, 15, 0, TU.translateDiff(PlayState.difficulty).toUpperCase(), 32, false); var deathCounter:FunkinText = new FunkinText(20, 15, 0, TU.translate("pause.deathCounter", [PlayState.deathCounter]), 32, false); var multiplayerText:FunkinText = null; - if(multiplayerInfo != "") + if(multiplayerInfo != null) multiplayerText = new FunkinText(20, 15, 0, TU.translate(multiplayerInfo), 32, false); for(k=>label in [levelInfo, levelDifficulty, deathCounter, multiplayerText]) { diff --git a/source/funkin/options/Options.hx b/source/funkin/options/Options.hx index 8d5dfee4..98fad245 100644 --- a/source/funkin/options/Options.hx +++ b/source/funkin/options/Options.hx @@ -35,7 +35,7 @@ class Options public static var songOffset:Float = 0; public static var framerate:Int = 120; public static var gpuOnlyBitmaps:Bool = #if (mac || web) false #else true #end; // causes issues on mac and web - public static var language = funkin.backend.utils.TranslationUtil.DEFAULT_LANGUAGE; + public static var language = "en"; // default to english, Flags.DEFAULT_LANGUAGE should not modify this public static var lastLoadedMod:String = null; diff --git a/source/funkin/options/categories/MiscOptions.hx b/source/funkin/options/categories/MiscOptions.hx index de0f1272..9971c8e3 100644 --- a/source/funkin/options/categories/MiscOptions.hx +++ b/source/funkin/options/categories/MiscOptions.hx @@ -11,7 +11,7 @@ class MiscOptions extends OptionsScreen { // TODO: add credits based on the config file add(new ArrayOption( getName("language"), - getDesc("language", [TranslationUtil.DEFAULT_LANGUAGE]), + getDesc("language", [Flags.DEFAULT_LANGUAGE]), [for(lan in lanArray) lan.split("/").first()], [for(lan in lanArray) lan.split("/").last()], "language",