Ver código fonte

Add translation reloading

Clean up translation file
Add translation entries for TabsPane
Move Translation to its own file

There isn't a way to reload translations in the gui just yet but it will
be available at some point
habeebweeb 4 anos atrás
pai
commit
10cc8274ac
18 arquivos alterados com 335 adições e 96 exclusões
  1. 36 22
      COM3D2.MeidoPhotoStudio.Plugin/Config/MeidoPhotoStudio/translations.en.json
  2. 29 64
      COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/Constants.cs
  3. 10 2
      COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Controls/DropDown.cs
  4. 6 0
      COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Controls/SelectionGrid.cs
  5. 17 0
      COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Panes/BackgroundWindowPanes/BackgroundSelectorPane.cs
  6. 14 1
      COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Panes/BasePane.cs
  7. 8 2
      COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Panes/CallWindowPanes/MaidSelectorPane.cs
  8. 15 0
      COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Panes/FaceWindowPanes/MaidFaceSliderPane.cs
  9. 23 0
      COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Panes/PoseWindowPanes/MaidDressingPane.cs
  10. 6 0
      COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Panes/PoseWindowPanes/MaidFaceLookPane.cs
  11. 7 0
      COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Panes/PoseWindowPanes/MaidIKPane.cs
  12. 31 1
      COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Panes/PoseWindowPanes/MaidPoseSelectorPane.cs
  13. 12 2
      COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Panes/TabsPane.cs
  14. 3 0
      COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Windows/MainWindows/BackgroundWindow.cs
  15. 8 2
      COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Windows/MainWindows/MaidCallWindow.cs
  16. 11 0
      COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Windows/MainWindows/MaidFaceWindow.cs
  17. 5 0
      COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Windows/MainWindows/MaidPoseWindow.cs
  18. 94 0
      COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/Translation.cs

+ 36 - 22
COM3D2.MeidoPhotoStudio.Plugin/Config/MeidoPhotoStudio/translations.en.json

@@ -1,10 +1,17 @@
 {
+    "tabs": {
+        "call": "Call",
+        "pose": "Pose",
+        "face": "Face",
+        "bg": "BG",
+        "bg2": "BG2"
+    },
     "maidCallWindow": {
+        "okButton": "OK",
         "clearButton": "Clear",
         "callButton": "Call"
     },
     "placementDropdown": {
-        "okButton": "OK",
         "normal": "Normal",
         "horizontalRow": "Horizontal Row",
         "diagonal": "Diagonal",
@@ -42,7 +49,9 @@
         "dance": "Dance",
         "danceMC": "Dance (MC)",
         "restraint": "Restraint",
-        "ero": "Ero"
+        "ero": "Ero",
+        "normal2": "Normal 2",
+        "ero2": "Ero 2"
     },
     "poseSave": {
         "saveToggle": "Save Pose",
@@ -289,6 +298,7 @@
         "cathedral": "Cathedral",
         "ClassRoom": "Classroom",
         "ClassRoom_Play": "Classroom (Yotogi)",
+        "classroom_nodesk": "Classroom (No Desks)",
         "com3d2pool": "Pool",
         "com3d2pool_night": "Pool (Night)",
         "DanceRoom": "Training",
@@ -304,37 +314,41 @@
         "gelaende": "Ski Slope",
         "gelaende_night": "Ski Slope (Night)",
         "HeroineRoom_A": "Innocence Room",
+        "HeroineRoom_A_Night": "Innocence Room (Night)",
         "HeroineRoom_A1": "Pure Room",
-        "HeroineRoom_A1_Night": "Pure Room",
-        "HeroineRoom_A_Night": "Innocence Room",
+        "HeroineRoom_A1_Night": "Pure Room (Night)",
         "HeroineRoom_B": "Kuudere Room",
+        "HeroineRoom_B_Night": "Kuudere Room (Night)",
         "HeroineRoom_B1": "Serious Room",
-        "HeroineRoom_B1_Night": "Serious Room",
-        "HeroineRoom_B_Night": "Kuudere Room",
+        "HeroineRoom_B1_Night": "Serious Room (Night)",
         "HeroineRoom_C": "Tsundere",
+        "HeroineRoom_C_Night": "Tsundere (Night)",
         "HeroineRoom_C1": "Rindere Room",
-        "HeroineRoom_C1_Night": "Rindere Room",
-        "HeroineRoom_C_Night": "Tsundere",
+        "HeroineRoom_C1_Night": "Rindere Room (Night)",
         "HeroineRoom_D": "Yandere Room",
+        "HeroineRoom_D_Night": "Yandere Room (Night)",
         "HeroineRoom_D1": "Bookworm Room",
-        "HeroineRoom_D1_Night": "Bookworm Room",
-        "HeroineRoom_D_Night": "Yandere Room",
+        "HeroineRoom_D1_Night": "Bookworm Room (Night)",
         "HeroineRoom_E": "Oneechan Room",
+        "HeroineRoom_E_Night": "Oneechan Room (Night)",
         "HeroineRoom_E1": "Koakuma Room",
-        "HeroineRoom_E1_Night": "Koakuma Room",
-        "HeroineRoom_E_Night": "Oneechan Room",
+        "HeroineRoom_E1_Night": "Koakuma Room (Night)",
         "HeroineRoom_F": "Genki Room",
+        "HeroineRoom_F_Night": "Genki Room (Night)",
         "HeroineRoom_F1": "Anesan Room",
-        "HeroineRoom_F1_Night": "Anesan Room",
-        "HeroineRoom_F_Night": "Genki Room",
+        "HeroineRoom_F1_Night": "Anesan Room (Night)",
         "HeroineRoom_G": "Sadist Room",
+        "HeroineRoom_G_Night": "Sadist Room (Night)",
         "HeroineRoom_G1": "Secretary Room",
-        "HeroineRoom_G1_Night": "Secretary Room",
-        "HeroineRoom_G_Night": "Sadist Room",
+        "HeroineRoom_G1_Night": "Secretary Room (Night)",
         "HeroineRoom_H1": "Imouto Room",
-        "HeroineRoom_H1_Night": "Imouto Room",
+        "HeroineRoom_H1_Night": "Imouto Room (Night)",
         "HeroineRoom_J1": "Wary Room",
-        "HeroineRoom_J1_Night": "Wary Room",
+        "HeroineRoom_J1_Night": "Wary Room (Night)",
+        "HeroineRoom_K1": "Ojou Room",
+        "HeroineRoom_K1_Night": "Ojou Room (Night)",
+        "HeroineRoom_L1": "Osanajimi Room",
+        "HeroineRoom_L1_Night": "Osanajimi Room (Night)",
         "homelesstents": "Homeless Tent",
         "HoneymoonRoom": "Honeymoon Room",
         "izakaya": "Japanese Bar",
@@ -348,7 +362,7 @@
         "LiveStage": "Live Stage",
         "LiveStage_Side": "Live Stage (On)",
         "LiveStage_use_dance": "Live Stage (Off)",
-        "LockerRoom": "Locker ROom",
+        "LockerRoom": "Locker Room",
         "luxurytoilet": "Luxury Bathroom",
         "machikado": "Street Corner",
         "machikado_night": "Street Corner (Night)",
@@ -392,8 +406,8 @@
         "opemcafe_raspberry_night": "Raspberry Cube Collab Cafe (Night)",
         "opemcafe_riddlejoker": "Riddle Joker Collab Cafe",
         "opemcafe_riddlejoker_night": "Riddle Joker Collab Cafe (Night)",
-        "opemcafe_sagapla": "Kin-iro Loveriche GT Collab Cafe",
-        "opemcafe_sagapla_night": "Kin-iro Loveriche GT Collab Cafe (Night)",
+        "opemcafe_sagapla": "Kin'iro Loveriche GT Collab Cafe",
+        "opemcafe_sagapla_night": "Kin'iro Loveriche GT Collab Cafe (Night)",
         "opemcafe_wanko": "Wanko no Yomeiri Collab Cafe",
         "opemcafe_wanko_night": "Wanko no Yomeiri Collab Cafe (Night)",
         "OutletPark": "Outlet Park",
@@ -557,7 +571,7 @@
         "nipple": "Nipple",
         "arms": "Arms",
         "belly": "Belly",
-        "anlke": "Ankle",
+        "ankle": "Ankle",
         "back": "Back",
         "tail": "Tail",
         "genitals": "Genitals"

+ 29 - 64
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/Constants.cs

@@ -55,15 +55,28 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
 
             BGList = new List<string>();
             MyRoomCustomBGList = new List<KeyValuePair<string, string>>();
+            DoguList = new List<string>();
+            OtherDoguList = new List<string>();
         }
 
         public static void Initialize()
         {
-            foreach (string dir in new[] { customPosePath, scenesPath, kankyoPath, configPath })
+            MakeDirectories();
+            InitializePoses();
+            InitializeFaceBlends();
+            InitializeBGs();
+        }
+
+        public static void MakeDirectories()
+        {
+            foreach (string directory in new[] { customPosePath, scenesPath, kankyoPath, configPath })
             {
-                if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
+                if (!Directory.Exists(directory)) Directory.CreateDirectory(directory);
             }
+        }
 
+        public static void InitializePoses()
+        {
             // Load Poses
             string poseListJson = File.ReadAllText(Path.Combine(configPath, "mm_pose_list.json"));
             List<SerializePoseList> poseLists = JsonConvert.DeserializeObject<List<SerializePoseList>>(poseListJson);
@@ -118,7 +131,6 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
                         }
                     }
                 }
-                // editPoseList.AddRange(otherPoseList);
                 PoseDict["normal"].AddRange(editPoseList);
                 PoseDict["normal2"] = otherPoseList;
                 PoseDict["ero2"] = eroPoseList;
@@ -126,7 +138,7 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
                 PoseGroupList.AddRange(new[] { "normal2", "ero2" });
             }
 
-            CustomPoseGroupsIndex = PoseDict.Count;
+            CustomPoseGroupsIndex = PoseGroupList.Count;
 
             Action<string> GetPoses = directory =>
             {
@@ -153,8 +165,10 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             {
                 GetPoses(directory);
             }
+        }
 
-            // Load Face Blends Presets
+        public static void InitializeFaceBlends()
+        {
             using (CsvParser csvParser = OpenCsvParser("phot_face_list.nei"))
             {
                 for (int cell_y = 1; cell_y < csvParser.max_cell_y; cell_y++)
@@ -166,7 +180,10 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
                     }
                 }
             }
+        }
 
+        public static void InitializeBGs()
+        {
             // Load BGs
             PhotoBGData.Create();
             List<PhotoBGData> photList = PhotoBGData.data;
@@ -218,6 +235,7 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
                     AFileBase file = fs.FileOpen(nei);
                     CsvParser csvParser = new CsvParser();
                     if (csvParser.Open(file)) return csvParser;
+                    else file?.Dispose();
                 }
             }
             catch { }
@@ -229,69 +247,16 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             return OpenCsvParser(nei, GameUty.FileSystem);
         }
 
-        public class SerializePoseList
+        public static void WriteToFile(string name, IEnumerable<string> list)
         {
-            public string UIName { get; set; }
-            public List<string> PoseList { get; set; }
+            if (Path.GetExtension(name) != ".txt") name += ".txt";
+            File.WriteAllLines(Path.Combine(configPath, name), list.ToArray());
         }
-    }
-
-    public static class Translation
-    {
-        public static Dictionary<string, Dictionary<string, string>> Translations;
-        public static string CurrentLanguage { get; set; }
-
-        public static void Initialize(string language)
-        {
-            CurrentLanguage = language;
 
-            string translationFile = $"translations.{language}.json";
-            string translationPath = Path.Combine(Constants.configPath, translationFile);
-            string translationJson = File.ReadAllText(translationPath);
-
-            JObject translation = JObject.Parse(translationJson);
-
-            Translations = new Dictionary<string, Dictionary<string, string>>(
-                StringComparer.InvariantCultureIgnoreCase
-            );
-
-            foreach (JProperty translationProp in translation.AsJEnumerable())
-            {
-                JToken token = translationProp.Value;
-                Translations[translationProp.Path] = token.ToObject<Dictionary<string, string>>();
-            }
-        }
-
-        public static string Get(string category, string text)
-        {
-            if (!Translations.ContainsKey(category))
-            {
-                Debug.LogWarning($"Could not find category '{category}'");
-                return null;
-            }
-
-            if (!Translations[category].ContainsKey(text))
-            {
-                Debug.LogWarning($"Could not find translation for '{text}'");
-                return null;
-            }
-            return Translations[category][text];
-        }
-
-        public static string[] GetArray(string category, IEnumerable<string> list)
-        {
-            return GetList(category, list).ToArray();
-        }
-
-        public static IEnumerable<string> GetList(string category, IEnumerable<string> list)
-        {
-
-            return list.Select(uiName => Get(category, uiName) ?? uiName);
-        }
-
-        public static string[] GetList(string category, IEnumerable<KeyValuePair<string, string>> list)
+        public class SerializePoseList
         {
-            return list.Select(kvp => Get(category, kvp.Key) ?? kvp.Key).ToArray();
+            public string UIName { get; set; }
+            public List<string> PoseList { get; set; }
         }
     }
 }

+ 10 - 2
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Controls/DropDown.cs

@@ -59,10 +59,17 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
 
         public void SetDropdownItems(string[] itemList, int selectedItemIndex = 0)
         {
-            this.scrollPos = (this.elementSize = Vector2.zero);
+            this.elementSize = Vector2.zero;
+
+            // TODO: Calculate scrollpos position maybe
+            if ((selectedItemIndex != this.selectedItemIndex) && (itemList.Length != this.DropdownList?.Length))
+            {
+                this.scrollPos = Vector2.zero;
+            }
             this.DropdownList = itemList;
             this.SelectedItemIndex = selectedItemIndex;
         }
+
         public void Step(int dir)
         {
             dir = (int)Mathf.Sign(dir);
@@ -92,7 +99,6 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             this.Draw(buttonStyle, layoutOptions);
         }
 
-
         private void OnChangeSelection(object sender, DropdownSelectArgs args)
         {
             if (args.DropdownID == this.DropdownID)
@@ -113,6 +119,7 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
                 OnDropdownEvent(DropdownClose);
             }
         }
+
         private void InitializeDropdown()
         {
             showDropdown = false;
@@ -156,6 +163,7 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
         public static Rect dropdownWindow;
         private static Rect dropdownScrollRect;
         private static Rect dropdownRect;
+
         public static Vector2 CalculateElementSize(string[] list)
         {
             if (!initialized) InitializeStyle();

+ 6 - 0
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Controls/SelectionGrid.cs

@@ -25,6 +25,12 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             this.selectedItem = selectedTab;
         }
 
+        public void SetItems(string[] items, int selectedIndex = 0)
+        {
+            this.Items = items;
+            this.SelectedItem = selectedIndex;
+        }
+
         public void Draw(GUIStyle gridStyle, params GUILayoutOption[] layoutOptions)
         {
             if (!Visible) return;

+ 17 - 0
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Panes/BackgroundWindowPanes/BackgroundSelectorPane.cs

@@ -28,6 +28,7 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             this.bgDropdown = new Dropdown(bgList.ToArray(), theaterIndex);
             this.bgDropdown.SelectionChange += (s, a) =>
             {
+                if (updating) return;
                 int selectedIndex = this.bgDropdown.SelectedItemIndex;
                 bool isCreative = this.bgDropdown.SelectedItemIndex >= Constants.MyRoomCustomBGIndex;
                 string bg = isCreative
@@ -44,6 +45,22 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             this.nextBGButton.ControlEvent += (s, a) => this.bgDropdown.Step(1);
         }
 
+        protected override void ReloadTranslation()
+        {
+            List<string> bgList = new List<string>(Translation.GetList("bgDropdown", Constants.BGList));
+            if (Constants.MyRoomCustomBGIndex >= 0)
+            {
+                foreach (KeyValuePair<string, string> kvp in Constants.MyRoomCustomBGList)
+                {
+                    bgList.Add(kvp.Value);
+                }
+            }
+
+            updating = true;
+            this.bgDropdown.SetDropdownItems(bgList.ToArray(), this.bgDropdown.SelectedItemIndex);
+            updating = false;
+        }
+
         public override void Draw(params GUILayoutOption[] layoutOptions)
         {
             float arrowButtonSize = 30;

+ 14 - 1
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Panes/BasePane.cs

@@ -1,5 +1,5 @@
+using System;
 using System.Collections.Generic;
-using UnityEngine;
 
 namespace COM3D2.MeidoPhotoStudio.Plugin
 {
@@ -11,8 +11,21 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
 
         public BasePane()
         {
+            Translation.ReloadTranslationEvent += OnReloadTranslation;
             Controls = new List<BaseControl>();
             Panes = new List<BasePane>();
         }
+
+        ~BasePane()
+        {
+            Translation.ReloadTranslationEvent -= OnReloadTranslation;
+        }
+
+        private void OnReloadTranslation(object sender, EventArgs args)
+        {
+            ReloadTranslation();
+        }
+
+        protected virtual void ReloadTranslation() { }
     }
 }

+ 8 - 2
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Panes/CallWindowPanes/MaidSelectorPane.cs

@@ -15,15 +15,21 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
         {
             this.meidoManager = meidoManager;
             selectedMaidList = new List<int>();
-            clearMaidsButton = new Button("Clear");
+            clearMaidsButton = new Button(Translation.Get("maidCallWindow", "clearButton"));
             clearMaidsButton.ControlEvent += (s, a) => selectedMaidList.Clear();
             Controls.Add(clearMaidsButton);
 
-            callMaidsButton = new Button("Call");
+            callMaidsButton = new Button(Translation.Get("maidCallWindow", "callButton"));
             callMaidsButton.ControlEvent += (s, a) => this.meidoManager.OnBeginCallMeidos(this.selectedMaidList);
             Controls.Add(callMaidsButton);
         }
 
+        protected override void ReloadTranslation()
+        {
+            clearMaidsButton.Label = Translation.Get("maidCallWindow", "clearButton");
+            callMaidsButton.Label = Translation.Get("maidCallWindow", "callButton");
+        }
+
         public override void Draw(params GUILayoutOption[] layoutOptions)
         {
             clearMaidsButton.Draw();

+ 15 - 0
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Panes/FaceWindowPanes/MaidFaceSliderPane.cs

@@ -101,6 +101,21 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             }
         }
 
+        protected override void ReloadTranslation()
+        {
+            for (int i = 0; i < faceKeys.Length; i++)
+            {
+                Slider slider = (Slider)Controls[i];
+                slider.Label = Translation.Get("faceBlendValues", faceKeys[i]);
+            }
+
+            for (int i = faceKeys.Length; i < faceKeys.Length + faceToggleKeys.Length; i++)
+            {
+                Toggle toggle = (Toggle)Controls[i];
+                toggle.Label = Translation.Get("faceBlendValues", faceToggleKeys[i - faceKeys.Length]);
+            }
+        }
+
         public void SetFaceValue(string key, float value)
         {
             if (updating) return;

+ 23 - 0
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Panes/PoseWindowPanes/MaidDressingPane.cs

@@ -77,6 +77,29 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             UpdateDetailedClothing();
         }
 
+        protected override void ReloadTranslation()
+        {
+            foreach (SlotID slot in clothingSlots)
+            {
+                Toggle clothingToggle = ClothingToggles[slot];
+                if (slot == SlotID.headset)
+                {
+                    clothingToggle.Label = detailedClothing
+                        ? Translation.Get("clothing", "headset")
+                        : Translation.Get("clothing", "headwear");
+                }
+                else
+                {
+                    clothingToggle.Label = Translation.Get("clothing", slot.ToString());
+                }
+            }
+
+            detailedClothingToggle.Label = Translation.Get("clothing", "detail");
+            curlingFrontToggle.Label = Translation.Get("clothing", "curlingFront");
+            curlingBackToggle.Label = Translation.Get("clothing", "curlingBack");
+            pantsuShiftToggle.Label = Translation.Get("clothing", "shiftPanties");
+        }
+
         public void ToggleClothing(SlotID slot, bool enabled)
         {
             if (this.updating) return;

+ 6 - 0
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Panes/PoseWindowPanes/MaidFaceLookPane.cs

@@ -22,6 +22,12 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
 
         }
 
+        protected override void ReloadTranslation()
+        {
+            this.lookXSlider.Label = Translation.Get("freeLook", "x");
+            this.lookYSlider.Label = Translation.Get("freeLook", "y");
+        }
+
         public void SetMaidLook()
         {
             if (updating) return;

+ 7 - 0
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Panes/PoseWindowPanes/MaidIKPane.cs

@@ -32,6 +32,13 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             this.boneIKToggle.ControlEvent += (s, a) => SetIK(IKToggle.Bone, this.boneIKToggle.Value);
         }
 
+        protected override void ReloadTranslation()
+        {
+            this.ikToggle.Label = Translation.Get("maidPoseWindow", "ikToggle");
+            this.releaseIKToggle.Label = Translation.Get("maidPoseWindow", "releaseToggle");
+            this.boneIKToggle.Label = Translation.Get("maidPoseWindow", "boneToggle");
+        }
+
         private void SetIK(IKToggle toggle, bool value)
         {
             if (updating) return;

+ 31 - 1
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Panes/PoseWindowPanes/MaidPoseSelectorPane.cs

@@ -21,7 +21,18 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
         {
             this.meidoManager = meidoManager;
 
-            this.poseGroupDropdown = new Dropdown(Translation.GetArray("poseGroupDropdown", Constants.PoseGroupList));
+            List<string> poseGroups = new List<string>(Constants.PoseGroupList.Count);
+
+            for (int i = 0; i < Constants.PoseGroupList.Count; i++)
+            {
+                string poseGroup = Constants.PoseGroupList[i];
+                poseGroups.Add(i < Constants.CustomPoseGroupsIndex
+                    ? Translation.Get("poseGroupDropdown", poseGroup)
+                    : poseGroup
+                );
+            }
+
+            this.poseGroupDropdown = new Dropdown(poseGroups.ToArray());
             this.poseGroupDropdown.SelectionChange += ChangePoseGroup;
 
             this.poseDropdown = new Dropdown(MakePoseList(Constants.PoseDict[Constants.PoseGroupList[0]]));
@@ -40,8 +51,27 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             this.poseRightButton.ControlEvent += (s, a) => poseDropdown.Step(1);
         }
 
+        protected override void ReloadTranslation()
+        {
+            List<string> poseGroups = new List<string>(Constants.PoseGroupList.Count);
+
+            for (int i = 0; i < Constants.PoseGroupList.Count; i++)
+            {
+                string poseGroup = Constants.PoseGroupList[i];
+                poseGroups.Add(i < Constants.CustomPoseGroupsIndex
+                    ? Translation.Get("poseGroupDropdown", poseGroup)
+                    : poseGroup
+                );
+            }
+
+            updating = true;
+            this.poseGroupDropdown.SetDropdownItems(poseGroups.ToArray(), this.poseGroupDropdown.SelectedItemIndex);
+            updating = false;
+        }
+
         private void ChangePoseGroup(object sender, EventArgs args)
         {
+            if (updating) return;
             string newPoseGroup = Constants.PoseGroupList[this.poseGroupDropdown.SelectedItemIndex];
             if (selectedPoseGroup == newPoseGroup)
             {

+ 12 - 2
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Panes/TabsPane.cs

@@ -14,15 +14,25 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
 
         }
         public static event EventHandler TabChange;
+        private static new bool updating;
+        private static readonly string[] tabNames = { "call", "pose", "face", "bg", "bg2" };
         static TabsPane()
         {
-            string[] tabs = { "Call", "Pose", "Face", "BG", "BG2" };
-            Tabs = new SelectionGrid(tabs, tabs.Length);
+            Translation.ReloadTranslationEvent += (s, a) => ReloadTranslation();
+            Tabs = new SelectionGrid(Translation.GetArray("tabs", tabNames), tabNames.Length);
             Tabs.ControlEvent += (s, a) => OnChangeTab();
         }
 
+        protected static new void ReloadTranslation()
+        {
+            updating = true;
+            Tabs.SetItems(Translation.GetArray("tabs", tabNames), Tabs.SelectedItem);
+            updating = false;
+        }
+
         private static void OnChangeTab()
         {
+            if (updating) return;
             selectedTab = (Constants.Window)Tabs.SelectedItem;
             TabChange?.Invoke(null, EventArgs.Empty);
         }

+ 3 - 0
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Windows/MainWindows/BackgroundWindow.cs

@@ -9,16 +9,19 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
     {
         private EnvironmentManager environmentManager;
         private BackgroundSelectorPane backgroundSelectorPane;
+        private PropsPane propsPane;
 
         public BackgroundWindow(EnvironmentManager environmentManager)
         {
             this.environmentManager = environmentManager;
             this.backgroundSelectorPane = new BackgroundSelectorPane(this.environmentManager);
+            this.propsPane = new PropsPane(this.environmentManager);
         }
 
         public override void Draw(params GUILayoutOption[] layoutOptions)
         {
             this.backgroundSelectorPane.Draw();
+            this.propsPane.Draw();
         }
     }
 }

+ 8 - 2
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Windows/MainWindows/MaidCallWindow.cs

@@ -14,17 +14,23 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
         public MaidCallWindow(MeidoManager meidoManager) : base()
         {
             this.meidoManager = meidoManager;
-            placementButton = new Button("Normal");
+            placementButton = new Button(Translation.Get("placementDropdown", "normal"));
             placementButton.ControlEvent += (o, a) => Debug.Log("Change placement");
             Controls.Add(placementButton);
 
-            placementOKButton = new Button("OK");
+            placementOKButton = new Button(Translation.Get("maidCallWindow", "okButton"));
             placementOKButton.ControlEvent += (o, a) => Debug.Log("Placement changed");
             Controls.Add(placementOKButton);
 
             maidSelectorPane = new MaidSelectorPane(meidoManager);
         }
 
+        protected override void ReloadTranslation()
+        {
+            placementButton.Label = Translation.Get("placementDropdown", "normal");
+            placementOKButton.Label = Translation.Get("maidCallWindow", "okButton");
+        }
+
         public override void Draw(params GUILayoutOption[] layoutOptions)
         {
             GUILayout.BeginHorizontal();

+ 11 - 0
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Windows/MainWindows/MaidFaceWindow.cs

@@ -28,6 +28,7 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             );
             this.faceBlendDropdown.SelectionChange += (s, a) =>
             {
+                if (updating) return;
                 string faceBlend = Constants.FaceBlendList[this.faceBlendDropdown.SelectedItemIndex];
                 this.meidoManager.ActiveMeido.SetFaceBlend(faceBlend);
                 this.UpdateFace();
@@ -45,6 +46,16 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             TabsPane.TabChange -= ChangeTab;
         }
 
+        protected override void ReloadTranslation()
+        {
+            updating = true;
+            faceBlendDropdown.SetDropdownItems(
+                Translation.GetArray("faceBlendPresetsDropdown", Constants.FaceBlendList),
+                faceBlendDropdown.SelectedItemIndex
+            );
+            updating = false;
+        }
+
         public override void Draw(params GUILayoutOption[] layoutOptions)
         {
             float arrowButtonSize = 30;

+ 5 - 0
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/GUI/Windows/MainWindows/MaidPoseWindow.cs

@@ -39,6 +39,11 @@ namespace COM3D2.MeidoPhotoStudio.Plugin
             TabsPane.TabChange -= OnTabChange;
         }
 
+        protected override void ReloadTranslation()
+        {
+            this.freeLookToggle.Label = Translation.Get("freeLook", "freeLookToggle");
+        }
+
         public override void Draw(params GUILayoutOption[] layoutOptions)
         {
             MaidSwitcherPane.Draw();

+ 94 - 0
COM3D2.MeidoPhotoStudio.Plugin/MeidoPhotoStudio/Translation.cs

@@ -0,0 +1,94 @@
+using System;
+using System.Linq;
+using System.IO;
+using System.Collections.Generic;
+using Newtonsoft.Json.Linq;
+using UnityEngine;
+
+namespace COM3D2.MeidoPhotoStudio.Plugin
+{
+    public static class Translation
+    {
+        public static Dictionary<string, Dictionary<string, string>> Translations;
+        public static string CurrentLanguage { get; private set; }
+        public static event EventHandler ReloadTranslationEvent;
+
+        public static void Initialize(string language)
+        {
+            CurrentLanguage = language;
+
+            string translationFile = $"translations.{language}.json";
+            string translationPath = Path.Combine(Constants.configPath, translationFile);
+            string translationJson = File.ReadAllText(translationPath);
+
+            JObject translation = JObject.Parse(translationJson);
+
+            Translations = new Dictionary<string, Dictionary<string, string>>(
+                StringComparer.InvariantCultureIgnoreCase
+            );
+
+            foreach (JProperty translationProp in translation.AsJEnumerable())
+            {
+                JToken token = translationProp.Value;
+                Translations[translationProp.Path] = token.ToObject<Dictionary<string, string>>();
+            }
+        }
+
+        public static void SetLanguage(string language)
+        {
+            Initialize(language);
+            OnReloadTranslation();
+        }
+
+        public static void ReloadTranslation()
+        {
+            Initialize(CurrentLanguage);
+            OnReloadTranslation();
+        }
+
+        public static void OnReloadTranslation()
+        {
+            ReloadTranslationEvent?.Invoke(null, EventArgs.Empty);
+        }
+
+        public static string Get(string category, string text)
+        {
+            if (!Translations.ContainsKey(category))
+            {
+                Debug.LogWarning($"Could not find category '{category}'");
+                return null;
+            }
+
+            if (!Translations[category].ContainsKey(text))
+            {
+                Debug.LogWarning($"Could not find translation for '{text}' in '{category}'");
+                return null;
+            }
+
+            return Translations[category][text];
+        }
+
+        public static string[] GetArray(string category, IEnumerable<string> list)
+        {
+            return GetList(category, list).ToArray();
+        }
+
+        public static IEnumerable<string> GetList(string category, IEnumerable<string> list)
+        {
+            return list.Select(uiName =>
+            {
+                string text = Get(category, uiName);
+                return string.IsNullOrEmpty(text) ? uiName : text;
+            });
+        }
+
+        public static string[] GetList(string category, IEnumerable<KeyValuePair<string, string>> list)
+        {
+            return list.Select(kvp =>
+            {
+                string text = Get(category, kvp.Key);
+                return string.IsNullOrEmpty(text) ? kvp.Key : text;
+            }).ToArray();
+        }
+    }
+}