From 8f2efa6e278f711da03372c247cf21bb928dec9b Mon Sep 17 00:00:00 2001 From: Psychpsyo Date: Tue, 2 Aug 2022 14:46:19 +0200 Subject: [PATCH] Added localization support Currently it doesn't update when the language is switched during gameplay. --- DefaultTooltips.cs | 276 ++++++++++++++++++++++------------------- DefaultTooltips.csproj | 41 ++++++ app.config | 15 +++ locales/en.json | 127 +++++++++++++++++++ packages.config | 12 ++ 5 files changed, 340 insertions(+), 131 deletions(-) create mode 100644 app.config create mode 100644 locales/en.json create mode 100644 packages.config diff --git a/DefaultTooltips.cs b/DefaultTooltips.cs index 49d9bf9..beb4daf 100644 --- a/DefaultTooltips.cs +++ b/DefaultTooltips.cs @@ -1,8 +1,8 @@ using FrooxEngine; using FrooxEngine.UIX; -using HarmonyLib; using NeosModLoader; using System.Collections.Generic; +using System.Text.Json; namespace DefaultTooltips { @@ -15,158 +15,173 @@ public class DefaultTooltips : NeosMod private static Dictionary createNewLabelDict = new Dictionary() { - {"", "Return to the previous menu."}, - {"/0", "Creates a new, empty slot."}, - {"/1", "Creates a new particle system."}, - {"/3DModel", "3D Models such as cubes, spheres and cylinders."}, - {"/3DModel/0", "Creates a new, resizeable box."}, - {"/3DModel/1", "Creates a new capsule. (pill)"}, - {"/3DModel/2", "Creates a new 3D cone."}, - {"/3DModel/3", "Creates a new cylinder."}, - {"/3DModel/4", "Creates a new, one-sided grid mesh."}, - {"/3DModel/5", "Creates a new, one-sided quad mesh."}, - {"/3DModel/6", "Creates a new, resizeable sphere."}, - {"/3DModel/7", "Creates a new torus. (donut)"}, - {"/3DModel/8", "Creates a new, one-sided triangle mesh."}, - {"/Collider", "Various types and shapes of colliders."}, - {"/Collider/0", "Creates a new box collider."}, - {"/Collider/1", "Creates a new capsule collider."}, - {"/Collider/2", "Creates a new cone collider."}, - {"/Collider/3", "Creates a new cylinder collider."}, - {"/Collider/4", "Creates a new mesh collider."}, - {"/Collider/5", "Creates a new sphere collider."}, - {"/Editor", "Various editing and optimization tools."}, - {"/Editor/0", "Creates a new asset optmization wizard."}, //TODO: Write what this is used for on a new line here - {"/Editor/1", "Creates a new cubemap creator.\nThis can be used to turn 6 separate images into a cubemap."}, - {"/Editor/2", "Creates a new world light sources wizard."}, //TODO: Write what this is used for on a new line here - {"/Editor/3", "Creates a new LogiX transfer wizard."}, //TODO: Write what this is used for on a new line here - {"/Editor/4", "Creates a new reflection probe wizard."}, //TODO: Write what this is used for on a new line here - {"/Editor/5", "Creates a new world text renderer wizard."}, //TODO: Write what this is used for on a new line here - {"/Editor/6", "Creates a new user inspector.\nThis can be used inspect users and access detailed information about them."}, - {"/Light", "Directional, point and spot lights."}, - {"/Light/0", "Creates a new, directional light source."}, - {"/Light/1", "Creates a new light source that shines in every direction."}, - {"/Light/2", "Creates a new spot light."}, - {"/Materials", "Different materials (shaders) to apply to 3D objects."}, - {"/Object", "Various pre-setup objects such as UI Elements, Mirrors, Cameras..."}, - {"/Text", "Floating Text."}, - {"/Text/0", "Creates a new piece of floating text with no outline."}, - {"/Text/1", "Creates a new piece of floating text with outlines on the letters."} + {"", "general.back"}, + {"/0", "createNew.emptyObject"}, + {"/1", "createNew.particleSystem"}, + {"/3DModel", "createNew.3DModelMenu"}, + {"/3DModel/0", "createNew.3DModel.box"}, + {"/3DModel/1", "createNew.3DModel.capsule"}, + {"/3DModel/2", "createNew.3DModel.cone"}, + {"/3DModel/3", "createNew.3DModel.cylinder"}, + {"/3DModel/4", "createNew.3DModel.grid"}, + {"/3DModel/5", "createNew.3DModel.quad"}, + {"/3DModel/6", "createNew.3DModel.sphere"}, + {"/3DModel/7", "createNew.3DModel.torus"}, + {"/3DModel/8", "createNew.3DModel.triangle"}, + {"/Collider", "createNew.colliderMenu"}, + {"/Collider/0", "createNew.collider.box"}, + {"/Collider/1", "createNew.collider.capsule"}, + {"/Collider/2", "createNew.collider.cone"}, + {"/Collider/3", "createNew.collider.cylinder"}, + {"/Collider/4", "createNew.collider.mesh"}, + {"/Collider/5", "createNew.collider.sphere"}, + {"/Editor", "createNew.editorMenu"}, + {"/Editor/0", "createNew.editor.assetOptimizationWizard"}, + {"/Editor/1", "createNew.editor.cubemapCreator"}, + {"/Editor/2", "createNew.editor.worldLightSourcesWizard"}, + {"/Editor/3", "createNew.editor.logixTransferWizard"}, + {"/Editor/4", "createNew.editor.reflectionProbeWizard"}, + {"/Editor/5", "createNew.editor.worldTextRendererWizard"}, + {"/Editor/6", "createNew.editor.userInspector"}, + {"/Light", "createNew.lightMenu"}, + {"/Light/0", "createNew.light.directional"}, + {"/Light/1", "createNew.light.point"}, + {"/Light/2", "createNew.light.spot"}, + {"/Materials", "createNew.materialMenu"}, + {"/Object", "createNew.objectMenu"}, + {"/Text", "createNew.textMenu"}, + {"/Text/0", "createNew.text.Basic"}, + {"/Text/1", "createNew.text.Outline"} }; private static Dictionary inspectorLabelDict = new Dictionary() { - {"OnObjectRootPressed", "Go up to the next object root in the hierarchy."}, - {"OnRootUpPressed", "Go up one slot in the hierarchy."}, - {"OnDestroyPressed", "Destroys the selected slot and related assets."}, - {"OnDestroyPreservingAssetsPressed", "Destroys the selected slot while preserving related assets."}, - {"OnInsertParentPressed", "Inserts a new parent above the selected slot."}, - {"OnAddChildPressed", "Appends a new child to the selected slot."}, - {"OnDuplicatePressed", "Duplicates the selected slot."}, - {"OnSetRootPressed", "Focuses the hierarchy onto the selected slot."}, - {"OnAttachComponentPressed", "Attach a component to the selected slot."} + {"OnObjectRootPressed", "inspector.objectRoot"}, + {"OnRootUpPressed", "inspector.hierarchyUp"}, + {"OnDestroyPressed", "inspector.destroySlot"}, + {"OnDestroyPreservingAssetsPressed", "inspector.destroyPreserveAssets"}, + {"OnInsertParentPressed", "inspector.insertParent"}, + {"OnAddChildPressed", "inspector.addChild"}, + {"OnDuplicatePressed", "inspector.duplicateSlot"}, + {"OnSetRootPressed", "inspector.focusHierarchyHere"}, + {"OnAttachComponentPressed", "inspector.attachComponent"} }; private static Dictionary inventoryLabelDict = new Dictionary() { - {"ShowInventoryOwners", "Go to group inventories."}, - {"GenerateLink", "Spawn selected folder.\n(This will make it public.)"}, - {"MakePrivate", "Make selected folder private again."}, - {"DeleteItem", "Delete selected item.\n(double click)"}, - {"AddCurrentAvatar", "Save your equipped avatar to this folder."}, - {"AddNew", "Create a new folder or save your currently held item."}, - {"OnOpenWorld", "Open as private world."}, - {"OnEquipAvatar", "Equip selected avatar."}, - {"OnSetDefaultHome", "Set as your default home world."}, - {"OnSetDefaultAvatar", "Set as your default avatar."}, - {"OnSetDefaultKeyboard", "Set as your default keyboard."}, - {"OnSetDefaultCamera", "Set as your default camera."}, - {"OnSpawnFacet", "Spawn selected facet in local space.\n(Facets don't work properly in desktop mode)"} + {"ShowInventoryOwners", "inventory.groups"}, + {"GenerateLink", "inventory.spawnFolder"}, + {"MakePrivate", "inventory.makePrivate"}, + {"DeleteItem", "inventory.delete"}, + {"AddCurrentAvatar", "inventory.saveAvatar"}, + {"AddNew", "inventory.addNew"}, + {"OnOpenWorld", "inventory.openWorld"}, + {"OnEquipAvatar", "inventory.equipAvatar"}, + {"OnSetDefaultHome", "inventory.favHome"}, + {"OnSetDefaultAvatar", "inventory.favAvatar"}, + {"OnSetDefaultKeyboard", "inventory.favKeyboard"}, + {"OnSetDefaultCamera", "inventory.favCamera"}, + {"OnSpawnFacet", "inventory.spawnFacet"} }; private static Dictionary voiceFacetLabelDict = new Dictionary() { - {VoiceMode.Whisper, "Open whisper bubble."}, - {VoiceMode.Normal, "Set your voice to 'normal'."}, - {VoiceMode.Shout, "Set your voice to 'shout'."}, - {VoiceMode.Broadcast, "Set your voice to 'broadcast'."}, + {VoiceMode.Whisper, "voiceModes.whisper"}, + {VoiceMode.Normal, "voiceModes.normal"}, + {VoiceMode.Shout, "voiceModes.shout"}, + {VoiceMode.Broadcast, "voiceModes.broadcast"}, }; private static Dictionary onlineStatusFacetLabelDict = new Dictionary() { - {CloudX.Shared.OnlineStatus.Online, "Set your status to 'Online'."}, - {CloudX.Shared.OnlineStatus.Away, "Set your status to 'Away'."}, - {CloudX.Shared.OnlineStatus.Busy, "Set your status to 'Busy'."}, - {CloudX.Shared.OnlineStatus.Invisible, "Set your status to 'Invisible'. (You will appear as offline to others.)"}, + {CloudX.Shared.OnlineStatus.Online, "onlineStatus.online"}, + {CloudX.Shared.OnlineStatus.Away, "onlineStatus.away"}, + {CloudX.Shared.OnlineStatus.Busy, "onlineStatus.busy"}, + {CloudX.Shared.OnlineStatus.Invisible, "onlineStatus.invisible"}, }; private static Dictionary fileBrowserLabelDict = new Dictionary() { - {"RunImport", "Import selected file or folder."}, - {"RunRawImport", "Import selected file as a raw file."}, - {"CreateNew", "Create a new Folder or export held item."}, - {"Reload", "Refresh the current view."} + {"RunImport", "fileBrowser.import"}, + {"RunRawImport", "fileBrowser.importRaw"}, + {"CreateNew", "fileBrowser.addNew"}, + {"Reload", "fileBrowser.refresh"} }; private static Dictionary imageImportLabelDict = new Dictionary() { - {"Preset_Image", "Import as regular image."}, - {"Preset_Screenshot", "Import as Neos screenshot with metadata."}, - {"Preset_360", "Import as a 360° photo."}, - {"Preset_StereoImage", "Sterep (3D) image import options..."}, - {"Preset_Stereo360", "360° Stereo (3D) image import options..."}, - {"Preset_180", "Import as a 180° photo."}, - {"Preset_Stereo180", "180° Stereo (3D) image import options..."}, - {"Preset_LUT", "Import as a LUT.\n(A LUT is a color lookup table)"}, - {"AsRawFile", "Import as a raw file."}, - {"Return", "Return to the previous menu."}, - {"Preset_HorizontalLR", "Import as side-by-side left-right 3D picture."}, - {"Preset_HorizontalRL", "Import as side-by-side right-left 3D picture."}, - {"Preset_VerticalLR", "Import as top-to-bottom left-right 3D picture."}, - {"Preset_VerticalRL", "Import as top-to-bottom right-left 3D picture."} + {"Return", "general.back"}, + {"Preset_Image", "imageImport.image"}, + {"Preset_Screenshot", "imageImport.screenshot"}, + {"Preset_360", "imageImport.360"}, + {"Preset_StereoImage", "imageImport.stereo"}, + {"Preset_Stereo360", "imageImport.stereo360"}, + {"Preset_180", "imageImport.180"}, + {"Preset_Stereo180", "imageImport.stereo180"}, + {"Preset_LUT", "imageImport.lut"}, + {"AsRawFile", "imageImport.rawFile"}, + {"Preset_HorizontalLR", "imageImport.stereo.horizontalLR"}, + {"Preset_HorizontalRL", "imageImport.stereo.horizontalRL"}, + {"Preset_VerticalLR", "imageImport.stereo.verticalLR"}, + {"Preset_VerticalRL", "imageImport.stereo.verticalRL"} }; private static Dictionary videoImportLabelDict = new Dictionary() { - {"Preset_Video", "Import as regular video."}, - {"Preset_360", "Import as a 360° video."}, - {"Preset_StereoVideo", "Sterep (3D) video import options..."}, - {"Preset_Stereo360", "360° Stereo (3D) video import options..."}, - {"Preset_Depth", "Depth video import options..."}, - {"Preset_180", "Import as a 180° video."}, - {"Preset_Stereo180", "180° Stereo (3D) video import options..."}, - {"AsRawFile", "Import as a raw file."}, - {"Return", "Return to the previous menu."}, - {"Preset_HorizontalLR", "Import as side-by-side left-right 3D video."}, - {"Preset_HorizontalRL", "Import as side-by-side right-left 3D video."}, - {"Preset_VerticalLR", "Import as top-to-bottom left-right 3D video."}, - {"Preset_VerticalRL", "Import as top-to-bottom right-left 3D video."}, - // TODO: What are these four? - {"Preset_DepthDefault", ""}, - {"Preset_DepthPFCapture", ""}, - {"Preset_DepthPFCaptureHorizontal", ""}, - {"Preset_DepthHolofix", ""} + {"Return", "general.back"}, + {"Preset_Video", "videoImport.video"}, + {"Preset_360", "videoImport.360"}, + {"Preset_StereoVideo", "videoImport.stereo"}, + {"Preset_Stereo360", "videoImport.stereo360"}, + {"Preset_Depth", "videoImport.depth"}, + {"Preset_180", "videoImport.180"}, + {"Preset_Stereo180", "videoImport.stereo180"}, + {"AsRawFile", "videoImport.rawFile"}, + {"Preset_HorizontalLR", "videoImport.stereo.horizontalLR"}, + {"Preset_HorizontalRL", "videoImport.stereo.horizontalRL"}, + {"Preset_VerticalLR", "videoImport.stereo.verticalLR"}, + {"Preset_VerticalRL", "videoImport.stereo.verticalRL"}, + {"Preset_DepthDefault", "videoImport.depth.default"}, + {"Preset_DepthPFCapture", "videoImport.depth.PFCapture"}, + {"Preset_DepthPFCaptureHorizontal", "videoImport.depth.PFCaptureHorizontal"}, + {"Preset_DepthHolofix", "videoImport.depth.holofix"} }; private static Dictionary avatarCreatorLabelDict = new Dictionary() { - {"AlignHeadForward", "Perfectly aligns the headset's forward direction."}, - {"AlignHeadUp", "Perfectly aligns the headset's up direction."}, - {"AlignHeadRight", "Perfectly aligns the headset's right direction."}, - {"AlignHeadPosition", "Perfectly aligns the headset's position."}, - {"AlignHands", "Attempts to detect where the avatar's hands should be and positions them there."}, - {"AlignToolAnchors", "Attempts to set where the avatar's equipped tools should go."}, - {"OnCreate", "Turn the aligned model into an avatar."}, - {"_useSymmetry", "Automatically mirrors the left hand along the headset's position, according to where the right hand is."}, - {"_showAnchors", "Display tool anchors and grab spheres to align those manually."}, - {"_setupVolumeMeter", ""}, // TODO: add description for this - {"_setupEyes", ""}, // TODO: add description for this - {"_setupFaceTracking", "Sets up face tracking for the avatar for something like the Vive face traker."}, - {"_setupProtection", "Makes it so that no one except you can save the avatar.\nProtection also deletes the avatar if you spawn it into a world and leave."} + {"AlignHeadForward", "avatarCreator.alignHeadForward"}, + {"AlignHeadUp", "avatarCreator.alignHeadUp"}, + {"AlignHeadRight", "avatarCreator.alignHeadRight"}, + {"AlignHeadPosition", "avatarCreator.centerHeadPosition"}, + {"AlignHands", "avatarCreator.alignHands"}, + {"AlignToolAnchors", "avatarCreator.alignToolAnchors"}, + {"_useSymmetry", "avatarCreator.useSymmetry"}, + {"_showAnchors", "avatarCreator.showAnchors"}, + {"_setupVolumeMeter", "avatarCreator.setupVolumeMeter"}, + {"_setupEyes", "avatarCreator.setupEyes"}, + {"_setupFaceTracking", "avatarCreator.setupFaceTracking"}, + {"_setupProtection", "avatarCreator.protectAvatar"}, + {"OnCreate", "avatarCreator.create"} }; + private static Dictionary localeStrings; + public override void OnEngineInit() { + // load localized text + string language; + Settings.TryReadValue("Interface.Locale", out language); + try + { + localeStrings = JsonSerializer.Deserialize>(System.IO.File.ReadAllText(@"./nml_mods/defaultTooltips/locales/" + language + ".json")); + } + catch + { + localeStrings = JsonSerializer.Deserialize>(System.IO.File.ReadAllText(@"./nml_mods/defaultTooltips/locales/en.json")); + } + + + // add label providers to Tooltippery mod Tooltippery.Tooltippery.labelProviders.Add(inspectorLabels); Tooltippery.Tooltippery.labelProviders.Add(createNewLabels); Tooltippery.Tooltippery.labelProviders.Add(inventoryLabels); @@ -183,7 +198,7 @@ private static string createNewLabels(IButton button, ButtonEventData eventData) if (button.Slot.GetComponentInParents() == null) return null; string target = button.Slot.GetComponent>()?.Argument.Value; if (target == null) return null; - if (createNewLabelDict.TryGetValue(target, out target)) return target; + if (createNewLabelDict.TryGetValue(target, out target)) return localeStrings[target]; return null; } @@ -207,7 +222,7 @@ private static string inspectorLabels(IButton button, ButtonEventData eventData) if (buttonTarget.Value.target == inspector.ReferenceID) { string retVal; - if (inspectorLabelDict.TryGetValue(methodName, out retVal)) return retVal; + if (inspectorLabelDict.TryGetValue(methodName, out retVal)) return localeStrings[retVal]; } return null; @@ -236,7 +251,7 @@ private static string inventoryLabels(IButton button, ButtonEventData eventData) if (buttonTarget.Value.method == "OnGoUp") { string[] path = inventory.CurrentPath == "" ? new[] { "Inventory" } : ("Inventory\\" + inventory.CurrentPath).Split('\\'); - return "Go back to '" + path[path.Length - 1 - relay.Argument] + "'."; + return localeStrings["general.goBackTo"].Replace("{{FOLDER}}", path[path.Length - 1 - relay.Argument]); } else { @@ -251,7 +266,7 @@ private static string inventoryLabels(IButton button, ButtonEventData eventData) if (buttonTarget.Value.target == inventory.ReferenceID) { string retVal; - if (inventoryLabelDict.TryGetValue(methodName, out retVal)) return retVal; + if (inventoryLabelDict.TryGetValue(methodName, out retVal)) return localeStrings[retVal]; } return null; @@ -266,13 +281,12 @@ private static string voiceFacetLabels(IButton button, ButtonEventData eventData IField targetToggle = button.Slot.GetComponent()?.TargetValue.Target; if (targetToggle?.Name == "GlobalMute") { - return targetToggle.Value ? "Unmute your mic." : "Mute your mic."; + return targetToggle.Value ? localeStrings["voiceModes.unmute"] : localeStrings["voiceModes.mute"]; } + return null; } - string retVal = null; - voiceFacetLabelDict.TryGetValue(targetVoiceMode.Value, out retVal); - return retVal; + return localeStrings[voiceFacetLabelDict[targetVoiceMode.Value]]; } private static string onlineStatusFacetLabels(IButton button, ButtonEventData eventData) @@ -283,7 +297,7 @@ private static string onlineStatusFacetLabels(IButton button, ButtonEventData ev string retVal = null; onlineStatusFacetLabelDict.TryGetValue(targetStatus.Value, out retVal); - return retVal; + return localeStrings[retVal]; } private static string fileBrowserLabels(IButton button, ButtonEventData eventData) @@ -309,7 +323,7 @@ private static string fileBrowserLabels(IButton button, ButtonEventData eventDat if (buttonTarget.Value.method == "OnGoUp") { string[] path = fileBrowser.CurrentPath == null ? new[] { "Computer" } : ("Computer\\" + fileBrowser.CurrentPath).Split('\\'); - return "Go back to '" + path[path.Length - 1 - relay.Argument] + "'."; + return localeStrings["general.goBackTo"].Replace("{{FOLDER}}", path[path.Length - 1 - relay.Argument]); } else { @@ -324,7 +338,7 @@ private static string fileBrowserLabels(IButton button, ButtonEventData eventDat if (buttonTarget.Value.target == fileBrowser.ReferenceID) { string retVal; - if (fileBrowserLabelDict.TryGetValue(methodName, out retVal)) return retVal; + if (fileBrowserLabelDict.TryGetValue(methodName, out retVal)) return localeStrings[retVal]; } return null; @@ -337,7 +351,7 @@ private static string imageImportLabels(IButton button, ButtonEventData eventDat if (button.Slot.GetComponentInParents() == null) return null; if (((Button)button).Pressed?.Target == null) return null; string target = ((Button)button).Pressed.Value.method; - if (imageImportLabelDict.TryGetValue(target, out target)) return target; + if (imageImportLabelDict.TryGetValue(target, out target)) return localeStrings[target]; return null; } private static string videoImportLabels(IButton button, ButtonEventData eventData) @@ -350,7 +364,7 @@ private static string videoImportLabels(IButton button, ButtonEventData eventDat if (((Button)button).Pressed?.Target != null) target = ((Button)button).Pressed.Value.method; if (button.Slot.GetComponent() != null) target = button.Slot.GetComponent().ButtonPressed?.Value.method; if (target == null) return null; - if (videoImportLabelDict.TryGetValue(target, out target)) return target; + if (videoImportLabelDict.TryGetValue(target, out target)) return localeStrings[target]; return null; } private static string avatarCreatorLabels(IButton button, ButtonEventData eventData) @@ -364,7 +378,7 @@ private static string avatarCreatorLabels(IButton button, ButtonEventData eventD if (((Button)button).Pressed?.Target != null) target = ((Button)button).Pressed.Value.method; if (button.Slot.GetComponent() != null) target = button.Slot.GetComponent().ButtonPressed?.Value.method; if (target == null) return null; - if (avatarCreatorLabelDict.TryGetValue(target, out target)) return target; + if (avatarCreatorLabelDict.TryGetValue(target, out target)) return localeStrings[target]; return null; } } diff --git a/DefaultTooltips.csproj b/DefaultTooltips.csproj index 5e6e04f..a06445b 100644 --- a/DefaultTooltips.csproj +++ b/DefaultTooltips.csproj @@ -12,6 +12,8 @@ v4.6.2 512 true + + true @@ -44,11 +46,39 @@ False ..\..\..\Games\Neos\app\Neos_Data\Managed\FrooxEngine.dll + + packages\Microsoft.Bcl.AsyncInterfaces.6.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + ..\..\..\Games\Neos\app\Libraries\NeosModLoader.dll + + packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + packages\System.Text.Encodings.Web.6.0.0\lib\net461\System.Text.Encodings.Web.dll + + + packages\System.Text.Json.6.0.5\lib\net461\System.Text.Json.dll + + + packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll + @@ -63,5 +93,16 @@ + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + \ No newline at end of file diff --git a/app.config b/app.config new file mode 100644 index 0000000..c16b61c --- /dev/null +++ b/app.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/locales/en.json b/locales/en.json new file mode 100644 index 0000000..6cb376d --- /dev/null +++ b/locales/en.json @@ -0,0 +1,127 @@ +{ + "general.back": "Return to the previous menu.", + "general.goBackTo": "Go back to '{{FOLDER}}'.", + + "voiceModes.whisper": "Open whisper bubble.", + "voiceModes.normal": "Set your voice to 'normal'.", + "voiceModes.shout": "Set your voice to 'shout'.", + "voiceModes.broadcast": "Set your voice to 'broadcast'.", + "voiceModes.mute": "Mute your mic.", + "voiceModes.unmute": "Unmute your mic.", + + "onlineStatus.online": "Set your status to 'Online'.", + "onlineStatus.away": "Set your status to 'Away'.", + "onlineStatus.busy": "Set your status to 'Busy'.", + "onlineStatus.invisible": "Set your status to 'Invisible'. (You will appear as offline to others.)", + + "avatarCreator.alignHeadForward": "Perfectly aligns the headset's forward direction.", + "avatarCreator.alignHeadUp": "Perfectly aligns the headset's up direction.", + "avatarCreator.alignHeadRight": "Perfectly aligns the headset's right direction.", + "avatarCreator.centerHeadPosition": "Perfectly centers the headset's position.", + "avatarCreator.alignHands": "Attempts to detect where the avatar's hands should be and positions them there.", + "avatarCreator.alignToolAnchors": "Attempts to set where the avatar's equipped tools should go.", + "avatarCreator.useSymmetry": "Automatically mirrors the left hand along the headset's position, according to where the right hand is.", + "avatarCreator.showAnchors": "Display tool anchors and grab spheres to align those manually.", + "avatarCreator.setupVolumeMeter": "", + "avatarCreator.setupEyes": "", + "avatarCreator.setupFaceTracking": "Sets up face tracking for the avatar for something like the Vive face traker.", + "avatarCreator.protectAvatar": "Makes it so that no one except you can save the avatar.\nProtection also deletes the avatar if you spawn it into a world and leave.", + "avatarCreator.create": "Turn the aligned model into an avatar.", + + "inspector.objectRoot": "Go up to the next object root in the hierarchy.", + "inspector.hierarchyUp": "Go up one slot in the hierarchy.", + "inspector.destroySlot": "Destroys the selected slot and related assets.", + "inspector.destroyPreserveAssets": "Destroys the selected slot while preserving related assets.", + "inspector.insertParent": "Inserts a new parent above the selected slot.", + "inspector.addChild": "Appends a new child to the selected slot.", + "inspector.duplicateSlot": "Duplicates the selected slot.", + "inspector.focusHierarchyHere": "Focuses the hierarchy onto the selected slot.", + "inspector.attachComponent": "Attach a component to the selected slot.", + + "inventory.groups": "Go to group inventories.", + "inventory.spawnFolder": "Spawn selected folder.\n(This will make it public.)", + "inventory.spawnFacet": "Make selected folder private again.", + "inventory.makePrivate": "Delete selected item.\n(double click)", + "inventory.delete": "Save your equipped avatar to this folder.", + "inventory.saveAvatar": "Create a new folder or save your currently held item.", + "inventory.addNew": "Open as private world.", + "inventory.openWorld": "Equip selected avatar.", + "inventory.equipAvatar": "Set as your default home world.", + "inventory.favHome": "Set as your default avatar.", + "inventory.favAvatar": "Set as your default keyboard.", + "inventory.favKeyboard": "Set as your default camera.", + "inventory.favCamera": "Spawn selected facet in local space.\n(Facets don't work properly in desktop mode)", + + "fileBrowser.import": "Import selected file or folder.", + "fileBrowser.importRaw": "Import selected file as a raw file.", + "fileBrowser.addNew": "Create a new Folder or export held item.", + "fileBrowser.refresh": "Refresh the current view.", + + "imageImport.image": "Import as regular image.", + "imageImport.screenshot": "Import as Neos screenshot with metadata.", + "imageImport.360": "Import as a 360° photo.", + "imageImport.stereo": "Sterep (3D) image import options...", + "imageImport.stereo360": "360° Stereo (3D) image import options...", + "imageImport.180": "Import as a 180° photo.", + "imageImport.stereo180": "180° Stereo (3D) image import options...", + "imageImport.lut": "Import as a LUT.\n(A LUT is a color lookup table)", + "imageImport.rawFile": "Import as a raw file.", + "imageImport.stereo.horizontalLR": "Import as side-by-side left-right 3D picture.", + "imageImport.stereo.horizontalRL": "Import as side-by-side right-left 3D picture.", + "imageImport.stereo.verticalLR": "Import as top-to-bottom left-right 3D picture.", + "imageImport.stereo.verticalRL": "Import as top-to-bottom right-left 3D picture.", + + "videoImport.video": "Import as regular video.", + "videoImport.360": "Import as a 360° video.", + "videoImport.stereo": "Sterep (3D) video import options...", + "videoImport.stereo360": "360° Stereo (3D) video import options...", + "videoImport.depth": "Depth video import options...", + "videoImport.180": "Import as a 180° video.", + "videoImport.stereo180": "180° Stereo (3D) video import options...", + "videoImport.rawFile": "Import as a raw file.", + "videoImport.stereo.horizontalLR": "Import as side-by-side left-right 3D video.", + "videoImport.stereo.horizontalRL": "Import as side-by-side right-left 3D video.", + "videoImport.stereo.verticalLR": "Import as top-to-bottom left-right 3D video.", + "videoImport.stereo.verticalRL": "Import as top-to-bottom right-left 3D video.", + "videoImport.depth.default": "", + "videoImport.depth.PFCapture": "", + "videoImport.depth.PFCaptureHorizontal": "", + "videoImport.depth.holofix": "", + + "createNew.emptyObject": "Creates a new, empty slot.", + "createNew.particleSystem": "Creates a new particle system.", + "createNew.3DModelMenu": "3D Models such as cubes, spheres and cylinders.", + "createNew.3DModel.box": "Creates a new, resizeable box.", + "createNew.3DModel.capsule": "Creates a new capsule. (pill shape)", + "createNew.3DModel.cone": "Creates a new 3D cone.", + "createNew.3DModel.cylinder": "Creates a new cylinder.", + "createNew.3DModel.grid": "Creates a new, one-sided grid mesh.", + "createNew.3DModel.quad": "Creates a new, one-sided quad mesh.", + "createNew.3DModel.sphere": "Creates a new, resizeable sphere.", + "createNew.3DModel.torus": "Creates a new torus. (donut)", + "createNew.3DModel.triangle": "Creates a new, one-sided triangle mesh.", + "createNew.colliderMenu": "Various types and shapes of colliders.", + "createNew.collider.box": "Creates a new box collider.", + "createNew.collider.capsule": "Creates a new capsule collider.", + "createNew.collider.cone": "Creates a new cone collider.", + "createNew.collider.cylinder": "Creates a new cylinder collider.", + "createNew.collider.mesh": "Creates a new mesh collider.", + "createNew.collider.sphere": "Creates a new sphere collider.", + "createNew.editorMenu": "Various editing and optimization tools.", + "createNew.editor.assetOptimizationWizard": "Creates a new asset optmization wizard.\n", + "createNew.editor.cubemapCreator": "Creates a new cubemap creator.\nThis can be used to turn 6 separate images into a cubemap.", + "createNew.editor.worldLightSourcesWizard": "Creates a new world light sources wizard.\n", + "createNew.editor.logixTransferWizard": "Creates a new LogiX transfer wizard.\n", + "createNew.editor.reflectionProbeWizard": "Creates a new reflection probe wizard.\n", + "createNew.editor.worldTextRendererWizard": "Creates a new world text renderer wizard.\n", + "createNew.editor.userInspector": "Creates a new user inspector.\nThis can be used inspect users and access detailed information about them.", + "createNew.lightMenu": "Directional, point and spot lights.", + "createNew.light.directional": "Creates a new, directional light source.", + "createNew.light.point": "Creates a new light source that shines in every direction.", + "createNew.light.spot": "Creates a new spot light.", + "createNew.materialMenu": "Different materials (shaders) to apply to 3D objects.", + "createNew.objectMenu": "Various pre-setup objects such as UI Elements, Mirrors, Cameras...", + "createNew.textMenu": "Floating Text.", + "createNew.text.Basic": "Creates a new piece of floating text with no outline.", + "createNew.text.Outline": "Creates a new piece of floating text with outlines on the letters." +} \ No newline at end of file diff --git a/packages.config b/packages.config new file mode 100644 index 0000000..6a90e26 --- /dev/null +++ b/packages.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file