Эх сурвалжийг харах

improved API, language checks, improved UI

randoman 6 жил өмнө
parent
commit
262a37f53b
26 өөрчлөгдсөн 201 нэмэгдсэн , 133 устгасан
  1. 29 12
      README.md
  2. 2 0
      src/Translators/BaiduTranslate/BaiduTranslateEndpoint.cs
  3. 2 1
      src/Translators/BingTranslate/BingTranslateEndpoint.cs
  4. 2 1
      src/Translators/BingTranslateLegitimate/BingTranslateLegitimateEndpoint.cs
  5. 2 1
      src/Translators/GoogleTranslate/GoogleTranslateEndpoint.cs
  6. 2 1
      src/Translators/GoogleTranslateLegitimate/GoogleTranslateLegitimateEndpoint.cs
  7. 3 2
      src/Translators/WatsonTranslate/WatsonTranslateEndpoint.cs
  8. 3 2
      src/Translators/YandexTranslate/YandexTranslateEndpoint.cs
  9. 21 16
      src/XUnity.AutoTranslator.Plugin.Core/AutoTranslationPlugin.cs
  10. 1 0
      src/XUnity.AutoTranslator.Plugin.Core/Configuration/Settings.cs
  11. 20 2
      src/XUnity.AutoTranslator.Plugin.Core/Endpoints/ConfiguredEndpoint.cs
  12. 0 1
      src/XUnity.AutoTranslator.Plugin.Core/Endpoints/ExtProtocol/ExtProtocolEndpoint.cs
  13. 10 28
      src/XUnity.AutoTranslator.Plugin.Core/Endpoints/Http/HttpEndpoint.cs
  14. 5 0
      src/XUnity.AutoTranslator.Plugin.Core/Endpoints/Http/HttpTranslationContext.cs
  15. 1 0
      src/XUnity.AutoTranslator.Plugin.Core/Endpoints/Http/IHttpTranslationContext.cs
  16. 1 0
      src/XUnity.AutoTranslator.Plugin.Core/Endpoints/ITranslationContext.cs
  17. 49 1
      src/XUnity.AutoTranslator.Plugin.Core/Endpoints/TranslationContext.cs
  18. 1 0
      src/XUnity.AutoTranslator.Plugin.Core/Endpoints/Www/IWwwTranslationContext.cs
  19. 30 55
      src/XUnity.AutoTranslator.Plugin.Core/Endpoints/Www/WwwEndpoint.cs
  20. 5 0
      src/XUnity.AutoTranslator.Plugin.Core/Endpoints/Www/WwwTranslationContext.cs
  21. 1 1
      src/XUnity.AutoTranslator.Plugin.Core/UI/GUIUtil.cs
  22. 1 4
      src/XUnity.AutoTranslator.Plugin.Core/UI/XuaWindow.cs
  23. 1 1
      src/XUnity.AutoTranslator.Plugin.Core/Web/HttpSecurity.cs
  24. 7 4
      src/XUnity.AutoTranslator.Setup/Program.cs
  25. 2 0
      src/XUnity.AutoTranslator.Setup/XUnity.AutoTranslator.Setup.csproj
  26. BIN
      src/XUnity.AutoTranslator.Setup/icon.ico

+ 29 - 12
README.md

@@ -24,12 +24,16 @@ Oh, and it is also capable of doing some basic image loading/dumping. Go to the
 
 
 If you intend on redistributing this plugin as part of a translation suite for a game, please read [this section](#regarding-redistribution).
 If you intend on redistributing this plugin as part of a translation suite for a game, please read [this section](#regarding-redistribution).
 
 
+From version 3.0.0 it is possible to implement custom translators. See [this section](#implementing-a-translator) for more info.
+
 ## Translators
 ## Translators
 The supported online translators are:
 The supported online translators are:
  * [GoogleTranslate](https://anonym.to/?https://translate.google.com/), based on the online Google translation service. Does not require authentication.
  * [GoogleTranslate](https://anonym.to/?https://translate.google.com/), based on the online Google translation service. Does not require authentication.
    * No limitations, but unstable.
    * No limitations, but unstable.
  * [GoogleTranslateLegitimate](https://anonym.to/?https://cloud.google.com/translate/), based on the Google cloud translation API. Requires an API key.
  * [GoogleTranslateLegitimate](https://anonym.to/?https://cloud.google.com/translate/), based on the Google cloud translation API. Requires an API key.
    * Provides trial period of 1 year with $300 credits. Enough for 15 million characters translations.
    * Provides trial period of 1 year with $300 credits. Enough for 15 million characters translations.
+ * [BingTranslate](https://anonym.to/?https://www.bing.com/translator), based on the online Bing translation service. Does not require authentication.
+   * No limitations, but unstable.
  * [BingTranslateLegitimate](https://anonym.to/?https://docs.microsoft.com/en-us/azure/cognitive-services/translator/translator-info-overview), based on the Azure text translation. Requires an API key.
  * [BingTranslateLegitimate](https://anonym.to/?https://docs.microsoft.com/en-us/azure/cognitive-services/translator/translator-info-overview), based on the Azure text translation. Requires an API key.
    * Free up to 2 million characters per month.
    * Free up to 2 million characters per month.
  * [BaiduTranslate](https://anonym.to/?https://fanyi.baidu.com/), based on Baidu translation service. Requires AppId and AppSecret.
  * [BaiduTranslate](https://anonym.to/?https://fanyi.baidu.com/), based on Baidu translation service. Requires AppId and AppSecret.
@@ -38,16 +42,15 @@ The supported online translators are:
    * Free up to 1 million characters per day, but max 10 million characters per month.
    * Free up to 1 million characters per day, but max 10 million characters per month.
  * [WatsonTranslate](https://anonym.to/?https://cloud.ibm.com/apidocs/language-translator), based on IBM's Watson. Requires a URL, username and password.
  * [WatsonTranslate](https://anonym.to/?https://cloud.ibm.com/apidocs/language-translator), based on IBM's Watson. Requires a URL, username and password.
    * Free up to 1 million characters per month.
    * Free up to 1 million characters per month.
- * [ExciteTranslate](https://anonym.to/?https://www.excite.co.jp/world/english_japanese/), based on the Excite online translation service. Does not require authentication.
-   * No limitations, but unstable. Also very slow.
+ * LecPowerTranslator15, based on LEC's Power Translator. Does not require authentication, but does require the software installed.
+   * No limitations.
  * CustomTranslate. Alternatively you can also specify any custom HTTP url that can be used as a translation endpoint (GET request). This must use the query parameters "from", "to" and "text" and return only a string with the result (try HTTP without SSL first, as unity-mono often has issues with SSL).
  * CustomTranslate. Alternatively you can also specify any custom HTTP url that can be used as a translation endpoint (GET request). This must use the query parameters "from", "to" and "text" and return only a string with the result (try HTTP without SSL first, as unity-mono often has issues with SSL).
    * Example Configuration:
    * Example Configuration:
-     * Endpoint=Custom
+     * Endpoint=CustomTranslate
      * [Custom]
      * [Custom]
      * Url=http://my-custom-translation-service.net/translate
      * Url=http://my-custom-translation-service.net/translate
    * Example Request: GET http://my-custom-translation-service.net/translate?from=ja&to=en&text=こんにちは
    * Example Request: GET http://my-custom-translation-service.net/translate?from=ja&to=en&text=こんにちは
    * Example Response (only body): Hello
    * Example Response (only body): Hello
-   * If someone is willing to implement it, this could provide support for offline translators such as ATLAS or LEC as well, since it enables the scenario where you simply run a local web server that can serve translation requests.
  
  
 **Do note that if you use any of the online translators that does not require some form of authentication, that this plugin may break at any time.**
 **Do note that if you use any of the online translators that does not require some form of authentication, that this plugin may break at any time.**
 
 
@@ -100,7 +103,7 @@ The default configuration file, looks as such (2.6.0+):
 
 
 ```ini
 ```ini
 [Service]
 [Service]
-Endpoint=GoogleTranslate         ;Endpoint to use. Can be ["GoogleTranslate", "GoogleTranslateLegitimate", "BingTranslateLegitimate", "BaiduTranslate", "YandexTranslate", "WatsonTranslate", "ExciteTranslate", "*any custom http endpoint*", ""]. If empty, it simply means: Only use cached translations
+Endpoint=GoogleTranslate         ;Endpoint to use. Can be ["GoogleTranslate", "GoogleTranslateLegitimate", "BingTranslate", "BingTranslateLegitimate", "BaiduTranslate", "YandexTranslate", "WatsonTranslate", "LecPowerTranslator15", "CustomTranslate", ""]. If empty, it simply means: Only use cached translations. Additional translators can also be used if downloaded externally.
 
 
 [General]
 [General]
 Language=en                      ;The language to translate into
 Language=en                      ;The language to translate into
@@ -137,6 +140,7 @@ ResizeUILineSpacingScale=        ;A decimal value that the default line spacing
 ForceUIResizing=True             ;Indicates whether the UI resize behavior should be applied to all UI components regardless of them being translated.
 ForceUIResizing=True             ;Indicates whether the UI resize behavior should be applied to all UI components regardless of them being translated.
 IgnoreTextStartingWith=\u180e;   ;Indicates that the plugin should ignore any strings starting with certain characters. This is a list seperated by ';'.
 IgnoreTextStartingWith=\u180e;   ;Indicates that the plugin should ignore any strings starting with certain characters. This is a list seperated by ';'.
 TextGetterCompatibilityMode=False ;Indicates whether or not to enable "Text Getter Compatibility Mode". Should only be enabled if required by the game. 
 TextGetterCompatibilityMode=False ;Indicates whether or not to enable "Text Getter Compatibility Mode". Should only be enabled if required by the game. 
+GameLogTextPaths=                ;Indicates specific paths for game objects that the game uses as "log components", where it continuously appends or prepends text to. Requires expert knowledge to setup. This is a list seperated by ';'.
 
 
 [Texture]
 [Texture]
 TextureDirectory=Translation\Texture ;Directory to dump textures to, and root of directories to load images from
 TextureDirectory=Translation\Texture ;Directory to dump textures to, and root of directories to load images from
@@ -169,6 +173,12 @@ WatsonAPIUrl=                    ;OPTIONAL, needed if WatsonTranslate is configu
 WatsonAPIUsername=               ;OPTIONAL, needed if WatsonTranslate is configured
 WatsonAPIUsername=               ;OPTIONAL, needed if WatsonTranslate is configured
 WatsonAPIPassword=               ;OPTIONAL, needed if WatsonTranslate is configured
 WatsonAPIPassword=               ;OPTIONAL, needed if WatsonTranslate is configured
 
 
+[Custom]
+Url=                             ;Optional, needed if CustomTranslated is configured
+
+[LecPowerTranslator15]
+InstallationPath=                ;Optional, needed if LecPowerTranslator15 is configured
+
 [Debug]
 [Debug]
 EnablePrintHierarchy=False       ;Used for debugging
 EnablePrintHierarchy=False       ;Used for debugging
 EnableConsole=False              ;Enables the console. Do not enable if other plugins (managers) handles this
 EnableConsole=False              ;Enables the console. Do not enable if other plugins (managers) handles this
@@ -227,7 +237,7 @@ The following aims at reducing the number of requests send to the translation en
 
 
 ## Key Mapping
 ## Key Mapping
 The following key inputs are mapped:
 The following key inputs are mapped:
- * ALT + 0: Enable XUnity AutoTranslator UI. (That's a zero, not an O)
+ * ALT + 0: Toggle XUnity AutoTranslator UI. (That's a zero, not an O)
  * ALT + T: Alternate between translated and untranslated versions of all texts provided by this plugin.
  * ALT + T: Alternate between translated and untranslated versions of all texts provided by this plugin.
  * ALT + D: Dump untranslated texts (if no endpoint is configured)
  * ALT + D: Dump untranslated texts (if no endpoint is configured)
  * ALT + R: Reload translation files. Useful if you change the text and texture files on the fly. Not guaranteed to work for all textures.
  * ALT + R: Reload translation files. Useful if you change the text and texture files on the fly. Not guaranteed to work for all textures.
@@ -249,6 +259,7 @@ The file structure should like like this:
 {GameDirectory}/BepInEx/XUnity.AutoTranslator.Plugin.Core.dll
 {GameDirectory}/BepInEx/XUnity.AutoTranslator.Plugin.Core.dll
 {GameDirectory}/BepInEx/XUnity.AutoTranslator.Plugin.Core.BepInEx.dll
 {GameDirectory}/BepInEx/XUnity.AutoTranslator.Plugin.Core.BepInEx.dll
 {GameDirectory}/BepInEx/ExIni.dll
 {GameDirectory}/BepInEx/ExIni.dll
+{GameDirectory}/BepInEx/Translators/{Translator}.dll
 {GameDirectory}/BepInEx/Translation/AnyTranslationFile.txt (these files will be auto generated by plugin!)
 {GameDirectory}/BepInEx/Translation/AnyTranslationFile.txt (these files will be auto generated by plugin!)
 ```
 ```
 
 
@@ -264,6 +275,7 @@ The file structure should like like this
 {GameDirectory}/Plugins/XUnity.AutoTranslator.Plugin.Core.IPA.dll
 {GameDirectory}/Plugins/XUnity.AutoTranslator.Plugin.Core.IPA.dll
 {GameDirectory}/Plugins/0Harmony.dll
 {GameDirectory}/Plugins/0Harmony.dll
 {GameDirectory}/Plugins/ExIni.dll
 {GameDirectory}/Plugins/ExIni.dll
+{GameDirectory}/Plugins/Translators/{Translator}.dll
 {GameDirectory}/Plugins/Translation/AnyTranslationFile.txt (these files will be auto generated by plugin!)
 {GameDirectory}/Plugins/Translation/AnyTranslationFile.txt (these files will be auto generated by plugin!)
  ```
  ```
 
 
@@ -279,6 +291,7 @@ The file structure should like like this
 {GameDirectory}/UnityInjector/XUnity.AutoTranslator.Plugin.Core.UnityInjector.dll
 {GameDirectory}/UnityInjector/XUnity.AutoTranslator.Plugin.Core.UnityInjector.dll
 {GameDirectory}/UnityInjector/0Harmony.dll
 {GameDirectory}/UnityInjector/0Harmony.dll
 {GameDirectory}/UnityInjector/ExIni.dll
 {GameDirectory}/UnityInjector/ExIni.dll
+{GameDirectory}/UnityInjector/Translators/{Translator}.dll
 {GameDirectory}/UnityInjector/Translation/AnyTranslationFile.txt (these files will be auto generated by plugin!)
 {GameDirectory}/UnityInjector/Translation/AnyTranslationFile.txt (these files will be auto generated by plugin!)
  ```
  ```
  
  
@@ -288,7 +301,7 @@ REQUIRES: Nothing, ReiPatcher is provided by this download.
  1. Download XUnity.AutoTranslator-ReiPatcher-{VERSION}.zip from [releases](../../releases).
  1. Download XUnity.AutoTranslator-ReiPatcher-{VERSION}.zip from [releases](../../releases).
  2. Extract directly into the game directory, such that "SetupReiPatcherAndAutoTranslator.exe" is placed alongside other exe files.
  2. Extract directly into the game directory, such that "SetupReiPatcherAndAutoTranslator.exe" is placed alongside other exe files.
  3. Execute "SetupReiPatcherAndAutoTranslator.exe". This will setup up ReiPatcher correctly.
  3. Execute "SetupReiPatcherAndAutoTranslator.exe". This will setup up ReiPatcher correctly.
- 4. Execute the shortcut {GameExeName}.lnk that was created besides existing executables. This will patch and launch the game.
+ 4. Execute the shortcut {GameExeName} (Patch and Run).lnk that was created besides existing executables. This will patch and launch the game.
  5. From now on you can launch the game from the {GameExeName}.exe instead.
  5. From now on you can launch the game from the {GameExeName}.exe instead.
 
 
 The file structure should like like this
 The file structure should like like this
@@ -305,7 +318,8 @@ The file structure should like like this
 {GameDirectory}/{GameExeName}_Data/Managed/XUnity.AutoTranslator.Plugin.Core.dll
 {GameDirectory}/{GameExeName}_Data/Managed/XUnity.AutoTranslator.Plugin.Core.dll
 {GameDirectory}/{GameExeName}_Data/Managed/0Harmony.dll
 {GameDirectory}/{GameExeName}_Data/Managed/0Harmony.dll
 {GameDirectory}/{GameExeName}_Data/Managed/ExIni.dll
 {GameDirectory}/{GameExeName}_Data/Managed/ExIni.dll
-{GameDirectory}/AutoTranslator/AnyTranslationFile.txt (these files will be auto generated by plugin!)
+{GameDirectory}/AutoTranslator/Translators/{Translator}.dll
+{GameDirectory}/AutoTranslator/Translation/AnyTranslationFile.txt (these files will be auto generated by plugin!)
  ```
  ```
 
 
 ## Translating Mods
 ## Translating Mods
@@ -501,9 +515,9 @@ I recommend using this class, or in case that cannot be used, falling back to th
 
 
 ### How-To
 ### How-To
 Follow these steps:
 Follow these steps:
- * Start a new project in Visual Studio 2017 or later. (Be a good boy and choose the "Class Library (.NET Standard)" template. After choosing that, edit the generated .csproj file and change the TargetFramework element to 'net35')
+ * Start a new project in Visual Studio 2017 or later. I recommend using the same name for your assembly/project as the "Id" you are going to use in your interface implementation.
  * Add a reference to the XUnity.AutoTranslator.Plugin.Core.dll
  * Add a reference to the XUnity.AutoTranslator.Plugin.Core.dll
- * Add a reference to UnityEngine.dll (Consider using an old version of this assembly (if `Translators` exists in the Managed folder, it is not an old version!))
+ * Add a reference to UnityEngine.dll (Consider using an old version of this assembly (if `UnityEngine.CoreModule.dll` exists in the Managed folder, it is not an old version!))
  * Create a new class that either:
  * Create a new class that either:
    * Implements the `ITranslateEndpoint` interface
    * Implements the `ITranslateEndpoint` interface
    * Inherits from the `HttpEndpoint` class
    * Inherits from the `HttpEndpoint` class
@@ -545,6 +559,7 @@ Let's take a look at a more advanced example that accesses the web:
 ```C#
 ```C#
 internal class YandexTranslateEndpoint : HttpEndpoint
 internal class YandexTranslateEndpoint : HttpEndpoint
 {
 {
+   private static readonly HashSet<string> SupportedLanguages = new HashSet<string> { "az", "sq", "am", "en", "ar", "hy", "af", "eu", "ba", "be", "bn", "my", "bg", "bs", "cy", "hu", "vi", "ht", "gl", "nl", "mrj", "el", "ka", "gu", "da", "he", "yi", "id", "ga", "it", "is", "es", "kk", "kn", "ca", "ky", "zh", "ko", "xh", "km", "lo", "la", "lv", "lt", "lb", "mg", "ms", "ml", "mt", "mk", "mi", "mr", "mhr", "mn", "de", "ne", "no", "pa", "pap", "fa", "pl", "pt", "ro", "ru", "ceb", "sr", "si", "sk", "sl", "sw", "su", "tg", "th", "tl", "ta", "tt", "te", "tr", "udm", "uz", "uk", "ur", "fi", "fr", "hi", "hr", "cs", "sv", "gd", "et", "eo", "jv", "ja" };
    private static readonly string HttpsServicePointTemplateUrl = "https://translate.yandex.net/api/v1.5/tr.json/translate?key={3}&text={2}&lang={0}-{1}&format=plain";
    private static readonly string HttpsServicePointTemplateUrl = "https://translate.yandex.net/api/v1.5/tr.json/translate?key={3}&text={2}&lang={0}-{1}&format=plain";
 
 
    private string _key;
    private string _key;
@@ -560,8 +575,8 @@ internal class YandexTranslateEndpoint : HttpEndpoint
 
 
       // if the plugin cannot be enabled, simply throw so the user cannot select the plugin
       // if the plugin cannot be enabled, simply throw so the user cannot select the plugin
       if( string.IsNullOrEmpty( _key ) ) throw new Exception( "The YandexTranslate endpoint requires an API key which has not been provided." );
       if( string.IsNullOrEmpty( _key ) ) throw new Exception( "The YandexTranslate endpoint requires an API key which has not been provided." );
-      if( context.SourceLanguage != "ja" ) throw new Exception( "Current implementation only supports japanese-to-english." );
-      if( context.DestinationLanguage != "en" ) throw new Exception( "Current implementation only supports japanese-to-english." );
+      if( !SupportedLanguages.Contains( context.SourceLanguage ) ) throw new Exception( $"The source language '{context.SourceLanguage}' is not supported." );
+      if( !SupportedLanguages.Contains( context.DestinationLanguage ) ) throw new Exception( $"The destination language '{context.DestinationLanguage}' is not supported." );
    }
    }
 
 
    public override void OnCreateRequest( IHttpRequestCreationContext context )
    public override void OnCreateRequest( IHttpRequestCreationContext context )
@@ -619,3 +634,5 @@ After implementing the class, simply build the project and place the generated D
 As mentioned earlier, you  can also use the abstract class `WwwEndpoint` to implement roughly the same thing. However, I do not recommend doing so, unless it is an authenticated service.
 As mentioned earlier, you  can also use the abstract class `WwwEndpoint` to implement roughly the same thing. However, I do not recommend doing so, unless it is an authenticated service.
 
 
 Another way to implement a translator is to implement the `ExtProtocolEndpoint` class. This can be used to delegate the actual translation logic to an external process. Currently there is no documentation on this, but you can take a look at the LEC implementation, which uses it.
 Another way to implement a translator is to implement the `ExtProtocolEndpoint` class. This can be used to delegate the actual translation logic to an external process. Currently there is no documentation on this, but you can take a look at the LEC implementation, which uses it.
+
+If instead, you use the interface, it is also possible to extend from MonoBehaviour to get access to all the normal lifecycle callbacks of Unity components.

+ 2 - 0
src/Translators/BaiduTranslate/BaiduTranslateEndpoint.cs

@@ -37,6 +37,8 @@ namespace BaiduTranslate
          if( string.IsNullOrEmpty( _appSecret ) ) throw new ArgumentException( "The BaiduTranslate endpoint requires an App Secret which has not been provided." );
          if( string.IsNullOrEmpty( _appSecret ) ) throw new ArgumentException( "The BaiduTranslate endpoint requires an App Secret which has not been provided." );
 
 
          context.EnableSslFor( "api.fanyi.baidu.com" );
          context.EnableSslFor( "api.fanyi.baidu.com" );
+
+         // frankly, I have no idea what languages this does, or does not support...
       }
       }
 
 
       public override void OnCreateRequest( IHttpRequestCreationContext context )
       public override void OnCreateRequest( IHttpRequestCreationContext context )

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

@@ -68,7 +68,8 @@ namespace BingTranslate
          // Configure service points / service point manager
          // Configure service points / service point manager
          context.EnableSslFor( "www.bing.com" );
          context.EnableSslFor( "www.bing.com" );
 
 
-         if( !SupportedLanguages.Contains( context.DestinationLanguage ) ) throw new Exception( $"The destination language {context.DestinationLanguage} is not supported." );
+         if( !SupportedLanguages.Contains( context.SourceLanguage ) ) throw new Exception( $"The source language '{context.SourceLanguage}' is not supported." );
+         if( !SupportedLanguages.Contains( context.DestinationLanguage ) ) throw new Exception( $"The destination language '{context.DestinationLanguage}' is not supported." );
       }
       }
 
 
       public override IEnumerator OnBeforeTranslate( IHttpTranslationContext context )
       public override IEnumerator OnBeforeTranslate( IHttpTranslationContext context )

+ 2 - 1
src/Translators/BingTranslateLegitimate/BingTranslateLegitimateEndpoint.cs

@@ -49,7 +49,8 @@ namespace BingTranslateLegitimate
          // Configure service points / service point manager
          // Configure service points / service point manager
          context.EnableSslFor( "api.cognitive.microsofttranslator.com" );
          context.EnableSslFor( "api.cognitive.microsofttranslator.com" );
 
 
-         if( !SupportedLanguages.Contains( context.DestinationLanguage ) ) throw new Exception( $"The destination language {context.DestinationLanguage} is not supported." );
+         if( !SupportedLanguages.Contains( context.SourceLanguage ) ) throw new Exception( $"The source language '{context.SourceLanguage}' is not supported." );
+         if( !SupportedLanguages.Contains( context.DestinationLanguage ) ) throw new Exception( $"The destination language '{context.DestinationLanguage}' is not supported." );
       }
       }
 
 
       public override void OnCreateRequest( IHttpRequestCreationContext context )
       public override void OnCreateRequest( IHttpRequestCreationContext context )

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

@@ -60,7 +60,8 @@ namespace GoogleTranslate
       {
       {
          context.EnableSslFor( "translate.google.com", "translate.googleapis.com" );
          context.EnableSslFor( "translate.google.com", "translate.googleapis.com" );
 
 
-         if( !SupportedLanguages.Contains( context.DestinationLanguage ) ) throw new Exception( $"The destination language {context.DestinationLanguage} is not supported." );
+         if( !SupportedLanguages.Contains( context.SourceLanguage ) ) throw new Exception( $"The source language '{context.SourceLanguage}' is not supported." );
+         if( !SupportedLanguages.Contains( context.DestinationLanguage ) ) throw new Exception( $"The destination language '{context.DestinationLanguage}' is not supported." );
       }
       }
 
 
       public override IEnumerator OnBeforeTranslate( IHttpTranslationContext context )
       public override IEnumerator OnBeforeTranslate( IHttpTranslationContext context )

+ 2 - 1
src/Translators/GoogleTranslateLegitimate/GoogleTranslateLegitimateEndpoint.cs

@@ -41,7 +41,8 @@ namespace GoogleTranslateLegitimate
          // Configure service points / service point manager
          // Configure service points / service point manager
          context.EnableSslFor( "translation.googleapis.com" );
          context.EnableSslFor( "translation.googleapis.com" );
 
 
-         if( !SupportedLanguages.Contains( context.DestinationLanguage ) ) throw new Exception( $"The destination language {context.DestinationLanguage} is not supported." );
+         if( !SupportedLanguages.Contains( context.SourceLanguage ) ) throw new Exception( $"The source language '{context.SourceLanguage}' is not supported." );
+         if( !SupportedLanguages.Contains( context.DestinationLanguage ) ) throw new Exception( $"The destination language '{context.DestinationLanguage}' is not supported." );
       }
       }
 
 
       public override void OnCreateRequest( IHttpRequestCreationContext context )
       public override void OnCreateRequest( IHttpRequestCreationContext context )

+ 3 - 2
src/Translators/WatsonTranslate/WatsonTranslateEndpoint.cs

@@ -18,6 +18,7 @@ namespace WatsonTranslate
 {
 {
    internal class WatsonTranslateEndpoint : WwwEndpoint
    internal class WatsonTranslateEndpoint : WwwEndpoint
    {
    {
+      private static readonly HashSet<string> SupportedLanguagePairs = new HashSet<string> { "ar-en", "ca-es", "zh-en", "zh-TW-en", "cs-en", "da-en", "nl-en", "en-ar", "en-cs", "en-da", "en-de", "en-es", "en-fi", "en-fr", "en-hi", "en-it", "en-ja", "en-ko", "en-nb", "en-nl", "en-pl", "en-pt", "en-ru", "en-sv", "en-tr", "en-zh", "en-zh-TW", "fi-en", "fr-de", "fr-en", "fr-es", "de-en", "de-fr", "de-it", "hi-en", "hu-en", "it-de", "it-en", "ja-en", "ko-en", "nb-en", "pl-en", "pt-en", "ru-en", "es-ca", "es-en", "es-fr", "sv-en", "tr-en" };
       private static readonly string RequestTemplate = "{{\"text\":[\"{2}\"],\"model_id\":\"{0}-{1}\"}}";
       private static readonly string RequestTemplate = "{{\"text\":[\"{2}\"],\"model_id\":\"{0}-{1}\"}}";
 
 
       private string _fullUrl;
       private string _fullUrl;
@@ -41,8 +42,8 @@ namespace WatsonTranslate
 
 
          _fullUrl = _url.TrimEnd( '/' ) + "/v3/translate?version=2018-05-01";
          _fullUrl = _url.TrimEnd( '/' ) + "/v3/translate?version=2018-05-01";
 
 
-         if( context.SourceLanguage != "ja" ) throw new Exception( "Current implementation only supports japanese-to-english." );
-         if( context.DestinationLanguage != "en" ) throw new Exception( "Current implementation only supports japanese-to-english." );
+         var model = context.SourceLanguage + "-" + context.DestinationLanguage;
+         if( !SupportedLanguagePairs.Contains( model ) ) throw new Exception( $"The language model '{model}' is not supported." );
       }
       }
 
 
       public override void OnCreateRequest( IWwwRequestCreationContext context )
       public override void OnCreateRequest( IWwwRequestCreationContext context )

+ 3 - 2
src/Translators/YandexTranslate/YandexTranslateEndpoint.cs

@@ -18,6 +18,7 @@ namespace YandexTranslate
 {
 {
    internal class YandexTranslateEndpoint : HttpEndpoint
    internal class YandexTranslateEndpoint : HttpEndpoint
    {
    {
+      private static readonly HashSet<string> SupportedLanguages = new HashSet<string> { "az", "sq", "am", "en", "ar", "hy", "af", "eu", "ba", "be", "bn", "my", "bg", "bs", "cy", "hu", "vi", "ht", "gl", "nl", "mrj", "el", "ka", "gu", "da", "he", "yi", "id", "ga", "it", "is", "es", "kk", "kn", "ca", "ky", "zh", "ko", "xh", "km", "lo", "la", "lv", "lt", "lb", "mg", "ms", "ml", "mt", "mk", "mi", "mr", "mhr", "mn", "de", "ne", "no", "pa", "pap", "fa", "pl", "pt", "ro", "ru", "ceb", "sr", "si", "sk", "sl", "sw", "su", "tg", "th", "tl", "ta", "tt", "te", "tr", "udm", "uz", "uk", "ur", "fi", "fr", "hi", "hr", "cs", "sv", "gd", "et", "eo", "jv", "ja" };
       private static readonly string HttpsServicePointTemplateUrl = "https://translate.yandex.net/api/v1.5/tr.json/translate?key={3}&text={2}&lang={0}-{1}&format=plain";
       private static readonly string HttpsServicePointTemplateUrl = "https://translate.yandex.net/api/v1.5/tr.json/translate?key={3}&text={2}&lang={0}-{1}&format=plain";
 
 
       private string _key;
       private string _key;
@@ -33,8 +34,8 @@ namespace YandexTranslate
 
 
          // if the plugin cannot be enabled, simply throw so the user cannot select the plugin
          // if the plugin cannot be enabled, simply throw so the user cannot select the plugin
          if( string.IsNullOrEmpty( _key ) ) throw new Exception( "The YandexTranslate endpoint requires an API key which has not been provided." );
          if( string.IsNullOrEmpty( _key ) ) throw new Exception( "The YandexTranslate endpoint requires an API key which has not been provided." );
-         if( context.SourceLanguage != "ja" ) throw new Exception( "Current implementation only supports japanese-to-english." );
-         if( context.DestinationLanguage != "en" ) throw new Exception( "Current implementation only supports japanese-to-english." );
+         if( !SupportedLanguages.Contains( context.SourceLanguage ) ) throw new Exception( $"The source language '{context.SourceLanguage}' is not supported." );
+         if( !SupportedLanguages.Contains( context.DestinationLanguage ) ) throw new Exception( $"The destination language '{context.DestinationLanguage}' is not supported." );
       }
       }
 
 
       public override void OnCreateRequest( IHttpRequestCreationContext context )
       public override void OnCreateRequest( IHttpRequestCreationContext context )

+ 21 - 16
src/XUnity.AutoTranslator.Plugin.Core/AutoTranslationPlugin.cs

@@ -98,8 +98,6 @@ namespace XUnity.AutoTranslator.Plugin.Core
       private Dictionary<string, byte[]> _translatedImages = new Dictionary<string, byte[]>( StringComparer.InvariantCultureIgnoreCase );
       private Dictionary<string, byte[]> _translatedImages = new Dictionary<string, byte[]>( StringComparer.InvariantCultureIgnoreCase );
       private HashSet<string> _untranslatedImages = new HashSet<string>();
       private HashSet<string> _untranslatedImages = new HashSet<string>();
 
 
-      private readonly List<string> _kickedOff = new List<string>();
-
       private Component _advEngine;
       private Component _advEngine;
       private float? _nextAdvUpdate;
       private float? _nextAdvUpdate;
 
 
@@ -2076,7 +2074,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
                {
                {
                   var key = kvp.Key;
                   var key = kvp.Key;
                   var job = kvp.Value;
                   var job = kvp.Value;
-                  _kickedOff.Add( key );
+                  _unstartedJobs.Remove( key );
 
 
                   if( !job.AnyComponentsStillHasOriginalUntranslatedTextOrContextual() ) continue;
                   if( !job.AnyComponentsStillHasOriginalUntranslatedTextOrContextual() ) continue;
 
 
@@ -2090,20 +2088,29 @@ namespace XUnity.AutoTranslator.Plugin.Core
 
 
                   var untranslatedText = batch.GetFullTranslationKey();
                   var untranslatedText = batch.GetFullTranslationKey();
                   XuaLogger.Current.Debug( "Starting translation for: " + untranslatedText );
                   XuaLogger.Current.Debug( "Starting translation for: " + untranslatedText );
-                  StartCoroutine( _endpoint.Translate( untranslatedText, Settings.FromLanguage, Settings.Language, translatedText => OnBatchTranslationCompleted( batch, translatedText ),
-                  ( msg, e ) => OnTranslationFailed( batch, msg, e ) ) );
+                  StartCoroutine(
+                     _endpoint.Translate(
+                        untranslatedText,
+                        Settings.FromLanguage,
+                        Settings.Language,
+                        translatedText => OnBatchTranslationCompleted( batch, translatedText ),
+                        ( msg, e ) => OnTranslationFailed( batch, msg, e ) ) );
                }
                }
             }
             }
          }
          }
          else
          else
          {
          {
-            foreach( var kvp in _unstartedJobs )
+            while( _unstartedJobs.Count > 0 )
             {
             {
                if( _endpoint.IsBusy ) break;
                if( _endpoint.IsBusy ) break;
 
 
+               // ERROR ERROR ERROR --- MEGA BUG MEGA BUG, MUST REMOVE FROM _unstartedJobs INSIDE LOOP!!!!
+
+               var kvp = _unstartedJobs.FirstOrDefault();
+
                var key = kvp.Key;
                var key = kvp.Key;
                var job = kvp.Value;
                var job = kvp.Value;
-               _kickedOff.Add( key );
+               _unstartedJobs.Remove( key );
 
 
                // lets see if the text should still be translated before kicking anything off
                // lets see if the text should still be translated before kicking anything off
                if( !job.AnyComponentsStillHasOriginalUntranslatedTextOrContextual() ) continue;
                if( !job.AnyComponentsStillHasOriginalUntranslatedTextOrContextual() ) continue;
@@ -2112,17 +2119,15 @@ namespace XUnity.AutoTranslator.Plugin.Core
 
 
                var untranslatedText = job.Key.GetDictionaryLookupKey();
                var untranslatedText = job.Key.GetDictionaryLookupKey();
                XuaLogger.Current.Debug( "Starting translation for: " + untranslatedText );
                XuaLogger.Current.Debug( "Starting translation for: " + untranslatedText );
-               StartCoroutine( _endpoint.Translate( untranslatedText, Settings.FromLanguage, Settings.Language, translatedText => OnSingleTranslationCompleted( job, translatedText ),
-               ( msg, e ) => OnTranslationFailed( job, msg, e ) ) );
+               StartCoroutine(
+                  _endpoint.Translate(
+                     untranslatedText,
+                     Settings.FromLanguage,
+                     Settings.Language,
+                     translatedText => OnSingleTranslationCompleted( job, translatedText ),
+                     ( msg, e ) => OnTranslationFailed( job, msg, e ) ) );
             }
             }
          }
          }
-
-         for( int i = 0 ; i < _kickedOff.Count ; i++ )
-         {
-            _unstartedJobs.Remove( _kickedOff[ i ] );
-         }
-
-         _kickedOff.Clear();
       }
       }
 
 
       private void OnBatchTranslationCompleted( TranslationBatch batch, string translatedTextBatch )
       private void OnBatchTranslationCompleted( TranslationBatch batch, string translatedTextBatch )

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

@@ -108,6 +108,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Configuration
          }
          }
          catch( Exception e )
          catch( Exception e )
          {
          {
+            ApplicationName = "Unknown";
             XuaLogger.Current.Error( e, "An error occurred while getting application name." );
             XuaLogger.Current.Error( e, "An error occurred while getting application name." );
          }
          }
 
 

+ 20 - 2
src/XUnity.AutoTranslator.Plugin.Core/Endpoints/ConfiguredEndpoint.cs

@@ -24,14 +24,32 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints
       {
       {
          var context = new TranslationContext( untranslatedText, from, to, success, failure );
          var context = new TranslationContext( untranslatedText, from, to, success, failure );
          _ongoingTranslations++;
          _ongoingTranslations++;
+
+         bool ok = false;
+         IEnumerator iterator = null;
          try
          try
          {
          {
-            var iterator = Endpoint.Translate( context );
+            iterator = Endpoint.Translate( context );
             if( iterator != null )
             if( iterator != null )
             {
             {
-               while( iterator.MoveNext() )
+               TryMe: try
+               {
+                  ok = iterator.MoveNext();
+               }
+               catch( TranslationContextException )
+               {
+                  ok = false;
+               }
+               catch( Exception e )
+               {
+                  ok = false;
+                  context.FailWithoutThrowing( "Error occurred during translation.", e );
+               }
+
+               if( ok )
                {
                {
                   yield return iterator.Current;
                   yield return iterator.Current;
+                  goto TryMe;
                }
                }
             }
             }
          }
          }

+ 0 - 1
src/XUnity.AutoTranslator.Plugin.Core/Endpoints/ExtProtocol/ExtProtocolEndpoint.cs

@@ -140,7 +140,6 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints.ExtProtocol
          if( _failed )
          if( _failed )
          {
          {
             context.Fail( "Translator failed.", null );
             context.Fail( "Translator failed.", null );
-            yield break;
          }
          }
 
 
          var result = new StreamReaderResult();
          var result = new StreamReaderResult();

+ 10 - 28
src/XUnity.AutoTranslator.Plugin.Core/Endpoints/Http/HttpEndpoint.cs

@@ -37,26 +37,17 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints.Http
             }
             }
          }
          }
 
 
-         XUnityWebResponse response = null;
-         try
+         // prepare request
+         OnCreateRequest( httpContext );
+         if( httpContext.Request == null )
          {
          {
-            // prepare request
-            OnCreateRequest( httpContext );
-            if( httpContext.Request == null )
-            {
-               httpContext.Fail( "No request object was provided by the translator.", null );
-               yield break;
-            }
-
-            var client = new XUnityWebClient();
-            response = client.Send( httpContext.Request );
-         }
-         catch( Exception e )
-         {
-            httpContext.Fail( "Error occurred while setting up translation request.", e );
-            yield break;
+            httpContext.Fail( "No request object was provided by the translator.", null );
          }
          }
 
 
+         // execute request
+         var client = new XUnityWebClient();
+         var response = client.Send( httpContext.Request );
+
          // wait for completion
          // wait for completion
          if( Features.SupportsCustomYieldInstruction )
          if( Features.SupportsCustomYieldInstruction )
          {
          {
@@ -77,25 +68,16 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints.Http
          if( response.Error != null )
          if( response.Error != null )
          {
          {
             httpContext.Fail( "Error occurred while retrieving translation.", response.Error );
             httpContext.Fail( "Error occurred while retrieving translation.", response.Error );
-            yield break;
          }
          }
 
 
          // failure
          // failure
          if( response.Data == null )
          if( response.Data == null )
          {
          {
             httpContext.Fail( "Error occurred while retrieving translation. Nothing was returned.", null );
             httpContext.Fail( "Error occurred while retrieving translation. Nothing was returned.", null );
-            yield break;
          }
          }
 
 
-         try
-         {
-            // attempt to extract translation from data
-            OnExtractTranslation( httpContext );
-         }
-         catch( Exception e )
-         {
-            httpContext.Fail( "Error occurred while retrieving translation.", e );
-         }
+         // extract text
+         OnExtractTranslation( httpContext );
       }
       }
    }
    }
 }
 }

+ 5 - 0
src/XUnity.AutoTranslator.Plugin.Core/Endpoints/Http/HttpTranslationContext.cs

@@ -26,6 +26,11 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints.Http
          _context.Fail( reason, exception );
          _context.Fail( reason, exception );
       }
       }
 
 
+      public void Fail( string reason )
+      {
+         _context.Fail( reason );
+      }
+
       void IHttpRequestCreationContext.Complete( XUnityWebRequest request )
       void IHttpRequestCreationContext.Complete( XUnityWebRequest request )
       {
       {
          Request = request;
          Request = request;

+ 1 - 0
src/XUnity.AutoTranslator.Plugin.Core/Endpoints/Http/IHttpTranslationContext.cs

@@ -9,5 +9,6 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints.Http
       string DestinationLanguage { get; }
       string DestinationLanguage { get; }
 
 
       void Fail( string reason, Exception exception );
       void Fail( string reason, Exception exception );
+      void Fail( string reason );
    }
    }
 }
 }

+ 1 - 0
src/XUnity.AutoTranslator.Plugin.Core/Endpoints/ITranslationContext.cs

@@ -10,5 +10,6 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints
 
 
       void Complete( string translatedText );
       void Complete( string translatedText );
       void Fail( string reason, Exception exception );
       void Fail( string reason, Exception exception );
+      void Fail( string reason );
    }
    }
 }
 }

+ 49 - 1
src/XUnity.AutoTranslator.Plugin.Core/Endpoints/TranslationContext.cs

@@ -30,6 +30,8 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints
 
 
       public void Complete( string translatedText )
       public void Complete( string translatedText )
       {
       {
+         if( IsDone ) return;
+
          try
          try
          {
          {
             if( !string.IsNullOrEmpty( translatedText ) )
             if( !string.IsNullOrEmpty( translatedText ) )
@@ -49,6 +51,40 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints
 
 
       public void Fail( string reason, Exception exception )
       public void Fail( string reason, Exception exception )
       {
       {
+         if( IsDone ) return;
+
+         try
+         {
+            _fail( reason, exception );
+
+            throw new TranslationContextException();
+         }
+         finally
+         {
+            IsDone = true;
+         }
+      }
+
+      public void Fail( string reason )
+      {
+         if( IsDone ) return;
+
+         try
+         {
+            _fail( reason, null );
+
+            throw new TranslationContextException();
+         }
+         finally
+         {
+            IsDone = true;
+         }
+      }
+
+      internal void FailWithoutThrowing( string reason, Exception exception )
+      {
+         if( IsDone ) return;
+
          try
          try
          {
          {
             _fail( reason, exception );
             _fail( reason, exception );
@@ -63,8 +99,20 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints
       {
       {
          if( !IsDone )
          if( !IsDone )
          {
          {
-            Fail( "The translation request was not completed before returning from translator.", null );
+            FailWithoutThrowing( "The translation request was not completed before returning from translator.", null );
          }
          }
       }
       }
    }
    }
+
+
+   [Serializable]
+   internal class TranslationContextException : Exception
+   {
+      public TranslationContextException() { }
+      public TranslationContextException( string message ) : base( message ) { }
+      public TranslationContextException( string message, Exception inner ) : base( message, inner ) { }
+      protected TranslationContextException(
+       System.Runtime.Serialization.SerializationInfo info,
+       System.Runtime.Serialization.StreamingContext context ) : base( info, context ) { }
+   }
 }
 }

+ 1 - 0
src/XUnity.AutoTranslator.Plugin.Core/Endpoints/Www/IWwwTranslationContext.cs

@@ -9,5 +9,6 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints.Www
       string DestinationLanguage { get; }
       string DestinationLanguage { get; }
 
 
       void Fail( string reason, Exception exception );
       void Fail( string reason, Exception exception );
+      void Fail( string reason );
    }
    }
 }
 }

+ 30 - 55
src/XUnity.AutoTranslator.Plugin.Core/Endpoints/Www/WwwEndpoint.cs

@@ -43,75 +43,50 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints.Www
             }
             }
          }
          }
 
 
-         object www = null;
-         try
+         // prepare request
+         OnCreateRequest( wwwContext );
+         if( wwwContext.RequestInfo == null )
          {
          {
-            // prepare request
-            OnCreateRequest( wwwContext );
-            if( wwwContext.RequestInfo == null )
-            {
-               wwwContext.Fail( "No request object was provided by the translator.", null );
-               yield break;
-            }
+            wwwContext.Fail( "No request object was provided by the translator.", null );
+         }
 
 
-            var request = wwwContext.RequestInfo;
-            var url = request.Address;
-            var data = request.Data;
-            var headers = request.Headers;
+         var request = wwwContext.RequestInfo;
+         var url = request.Address;
+         var data = request.Data;
+         var headers = request.Headers;
+
+         // execute request
+         var www = WwwConstructor.Invoke( new object[] { request.Address, data != null ? Encoding.UTF8.GetBytes( data ) : null, headers } );
+
+         // wait for completion
+         yield return www;
 
 
-            // execute request
-            www = WwwConstructor.Invoke( new object[] { request.Address, data != null ? Encoding.UTF8.GetBytes( data ) : null, headers } );
+         // extract error
+         string error = null;
+         try
+         {
+            error = (string)AccessTools.Property( Constants.ClrTypes.WWW, "error" ).GetValue( www, null );
          }
          }
          catch( Exception e )
          catch( Exception e )
          {
          {
-            wwwContext.Fail( "Error occurred while setting up translation request.", e );
-            yield break;
+            error = e.ToString();
          }
          }
 
 
-         if( www == null )
+         if( error != null )
          {
          {
-            wwwContext.Fail( "Unexpected error occurred while retrieving translation.", null );
-            yield break;
+            wwwContext.Fail( "Error occurred while retrieving translation." + Environment.NewLine + error, null );
          }
          }
 
 
-         // wait for completion
-         yield return www;
-
-         try
+         // extract text
+         var text = (string)AccessTools.Property( Constants.ClrTypes.WWW, "text" ).GetValue( www, null );
+         if( text == null )
          {
          {
-            // extract error
-            string error = null;
-            try
-            {
-               error = (string)AccessTools.Property( Constants.ClrTypes.WWW, "error" ).GetValue( www, null );
-            }
-            catch( Exception e )
-            {
-               error = e.ToString();
-            }
-
-            if( error != null )
-            {
-               wwwContext.Fail( "Error occurred while retrieving translation." + Environment.NewLine + error, null );
-               yield break;
-            }
-
-            // extract text
-            var text = (string)AccessTools.Property( Constants.ClrTypes.WWW, "text" ).GetValue( www, null );
-            if( text == null )
-            {
-               wwwContext.Fail( "Error occurred while extracting text from response.", null );
-               yield break;
-            }
+            wwwContext.Fail( "Error occurred while extracting text from response.", null );
+         }
 
 
-            wwwContext.ResponseData = text;
+         wwwContext.ResponseData = text;
 
 
-            OnExtractTranslation( wwwContext );
-         }
-         catch( Exception e )
-         {
-            wwwContext.Fail( "Error occurred while retrieving translation.", e );
-         }
+         OnExtractTranslation( wwwContext );
       }
       }
    }
    }
 }
 }

+ 5 - 0
src/XUnity.AutoTranslator.Plugin.Core/Endpoints/Www/WwwTranslationContext.cs

@@ -34,5 +34,10 @@ namespace XUnity.AutoTranslator.Plugin.Core.Endpoints.Www
       {
       {
          _context.Fail( reason, exception );
          _context.Fail( reason, exception );
       }
       }
+
+      public void Fail( string reason )
+      {
+         _context.Fail( reason );
+      }
    }
    }
 }
 }

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

@@ -8,7 +8,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
 {
 {
    internal static class GUIUtil
    internal static class GUIUtil
    {
    {
-      public const int WindowTitleClearance = 15;
+      public const int WindowTitleClearance = 10;
       public const int ComponentSpacing = 10;
       public const int ComponentSpacing = 10;
       public const int LabelWidth = 60;
       public const int LabelWidth = 60;
       public const int LabelHeight = 21;
       public const int LabelHeight = 21;

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

@@ -8,7 +8,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
 {
 {
    internal class XuaWindow
    internal class XuaWindow
    {
    {
-      private const int WindowHeight = 510;
+      private const int WindowHeight = 480;
       private const int WindowWidth = 320;
       private const int WindowWidth = 320;
 
 
       private const int AvailableWidth = WindowWidth - ( GUIUtil.ComponentSpacing * 2 );
       private const int AvailableWidth = WindowWidth - ( GUIUtil.ComponentSpacing * 2 );
@@ -133,9 +133,6 @@ namespace XUnity.AutoTranslator.Plugin.Core.UI
             posy += GUIUtil.RowHeight + GUIUtil.ComponentSpacing;
             posy += GUIUtil.RowHeight + GUIUtil.ComponentSpacing;
          }
          }
 
 
-         GUI.Label( GUIUtil.R( col1x, posy, col12, GUIUtil.LabelHeight ), "<b>_______________________________________</b>", GUIUtil.LabelCenter );
-         posy += GUIUtil.RowHeight + GUIUtil.ComponentSpacing;
-
          var endpointDropdown = _endpointDropdown ?? ( _endpointDropdown = new DropdownGUI<TranslatorDropdownOptionViewModel, ConfiguredEndpoint>( col2x, endpointDropdownPosy, col2, _endpointOptions ) );
          var endpointDropdown = _endpointDropdown ?? ( _endpointDropdown = new DropdownGUI<TranslatorDropdownOptionViewModel, ConfiguredEndpoint>( col2x, endpointDropdownPosy, col2, _endpointOptions ) );
          endpointDropdown.OnGUI();
          endpointDropdown.OnGUI();
 
 

+ 1 - 1
src/XUnity.AutoTranslator.Plugin.Core/Web/HttpSecurity.cs

@@ -8,7 +8,7 @@ using System.Text;
 
 
 namespace XUnity.AutoTranslator.Plugin.Core.Web
 namespace XUnity.AutoTranslator.Plugin.Core.Web
 {
 {
-   public class HttpSecurity
+   internal class HttpSecurity
    {
    {
       public readonly HashSet<string> _hosts = new HashSet<string>();
       public readonly HashSet<string> _hosts = new HashSet<string>();
 
 

+ 7 - 4
src/XUnity.AutoTranslator.Setup/Program.cs

@@ -91,12 +91,14 @@ namespace XUnity.AutoTranslator.Setup
                Console.WriteLine( iniInfo.Name + " already exists. skipping..." );
                Console.WriteLine( iniInfo.Name + " already exists. skipping..." );
             }
             }
 
 
-            var lnkInfo = new FileInfo( Path.GetFileNameWithoutExtension( launcher.Executable.Name ) + ".lnk" );
+            var shortcutPath = Path.GetFileNameWithoutExtension( launcher.Executable.Name ) + " (Patch and Run).lnk";
+            var lnkInfo = new FileInfo( shortcutPath );
             if( !lnkInfo.Exists )
             if( !lnkInfo.Exists )
             {
             {
                // create shortcuts
                // create shortcuts
                CreateShortcut(
                CreateShortcut(
-                  Path.GetFileNameWithoutExtension( launcher.Executable.Name ) + ".lnk",
+                  launcher.Executable.FullName,
+                  shortcutPath,
                   gamePath,
                   gamePath,
                   Path.Combine( reiPath, "ReiPatcher.exe" ) );
                   Path.Combine( reiPath, "ReiPatcher.exe" ) );
 
 
@@ -132,7 +134,7 @@ namespace XUnity.AutoTranslator.Setup
          }
          }
       }
       }
 
 
-      public static void CreateShortcut( string shortcutName, string shortcutPath, string targetFileLocation )
+      public static void CreateShortcut( string gameExePath, string shortcutName, string shortcutPath, string targetFileLocation )
       {
       {
          string shortcutLocation = Path.Combine( shortcutPath, shortcutName );
          string shortcutLocation = Path.Combine( shortcutPath, shortcutName );
 
 
@@ -147,8 +149,9 @@ namespace XUnity.AutoTranslator.Setup
 
 
          // Set the .lnk file properties
          // Set the .lnk file properties
          lnk.Path = targetFileLocation;
          lnk.Path = targetFileLocation;
-         lnk.Arguments = "-c \"" + Path.GetFileNameWithoutExtension( shortcutName ) + ".ini\"";
+         lnk.Arguments = "-c \"" + Path.GetFileNameWithoutExtension( gameExePath ) + ".ini\"";
          lnk.WorkingDirectory = Path.GetDirectoryName( targetFileLocation );
          lnk.WorkingDirectory = Path.GetDirectoryName( targetFileLocation );
+         lnk.SetIconLocation( gameExePath.Replace( '\\', '/' ), 0 );
 
 
          lnk.Save( shortcutName );
          lnk.Save( shortcutName );
       }
       }

+ 2 - 0
src/XUnity.AutoTranslator.Setup/XUnity.AutoTranslator.Setup.csproj

@@ -5,6 +5,8 @@
       <TargetFramework>net40</TargetFramework>
       <TargetFramework>net40</TargetFramework>
       <AssemblyName>SetupReiPatcherAndAutoTranslator</AssemblyName>
       <AssemblyName>SetupReiPatcherAndAutoTranslator</AssemblyName>
       <Version>3.0.0</Version>
       <Version>3.0.0</Version>
+      <ApplicationIcon>icon.ico</ApplicationIcon>
+      <Win32Resource />
    </PropertyGroup>
    </PropertyGroup>
 
 
    <ItemGroup>
    <ItemGroup>

BIN
src/XUnity.AutoTranslator.Setup/icon.ico