Browse Source

bepinex 5.x support

randoman 6 years ago
parent
commit
5d1444364d
39 changed files with 586 additions and 228 deletions
  1. 5 1
      CHANGELOG.md
  2. 11 12
      README.md
  3. 11 0
      XUnity.AutoTranslator.sln
  4. BIN
      libs/BepInEx 5.0/BepInEx.Harmony.dll
  5. 51 0
      libs/BepInEx 5.0/BepInEx.Harmony.xml
  6. BIN
      libs/BepInEx 5.0/BepInEx.Preloader.dll
  7. BIN
      libs/BepInEx 5.0/BepInEx.dll
  8. 7 1
      src/Translators/BingTranslate/BingTranslateEndpoint.cs
  9. 7 1
      src/Translators/GoogleTranslate/GoogleTranslateEndpoint.cs
  10. 1 1
      src/XUnity.AutoTranslator.Patcher/Patcher.cs
  11. 62 0
      src/XUnity.AutoTranslator.Plugin.BepIn-5x/AutoTranslatorPlugin.cs
  12. 42 0
      src/XUnity.AutoTranslator.Plugin.BepIn-5x/BepInLogger.cs
  13. 37 0
      src/XUnity.AutoTranslator.Plugin.BepIn-5x/XUnity.AutoTranslator.Plugin.BepIn-5x.csproj
  14. 1 1
      src/XUnity.AutoTranslator.Plugin.BepIn/XUnity.AutoTranslator.Plugin.BepIn.csproj
  15. 97 58
      src/XUnity.AutoTranslator.Plugin.Core/AutoTranslationPlugin.cs
  16. 6 1
      src/XUnity.AutoTranslator.Plugin.Core/Configuration/Settings.cs
  17. 1 1
      src/XUnity.AutoTranslator.Plugin.Core/Constants/PluginData.cs
  18. 4 4
      src/XUnity.AutoTranslator.Plugin.Core/Constants/UserAgents.cs
  19. 75 50
      src/XUnity.AutoTranslator.Plugin.Core/Endpoints/TranslationEndpointManager.cs
  20. 3 6
      src/XUnity.AutoTranslator.Plugin.Core/TextTranslationCache.cs
  21. 1 1
      src/XUnity.AutoTranslator.Plugin.Core/TextTranslationInfo.cs
  22. 0 3
      src/XUnity.AutoTranslator.Plugin.Core/TranslationManager.cs
  23. 21 7
      src/XUnity.AutoTranslator.Plugin.Core/UI/AggregatedTranslationViewModel.cs
  24. 20 30
      src/XUnity.AutoTranslator.Plugin.Core/UI/DropdownGUI.cs
  25. 37 5
      src/XUnity.AutoTranslator.Plugin.Core/UI/DropdownOptionViewModel.cs
  26. 3 1
      src/XUnity.AutoTranslator.Plugin.Core/UI/GUIUtil.cs
  27. 7 1
      src/XUnity.AutoTranslator.Plugin.Core/UI/IndividualTranslationViewModel.cs
  28. 0 2
      src/XUnity.AutoTranslator.Plugin.Core/UI/ScrollViewGUI.cs
  29. 4 1
      src/XUnity.AutoTranslator.Plugin.Core/UI/ToggleViewModel.cs
  30. 18 7
      src/XUnity.AutoTranslator.Plugin.Core/UI/TranslationAggregatorOptionsWindow.cs
  31. 20 8
      src/XUnity.AutoTranslator.Plugin.Core/UI/TranslationAggregatorViewModel.cs
  32. 17 17
      src/XUnity.AutoTranslator.Plugin.Core/UI/TranslationAggregatorWindow.cs
  33. 4 3
      src/XUnity.AutoTranslator.Plugin.Core/UI/XuaViewModel.cs
  34. 1 1
      src/XUnity.AutoTranslator.Plugin.Core/UI/XuaWindow.cs
  35. 8 0
      src/XUnity.AutoTranslator.Plugin.Core/Utilities/LanguageHelper.cs
  36. 1 1
      src/XUnity.AutoTranslator.Plugin.Core/XUnity.AutoTranslator.Plugin.Core.csproj
  37. 1 1
      src/XUnity.AutoTranslator.Plugin.IPA/XUnity.AutoTranslator.Plugin.IPA.csproj
  38. 1 1
      src/XUnity.AutoTranslator.Plugin.UnityInjector/XUnity.AutoTranslator.Plugin.UnityInjector.csproj
  39. 1 1
      src/XUnity.AutoTranslator.Setup/XUnity.AutoTranslator.Setup.csproj

+ 5 - 1
CHANGELOG.md

@@ -1,9 +1,13 @@
 ### 3.2.0
 ### 3.2.0
+ * FEATURE - BepInEx 5.x plugin support
  * CHANGE - Restructured large portions of the internal code to support more features going forward
  * CHANGE - Restructured large portions of the internal code to support more features going forward
  * BUG FIX - Interacting with UI now blocks input to game
  * BUG FIX - Interacting with UI now blocks input to game
  * BUG FIX - Better handling of error'ed translations in relation to rich text
  * BUG FIX - Better handling of error'ed translations in relation to rich text
+ * BUG FIX - Minor fixes to 'copy to clipboard' to disable IMGUI spam
+ * BUG FIX - Fixed potential NullReferenceException in GoogleTranslate and BingTranslate during timeout errors
  * MISC - Removed 'Dump Untranslated Texts' hotkey due to feature bloat
  * MISC - Removed 'Dump Untranslated Texts' hotkey due to feature bloat
- * MISC - Improved Utage image hooking to support DicingImage
+ * MISC - Allow unselecting translation endpoint in UI
+ * MISC - Increased request timeout from 50 to 150 seconds to ensure better error logging of failed requests
 
 
 ### 3.1.0
 ### 3.1.0
  * FEATURE - Support for games with 'netstandard2.0' API surface through config option 'EnableExperimentalHooks'
  * FEATURE - Support for games with 'netstandard2.0' API surface through config option 'EnableExperimentalHooks'

+ 11 - 12
README.md

@@ -2,11 +2,11 @@
 
 
 ## Index
 ## Index
  * [Introduction](#introduction)
  * [Introduction](#introduction)
+ * [Plugin Frameworks](#plugin-frameworks)
  * [Installation](#installation)
  * [Installation](#installation)
  * [Key Mapping](#key-mapping)
  * [Key Mapping](#key-mapping)
  * [Translators](#translators)
  * [Translators](#translators)
  * [Text Frameworks](#text-frameworks)
  * [Text Frameworks](#text-frameworks)
- * [Plugin Frameworks](#plugin-frameworks)
  * [Configuration](#configuration)
  * [Configuration](#configuration)
  * [Frequently Asked Questions](#frequently-asked-questions)
  * [Frequently Asked Questions](#frequently-asked-questions)
  * [Translating Mods](#translating-mods)
  * [Translating Mods](#translating-mods)
@@ -27,6 +27,16 @@ If you intend on redistributing this plugin as part of a translation suite for a
 
 
 From version 3.0.0 it is possible to implement custom translators. See [this section](#implementing-a-translator) for more info.
 From version 3.0.0 it is possible to implement custom translators. See [this section](#implementing-a-translator) for more info.
 
 
+## Plugin Frameworks
+The mod can be installed into the following Plugin Managers:
+ * [BepInEx](https://github.com/bbepis/BepInEx) (preferred approach)
+ * [IPA](https://github.com/Eusth/IPA)
+ * UnityInjector
+
+Installation instructions for all methods can be found below.
+
+Additionally it can be installed without a dependency on a plugin manager through ReiPatcher. However, this approach is not recommended if you use one of the above mentioned Plugin Managers!
+
 ## Installation
 ## Installation
 The plugin can be installed in following ways:
 The plugin can be installed in following ways:
 
 
@@ -188,16 +198,6 @@ The following text frameworks are supported.
  * [TextMeshPro](http://digitalnativestudios.com/textmeshpro/docs/)
  * [TextMeshPro](http://digitalnativestudios.com/textmeshpro/docs/)
  * [Utage (VN Game Engine)](http://madnesslabo.net/utage/?lang=en)
  * [Utage (VN Game Engine)](http://madnesslabo.net/utage/?lang=en)
 
 
-## Plugin Frameworks
-The mod can be installed into the following Plugin Managers:
- * [BepInEx](https://github.com/bbepis/BepInEx) (preferred approach)
- * [IPA](https://github.com/Eusth/IPA)
- * UnityInjector
-
-Installation instructions for all methods can be found below.
-
-Additionally it can be installed without a dependency on a plugin manager through ReiPatcher. However, this approach is not recommended if you use one of the above mentioned Plugin Managers!
-
 ## Configuration
 ## Configuration
 The default configuration file, looks as such:
 The default configuration file, looks as such:
 
 
@@ -390,7 +390,6 @@ In this context, the `Translation\_AutoGeneratedTranslations.{lang}.txt` (Output
 ## Regarding Redistribution
 ## Regarding Redistribution
 Redistributing this plugin for various games is absolutely encouraged. However, if you do so, please keep the following in mind:
 Redistributing this plugin for various games is absolutely encouraged. However, if you do so, please keep the following in mind:
  * **Distribute the _AutoGeneratedTranslations.{lang}.txt file along with the redistribution with as many translations as possible to ensure the online translator is hit as little as possible.**
  * **Distribute the _AutoGeneratedTranslations.{lang}.txt file along with the redistribution with as many translations as possible to ensure the online translator is hit as little as possible.**
- * **Do not redistribute the mod with the configuration option `EnableIMGUI=True`.**
  * **Test your redistribution with logging/console enabled to ensure the game does not exhibit undesirable behaviour such as spamming the endpoints.**
  * **Test your redistribution with logging/console enabled to ensure the game does not exhibit undesirable behaviour such as spamming the endpoints.**
  * Ensure you keep the plugin up-to-date, as much as reasonably possible.
  * Ensure you keep the plugin up-to-date, as much as reasonably possible.
  * If you use image loading feature, make sure you read [this section](#texture-translation).
  * If you use image loading feature, make sure you read [this section](#texture-translation).

+ 11 - 0
XUnity.AutoTranslator.sln

@@ -83,6 +83,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XUnity.RuntimeHooker.Consol
 EndProject
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XUnity.RuntimeHooker.Benchmark", "test\XUnity.RuntimeHooker.Benchmark\XUnity.RuntimeHooker.Benchmark.csproj", "{E2F50278-9134-4DC8-9C50-4C0A52063A89}"
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XUnity.RuntimeHooker.Benchmark", "test\XUnity.RuntimeHooker.Benchmark\XUnity.RuntimeHooker.Benchmark.csproj", "{E2F50278-9134-4DC8-9C50-4C0A52063A89}"
 EndProject
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XUnity.AutoTranslator.Plugin.BepIn-5x", "src\XUnity.AutoTranslator.Plugin.BepIn-5x\XUnity.AutoTranslator.Plugin.BepIn-5x.csproj", "{ADCCF172-7D31-42C6-B9D4-1779EAC8B403}"
+EndProject
 Global
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
 		Debug|Any CPU = Debug|Any CPU
@@ -291,6 +293,14 @@ Global
 		{E2F50278-9134-4DC8-9C50-4C0A52063A89}.Release|Any CPU.Build.0 = Release|Any CPU
 		{E2F50278-9134-4DC8-9C50-4C0A52063A89}.Release|Any CPU.Build.0 = Release|Any CPU
 		{E2F50278-9134-4DC8-9C50-4C0A52063A89}.Release|x86.ActiveCfg = Release|Any CPU
 		{E2F50278-9134-4DC8-9C50-4C0A52063A89}.Release|x86.ActiveCfg = Release|Any CPU
 		{E2F50278-9134-4DC8-9C50-4C0A52063A89}.Release|x86.Build.0 = Release|Any CPU
 		{E2F50278-9134-4DC8-9C50-4C0A52063A89}.Release|x86.Build.0 = Release|Any CPU
+		{ADCCF172-7D31-42C6-B9D4-1779EAC8B403}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{ADCCF172-7D31-42C6-B9D4-1779EAC8B403}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{ADCCF172-7D31-42C6-B9D4-1779EAC8B403}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{ADCCF172-7D31-42C6-B9D4-1779EAC8B403}.Debug|x86.Build.0 = Debug|Any CPU
+		{ADCCF172-7D31-42C6-B9D4-1779EAC8B403}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{ADCCF172-7D31-42C6-B9D4-1779EAC8B403}.Release|Any CPU.Build.0 = Release|Any CPU
+		{ADCCF172-7D31-42C6-B9D4-1779EAC8B403}.Release|x86.ActiveCfg = Release|Any CPU
+		{ADCCF172-7D31-42C6-B9D4-1779EAC8B403}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 		HideSolutionNode = FALSE
@@ -322,6 +332,7 @@ Global
 		{BE7ED338-106C-4CC6-BA19-8ADB2534326A} = {0F9B38FC-4E57-4B83-AF0B-0993B8470823}
 		{BE7ED338-106C-4CC6-BA19-8ADB2534326A} = {0F9B38FC-4E57-4B83-AF0B-0993B8470823}
 		{20E57B16-F3C7-4D80-88FC-09D6C5A4CCC3} = {2A4A3DDF-338C-40C0-8E26-2A810BAAADD6}
 		{20E57B16-F3C7-4D80-88FC-09D6C5A4CCC3} = {2A4A3DDF-338C-40C0-8E26-2A810BAAADD6}
 		{E2F50278-9134-4DC8-9C50-4C0A52063A89} = {2A4A3DDF-338C-40C0-8E26-2A810BAAADD6}
 		{E2F50278-9134-4DC8-9C50-4C0A52063A89} = {2A4A3DDF-338C-40C0-8E26-2A810BAAADD6}
+		{ADCCF172-7D31-42C6-B9D4-1779EAC8B403} = {0F9B38FC-4E57-4B83-AF0B-0993B8470823}
 	EndGlobalSection
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {EE803FED-4447-4D19-B3D6-88C56E8DFCCA}
 		SolutionGuid = {EE803FED-4447-4D19-B3D6-88C56E8DFCCA}

BIN
libs/BepInEx 5.0/BepInEx.Harmony.dll


+ 51 - 0
libs/BepInEx 5.0/BepInEx.Harmony.xml

@@ -0,0 +1,51 @@
+<?xml version="1.0"?>
+<doc>
+    <assembly>
+        <name>BepInEx.Harmony</name>
+    </assembly>
+    <members>
+        <member name="T:BepInEx.Harmony.ParameterByRefAttribute">
+            <summary>
+            Specifies the indices of parameters that are ByRef.
+            </summary>
+        </member>
+        <member name="P:BepInEx.Harmony.ParameterByRefAttribute.ParameterIndices">
+            <summary>
+            The indices of parameters that are ByRef.
+            </summary>
+        </member>
+        <member name="M:BepInEx.Harmony.ParameterByRefAttribute.#ctor(System.Int32[])">
+            <param name="parameterIndices">The indices of parameters that are ByRef.</param>
+        </member>
+        <member name="T:BepInEx.Harmony.HarmonyWrapper">
+            <summary>
+            A wrapper for Harmony based operations.
+            </summary>
+        </member>
+        <member name="P:BepInEx.Harmony.HarmonyWrapper.DefaultInstance">
+            <summary>
+            The HarmonyInstance that is used when none is specified.
+            </summary>
+        </member>
+        <member name="M:BepInEx.Harmony.HarmonyWrapper.PatchAll(System.Type,Harmony.HarmonyInstance)">
+            <summary>
+            Applies all patches specified in the type.
+            </summary>
+            <param name="type">The type to scan.</param>
+            <param name="harmonyInstance">The HarmonyInstance to use.</param>
+        </member>
+        <member name="M:BepInEx.Harmony.HarmonyWrapper.PatchAll(System.Reflection.Assembly,Harmony.HarmonyInstance)">
+            <summary>
+            Applies all patches specified in the assembly.
+            </summary>
+            <param name="assembly">The assembly to scan.</param>
+            <param name="harmonyInstance">The HarmonyInstance to use.</param>
+        </member>
+        <member name="M:BepInEx.Harmony.HarmonyWrapper.PatchAll(Harmony.HarmonyInstance)">
+            <summary>
+            Applies all patches specified in the calling assembly.
+            </summary>
+            <param name="harmonyInstance">The HarmonyInstance to use.</param>
+        </member>
+    </members>
+</doc>

BIN
libs/BepInEx 5.0/BepInEx.Preloader.dll


BIN
libs/BepInEx 5.0/BepInEx.dll


+ 7 - 1
src/Translators/BingTranslate/BingTranslateEndpoint.cs

@@ -200,7 +200,11 @@ namespace BingTranslate
          var iterator = response.GetSupportedEnumerator();
          var iterator = response.GetSupportedEnumerator();
          while( iterator.MoveNext() ) yield return iterator.Current;
          while( iterator.MoveNext() ) yield return iterator.Current;
 
 
-         InspectResponse( response );
+         if( response.IsTimedOut )
+         {
+            XuaLogger.Current.Warn( "A timeout error occurred while setting up BingTranslate IG. Proceeding without..." );
+            yield break;
+         }
 
 
          // failure
          // failure
          if( response.Error != null )
          if( response.Error != null )
@@ -216,6 +220,8 @@ namespace BingTranslate
             yield break;
             yield break;
          }
          }
 
 
+         InspectResponse( response );
+
          try
          try
          {
          {
             var html = response.Data;
             var html = response.Data;

+ 7 - 1
src/Translators/GoogleTranslate/GoogleTranslateEndpoint.cs

@@ -249,7 +249,11 @@ namespace GoogleTranslate
          var iterator = response.GetSupportedEnumerator();
          var iterator = response.GetSupportedEnumerator();
          while( iterator.MoveNext() ) yield return iterator.Current;
          while( iterator.MoveNext() ) yield return iterator.Current;
 
 
-         InspectResponse( response );
+         if( response.IsTimedOut )
+         {
+            XuaLogger.Current.Warn( "A timeout error occurred while setting up GoogleTranslate TKK. Using fallback TKK values instead." );
+            yield break;
+         }
 
 
          // failure
          // failure
          if( response.Error != null )
          if( response.Error != null )
@@ -265,6 +269,8 @@ namespace GoogleTranslate
             yield break;
             yield break;
          }
          }
 
 
+         InspectResponse( response );
+
          try
          try
          {
          {
             var html = response.Data;
             var html = response.Data;

+ 1 - 1
src/XUnity.AutoTranslator.Patcher/Patcher.cs

@@ -29,7 +29,7 @@ namespace XUnity.AutoTranslator.Patcher
       {
       {
          get
          get
          {
          {
-            return "3.1.0";
+            return "3.2.0";
          }
          }
       }
       }
 
 

+ 62 - 0
src/XUnity.AutoTranslator.Plugin.BepIn-5x/AutoTranslatorPlugin.cs

@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using BepInEx;
+using ExIni;
+using XUnity.AutoTranslator.Plugin.Core;
+using XUnity.AutoTranslator.Plugin.Core.Configuration;
+using XUnity.AutoTranslator.Plugin.Core.Constants;
+
+namespace XUnity.AutoTranslator.Plugin.BepIn_5x
+{
+   [BepInPlugin( GUID: PluginData.Identifier, Name: PluginData.Name, Version: PluginData.Version )]
+   public class AutoTranslatorPlugin : BaseUnityPlugin, IPluginEnvironment
+   {
+      private IniFile _file;
+      private string _configPath;
+
+      public AutoTranslatorPlugin()
+      {
+         DataPath = @"BepInEx\plugins\XUnity.AutoTranslator";
+         _configPath = Path.Combine( DataPath, "AutoTranslatorConfig.ini" );
+         XuaLogger.Current = new BepInLogger();
+      }
+
+      public IniFile Preferences
+      {
+         get
+         {
+            return ( _file ?? ( _file = ReloadConfig() ) );
+         }
+      }
+
+      public string DataPath { get; }
+
+      public IniFile ReloadConfig()
+      {
+         if( !File.Exists( _configPath ) )
+         {
+            return ( _file ?? new IniFile() );
+         }
+         IniFile ini = IniFile.FromFile( _configPath );
+         if( _file == null )
+         {
+            return ( _file = ini );
+         }
+         _file.Merge( ini );
+         return _file;
+      }
+
+      public void SaveConfig()
+      {
+         _file.Save( _configPath );
+      }
+
+      void Awake()
+      {
+         PluginLoader.LoadWithConfig( this );
+      }
+   }
+}

+ 42 - 0
src/XUnity.AutoTranslator.Plugin.BepIn-5x/BepInLogger.cs

@@ -0,0 +1,42 @@
+using BepInEx.Logging;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using XUnity.AutoTranslator.Plugin.Core;
+using XUnity.AutoTranslator.Plugin.Core.Constants;
+
+namespace XUnity.AutoTranslator.Plugin.BepIn_5x
+{
+   public class BepInLogger : XuaLogger
+   {
+      private readonly ManualLogSource _logger = Logger.CreateLogSource( "XUnity.AutoTranslator " + PluginData.Version );
+
+      public BepInLogger()
+      {
+         RespectSettings = false;
+      }
+
+      protected override void Log( Core.LogLevel level, string message )
+      {
+         _logger.Log( Convert( level ), message );
+      }
+
+      public BepInEx.Logging.LogLevel Convert( Core.LogLevel level )
+      {
+         switch( level )
+         {
+            case Core.LogLevel.Debug:
+               return BepInEx.Logging.LogLevel.Debug;
+            case Core.LogLevel.Info:
+               return BepInEx.Logging.LogLevel.Info;
+            case Core.LogLevel.Warn:
+               return BepInEx.Logging.LogLevel.Warning;
+            case Core.LogLevel.Error:
+               return BepInEx.Logging.LogLevel.Error;
+            default:
+               return BepInEx.Logging.LogLevel.None;
+         }
+      }
+   }
+}

+ 37 - 0
src/XUnity.AutoTranslator.Plugin.BepIn-5x/XUnity.AutoTranslator.Plugin.BepIn-5x.csproj

@@ -0,0 +1,37 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net35</TargetFramework>
+    <Version>3.2.0</Version>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\XUnity.AutoTranslator.Plugin.Core\XUnity.AutoTranslator.Plugin.Core.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Reference Include="BepInEx">
+      <HintPath>..\..\libs\BepInEx 5.0\BepInEx.dll</HintPath>
+    </Reference>
+    <Reference Include="ExIni">
+      <HintPath>..\..\libs\ExIni.dll</HintPath>
+    </Reference>
+    <Reference Include="UnityEngine">
+      <HintPath>..\..\libs\UnityEngine.dll</HintPath>
+    </Reference>
+    <Reference Include="UnityEngine.UI">
+      <HintPath>..\..\libs\UnityEngine.UI.dll</HintPath>
+    </Reference>
+  </ItemGroup>
+
+  <Target Name="PostBuild" AfterTargets="PostBuildEvent">
+    <GetAssemblyIdentity AssemblyFiles="$(TargetPath)">
+      <Output TaskParameter="Assemblies" ItemName="Targets" />
+    </GetAssemblyIdentity>
+    <ItemGroup>
+      <VersionNumber Include="$([System.Text.RegularExpressions.Regex]::Replace(&quot;%(Targets.Version)&quot;, &quot;^(.+?)(\.0+)$&quot;, &quot;$1&quot;))" />
+    </ItemGroup>
+    <Exec Command="if $(ConfigurationName) == Release (&#xD;&#xA;   for %%f in (&quot;$(SolutionDir)dist\Translators\*&quot;) do XCOPY /Y /I &quot;%%f&quot; &quot;$(SolutionDir)dist\BepIn-5x\BepInEx\plugins\XUnity.AutoTranslator\Translators\&quot;&#xD;&#xA;   XCOPY /Y /I &quot;$(TargetDir)ExIni.dll&quot; &quot;$(SolutionDir)dist\BepIn-5x\BepInEx\plugins\XUnity.AutoTranslator\&quot;&#xD;&#xA;   XCOPY /Y /I &quot;$(TargetDir)XUnity.AutoTranslator.Plugin.Core.dll&quot; &quot;$(SolutionDir)dist\BepIn-5x\BepInEx\plugins\XUnity.AutoTranslator\&quot;&#xD;&#xA;   XCOPY /Y /I &quot;$(TargetDir)XUnity.AutoTranslator.Plugin.ExtProtocol.dll&quot; &quot;$(SolutionDir)dist\BepIn-5x\BepInEx\plugins\XUnity.AutoTranslator\&quot;&#xD;&#xA;   XCOPY /Y /I &quot;$(TargetDir)XUnity.RuntimeHooker.Core.dll&quot; &quot;$(SolutionDir)dist\BepIn-5x\BepInEx\plugins\XUnity.AutoTranslator\&quot;&#xD;&#xA;   XCOPY /Y /I &quot;$(TargetDir)XUnity.RuntimeHooker.dll&quot; &quot;$(SolutionDir)dist\BepIn-5x\BepInEx\plugins\XUnity.AutoTranslator\&quot;&#xD;&#xA;   XCOPY /Y /I &quot;$(TargetDir)$(TargetName)$(TargetExt)&quot; &quot;$(SolutionDir)dist\BepIn-5x\BepInEx\plugins\XUnity.AutoTranslator\&quot;&#xD;&#xA;   COPY /Y &quot;$(SolutionDir)README.md&quot; &quot;$(SolutionDir)dist\BepIn-5x\BepInEx\README (AutoTranslator).md&quot;&#xD;&#xA;   powershell Compress-Archive -Path '$(SolutionDir)dist\BepIn-5x\BepInEx' -DestinationPath '$(SolutionDir)dist\BepIn-5x\XUnity.AutoTranslator-BepIn-5x-@(VersionNumber).zip' -Force)&#xD;&#xA;)" />
+  </Target>
+
+</Project>

+ 1 - 1
src/XUnity.AutoTranslator.Plugin.BepIn/XUnity.AutoTranslator.Plugin.BepIn.csproj

@@ -2,7 +2,7 @@
 
 
    <PropertyGroup>
    <PropertyGroup>
       <TargetFramework>net35</TargetFramework>
       <TargetFramework>net35</TargetFramework>
-      <Version>3.1.0</Version>
+      <Version>3.2.0</Version>
    </PropertyGroup>
    </PropertyGroup>
 
 
    <ItemGroup>
    <ItemGroup>

+ 97 - 58
src/XUnity.AutoTranslator.Plugin.Core/AutoTranslationPlugin.cs

@@ -145,9 +145,10 @@ namespace XUnity.AutoTranslator.Plugin.Core
 
 
             MainWindow = new XuaWindow( CreateXuaViewModel() );
             MainWindow = new XuaWindow( CreateXuaViewModel() );
 
 
-            var vm = CreateTranslationAggregatorViewModel();
-            TranslationAggregatorWindow = new TranslationAggregatorWindow( vm );
-            TranslationAggregatorOptionsWindow = new TranslationAggregatorOptionsWindow( vm );
+            // UNRELEASED: Not included in current release
+            //var vm = CreateTranslationAggregatorViewModel();
+            //TranslationAggregatorWindow = new TranslationAggregatorWindow( vm );
+            //TranslationAggregatorOptionsWindow = new TranslationAggregatorOptionsWindow( vm );
          }
          }
          catch( Exception e )
          catch( Exception e )
          {
          {
@@ -161,7 +162,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
 
 
       private TranslationAggregatorViewModel CreateTranslationAggregatorViewModel()
       private TranslationAggregatorViewModel CreateTranslationAggregatorViewModel()
       {
       {
-         return new TranslationAggregatorViewModel( TranslationManager.ConfiguredEndpoints );
+         return new TranslationAggregatorViewModel( TranslationManager );
       }
       }
 
 
       private XuaViewModel CreateXuaViewModel()
       private XuaViewModel CreateXuaViewModel()
@@ -175,7 +176,10 @@ namespace XUnity.AutoTranslator.Plugin.Core
                   "<b>NOT TRANSLATED</b>\nThe plugin currently displays untranslated texts.",
                   "<b>NOT TRANSLATED</b>\nThe plugin currently displays untranslated texts.",
                   ToggleTranslation, () => _isInTranslatedMode )
                   ToggleTranslation, () => _isInTranslatedMode )
             },
             },
-            TranslationManager.AllEndpoints.Select( x => new TranslatorDropdownOptionViewModel( () => x == TranslationManager.CurrentEndpoint, x, OnEndpointSelected ) ).ToList(),
+            new DropdownViewModel<TranslatorDropdownOptionViewModel, TranslationEndpointManager>(
+               TranslationManager.AllEndpoints.Select( x => new TranslatorDropdownOptionViewModel( () => x == TranslationManager.CurrentEndpoint, x ) ).ToList(),
+               OnEndpointSelected
+            ),
             new List<ButtonViewModel>
             new List<ButtonViewModel>
             {
             {
                new ButtonViewModel( "Reboot", "<b>REBOOT PLUGIN</b>\nReboots the plugin if it has been shutdown. This only works if the plugin was shut down due to consequtive errors towards the translation endpoint.", RebootPlugin, null ),
                new ButtonViewModel( "Reboot", "<b>REBOOT PLUGIN</b>\nReboots the plugin if it has been shutdown. This only works if the plugin was shut down due to consequtive errors towards the translation endpoint.", RebootPlugin, null ),
@@ -186,7 +190,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
             {
             {
                new LabelViewModel( "Version: ", () => PluginData.Version ),
                new LabelViewModel( "Version: ", () => PluginData.Version ),
                new LabelViewModel( "Plugin status: ", () => Settings.IsShutdown ? "Shutdown" : "Running" ),
                new LabelViewModel( "Plugin status: ", () => Settings.IsShutdown ? "Shutdown" : "Running" ),
-               new LabelViewModel( "Translator status: ", () => TranslationManager.CurrentEndpoint.HasFailedDueToConsecutiveErrors ? "Shutdown" : "Running" ),
+               new LabelViewModel( "Translator status: ", GetCurrentEndpointStatus ),
                new LabelViewModel( "Running translations: ", () => $"{(TranslationManager.OngoingTranslations)}" ),
                new LabelViewModel( "Running translations: ", () => $"{(TranslationManager.OngoingTranslations)}" ),
                new LabelViewModel( "Served translations: ", () => $"{Settings.TranslationCount} / {Settings.MaxTranslationsBeforeShutdown}" ),
                new LabelViewModel( "Served translations: ", () => $"{Settings.TranslationCount} / {Settings.MaxTranslationsBeforeShutdown}" ),
                new LabelViewModel( "Queued translations: ", () => $"{(TranslationManager.UnstartedTranslations)} / {Settings.MaxUnstartedJobs}" ),
                new LabelViewModel( "Queued translations: ", () => $"{(TranslationManager.UnstartedTranslations)} / {Settings.MaxUnstartedJobs}" ),
@@ -194,6 +198,20 @@ namespace XUnity.AutoTranslator.Plugin.Core
             } );
             } );
       }
       }
 
 
+      private string GetCurrentEndpointStatus()
+      {
+         var endpoint = TranslationManager.CurrentEndpoint;
+         if( endpoint == null )
+         {
+            return "Not selected";
+         }
+         else if( endpoint.HasFailedDueToConsecutiveErrors )
+         {
+            return "Shutdown";
+         }
+         return "Running";
+      }
+
       private void StartMaintenance()
       private void StartMaintenance()
       {
       {
          // start a thread that will periodically removed unused references
          // start a thread that will periodically removed unused references
@@ -235,17 +253,22 @@ namespace XUnity.AutoTranslator.Plugin.Core
          if( TranslationManager.CurrentEndpoint != endpoint )
          if( TranslationManager.CurrentEndpoint != endpoint )
          {
          {
             TranslationManager.CurrentEndpoint = endpoint;
             TranslationManager.CurrentEndpoint = endpoint;
-
-            if( !Settings.IsShutdown )
+            
+            if( TranslationManager.CurrentEndpoint != null )
             {
             {
-               if( TranslationManager.CurrentEndpoint.HasFailedDueToConsecutiveErrors )
+               if( !Settings.IsShutdown )
                {
                {
-                  RebootPlugin();
+                  if( TranslationManager.CurrentEndpoint.HasFailedDueToConsecutiveErrors )
+                  {
+                     RebootPlugin();
+                  }
+                  ManualHook();
                }
                }
-               ManualHook();
             }
             }
 
 
-            Settings.SetEndpoint( TranslationManager.CurrentEndpoint.Endpoint.Id );
+            Settings.SetEndpoint( TranslationManager.CurrentEndpoint?.Endpoint.Id );
+
+            XuaLogger.Current.Info( $"Set translator endpoint to '{TranslationManager.CurrentEndpoint?.Endpoint.Id}'." );
          }
          }
       }
       }
 
 
@@ -353,7 +376,6 @@ namespace XUnity.AutoTranslator.Plugin.Core
          var added = endpoint.EnqueueTranslation( ui, key, translationResult, context );
          var added = endpoint.EnqueueTranslation( ui, key, translationResult, context );
          if( added && translationResult == null )
          if( added && translationResult == null )
          {
          {
-            // FIXME: Check that we still enter this!
             SpamChecker.PerformChecks( key.GetDictionaryLookupKey() );
             SpamChecker.PerformChecks( key.GetDictionaryLookupKey() );
          }
          }
       }
       }
@@ -970,8 +992,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
          }
          }
 
 
          // Ensure that we actually want to translate this text and its owning UI element.
          // Ensure that we actually want to translate this text and its owning UI element.
-         // FIXME: Using TextCache here is wrong!
-         if( !string.IsNullOrEmpty( text ) && TextCache.IsTranslatable( text ) && IsBelowMaxLength( text ) )
+         if( !string.IsNullOrEmpty( text ) && endpoint.IsTranslatable( text ) && IsBelowMaxLength( text ) )
          {
          {
             var textKey = new TranslationKey( null, text, false, context != null );
             var textKey = new TranslationKey( null, text, false, context != null );
 
 
@@ -1041,7 +1062,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
          {
          {
             var variableName = kvp.Key;
             var variableName = kvp.Key;
             var untranslatedTextPart = kvp.Value;
             var untranslatedTextPart = kvp.Value;
-            if( !string.IsNullOrEmpty( untranslatedTextPart ) && TextCache.IsTranslatable( untranslatedTextPart ) && IsBelowMaxLength( untranslatedTextPart ) )
+            if( !string.IsNullOrEmpty( untranslatedTextPart ) && endpoint.IsTranslatable( untranslatedTextPart ) && IsBelowMaxLength( untranslatedTextPart ) )
             {
             {
                string partTranslation;
                string partTranslation;
                if( endpoint.TryGetTranslation( untranslatedTextPart, out partTranslation ) )
                if( endpoint.TryGetTranslation( untranslatedTextPart, out partTranslation ) )
@@ -1244,7 +1265,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
                                     {
                                     {
                                        if( IsBelowMaxLength( stabilizedText ) )
                                        if( IsBelowMaxLength( stabilizedText ) )
                                        {
                                        {
-                                          if( !Settings.IsShutdown || !endpoint.HasFailedDueToConsecutiveErrors )
+                                          if( !Settings.IsShutdown && !endpoint.HasFailedDueToConsecutiveErrors )
                                           {
                                           {
                                              CreateTranslationJobFor( endpoint, ui, stabilizedTextKey, null, context );
                                              CreateTranslationJobFor( endpoint, ui, stabilizedTextKey, null, context );
                                           }
                                           }
@@ -1268,7 +1289,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
                      var endpoint = context?.Endpoint ?? TranslationManager.CurrentEndpoint;
                      var endpoint = context?.Endpoint ?? TranslationManager.CurrentEndpoint;
                      if( endpoint != null )
                      if( endpoint != null )
                      {
                      {
-                        if( !Settings.IsShutdown || !endpoint.HasFailedDueToConsecutiveErrors )
+                        if( !Settings.IsShutdown && !endpoint.HasFailedDueToConsecutiveErrors )
                         {
                         {
                            // once the text has stabilized, attempt to look it up
                            // once the text has stabilized, attempt to look it up
                            CreateTranslationJobFor( endpoint, ui, textKey, null, context );
                            CreateTranslationJobFor( endpoint, ui, textKey, null, context );
@@ -1288,7 +1309,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
                               if( endpoint != null )
                               if( endpoint != null )
                               {
                               {
                                  // once the text has stabilized, attempt to look it up
                                  // once the text has stabilized, attempt to look it up
-                                 if( !Settings.IsShutdown || !endpoint.HasFailedDueToConsecutiveErrors )
+                                 if( !Settings.IsShutdown && !endpoint.HasFailedDueToConsecutiveErrors )
                                  {
                                  {
                                     if( !TextCache.TryGetTranslation( textKey, out translation ) )
                                     if( !TextCache.TryGetTranslation( textKey, out translation ) )
                                     {
                                     {
@@ -1483,55 +1504,73 @@ namespace XUnity.AutoTranslator.Plugin.Core
             }
             }
 
 
             // perform this check every 100 frames!
             // perform this check every 100 frames!
-            if( Time.frameCount % 100 == 0 && TranslationManager.OngoingTranslations == 0 )
+            if( Time.frameCount % 100 == 0
+               && TranslationManager.OngoingTranslations == 0
+               && TranslationManager.UnstartedTranslations == 0 )
             {
             {
                ConnectionTrackingWebClient.CheckServicePoints();
                ConnectionTrackingWebClient.CheckServicePoints();
             }
             }
-
+            
             if( Input.anyKey )
             if( Input.anyKey )
             {
             {
                var isAltPressed = Input.GetKey( KeyCode.LeftAlt ) || Input.GetKey( KeyCode.RightAlt );
                var isAltPressed = Input.GetKey( KeyCode.LeftAlt ) || Input.GetKey( KeyCode.RightAlt );
 
 
-               if( Settings.EnablePrintHierarchy && isAltPressed && Input.GetKeyDown( KeyCode.Y ) )
-               {
-                  PrintObjects();
-               }
-               else if( isAltPressed && Input.GetKeyDown( KeyCode.T ) )
-               {
-                  ToggleTranslation();
-               }
-               else if( isAltPressed && Input.GetKeyDown( KeyCode.F ) )
-               {
-                  ToggleFont();
-               }
-               else if( isAltPressed && Input.GetKeyDown( KeyCode.R ) )
-               {
-                  ReloadTranslations();
-               }
-               else if( isAltPressed && Input.GetKeyDown( KeyCode.U ) )
-               {
-                  ManualHook();
-               }
-               else if( isAltPressed && Input.GetKeyDown( KeyCode.Q ) )
+               if( isAltPressed )
                {
                {
-                  RebootPlugin();
-               }
-               //else if( isAltPressed && Input.GetKeyDown( KeyCode.B ) )
-               //{
-               //   ConnectionTrackingWebClient.CloseServicePoints();
-               //}
-               else if( isAltPressed && ( Input.GetKeyDown( KeyCode.Alpha0 ) || Input.GetKeyDown( KeyCode.Keypad0 ) ) )
-               {
-                  if( MainWindow != null )
+                  var isCtrlPressed = Input.GetKey( KeyCode.LeftControl );
+
+                  if( Settings.EnablePrintHierarchy && Input.GetKeyDown( KeyCode.Y ) )
                   {
                   {
-                     MainWindow.IsShown = !MainWindow.IsShown;
+                     PrintObjects();
                   }
                   }
-               }
-               else if( isAltPressed && ( Input.GetKeyDown( KeyCode.Alpha1 ) || Input.GetKeyDown( KeyCode.Keypad1 ) ) )
-               {
-                  if( TranslationAggregatorWindow != null )
+                  else if( Input.GetKeyDown( KeyCode.T ) )
+                  {
+                     ToggleTranslation();
+                  }
+                  else if( Input.GetKeyDown( KeyCode.F ) )
                   {
                   {
-                     TranslationAggregatorWindow.IsShown = !TranslationAggregatorWindow.IsShown;
+                     ToggleFont();
+                  }
+                  else if( Input.GetKeyDown( KeyCode.R ) )
+                  {
+                     ReloadTranslations();
+                  }
+                  else if( Input.GetKeyDown( KeyCode.U ) )
+                  {
+                     ManualHook();
+                  }
+                  else if( Input.GetKeyDown( KeyCode.Q ) )
+                  {
+                     RebootPlugin();
+                  }
+                  //else if( Input.GetKeyDown( KeyCode.B ) )
+                  //{
+                  //   ConnectionTrackingWebClient.CloseServicePoints();
+                  //}
+                  else if( Input.GetKeyDown( KeyCode.Alpha0 ) || Input.GetKeyDown( KeyCode.Keypad0 ) )
+                  {
+                     if( MainWindow != null )
+                     {
+                        MainWindow.IsShown = !MainWindow.IsShown;
+                     }
+                  }
+                  else if( Input.GetKeyDown( KeyCode.Alpha1 ) || Input.GetKeyDown( KeyCode.Keypad1 ) )
+                  {
+                     if( TranslationAggregatorWindow != null )
+                     {
+                        TranslationAggregatorWindow.IsShown = !TranslationAggregatorWindow.IsShown;
+                     }
+                  }
+                  else if( isCtrlPressed )
+                  {
+                     if( Input.GetKeyDown( KeyCode.Keypad9 ) )
+                     {
+                        Settings.SimulateError = !Settings.SimulateError;
+                     }
+                     else if( Input.GetKeyDown( KeyCode.Keypad8 ) )
+                     {
+                        Settings.SimulateDelayedError = !Settings.SimulateDelayedError;
+                     }
                   }
                   }
                }
                }
             }
             }
@@ -1707,7 +1746,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
                            TextCache.AddTranslationToCache( context.Result.OriginalText, translatedText );
                            TextCache.AddTranslationToCache( context.Result.OriginalText, translatedText );
                         }
                         }
 
 
-                        job.Endpoint.AddTranslationToCache( job.Key, job.TranslatedText );
+                        job.Endpoint.AddTranslationToCache( context.Result.OriginalText, translatedText );
                      }
                      }
 
 
                      if( text == result.OriginalText )
                      if( text == result.OriginalText )

+ 6 - 1
src/XUnity.AutoTranslator.Plugin.Core/Configuration/Settings.cs

@@ -33,7 +33,10 @@ namespace XUnity.AutoTranslator.Plugin.Core.Configuration
       public static readonly int MaximumConsecutiveSecondsTranslated = 60;
       public static readonly int MaximumConsecutiveSecondsTranslated = 60;
       public static bool UsesWhitespaceBetweenWords = false;
       public static bool UsesWhitespaceBetweenWords = false;
       public static string ApplicationName;
       public static string ApplicationName;
-      public static float Timeout = 50.0f;
+      public static float Timeout = 150.0f;
+
+      public static bool SimulateError = false;
+      public static bool SimulateDelayedError = false;
 
 
       public static bool InvokeEvents = true;
       public static bool InvokeEvents = true;
       public static Action<object> RemakeTextData = null;
       public static Action<object> RemakeTextData = null;
@@ -197,6 +200,8 @@ namespace XUnity.AutoTranslator.Plugin.Core.Configuration
 
 
       public static void SetEndpoint( string id )
       public static void SetEndpoint( string id )
       {
       {
+         id = id ?? string.Empty;
+
          ServiceEndpoint = id;
          ServiceEndpoint = id;
          PluginEnvironment.Current.Preferences[ "Service" ][ "Endpoint" ].Value = id;
          PluginEnvironment.Current.Preferences[ "Service" ][ "Endpoint" ].Value = id;
          Save();
          Save();

+ 1 - 1
src/XUnity.AutoTranslator.Plugin.Core/Constants/PluginData.cs

@@ -23,6 +23,6 @@ namespace XUnity.AutoTranslator.Plugin.Core.Constants
       /// <summary>
       /// <summary>
       /// Gets the version of the plugin.
       /// Gets the version of the plugin.
       /// </summary>
       /// </summary>
-      public const string Version = "3.1.0";
+      public const string Version = "3.2.0";
    }
    }
 }
 }

+ 4 - 4
src/XUnity.AutoTranslator.Plugin.Core/Constants/UserAgents.cs

@@ -15,21 +15,21 @@ namespace XUnity.AutoTranslator.Plugin.Core.Constants
       /// <summary>
       /// <summary>
       /// Latest Chrome Win10 user-agent as of 2019-02-09.
       /// Latest Chrome Win10 user-agent as of 2019-02-09.
       /// </summary>
       /// </summary>
-      public static readonly string Chrome_Win10_Latest = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36";
+      public static readonly string Chrome_Win10_Latest = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36";
 
 
       /// <summary>
       /// <summary>
       /// Latest Chrome Win7 user-agent as of 2019-02-09.
       /// Latest Chrome Win7 user-agent as of 2019-02-09.
       /// </summary>
       /// </summary>
-      public static readonly string Chrome_Win7_Latest = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36";
+      public static readonly string Chrome_Win7_Latest = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36";
 
 
       /// <summary>
       /// <summary>
       /// Latest Firefox Win10 user-agent as of 2019-02-09.
       /// Latest Firefox Win10 user-agent as of 2019-02-09.
       /// </summary>
       /// </summary>
-      public static readonly string Firefox_Win10_Latest = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0";
+      public static readonly string Firefox_Win10_Latest = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0";
 
 
       /// <summary>
       /// <summary>
       /// Latest Edge Win10 user-agent as of 2019-02-09.
       /// Latest Edge Win10 user-agent as of 2019-02-09.
       /// </summary>
       /// </summary>
-      public static readonly string Edge_Win10_Latest = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134";
+      public static readonly string Edge_Win10_Latest = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/18.17763";
    }
    }
 }
 }

+ 75 - 50
src/XUnity.AutoTranslator.Plugin.Core/Endpoints/TranslationEndpointManager.cs

@@ -20,6 +20,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints
 
 
       // used for prototyping
       // used for prototyping
       private Dictionary<string, string> _translations;
       private Dictionary<string, string> _translations;
+      private Dictionary<string, string> _reverseTranslations;
 
 
       public TranslationEndpointManager( ITranslateEndpoint endpoint, Exception error )
       public TranslationEndpointManager( ITranslateEndpoint endpoint, Exception error )
       {
       {
@@ -32,6 +33,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints
          _ongoingJobs = new Dictionary<string, TranslationJob>();
          _ongoingJobs = new Dictionary<string, TranslationJob>();
 
 
          _translations = new Dictionary<string, string>();
          _translations = new Dictionary<string, string>();
+         _reverseTranslations = new Dictionary<string, string>();
 
 
          HasBatchLogicFailed = false;
          HasBatchLogicFailed = false;
          AvailableBatchOperations = Settings.MaxAvailableBatchOperations;
          AvailableBatchOperations = Settings.MaxAvailableBatchOperations;
@@ -69,15 +71,10 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints
          return _translations.TryGetValue( key, out value );
          return _translations.TryGetValue( key, out value );
       }
       }
 
 
-      private void AddTranslation( TranslationKey key, string value )
-      {
-         var lookup = key.GetDictionaryLookupKey();
-         _translations[ lookup ] = value;
-      }
-
       private void AddTranslation( string key, string value )
       private void AddTranslation( string key, string value )
       {
       {
          _translations[ key ] = value;
          _translations[ key ] = value;
+         _reverseTranslations[ value ] = key;
       }
       }
 
 
       private void QueueNewTranslationForDisk( string key, string value )
       private void QueueNewTranslationForDisk( string key, string value )
@@ -87,16 +84,28 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints
 
 
       public void AddTranslationToCache( TranslationKey key, string value )
       public void AddTranslationToCache( TranslationKey key, string value )
       {
       {
-         AddTranslationToCache( key.GetDictionaryLookupKey(), value );
+         // UNRELEASED: Not included in current release
+         //AddTranslationToCache( key.GetDictionaryLookupKey(), value );
       }
       }
 
 
       public void AddTranslationToCache( string key, string value )
       public void AddTranslationToCache( string key, string value )
       {
       {
-         if( !HasTranslated( key ) )
-         {
-            AddTranslation( key, value );
-            QueueNewTranslationForDisk( key, value );
-         }
+         // UNRELEASED: Not included in current release
+         //if( !HasTranslated( key ) )
+         //{
+         //   AddTranslation( key, value );
+         //   QueueNewTranslationForDisk( key, value );
+         //}
+      }
+
+      public bool IsTranslatable( string text )
+      {
+         return LanguageHelper.IsTranslatable( text ) && !IsTranslation( text );
+      }
+
+      private bool IsTranslation( string translation )
+      {
+         return _reverseTranslations.ContainsKey( translation );
       }
       }
 
 
       private bool HasTranslated( string key )
       private bool HasTranslated( string key )
@@ -224,8 +233,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints
                job.TranslatedText = PostProcessTranslation( job.Key, translatedText );
                job.TranslatedText = PostProcessTranslation( job.Key, translatedText );
                job.State = TranslationJobState.Succeeded;
                job.State = TranslationJobState.Succeeded;
 
 
-               _ongoingJobs.Remove( job.Key.GetDictionaryLookupKey() );
-               Manager.OngoingTranslations--;
+               RemoveOngoingTranslation( job.Key.GetDictionaryLookupKey() );
 
 
                XuaLogger.Current.Info( $"Completed: '{job.Key.GetDictionaryLookupKey()}' => '{job.TranslatedText}'" );
                XuaLogger.Current.Info( $"Completed: '{job.Key.GetDictionaryLookupKey()}' => '{job.TranslatedText}'" );
 
 
@@ -246,8 +254,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints
 
 
                var key = job.Key.GetDictionaryLookupKey();
                var key = job.Key.GetDictionaryLookupKey();
                AddUnstartedJob( key, job );
                AddUnstartedJob( key, job );
-               _ongoingJobs.Remove( key );
-               Manager.OngoingTranslations--;
+               RemoveOngoingTranslation( key );
             }
             }
 
 
             XuaLogger.Current.Error( "A batch operation failed. Disabling batching and restarting failed jobs." );
             XuaLogger.Current.Error( "A batch operation failed. Disabling batching and restarting failed jobs." );
@@ -263,8 +270,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints
          job.TranslatedText = PostProcessTranslation( job.Key, translatedText );
          job.TranslatedText = PostProcessTranslation( job.Key, translatedText );
          job.State = TranslationJobState.Succeeded;
          job.State = TranslationJobState.Succeeded;
 
 
-         _ongoingJobs.Remove( job.Key.GetDictionaryLookupKey() );
-         Manager.OngoingTranslations--;
+         RemoveOngoingTranslation( job.Key.GetDictionaryLookupKey() );
 
 
          XuaLogger.Current.Info( $"Completed: '{job.Key.GetDictionaryLookupKey()}' => '{job.TranslatedText}'" );
          XuaLogger.Current.Info( $"Completed: '{job.Key.GetDictionaryLookupKey()}' => '{job.TranslatedText}'" );
 
 
@@ -315,8 +321,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints
                job.State = TranslationJobState.Failed;
                job.State = TranslationJobState.Failed;
                job.ErrorMessage = error;
                job.ErrorMessage = error;
 
 
-               _ongoingJobs.Remove( untranslatedText );
-               Manager.OngoingTranslations--;
+               RemoveOngoingTranslation( untranslatedText );
 
 
                RegisterTranslationFailureFor( untranslatedText );
                RegisterTranslationFailureFor( untranslatedText );
 
 
@@ -337,8 +342,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints
 
 
                var key = job.Key.GetDictionaryLookupKey();
                var key = job.Key.GetDictionaryLookupKey();
                AddUnstartedJob( key, job );
                AddUnstartedJob( key, job );
-               _ongoingJobs.Remove( key );
-               Manager.OngoingTranslations--;
+               RemoveOngoingTranslation( key );
             }
             }
 
 
             XuaLogger.Current.Error( "A batch operation failed. Disabling batching and restarting failed jobs." );
             XuaLogger.Current.Error( "A batch operation failed. Disabling batching and restarting failed jobs." );
@@ -350,7 +354,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints
 
 
             if( HasFailedDueToConsecutiveErrors )
             if( HasFailedDueToConsecutiveErrors )
             {
             {
-               XuaLogger.Current.Error( $"{Settings.MaxErrors} or more consecutive errors occurred. Shutting down plugin." );
+               XuaLogger.Current.Error( $"{Settings.MaxErrors} or more consecutive errors occurred. Shutting down translator endpoint." );
 
 
                ClearAllJobs();
                ClearAllJobs();
             }
             }
@@ -403,7 +407,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints
          return AddUnstartedJob( lookupKey, newJob );
          return AddUnstartedJob( lookupKey, newJob );
       }
       }
 
 
-      public bool AssociateWithExistingJobIfPossible( object ui, string key, TranslationResult translationResult, ParserTranslationContext context )
+      private bool AssociateWithExistingJobIfPossible( object ui, string key, TranslationResult translationResult, ParserTranslationContext context )
       {
       {
          if( _unstartedJobs.TryGetValue( key, out TranslationJob unstartedJob ) )
          if( _unstartedJobs.TryGetValue( key, out TranslationJob unstartedJob ) )
          {
          {
@@ -439,6 +443,14 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints
          return false;
          return false;
       }
       }
 
 
+      private void RemoveOngoingTranslation( string key )
+      {
+         if( _ongoingJobs.Remove( key ) )
+         {
+            Manager.OngoingTranslations--;
+         }
+      }
+
       public void ClearAllJobs()
       public void ClearAllJobs()
       {
       {
          var ongoingCount = _ongoingJobs.Count;
          var ongoingCount = _ongoingJobs.Count;
@@ -464,7 +476,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints
          Manager.UnscheduleUnstartedJobs( this );
          Manager.UnscheduleUnstartedJobs( this );
       }
       }
 
 
-      public bool CanTranslate( string untranslatedText )
+      private bool CanTranslate( string untranslatedText )
       {
       {
          if( _failedTranslations.TryGetValue( untranslatedText, out var count ) )
          if( _failedTranslations.TryGetValue( untranslatedText, out var count ) )
          {
          {
@@ -473,7 +485,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints
          return true;
          return true;
       }
       }
 
 
-      public void RegisterTranslationFailureFor( string untranslatedText )
+      private void RegisterTranslationFailureFor( string untranslatedText )
       {
       {
          byte count;
          byte count;
          if( !_failedTranslations.TryGetValue( untranslatedText, out count ) )
          if( !_failedTranslations.TryGetValue( untranslatedText, out count ) )
@@ -496,36 +508,49 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints
 
 
          try
          try
          {
          {
-            bool ok = false;
-            var iterator = Endpoint.Translate( context );
-            if( iterator != null )
+            if( Settings.SimulateDelayedError )
             {
             {
-            TryMe: try
-               {
-                  ok = iterator.MoveNext();
+               yield return new WaitForSeconds( 1 );
 
 
-                  // check for timeout
-                  var now = Time.realtimeSinceStartup;
-                  if( now - startTime > Settings.Timeout )
+               context.FailWithoutThrowing( "Simulating delayed error. Press CTRL+ALT+NP8 to disable!", null );
+            }
+            else if( Settings.SimulateError )
+            {
+               context.FailWithoutThrowing( "Simulating error. Press CTRL+ALT+NP9 to disable!", null );
+            }
+            else
+            {
+               bool ok = false;
+               var iterator = Endpoint.Translate( context );
+               if( iterator != null )
+               {
+               TryMe: try
+                  {
+                     ok = iterator.MoveNext();
+
+                     // check for timeout
+                     var now = Time.realtimeSinceStartup;
+                     if( now - startTime > Settings.Timeout )
+                     {
+                        ok = false;
+                        context.FailWithoutThrowing( $"Timeout occurred during translation (took more than {Settings.Timeout} seconds)", null );
+                     }
+                  }
+                  catch( TranslationContextException )
                   {
                   {
                      ok = false;
                      ok = false;
-                     context.FailWithoutThrowing( $"Timeout occurred during translation (took more than {Settings.Timeout} seconds)", null );
                   }
                   }
-               }
-               catch( TranslationContextException )
-               {
-                  ok = false;
-               }
-               catch( Exception e )
-               {
-                  ok = false;
-                  context.FailWithoutThrowing( "Error occurred during translation.", e );
-               }
+                  catch( Exception e )
+                  {
+                     ok = false;
+                     context.FailWithoutThrowing( "Error occurred during translation.", e );
+                  }
 
 
-               if( ok )
-               {
-                  yield return iterator.Current;
-                  goto TryMe;
+                  if( ok )
+                  {
+                     yield return iterator.Current;
+                     goto TryMe;
+                  }
                }
                }
             }
             }
          }
          }

+ 3 - 6
src/XUnity.AutoTranslator.Plugin.Core/TextTranslationCache.cs

@@ -174,7 +174,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
 
 
       internal void AddTranslationToCache( TranslationKey key, string value )
       internal void AddTranslationToCache( TranslationKey key, string value )
       {
       {
-         AddTranslation( key.GetDictionaryLookupKey(), value );
+         AddTranslationToCache( key.GetDictionaryLookupKey(), value );
       }
       }
 
 
       internal void AddTranslationToCache( string key, string value )
       internal void AddTranslationToCache( string key, string value )
@@ -215,12 +215,9 @@ namespace XUnity.AutoTranslator.Plugin.Core
          return _reverseTranslations.TryGetValue( value, out key );
          return _reverseTranslations.TryGetValue( value, out key );
       }
       }
 
 
-      internal bool IsTranslatable( string str )
+      internal bool IsTranslatable( string text )
       {
       {
-         return LanguageHelper.ContainsLanguageSymbolsForSourceLanguage( str )
-            //&& str.Length <= Settings.MaxCharactersPerTranslation
-            && !IsTranslation( str )
-            && !Settings.IgnoreTextStartingWith.Any( x => str.StartsWithStrict( x ) );
+         return LanguageHelper.IsTranslatable( text ) && !IsTranslation( text );
       }
       }
    }
    }
 }
 }

+ 1 - 1
src/XUnity.AutoTranslator.Plugin.Core/TextTranslationInfo.cs

@@ -96,7 +96,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
          if( graphic is Text )
          if( graphic is Text )
          {
          {
             var ui = (Text)graphic;
             var ui = (Text)graphic;
-
+            
             // text is likely to be longer than there is space for, simply expand out anyway then
             // text is likely to be longer than there is space for, simply expand out anyway then
             var componentWidth = ( (RectTransform)ui.transform ).rect.width;
             var componentWidth = ( (RectTransform)ui.transform ).rect.width;
             var quarterScreenSize = Screen.width / 4;
             var quarterScreenSize = Screen.width / 4;

+ 0 - 3
src/XUnity.AutoTranslator.Plugin.Core/TranslationManager.cs

@@ -191,9 +191,6 @@ namespace XUnity.AutoTranslator.Plugin.Core
          {
          {
             endpoint.ClearAllJobs();
             endpoint.ClearAllJobs();
          }
          }
-
-         UnstartedTranslations = 0;
-         OngoingTranslations = 0;
       }
       }
 
 
       public void RebootAllEndpoints()
       public void RebootAllEndpoints()

+ 21 - 7
src/XUnity.AutoTranslator.Plugin.Core/UI/AggregatedTranslationViewModel.cs

@@ -10,14 +10,13 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
    {
    {
       private List<Translation> _translations;
       private List<Translation> _translations;
       private TranslationAggregatorViewModel _parent;
       private TranslationAggregatorViewModel _parent;
-      private float _started;
+      private float? _started;
 
 
       public AggregatedTranslationViewModel( TranslationAggregatorViewModel parent, List<Translation> translations )
       public AggregatedTranslationViewModel( TranslationAggregatorViewModel parent, List<Translation> translations )
       {
       {
-         _started = Time.realtimeSinceStartup;
          _parent = parent;
          _parent = parent;
          _translations = translations;
          _translations = translations;
-         AggregatedTranslations = parent.Endpoints.Select(
+         AggregatedTranslations = parent.AvailableTranslators.Select(
             x => new IndividualTranslatorTranslationViewModel(
             x => new IndividualTranslatorTranslationViewModel(
                x,
                x,
                new IndividualTranslationViewModel(
                new IndividualTranslationViewModel(
@@ -35,13 +34,28 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
       {
       {
          if( _parent.IsShown )
          if( _parent.IsShown )
          {
          {
-            var timeSince = Time.realtimeSinceStartup - _started;
-            if( timeSince > 1.0f )
+            if( _parent.Manager.OngoingTranslations == 0 && _parent.Manager.UnstartedTranslations == 0 )
             {
             {
-               foreach( var additionTranslation in AggregatedTranslations )
+               if( _started.HasValue )
                {
                {
-                  additionTranslation.Translation.Update();
+                  var timeSince = Time.realtimeSinceStartup - _started.Value;
+                  if( timeSince > 1.0f )
+                  {
+                     foreach( var additionTranslation in AggregatedTranslations )
+                     {
+                        additionTranslation.Translation.StartTranslations();
+                     }
+                  }
                }
                }
+               else
+               {
+                  _started = Time.realtimeSinceStartup;
+               }
+            }
+
+            foreach( var additionTranslation in AggregatedTranslations )
+            {
+               additionTranslation.Translation.CheckCompleted();
             }
             }
          }
          }
       }
       }

+ 20 - 30
src/XUnity.AutoTranslator.Plugin.Core/UI/DropdownGUI.cs

@@ -6,13 +6,14 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
 
 
    internal class DropdownGUI<TDropdownOptionViewModel, TSelection>
    internal class DropdownGUI<TDropdownOptionViewModel, TSelection>
       where TDropdownOptionViewModel : DropdownOptionViewModel<TSelection>
       where TDropdownOptionViewModel : DropdownOptionViewModel<TSelection>
+      where TSelection : class
    {
    {
 
 
       private const float MaxHeight = GUIUtil.RowHeight * 5;
       private const float MaxHeight = GUIUtil.RowHeight * 5;
 
 
       private GUIContent _noSelection;
       private GUIContent _noSelection;
-      private List<TDropdownOptionViewModel> _options;
-      private TDropdownOptionViewModel _currentSelection;
+      private GUIContent _unselect;
+      private DropdownViewModel<TDropdownOptionViewModel, TSelection> _viewModel;
 
 
       private float _x;
       private float _x;
       private float _y;
       private float _y;
@@ -20,35 +21,20 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
       private bool _isShown;
       private bool _isShown;
       private Vector2 _scrollPosition;
       private Vector2 _scrollPosition;
 
 
-      public DropdownGUI( float x, float y, float width, IEnumerable<TDropdownOptionViewModel> options )
+      public DropdownGUI( float x, float y, float width, DropdownViewModel<TDropdownOptionViewModel, TSelection> viewModel )
       {
       {
          _x = x;
          _x = x;
          _y = y;
          _y = y;
          _width = width;
          _width = width;
          _noSelection = new GUIContent( "----", "<b>SELECT TRANSLATOR</b>\nNo translator is currently selected, which means no new translations will be performed. Please select one from the dropdown." );
          _noSelection = new GUIContent( "----", "<b>SELECT TRANSLATOR</b>\nNo translator is currently selected, which means no new translations will be performed. Please select one from the dropdown." );
+         _unselect = new GUIContent( "----", "<b>UNSELECT TRANSLATOR</b>\nThis will unselect the current translator, which means no new translations will be performed." );
 
 
-         _options = new List<TDropdownOptionViewModel>();
-         foreach( var item in options )
-         {
-            if( item.IsSelected() )
-            {
-               _currentSelection = item;
-            }
-            _options.Add( item );
-         }
-      }
-
-      public void Select( TDropdownOptionViewModel option )
-      {
-         if( option.IsSelected() ) return;
-
-         _currentSelection = option;
-         _currentSelection.OnSelected?.Invoke( _currentSelection.Selection );
+         _viewModel = viewModel;
       }
       }
 
 
       public void OnGUI()
       public void OnGUI()
       {
       {
-         bool clicked = GUI.Button( GUIUtil.R( _x, _y, _width, GUIUtil.RowHeight ), _currentSelection?.Text ?? _noSelection, _isShown ? GUIUtil.NoMarginButtonPressedStyle : GUI.skin.button );
+         bool clicked = GUI.Button( GUIUtil.R( _x, _y, _width, GUIUtil.RowHeight ), _viewModel.CurrentSelection?.Text ?? _noSelection, _isShown ? GUIUtil.NoMarginButtonPressedStyle : GUI.skin.button );
          if( clicked )
          if( clicked )
          {
          {
             _isShown = !_isShown;
             _isShown = !_isShown;
@@ -56,7 +42,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
 
 
          if( _isShown )
          if( _isShown )
          {
          {
-            _scrollPosition = ShowDropdown( _x, _y + GUIUtil.RowHeight, _width, GUI.skin.button, _scrollPosition );
+            ShowDropdown( _x, _y + GUIUtil.RowHeight, _width, GUI.skin.button );
          }
          }
 
 
          if( !clicked && Event.current.isMouse )
          if( !clicked && Event.current.isMouse )
@@ -65,21 +51,27 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
          }
          }
       }
       }
 
 
-      private Vector2 ShowDropdown( float x, float y, float width, GUIStyle buttonStyle, Vector2 scrollPosition )
+      private void ShowDropdown( float x, float y, float width, GUIStyle buttonStyle )
       {
       {
-         var rect = GUIUtil.R( x, y, width, _options.Count * GUIUtil.RowHeight > MaxHeight ? MaxHeight : _options.Count * GUIUtil.RowHeight );
+         var rect = GUIUtil.R( x, y, width, _viewModel.Options.Count * GUIUtil.RowHeight > MaxHeight ? MaxHeight : _viewModel.Options.Count * GUIUtil.RowHeight );
 
 
          GUILayout.BeginArea( rect, GUIUtil.NoSpacingBoxStyle );
          GUILayout.BeginArea( rect, GUIUtil.NoSpacingBoxStyle );
-         scrollPosition = GUILayout.BeginScrollView( scrollPosition );
+         _scrollPosition = GUILayout.BeginScrollView( _scrollPosition );
 
 
-         foreach( var option in _options )
+         var style = _viewModel.CurrentSelection == null ? GUIUtil.NoMarginButtonPressedStyle : GUIUtil.NoMarginButtonStyle;
+         if( GUILayout.Button( _unselect, style ) )
          {
          {
-            var style = option.IsSelected() ? GUIUtil.NoMarginButtonPressedStyle : GUIUtil.NoMarginButtonStyle;
+            _viewModel.Select( null );
+            _isShown = false;
+         }
 
 
+         foreach( var option in _viewModel.Options )
+         {
+            style = option.IsSelected() ? GUIUtil.NoMarginButtonPressedStyle : GUIUtil.NoMarginButtonStyle;
             GUI.enabled = option?.IsEnabled() ?? true;
             GUI.enabled = option?.IsEnabled() ?? true;
             if( GUILayout.Button( option.Text, style ) )
             if( GUILayout.Button( option.Text, style ) )
             {
             {
-               Select( option );
+               _viewModel.Select( option );
                _isShown = false;
                _isShown = false;
             }
             }
             GUI.enabled = true;
             GUI.enabled = true;
@@ -87,8 +79,6 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
 
 
          GUILayout.EndScrollView();
          GUILayout.EndScrollView();
          GUILayout.EndArea();
          GUILayout.EndArea();
-
-         return scrollPosition;
       }
       }
    }
    }
 }
 }

+ 37 - 5
src/XUnity.AutoTranslator.Plugin.Core/UI/DropdownOptionViewModel.cs

@@ -1,18 +1,52 @@
 using System;
 using System;
+using System.Collections.Generic;
 using UnityEngine;
 using UnityEngine;
 using XUnity.AutoTranslator.Plugin.Core.Endpoints;
 using XUnity.AutoTranslator.Plugin.Core.Endpoints;
 
 
 namespace XUnity.AutoTranslator.Plugin.Core.UI
 namespace XUnity.AutoTranslator.Plugin.Core.UI
 {
 {
+   internal class DropdownViewModel<TDropdownOptionViewModel, TSelection>
+      where TDropdownOptionViewModel : DropdownOptionViewModel<TSelection>
+      where TSelection : class
+   {
+      private Action<TSelection> _onSelected;
+
+      public DropdownViewModel( IEnumerable<TDropdownOptionViewModel> options, Action<TSelection> onSelected )
+      {
+         _onSelected = onSelected;
+
+         Options = new List<TDropdownOptionViewModel>();
+         foreach( var item in options )
+         {
+            if( item.IsSelected() )
+            {
+               CurrentSelection = item;
+            }
+            Options.Add( item );
+         }
+      }
+
+      public TDropdownOptionViewModel CurrentSelection { get; set; }
+
+      public List<TDropdownOptionViewModel> Options { get; set; }
+
+      public void Select( TDropdownOptionViewModel option )
+      {
+         if( option?.IsSelected() == true ) return;
+
+         CurrentSelection = option;
+         _onSelected?.Invoke( CurrentSelection?.Selection );
+      }
+   }
+
    internal class DropdownOptionViewModel<TSelection>
    internal class DropdownOptionViewModel<TSelection>
    {
    {
-      public DropdownOptionViewModel( string text, Func<bool> isSelected, Func<bool> isEnabled, TSelection selection, Action<TSelection> onSelected )
+      public DropdownOptionViewModel( string text, Func<bool> isSelected, Func<bool> isEnabled, TSelection selection )
       {
       {
          Text = new GUIContent( text );
          Text = new GUIContent( text );
          IsSelected = isSelected;
          IsSelected = isSelected;
          IsEnabled = isEnabled;
          IsEnabled = isEnabled;
          Selection = selection;
          Selection = selection;
-         OnSelected = onSelected;
       }
       }
 
 
       public virtual GUIContent Text { get; set; }
       public virtual GUIContent Text { get; set; }
@@ -22,8 +56,6 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
       public Func<bool> IsSelected { get; set; }
       public Func<bool> IsSelected { get; set; }
 
 
       public TSelection Selection { get; set; }
       public TSelection Selection { get; set; }
-
-      public Action<TSelection> OnSelected { get; set; }
    }
    }
 
 
    internal class TranslatorDropdownOptionViewModel : DropdownOptionViewModel<TranslationEndpointManager>
    internal class TranslatorDropdownOptionViewModel : DropdownOptionViewModel<TranslationEndpointManager>
@@ -32,7 +64,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
       private GUIContent _normal;
       private GUIContent _normal;
       private GUIContent _disabled;
       private GUIContent _disabled;
 
 
-      public TranslatorDropdownOptionViewModel( Func<bool> isSelected, TranslationEndpointManager selection, Action<TranslationEndpointManager> onSelected ) : base( selection.Endpoint.FriendlyName, isSelected, () => selection.Error == null, selection, onSelected )
+      public TranslatorDropdownOptionViewModel( Func<bool> isSelected, TranslationEndpointManager selection ) : base( selection.Endpoint.FriendlyName, isSelected, () => selection.Error == null, selection )
       {
       {
          _selected = new GUIContent( selection.Endpoint.FriendlyName, $"<b>CURRENT TRANSLATOR</b>\n{selection.Endpoint.FriendlyName} is the currently selected translator that will be used to perform translations." );
          _selected = new GUIContent( selection.Endpoint.FriendlyName, $"<b>CURRENT TRANSLATOR</b>\n{selection.Endpoint.FriendlyName} is the currently selected translator that will be used to perform translations." );
          _disabled = new GUIContent( selection.Endpoint.FriendlyName, $"<b>CANNOT SELECT TRANSLATOR</b>\n{selection.Endpoint.FriendlyName} cannot be selected because the initialization failed. {selection.Error?.Message}" );
          _disabled = new GUIContent( selection.Endpoint.FriendlyName, $"<b>CANNOT SELECT TRANSLATOR</b>\n{selection.Endpoint.FriendlyName} cannot be selected because the initialization failed. {selection.Error?.Message}" );

+ 3 - 1
src/XUnity.AutoTranslator.Plugin.Core/UI/GUIUtil.cs

@@ -19,7 +19,9 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
 
 
       public static readonly GUIStyle LabelTranslation = new GUIStyle( GUI.skin.label )
       public static readonly GUIStyle LabelTranslation = new GUIStyle( GUI.skin.label )
       {
       {
-         richText = false
+         richText = false,
+         margin = new RectOffset( GUI.skin.label.margin.left, GUI.skin.label.margin.right, 0, 0 ),
+         padding = new RectOffset( GUI.skin.label.padding.left, GUI.skin.label.padding.right, 0, 0 )
       };
       };
 
 
       public static readonly GUIStyle LabelCenter = new GUIStyle( GUI.skin.label )
       public static readonly GUIStyle LabelCenter = new GUIStyle( GUI.skin.label )

+ 7 - 1
src/XUnity.AutoTranslator.Plugin.Core/UI/IndividualTranslationViewModel.cs

@@ -37,7 +37,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
          }
          }
       }
       }
 
 
-      public void Update()
+      public void StartTranslations()
       {
       {
          if( _translator.IsEnabled )
          if( _translator.IsEnabled )
          {
          {
@@ -50,7 +50,13 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
                   translation.PerformTranslation( _translator.Endpoint );
                   translation.PerformTranslation( _translator.Endpoint );
                }
                }
             }
             }
+         }
+      }
 
 
+      public void CheckCompleted()
+      {
+         if( _translator.IsEnabled )
+         {
             if( !_isTranslated )
             if( !_isTranslated )
             {
             {
                if( _translations.All( x => x.TranslatedText != null ) )
                if( _translations.All( x => x.TranslatedText != null ) )

+ 0 - 2
src/XUnity.AutoTranslator.Plugin.Core/UI/ScrollViewGUI.cs

@@ -5,8 +5,6 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
 {
 {
    internal class ScrollPositioned
    internal class ScrollPositioned
    {
    {
-      private Vector2 _scrollPosition;
-
       public ScrollPositioned()
       public ScrollPositioned()
       {
       {
       }
       }

+ 4 - 1
src/XUnity.AutoTranslator.Plugin.Core/UI/ToggleViewModel.cs

@@ -8,12 +8,13 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
       private GUIContent _enabled;
       private GUIContent _enabled;
       private GUIContent _disabled;
       private GUIContent _disabled;
 
 
-      public ToggleViewModel( string text, string enabledTooltip, string disabledTooltip, Action onToggled, Func<bool> isToggled )
+      public ToggleViewModel( string text, string enabledTooltip, string disabledTooltip, Action onToggled, Func<bool> isToggled, bool enabled = true )
       {
       {
          _enabled = new GUIContent( text, enabledTooltip );
          _enabled = new GUIContent( text, enabledTooltip );
          _disabled = new GUIContent( text, disabledTooltip );
          _disabled = new GUIContent( text, disabledTooltip );
          OnToggled = onToggled;
          OnToggled = onToggled;
          IsToggled = isToggled;
          IsToggled = isToggled;
+         Enabled = enabled;
       }
       }
 
 
       public GUIContent Text
       public GUIContent Text
@@ -28,6 +29,8 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
          }
          }
       }
       }
 
 
+      public bool Enabled { get; set; }
+
       public Action OnToggled { get; set; }
       public Action OnToggled { get; set; }
 
 
       public Func<bool> IsToggled { get; set; }
       public Func<bool> IsToggled { get; set; }

+ 18 - 7
src/XUnity.AutoTranslator.Plugin.Core/UI/TranslationAggregatorOptionsWindow.cs

@@ -7,7 +7,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
    internal class TranslationAggregatorOptionsWindow
    internal class TranslationAggregatorOptionsWindow
    {
    {
       private const int WindowId = 45733721;
       private const int WindowId = 45733721;
-      private const float WindowWidth = 300;
+      private const float WindowWidth = 320;
 
 
       private Rect _windowRect = new Rect( 20, 20, WindowWidth, 400 );
       private Rect _windowRect = new Rect( 20, 20, WindowWidth, 400 );
       private bool _isMouseDownOnWindow = false;
       private bool _isMouseDownOnWindow = false;
@@ -18,13 +18,14 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
       public TranslationAggregatorOptionsWindow( TranslationAggregatorViewModel viewModel )
       public TranslationAggregatorOptionsWindow( TranslationAggregatorViewModel viewModel )
       {
       {
          _viewModel = viewModel;
          _viewModel = viewModel;
-         _toggles = _viewModel.Endpoints.Select( x =>
+         _toggles = _viewModel.AllTranslators.Select( x =>
          new ToggleViewModel(
          new ToggleViewModel(
-            x.Endpoint.Endpoint.FriendlyName,
+            " " + x.Endpoint.Endpoint.FriendlyName,
             null,
             null,
             null,
             null,
             () => x.IsEnabled = !x.IsEnabled,
             () => x.IsEnabled = !x.IsEnabled,
-            () => x.IsEnabled ) ).ToList();
+            () => x.IsEnabled,
+            x.Endpoint.Error == null ) ).ToList();
       }
       }
 
 
       public bool IsShown
       public bool IsShown
@@ -72,22 +73,32 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
          
          
          foreach( var vm in _toggles )
          foreach( var vm in _toggles )
          {
          {
+            var previousEnabled = GUI.enabled;
+
+            GUI.enabled = vm.Enabled;
             var previousValue = vm.IsToggled();
             var previousValue = vm.IsToggled();
             var newValue = GUILayout.Toggle( previousValue, vm.Text );
             var newValue = GUILayout.Toggle( previousValue, vm.Text );
             if( previousValue != newValue )
             if( previousValue != newValue )
             {
             {
                vm.OnToggled();
                vm.OnToggled();
             }
             }
+
+            GUI.enabled = previousEnabled;
          }
          }
 
 
          GUILayout.EndScrollView();
          GUILayout.EndScrollView();
 
 
-         GUILayout.Label( "Height per Translator" );
+         GUILayout.BeginHorizontal();
+         GUILayout.Label( "Height" );
+         _viewModel.Height = GUILayout.HorizontalSlider( _viewModel.Height, 50, 300, GUILayout.MaxWidth( 250 ) );
+         GUILayout.EndHorizontal();
 
 
-         _viewModel.HeightPerTranslator = GUILayout.HorizontalSlider( _viewModel.HeightPerTranslator, 50, 300 );
+         GUILayout.BeginHorizontal();
+         GUILayout.Label( "Width" );
+         _viewModel.Width = GUILayout.HorizontalSlider( _viewModel.Width, 200, 1000, GUILayout.MaxWidth( 250 ) );
+         GUILayout.EndHorizontal();
 
 
          GUI.DragWindow();
          GUI.DragWindow();
-
       }
       }
    }
    }
 }
 }

+ 20 - 8
src/XUnity.AutoTranslator.Plugin.Core/UI/TranslationAggregatorViewModel.cs

@@ -15,24 +15,36 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
       private HashSet<string> _textsToAggregate = new HashSet<string>();
       private HashSet<string> _textsToAggregate = new HashSet<string>();
       private float _lastUpdate = 0.0f;
       private float _lastUpdate = 0.0f;
 
 
-      public TranslationAggregatorViewModel( IEnumerable<TranslationEndpointManager> endpoints )
+      public TranslationAggregatorViewModel( TranslationManager translationManager )
       {
       {
          _translations = new LinkedList<AggregatedTranslationViewModel>();
          _translations = new LinkedList<AggregatedTranslationViewModel>();
 
 
-         HeightPerTranslator = 100; // TODO: Get from config
-         Endpoints = endpoints
-            .Where( x => x.Error == null )
+         Manager = translationManager;
+         Height = 100; // TODO: Get from config
+         Width = 400; // TODO: Get from config
+
+         AllTranslators = translationManager.AllEndpoints
             .Select( x => new TranslatorViewModel( x ) )
             .Select( x => new TranslatorViewModel( x ) )
             .ToList();
             .ToList();
+
+         AvailableTranslators = AllTranslators
+            .Where( x => x.Endpoint.Error == null )
+            .ToList();
       }
       }
 
 
       public bool IsShown { get; set; }
       public bool IsShown { get; set; }
 
 
       public bool IsShowingOptions { get; set; }
       public bool IsShowingOptions { get; set; }
 
 
-      public float HeightPerTranslator { get; set; }
+      public float Height { get; set; }
+
+      public float Width { get; set; }
+
+      public List<TranslatorViewModel> AvailableTranslators { get; }
+
+      public List<TranslatorViewModel> AllTranslators { get; }
 
 
-      public List<TranslatorViewModel> Endpoints { get; }
+      public TranslationManager Manager { get; set; }
 
 
       public AggregatedTranslationViewModel Current => _current?.Value;
       public AggregatedTranslationViewModel Current => _current?.Value;
 
 
@@ -78,8 +90,8 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
                }
                }
             }
             }
 
 
-            // ensure we never have more than 1000
-            if( _translations.Count >= 1000 )
+            // ensure we never have more than 100
+            if( _translations.Count >= 100 )
             {
             {
                var first = _translations.First;
                var first = _translations.First;
                _translations.RemoveFirst();
                _translations.RemoveFirst();

+ 17 - 17
src/XUnity.AutoTranslator.Plugin.Core/UI/TranslationAggregatorWindow.cs

@@ -11,7 +11,6 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
       private static string[] Empty = new string[ 0 ];
       private static string[] Empty = new string[ 0 ];
 
 
       private const int WindowId = 2387602;
       private const int WindowId = 2387602;
-      private const float WindowWidth = 400;
 
 
       private Rect _windowRect;
       private Rect _windowRect;
       private bool _isMouseDownOnWindow = false;
       private bool _isMouseDownOnWindow = false;
@@ -25,11 +24,11 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
       {
       {
          _viewModel = viewModel;
          _viewModel = viewModel;
 
 
-         _windowRect = new Rect( 20, 20, WindowWidth, WindowHeight );
+         _windowRect = new Rect( 20, 20, _viewModel.Width, WindowHeight );
 
 
          _originalText = new ScrollPositioned();
          _originalText = new ScrollPositioned();
          _defaultTranslation = new ScrollPositioned();
          _defaultTranslation = new ScrollPositioned();
-         _translationViews = viewModel.Endpoints.Select( x => new ScrollPositioned<TranslatorViewModel>( x ) ).ToArray();
+         _translationViews = viewModel.AvailableTranslators.Select( x => new ScrollPositioned<TranslatorViewModel>( x ) ).ToArray();
       }
       }
 
 
       public bool IsShown
       public bool IsShown
@@ -38,11 +37,12 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
          set => _viewModel.IsShown = value;
          set => _viewModel.IsShown = value;
       }
       }
 
 
-      private float WindowHeight => ( ( _viewModel.Endpoints.Count( x => x.IsEnabled ) + 2 ) * _viewModel.HeightPerTranslator ) + 30 + GUIUtil.LabelHeight + GUIUtil.ComponentSpacing;
+      private float WindowHeight => ( ( _viewModel.AvailableTranslators.Count( x => x.IsEnabled ) + 2 ) * _viewModel.Height ) + 30 + GUIUtil.LabelHeight + GUIUtil.ComponentSpacing;
 
 
       public void OnGUI()
       public void OnGUI()
       {
       {
          _windowRect.height = WindowHeight;
          _windowRect.height = WindowHeight;
+         _windowRect.width = _viewModel.Width;
          _windowRect = GUI.Window( WindowId, _windowRect, CreateWindowUI, "---- Translation Aggregator ----" );
          _windowRect = GUI.Window( WindowId, _windowRect, CreateWindowUI, "---- Translation Aggregator ----" );
 
 
          if( GUIUtil.IsAnyMouseButtonOrScrollWheelDown )
          if( GUIUtil.IsAnyMouseButtonOrScrollWheelDown )
@@ -78,7 +78,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
       {
       {
          float posy = GUIUtil.WindowTitleClearance + GUIUtil.ComponentSpacing;
          float posy = GUIUtil.WindowTitleClearance + GUIUtil.ComponentSpacing;
 
 
-         if( GUI.Button( GUIUtil.R( WindowWidth - 22, 2, 20, 16 ), "X" ) )
+         if( GUI.Button( GUIUtil.R( _viewModel.Width - 22, 2, 20, 16 ), "X" ) )
          {
          {
             IsShown = false;
             IsShown = false;
          }
          }
@@ -87,10 +87,10 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
          if( current != null )
          if( current != null )
          {
          {
             DrawTextArea( posy, _originalText, "Original Text", current.OriginalTexts );
             DrawTextArea( posy, _originalText, "Original Text", current.OriginalTexts );
-            posy += _viewModel.HeightPerTranslator;
+            posy += _viewModel.Height;
 
 
             DrawTextArea( posy, _defaultTranslation, "Default Translation", current.DefaultTranslations );
             DrawTextArea( posy, _defaultTranslation, "Default Translation", current.DefaultTranslations );
-            posy += _viewModel.HeightPerTranslator;
+            posy += _viewModel.Height;
 
 
             for( int i = 0; i < current.AggregatedTranslations.Count; i++ )
             for( int i = 0; i < current.AggregatedTranslations.Count; i++ )
             {
             {
@@ -104,21 +104,21 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
                      scroller,
                      scroller,
                      aggregatedTranslation.Translator.Endpoint.Endpoint.FriendlyName,
                      aggregatedTranslation.Translator.Endpoint.Endpoint.FriendlyName,
                      aggregatedTranslation.Translation.Translations );
                      aggregatedTranslation.Translation.Translations );
-                  posy += _viewModel.HeightPerTranslator;
+                  posy += _viewModel.Height;
                }
                }
             }
             }
          }
          }
          else
          else
          {
          {
             DrawTextArea( posy, _originalText, "Original Text", Empty );
             DrawTextArea( posy, _originalText, "Original Text", Empty );
-            posy += _viewModel.HeightPerTranslator;
+            posy += _viewModel.Height;
 
 
             DrawTextArea( posy, _defaultTranslation, "Default Translation", Empty );
             DrawTextArea( posy, _defaultTranslation, "Default Translation", Empty );
-            posy += _viewModel.HeightPerTranslator;
+            posy += _viewModel.Height;
 
 
-            for( int i = 0; i < _viewModel.Endpoints.Count; i++ )
+            for( int i = 0; i < _viewModel.AvailableTranslators.Count; i++ )
             {
             {
-               var translator = _viewModel.Endpoints[ i ];
+               var translator = _viewModel.AvailableTranslators[ i ];
                if( translator.IsEnabled )
                if( translator.IsEnabled )
                {
                {
                   var scroller = _translationViews[ i ];
                   var scroller = _translationViews[ i ];
@@ -128,7 +128,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
                      scroller,
                      scroller,
                      translator.Endpoint.Endpoint.FriendlyName,
                      translator.Endpoint.Endpoint.FriendlyName,
                      Empty );
                      Empty );
-                  posy += _viewModel.HeightPerTranslator;
+                  posy += _viewModel.Height;
                }
                }
             }
             }
          }
          }
@@ -156,7 +156,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
          }
          }
 
 
          GUI.enabled = true;
          GUI.enabled = true;
-         if( GUI.Button( GUIUtil.R( GUIUtil.HalfComponentSpacing * 4 + 75 * 3, posy, 75, GUIUtil.LabelHeight ), "Options" ) )
+         if( GUI.Button( GUIUtil.R( _viewModel.Width - GUIUtil.HalfComponentSpacing - 75, posy, 75, GUIUtil.LabelHeight ), "Options" ) )
          {
          {
             _viewModel.IsShowingOptions = true;
             _viewModel.IsShowingOptions = true;
          }
          }
@@ -168,12 +168,12 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
 
 
       private void DrawTextArea( float posy, ScrollPositioned positioned, string title, IEnumerable<string> texts )
       private void DrawTextArea( float posy, ScrollPositioned positioned, string title, IEnumerable<string> texts )
       {
       {
-         GUI.Label( GUIUtil.R( GUIUtil.HalfComponentSpacing + 5, posy + 5, WindowWidth - GUIUtil.ComponentSpacing, GUIUtil.LabelHeight ), title );
+         GUI.Label( GUIUtil.R( GUIUtil.HalfComponentSpacing + 5, posy + 5, _viewModel.Width - GUIUtil.ComponentSpacing, GUIUtil.LabelHeight ), title );
 
 
          posy += GUIUtil.LabelHeight + GUIUtil.HalfComponentSpacing;
          posy += GUIUtil.LabelHeight + GUIUtil.HalfComponentSpacing;
 
 
-         float boxWidth = WindowWidth - GUIUtil.ComponentSpacing;
-         float boxHeight = _viewModel.HeightPerTranslator - GUIUtil.LabelHeight;
+         float boxWidth = _viewModel.Width - GUIUtil.ComponentSpacing;
+         float boxHeight = _viewModel.Height - GUIUtil.LabelHeight;
          GUILayout.BeginArea( GUIUtil.R( GUIUtil.HalfComponentSpacing, posy, boxWidth, boxHeight ) );
          GUILayout.BeginArea( GUIUtil.R( GUIUtil.HalfComponentSpacing, posy, boxWidth, boxHeight ) );
          positioned.ScrollPosition = GUILayout.BeginScrollView( positioned.ScrollPosition, GUI.skin.box );
          positioned.ScrollPosition = GUILayout.BeginScrollView( positioned.ScrollPosition, GUI.skin.box );
 
 

+ 4 - 3
src/XUnity.AutoTranslator.Plugin.Core/UI/XuaViewModel.cs

@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Text;
 using System.Text;
+using XUnity.AutoTranslator.Plugin.Core.Endpoints;
 
 
 namespace XUnity.AutoTranslator.Plugin.Core.UI
 namespace XUnity.AutoTranslator.Plugin.Core.UI
 {
 {
@@ -9,12 +10,12 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
    {
    {
       public XuaViewModel(
       public XuaViewModel(
          List<ToggleViewModel> toggles,
          List<ToggleViewModel> toggles,
-         List<TranslatorDropdownOptionViewModel> endpoints,
+         DropdownViewModel<TranslatorDropdownOptionViewModel, TranslationEndpointManager> dropdown,
          List<ButtonViewModel> commandButtons,
          List<ButtonViewModel> commandButtons,
          List<LabelViewModel> labels )
          List<LabelViewModel> labels )
       {
       {
          Toggles = toggles;
          Toggles = toggles;
-         EndpointOptions = endpoints;
+         Dropdown = dropdown;
          CommandButtons = commandButtons;
          CommandButtons = commandButtons;
          Labels = labels;
          Labels = labels;
       }
       }
@@ -23,7 +24,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
 
 
       public List<ToggleViewModel> Toggles { get; }
       public List<ToggleViewModel> Toggles { get; }
 
 
-      public List<TranslatorDropdownOptionViewModel> EndpointOptions { get; }
+      public DropdownViewModel<TranslatorDropdownOptionViewModel, TranslationEndpointManager> Dropdown { get; }
 
 
       public List<ButtonViewModel> CommandButtons { get; }
       public List<ButtonViewModel> CommandButtons { get; }
 
 

+ 1 - 1
src/XUnity.AutoTranslator.Plugin.Core/UI/XuaWindow.cs

@@ -145,7 +145,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
             posy += GUIUtil.RowHeight + GUIUtil.ComponentSpacing;
             posy += GUIUtil.RowHeight + GUIUtil.ComponentSpacing;
          }
          }
 
 
-         var endpointDropdown = _endpointDropdown ?? ( _endpointDropdown = new DropdownGUI<TranslatorDropdownOptionViewModel, TranslationEndpointManager>( col2x, endpointDropdownPosy, col2, _viewModel.EndpointOptions ) );
+         var endpointDropdown = _endpointDropdown ?? ( _endpointDropdown = new DropdownGUI<TranslatorDropdownOptionViewModel, TranslationEndpointManager>( col2x, endpointDropdownPosy, col2, _viewModel.Dropdown ) );
          endpointDropdown.OnGUI();
          endpointDropdown.OnGUI();
 
 
          GUI.Label( GUIUtil.R( col1x, posy, col12, GUIUtil.RowHeight * 5 ), GUI.tooltip, GUIUtil.LabelRich );
          GUI.Label( GUIUtil.R( col1x, posy, col12, GUIUtil.RowHeight * 5 ), GUI.tooltip, GUIUtil.LabelRich );

+ 8 - 0
src/XUnity.AutoTranslator.Plugin.Core/Utilities/LanguageHelper.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Text;
 using System.Text;
 using XUnity.AutoTranslator.Plugin.Core.Configuration;
 using XUnity.AutoTranslator.Plugin.Core.Configuration;
+using XUnity.AutoTranslator.Plugin.Core.Extensions;
 
 
 namespace XUnity.AutoTranslator.Plugin.Core.Utilities
 namespace XUnity.AutoTranslator.Plugin.Core.Utilities
 {
 {
@@ -53,6 +54,13 @@ namespace XUnity.AutoTranslator.Plugin.Core.Utilities
          return DefaultSymbolCheck( text );
          return DefaultSymbolCheck( text );
       }
       }
 
 
+      public static bool IsTranslatable( string text )
+      {
+         return ContainsLanguageSymbolsForSourceLanguage( text )
+            //&& str.Length <= Settings.MaxCharactersPerTranslation
+            && !Settings.IgnoreTextStartingWith.Any( x => text.StartsWithStrict( x ) );
+      }
+
       public static bool ContainsJapaneseSymbols( string text )
       public static bool ContainsJapaneseSymbols( string text )
       {
       {
          // Unicode Kanji Table:
          // Unicode Kanji Table:

+ 1 - 1
src/XUnity.AutoTranslator.Plugin.Core/XUnity.AutoTranslator.Plugin.Core.csproj

@@ -2,7 +2,7 @@
 
 
    <PropertyGroup>
    <PropertyGroup>
       <TargetFramework>net35</TargetFramework>
       <TargetFramework>net35</TargetFramework>
-      <Version>3.1.0</Version>
+      <Version>3.2.0</Version>
       <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
       <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
    </PropertyGroup>
    </PropertyGroup>
 
 

+ 1 - 1
src/XUnity.AutoTranslator.Plugin.IPA/XUnity.AutoTranslator.Plugin.IPA.csproj

@@ -2,7 +2,7 @@
 
 
    <PropertyGroup>
    <PropertyGroup>
       <TargetFramework>net35</TargetFramework>
       <TargetFramework>net35</TargetFramework>
-      <Version>3.1.0</Version>
+      <Version>3.2.0</Version>
    </PropertyGroup>
    </PropertyGroup>
 
 
    <ItemGroup>
    <ItemGroup>

+ 1 - 1
src/XUnity.AutoTranslator.Plugin.UnityInjector/XUnity.AutoTranslator.Plugin.UnityInjector.csproj

@@ -2,7 +2,7 @@
 
 
    <PropertyGroup>
    <PropertyGroup>
       <TargetFramework>net35</TargetFramework>
       <TargetFramework>net35</TargetFramework>
-      <Version>3.1.0</Version>
+      <Version>3.2.0</Version>
    </PropertyGroup>
    </PropertyGroup>
 
 
    <ItemGroup>
    <ItemGroup>

+ 1 - 1
src/XUnity.AutoTranslator.Setup/XUnity.AutoTranslator.Setup.csproj

@@ -4,7 +4,7 @@
       <OutputType>Exe</OutputType>
       <OutputType>Exe</OutputType>
       <TargetFramework>net40</TargetFramework>
       <TargetFramework>net40</TargetFramework>
       <AssemblyName>SetupReiPatcherAndAutoTranslator</AssemblyName>
       <AssemblyName>SetupReiPatcherAndAutoTranslator</AssemblyName>
-      <Version>3.1.0</Version>
+      <Version>3.2.0</Version>
       <ApplicationIcon>icon.ico</ApplicationIcon>
       <ApplicationIcon>icon.ico</ApplicationIcon>
       <Win32Resource />
       <Win32Resource />
    </PropertyGroup>
    </PropertyGroup>