diff --git a/assets/languages/en/Main.xml b/assets/languages/en/Main.xml index 1430702b..f207e6a8 100644 --- a/assets/languages/en/Main.xml +++ b/assets/languages/en/Main.xml @@ -45,13 +45,13 @@ - Resume Restart Song Change Controls Change Options Exit to menu - Exit to charter --> + Exit to charter Blue balled: {0} diff --git a/assets/languages/en/Options.xml b/assets/languages/en/Options.xml new file mode 100644 index 00000000..37d21ac7 --- /dev/null +++ b/assets/languages/en/Options.xml @@ -0,0 +1,115 @@ + + + + Options Menu + Select a category to continue. + + Controls + Change Controls for Player 1 and Player 2! + + Gameplay > + Change Gameplay options such as Downscroll, Scroll Speed, Naughtyness... + + Appearance > + Change Appearance options such as Flashing menus... + + Miscellaneous > + Use this menu to reset save data or engine settings. + + + + + + Framerate + Pretty self explanatory, isn't it? + + Antialiasing + If unchecked, will disable antialiasing on every sprite. Can boost performances at the cost of sharper, more pixely sprites. + + Colored Healthbar + If unchecked, the game will use the orginal red/green health bar from the week 6 fnf game. + + Pixel Perfect Effect + If checked, Week 6 will have a pixel perfect effect to it enabled, aligning every pixel on the screen. + + Gameplay Shaders + If unchecked, gameplay shaders (visual effects like Thorns's Chromatic Aberration) wont be loaded; this may be helpful on weak devices. + + Flashing Menu + If unchecked, will disable menu flashing when you select an option in the Main Menu, and other flashes will be slower. + + Low Memory Mode + If checked, will disable certain background elements in stages to reduce memory usage. + + VRAM-Only Sprites + If checked, will only store the bitmaps in the GPU, freeing a LOT of memory (EXPERIMENTAL). Turning this off will consume a lot of memory, especially on bigger sprites. If you aren't sure, leave this on. + + Auto Pause + If checked, switching windows will pause the game. + + + + Downscroll + If checked, notes will go from up to down instead of down to up, as if they're falling. + + Ghost Tapping + If unchecked, trying to hit any strum that have no note that can be hit will cause a miss. + + Song Offset + Changes the offset that songs should start with. + + Naughtyness + If unchecked, will censor the Week 7 cutscenes. + + Camera Zoom on Beat + If unchecked, will stop the camera from zooming in every 4 beats + + + + Language + The language that the engine currently uses (the default one is {0}). + + Force Crash + Select this option to force a crash. + + Enable Nightly Updates + If checked, will also include nightly builds in the update checking. + + Check for Updates + Select this option to check for new engine updates. + No update found. + + Reset Save Data + Select this option to reset save data. This will remove all of your highscores. + + + + Debug Options + Use this menu to enable or disable debug options. + + Show Console + Select this to show the debug console, which contains log information about the game. + + Editor SFXs + If checked, will play sound effects when working on editors (ex: will play sfxs when checking checkboxes...) + + Pretty Print + If checked, the saved files from the editor will be formatted to be easily viewable (does not apply to xmls...) + + Intensive Blur + If checked, will use more intensive blur that may be laggier but look better. + + Editor Autosaves + If checked, this will autosave your files in the editor, with the settings listed below. + + Autosaving Time + This controls how often the editor will autosave your file (in seconds...) + + Save Warning Time + This controls how long the editor will warn you before it autosaves (in seconds..., 0 to disable) + + Autosaves Folder + If checked, this will autosave your file in a seperate folder with a time stamp instead of overriding your current file. (song/autosaves/) + + + \ No newline at end of file diff --git a/assets/languages/en/config.ini b/assets/languages/en/config.ini index c70c3efc..80a92684 100644 --- a/assets/languages/en/config.ini +++ b/assets/languages/en/config.ini @@ -1,3 +1,7 @@ name="English" -credits="Codename Engine" -version="0.0.1" \ No newline at end of file +credits="Codename Engine Devs" +version="0.0.1" + +; Shows missing ID:s as {id} and prints in the console Missing Translation IDs: id +; otherwhise uses the other languages (based on the chosen order) for the missing translations +showMissingIds="true" \ No newline at end of file diff --git a/source/funkin/backend/utils/TranslationUtil.hx b/source/funkin/backend/utils/TranslationUtil.hx index c7ca0f95..b8ba5080 100644 --- a/source/funkin/backend/utils/TranslationUtil.hx +++ b/source/funkin/backend/utils/TranslationUtil.hx @@ -61,6 +61,8 @@ final class TranslationUtil */ public static var foundLanguages:Array = []; + public static var showMissingIds:Bool = false; + // Private private static inline var LANG_FOLDER:String = "languages"; private static var langConfigs:Map = []; @@ -98,6 +100,8 @@ final class TranslationUtil config = getConfig(name); stringMap = loadLanguage(name); + + showMissingIds = config.get("showMissingIds").getDefault("false") == "true"; #end } @@ -115,13 +119,31 @@ final class TranslationUtil public static inline function translateDiff(?id:String, ?params:Array):String 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 + public static function getUnformatted(id:String, ?def:String):IFormatInfo { #if TRANSLATIONS_SUPPORT if (stringMap.exists(id)) return stringMap.get(id); #end - if(def != null) id = def; - return FormatUtil.get(id); + + if(def != null) + return FormatUtil.get(def); + + return FormatUtil.getStr("{" + id + "}"); + + /*if(curLanguage == DEFAULT_LANGUAGE) { + return FormatUtil.getStr("{" + id + "}"); + } else { + + } + + return FormatUtil.get(showMissingIds ? "{" + id + "}" : id);*/ } /** @@ -187,13 +209,16 @@ final class TranslationUtil FormatUtil.clear(); // Clean up the format cache var mainPath:String = translationsMain(lang); var leMap:Map = []; - var translations = []; - function parseXml(xml:Access) { + var translations:Array = []; + function parseXml(xml:Access, prefix:String = "") { for(node in xml.elements) { if (node.name == "group") // Cosmetic name - parseXml(node); + parseXml(node, prefix + (node.has.prefix ? node.att.prefix : "")); else if(["text", "trans", "lang", "string", "str"].contains(node.name)) - translations.push(node); + translations.push({ + prefix: prefix, + node: node + }); } } @@ -220,18 +245,22 @@ final class TranslationUtil parseXml(langNode); } - for(node in translations) { + for(pair in translations) { + var node = pair.node; if (!node.has.id) { Logs.warn('A <${node.name}> node requires an ID attribute.', "Language"); continue; } + var prefix = pair.prefix; - if(leMap.exists(node.att.id)) continue; + var id = prefix + node.att.id; + + if(leMap.exists(id)) continue; var value:String = node.has.string ? node.att.string : node.innerData; if(node.getAtt("notrim").getDefault("true") != "true") value = value.trim(); value = value.replace("\\n", "\n").replace("\r", ""); // remove stupid windows line breaks and convert newline literals to newlines - leMap.set(node.att.id, FormatUtil.get(value)); - //Logs.trace("Added " + node.att.id + " -> `" + value + "`", "Language"); + leMap.set(id, FormatUtil.get(value)); + //Logs.trace("Added " + id + " -> `" + value + "`", "Language"); } return leMap; @@ -288,4 +317,10 @@ final class TranslationUtil @:noCompletion private static function get_isLanguageLoaded():Bool return Lambda.count(stringMap) > 0; +} + +@:structInit +class TranslationPair { + public var prefix:String; + public var node:Access; } \ No newline at end of file diff --git a/source/funkin/backend/utils/translations/FormatUtil.hx b/source/funkin/backend/utils/translations/FormatUtil.hx index 5d1a0b09..9c22d4e7 100644 --- a/source/funkin/backend/utils/translations/FormatUtil.hx +++ b/source/funkin/backend/utils/translations/FormatUtil.hx @@ -7,6 +7,7 @@ package funkin.backend.utils.translations; */ class FormatUtil { private static var cache:Map = new Map(); + private static var cacheStr:Map = new Map(); public static function get(id:String):IFormatInfo { if (cache.exists(id)) @@ -18,8 +19,18 @@ class FormatUtil { return fi; } + public static function getStr(id:String):IFormatInfo { + if (cacheStr.exists(id)) + return cacheStr.get(id); + + var fi:IFormatInfo = new StrFormatInfo(id); + cacheStr.set(id, fi); + return fi; + } + public inline static function clear() { cache.clear(); + cacheStr.clear(); } } diff --git a/source/funkin/editors/DebugOptions.hx b/source/funkin/editors/DebugOptions.hx index 7f5201b9..b6054b22 100644 --- a/source/funkin/editors/DebugOptions.hx +++ b/source/funkin/editors/DebugOptions.hx @@ -26,46 +26,47 @@ class DebugOptions extends TreeMenu { class DebugOptionsScreen extends OptionsScreen { public override function new() { - super("Debug Options", "Use this menu to change debug options."); + prefix = "DebugOptions"; + super(translate("title"), translate("desc"), prefix); #if windows add(new TextOption( - "Show Console", - "Select this to show the debug console, which contains log information about the game.", + getName("showConsole"), + getDesc("showConsole"), function() { NativeAPI.allocConsole(); })); #end add(new Checkbox( - "Editor SFXs", - "If checked, will play sound effects when working on editors (ex: will play sfxs when checking checkboxes...)", + getName("editorSFX"), + getDesc("editorSFX"), "editorSFX")); add(new Checkbox( - "Pretty Print", - "If checked, the saved files from the editor will be formatted to be easily viewable (does not apply to xmls...)", + getName("editorPrettyPrint"), + getDesc("editorPrettyPrint"), "editorPrettyPrint")); add(new Checkbox( - "Intensive Blur", - "If checked, will use more intensive blur that may be laggier but look better.", + getName("intensiveBlur"), + getDesc("intensiveBlur"), "intensiveBlur")); add(new Checkbox( - "Editor Autosaves", - "If checked, this will autosave your files in the editor, with the settings listed below.", + getName("charterAutoSaves"), + getDesc("charterAutoSaves"), "charterAutoSaves")); add(new NumOption( - "Autosaving Time", - "This controls how often the editor will autosave your file (in seconds...)", + getName("charterAutoSaveTime"), + getDesc("charterAutoSaveTime"), 60, 60*10, 30, "charterAutoSaveTime" )); add(new NumOption( - "Save Warning Time", - "This controls how long the editor will warn you before it autosaves (in seconds..., 0 to disable)", + getName("charterAutoSaveWarningTime"), + getDesc("charterAutoSaveWarningTime"), 0, 15, 1, "charterAutoSaveWarningTime" )); add(new Checkbox( - "Autosaves Folder", - "If checked, this will autosave your file in a seperate folder with a time stamp instead of overriding your current file. (song/autosaves/)", + getName("charterAutoSavesSeperateFolder"), + getDesc("charterAutoSavesSeperateFolder"), "charterAutoSavesSeperateFolder")); } } \ No newline at end of file diff --git a/source/funkin/editors/EditorPicker.hx b/source/funkin/editors/EditorPicker.hx index 8a2ebd89..6a877a99 100644 --- a/source/funkin/editors/EditorPicker.hx +++ b/source/funkin/editors/EditorPicker.hx @@ -9,26 +9,33 @@ class EditorPicker extends MusicBeatSubstate { public var options:Array = [ { name: "Chart Editor", + id: "charter", iconID: 0, state: funkin.editors.charter.CharterSelection }, { name: "Character Editor", + id: "character", iconID: 1, state: funkin.editors.character.CharacterSelection }, { name: "Stage Editor", + id: "stage", iconID: 2, state: null }, + #if (debug || debug_ui) { name: "UI Debug State", + id: "ui-debug", iconID: 3, state: UIDebugState }, + #end { name: "Debug Options", + id: "debug-options", iconID: 4, state: DebugOptions } @@ -143,6 +150,7 @@ class EditorPicker extends MusicBeatSubstate { typedef Editor = { var name:String; + var id:String; var iconID:Int; var state:Class; } diff --git a/source/funkin/options/OptionsMenu.hx b/source/funkin/options/OptionsMenu.hx index af0e8c12..285b54aa 100644 --- a/source/funkin/options/OptionsMenu.hx +++ b/source/funkin/options/OptionsMenu.hx @@ -9,25 +9,25 @@ import haxe.xml.Access; class OptionsMenu extends TreeMenu { public static var mainOptions:Array = [ - { - name: 'Controls', - desc: 'Change Controls for Player 1 and Player 2!', + { // name and desc are actually the translations ids! - Nex + name: 'optionsTree.controls-name', + desc: 'optionsTree.controls-desc', state: null, substate: funkin.options.keybinds.KeybindsOptions }, { - name: 'Gameplay >', - desc: 'Change Gameplay options such as Downscroll, Scroll Speed, Naughtyness...', + name: 'optionsTree.gameplay-name', + desc: 'optionsTree.gameplay-desc', state: GameplayOptions }, { - name: 'Appearance >', - desc: 'Change Appearance options such as Flashing menus...', + name: 'optionsTree.appearance-name', + desc: 'optionsTree.appearance-desc', state: AppearanceOptions }, { - name: 'Miscellaneous >', - desc: 'Use this menu to reset save data or engine settings.', + name: 'optionsTree.miscellaneous-name', + desc: 'optionsTree.miscellaneous-desc', state: MiscOptions } ]; @@ -48,23 +48,29 @@ class OptionsMenu extends TreeMenu { bg.antialiasing = true; add(bg); - main = new OptionsScreen("Options", "Select a category to continue.", [for(o in mainOptions) new TextOption(o.name, o.desc, function() { - if (o.substate != null) { - persistentUpdate = false; - persistentDraw = true; - if (o.substate is MusicBeatSubstate) { - openSubState(o.substate); + main = new OptionsScreen(TU.translate("optionsMenu.header.title"), TU.translate("optionsMenu.header.desc"), [for(o in mainOptions) { + var name = o.name; + var desc = o.desc; + if(TU.exists(name)) name = TU.translate(name); + if(TU.exists(desc)) desc = TU.translate(desc); + new TextOption(name, desc, function() { + if (o.substate != null) { + persistentUpdate = false; + persistentDraw = true; + if (o.substate is MusicBeatSubstate) { + openSubState(o.substate); + } else { + openSubState(Type.createInstance(o.substate, [name, desc])); + } } else { - openSubState(Type.createInstance(o.substate, [])); + if (o.state is OptionsScreen) { + optionsTree.add(o.state); + } else { + optionsTree.add(Type.createInstance(o.state, [name, desc])); + } } - } else { - if (o.state is OptionsScreen) { - optionsTree.add(o.state); - } else { - optionsTree.add(Type.createInstance(o.state, [])); - } - } - })]); + }); + }]); var xmlPath = Paths.xml("config/options"); for(source in [funkin.backend.assets.AssetSource.SOURCE, funkin.backend.assets.AssetSource.MODS]) { diff --git a/source/funkin/options/OptionsScreen.hx b/source/funkin/options/OptionsScreen.hx index 45580821..89ef09d6 100644 --- a/source/funkin/options/OptionsScreen.hx +++ b/source/funkin/options/OptionsScreen.hx @@ -14,11 +14,23 @@ class OptionsScreen extends FlxTypedSpriteGroup { public var name:String; public var desc:String; + public var prefix:String = ""; - public function new(name:String, desc:String, ?options:Array) { + public function getName(name:String, ?args:Array):String { + return TU.translate(prefix + name + "-name", args); + } + public function getDesc(name:String, ?args:Array):String { + return TU.translate(prefix + name + "-desc", args); + } + public function translate(name:String, ?args:Array):String { + return TU.translate(prefix + name, args); + } + + public function new(name:String, desc:String, ?prefix:String, ?options:Array) { super(); this.name = name; this.desc = desc; + if (prefix != null) this.prefix = prefix; if (options != null) for(o in options) add(o); } diff --git a/source/funkin/options/categories/AppearanceOptions.hx b/source/funkin/options/categories/AppearanceOptions.hx index 92621343..e673502b 100644 --- a/source/funkin/options/categories/AppearanceOptions.hx +++ b/source/funkin/options/categories/AppearanceOptions.hx @@ -1,51 +1,51 @@ package funkin.options.categories; class AppearanceOptions extends OptionsScreen { - public override function new() { - super("Appearance", "Change Appearance options such as Flashing menus..."); + public override function new(title:String, desc:String) { + super(title, desc, "AppearanceOptions"); add(new NumOption( - "Framerate", - "Pretty self explanatory, isn't it?", + getName("framerate"), + getDesc("framerate"), 30, // minimum 240, // maximum 10, // change "framerate", // save name or smth __changeFPS)); // callback add(new Checkbox( - "Antialiasing", - "If unchecked, will disable antialiasing on every sprite. Can boost performances at the cost of sharper, more pixely sprites.", + getName("antialiasing"), + getDesc("antialiasing"), "antialiasing")); add(new Checkbox( - "Colored Healthbar", - "If unchecked, the game will use the orginal red/green health bar from the week 6 fnf game.", + getName("colorHealthBar"), + getDesc("colorHealthBar"), "colorHealthBar")); add(new Checkbox( - "Pixel Perfect Effect", - "If checked, Week 6 will have a pixel perfect effect to it enabled, aligning every pixel on the screen.", + getName("week6PixelPerfect"), + getDesc("week6PixelPerfect"), "week6PixelPerfect")); add(new Checkbox( - "Gameplay Shaders", - "If unchecked, gameplay shaders (visual effects like Thorns's Chromatic Aberration) wont be loaded; this may be helpful on weak devices.", + getName("gameplayShaders"), + getDesc("gameplayShaders"), "gameplayShaders")); add(new Checkbox( - "Flashing Menu", - "If unchecked, will disable menu flashing when you select an option in the Main Menu, and other flashes will be slower.", + getName("flashingMenu"), + getDesc("flashingMenu"), "flashingMenu")); add(new Checkbox( - "Low Memory Mode", - "If checked, will disable certain background elements in stages to reduce memory usage.", + getName("lowMemoryMode"), + getDesc("lowMemoryMode"), "lowMemoryMode")); #if sys if (!Main.forceGPUOnlyBitmapsOff) { add(new Checkbox( - "VRAM-Only Sprites", - "If checked, will only store the bitmaps in the GPU, freeing a LOT of memory (EXPERIMENTAL). Turning this off will consume a lot of memory, especially on bigger sprites. If you aren't sure, leave this on.", + getName("gpuOnlyBitmaps"), + getDesc("gpuOnlyBitmaps"), "gpuOnlyBitmaps")); } #end add(new Checkbox( - "Auto Pause", - "If checked, switching windows will pause the game.", + getName("autoPause"), + getDesc("autoPause"), "autoPause")); } diff --git a/source/funkin/options/categories/GameplayOptions.hx b/source/funkin/options/categories/GameplayOptions.hx index 15c0ba60..16cbb02a 100644 --- a/source/funkin/options/categories/GameplayOptions.hx +++ b/source/funkin/options/categories/GameplayOptions.hx @@ -8,31 +8,31 @@ class GameplayOptions extends OptionsScreen { var offsetSetting:NumOption; - public override function new() { - super("Gameplay", 'Change Gameplay options such as Downscroll, Scroll Speed, Naughtyness...'); + public override function new(title:String, desc:String) { + super(title, desc, "GameplayOptions"); add(new Checkbox( - "Downscroll", - "If checked, notes will go from up to down instead of down to up, as if they're falling.", + getName("downscroll"), + getDesc("downscroll"), "downscroll")); add(new Checkbox( - "Ghost Tapping", - "If unchecked, trying to hit any strum that have no note that can be hit will cause a miss.", + getName("ghostTapping"), + getDesc("ghostTapping"), "ghostTapping")); add(offsetSetting = new NumOption( - "Song Offset", - "Changes the offset that songs should start with.", + getName("songOffset"), + getDesc("songOffset"), -999, // minimum 999, // maximum 1, // change "songOffset", // save name or smth __changeOffset)); // callback add(new Checkbox( - "Naughtyness", - "If unchecked, will censor the Week 7 cutscenes.", + getName("naughtyness"), + getDesc("naughtyness"), "naughtyness")); add(new Checkbox( - "Camera Zoom on Beat", - "If unchecked, will stop the camera from zooming in every 4 beats", + getName("camZoomOnBeat"), + getDesc("camZoomOnBeat"), "camZoomOnBeat")); } diff --git a/source/funkin/options/categories/MiscOptions.hx b/source/funkin/options/categories/MiscOptions.hx index 37078070..3c92893f 100644 --- a/source/funkin/options/categories/MiscOptions.hx +++ b/source/funkin/options/categories/MiscOptions.hx @@ -2,54 +2,55 @@ package funkin.options.categories; class MiscOptions extends OptionsScreen { - public override function new() { - super("Miscellaneous", "Use this menu to reset save data or engine settings."); + public override function new(title:String, desc:String) { + super(title, desc, "MiscOptions"); { // Language Option var lanArray:Array = TranslationUtil.foundLanguages; // TODO: add credits based on the config file add(new ArrayOption( - "Language", - 'The language that the engine currently uses (the default one is ${TranslationUtil.DEFAULT_LANGUAGE}).', + getName("language"), + getDesc("language", [TranslationUtil.DEFAULT_LANGUAGE]), [for(lan in lanArray) lan.split("/").first()], - [for(lan in lanArray) lan.split("/").last()], "language", + [for(lan in lanArray) lan.split("/").last()], + "language", function(path:String) { TranslationUtil.setLanguage(path); parent.remove(this); // Reload the current screen // todo add parent.reload(); this.clear(); - parent.add(new MiscOptions()); + parent.add(new MiscOptions(title, desc)); })); } add(new TextOption( - "Force Crash", - "Select this option to force a crash.", + getName("forceCrash"), + getDesc("forceCrash"), () -> { throw new haxe.Exception("Forced crash."); } )); #if UPDATE_CHECKING add(new Checkbox( - "Enable Nightly Updates", - "If checked, will also include nightly builds in the update checking.", - "betaUpdates"));, + getName("betaUpdates"), + getDesc("betaUpdates"), + "betaUpdates")); add(new TextOption( - "Check for Updates", - "Select this option to check for new engine updates.", + getName("checkForUpdates"), + getDesc("checkForUpdates"), function() { var report = funkin.backend.system.updating.UpdateUtil.checkForUpdates(); if (report.newUpdate) { FlxG.switchState(new funkin.backend.system.updating.UpdateAvailableScreen(report)); } else { CoolUtil.playMenuSFX(CANCEL); - updateDescText("No update found."); + updateDescText(TU.translate(prefix + "checkForUpdates-noUpdateFound")); } - }));, + })); #end add(new TextOption( - "Reset Save Data", - "Select this option to reset save data. This will remove all of your highscores.", + getName("resetSaveData"), + getDesc("resetSaveData"), function() { // TODO: SAVE DATA RESETTING }));