|
@@ -13,6 +13,7 @@
|
|
|
* [Regarding Redistribution](#regarding-redistribution)
|
|
|
* [Texture Translation](#texture-translation)
|
|
|
* [Integrating with Auto Translator](#integrating-with-auto-translator)
|
|
|
+ * [Implementing a Translator](#implementing-a-translator)
|
|
|
|
|
|
## Introduction
|
|
|
This is a plugin that is capable of using various online translators to provide on-the-fly translations for various Unity-based games.
|
|
@@ -23,30 +24,38 @@ 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).
|
|
|
|
|
|
+From version 3.0.0 it is possible to implement custom translators. See [this section](#implementing-a-translator) for more info.
|
|
|
+
|
|
|
## Translators
|
|
|
-The supported online translators are:
|
|
|
+The supported translators are:
|
|
|
* [GoogleTranslate](https://anonym.to/?https://translate.google.com/), based on the online Google translation service. Does not require authentication.
|
|
|
* No limitations, but unstable.
|
|
|
* [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.
|
|
|
+ * [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.
|
|
|
* 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.
|
|
|
* Not sure on quotas on this one.
|
|
|
* [YandexTranslate](https://anonym.to/?https://tech.yandex.com/translate/), based on the Yandex translation service. Requires an API key.
|
|
|
* 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 and an API key.
|
|
|
* 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.
|
|
|
- * Custom. 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 Endpoint=http://my-custom-translation-service.net/translate
|
|
|
+ * 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).
|
|
|
+ * Example Configuration:
|
|
|
+ * Endpoint=CustomTranslate
|
|
|
+ * [Custom]
|
|
|
+ * Url=http://my-custom-translation-service.net/translate
|
|
|
* Example Request: GET http://my-custom-translation-service.net/translate?from=ja&to=en&text=こんにちは
|
|
|
* 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.**
|
|
|
|
|
|
+Since 3.0.0, you can also implement your own translators. To do so, follow the instruction [here](#implementing-a-translator).
|
|
|
+
|
|
|
### About Authenticated Translators
|
|
|
If you decide to use an authenticated service *do not ever share your key or secret*. If you do so by accident, you should revoke it immediately. Most, if not all services provides an option for this.
|
|
|
|
|
@@ -90,19 +99,19 @@ 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
|
|
|
-The default configuration file, looks as such (2.6.0+):
|
|
|
+The default configuration file, looks as such:
|
|
|
|
|
|
```ini
|
|
|
[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]
|
|
|
Language=en ;The language to translate into
|
|
|
FromLanguage=ja ;The original language of the game
|
|
|
|
|
|
[Files]
|
|
|
-Directory=Translation ;Directory to search for cached translation files
|
|
|
-OutputFile=Translation\_AutoGeneratedTranslations.{lang}.txt ;File to insert generated translations into
|
|
|
+Directory=Translation ;Directory to search for cached translation files. Can use placeholder: {GameExeName}
|
|
|
+OutputFile=Translation\_AutoGeneratedTranslations.{lang}.txt ;File to insert generated translations into. Can use placeholders: {GameExeName}, {lang}
|
|
|
|
|
|
[TextFrameworks]
|
|
|
EnableUGUI=True ;Enable or disable UGUI translation
|
|
@@ -131,9 +140,11 @@ 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.
|
|
|
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.
|
|
|
+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 ';'.
|
|
|
+RomajiPostProcessing=RemoveAllDiacritics;RemoveApostrophes ;Indicates what type of post processing to do on 'translated' romaji texts. This can be important in certain games because the font used does not support various diacritics properly. This is a list seperated by ';'. Possible values: ["RemoveAllDiacritics", "ReplaceMacronWithCircumflex", "RemoveApostrophes"]
|
|
|
|
|
|
[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. Can use placeholder: {GameExeName}
|
|
|
EnableTextureTranslation=False ;Indicates whether the plugin will attempt to replace in-game images with those from the TextureDirectory directory
|
|
|
EnableTextureDumping=False ;Indicates whether the plugin will dump texture it is capable of replacing to the TextureDirectory. Has significant performance impact
|
|
|
EnableTextureToggling=False ;Indicates whether or not toggling the translation with the ALT+T hotkey will also affect textures. Not guaranteed to work for all textures. Has significant performance impact
|
|
@@ -159,9 +170,14 @@ BaiduAppSecret= ;OPTIONAL, needed if BaiduTranslate is configur
|
|
|
YandexAPIKey= ;OPTIONAL, needed if YandexTranslate is configured
|
|
|
|
|
|
[Watson]
|
|
|
-WatsonAPIUrl= ;OPTIONAL, needed if WatsonTranslate is configured
|
|
|
-WatsonAPIUsername= ;OPTIONAL, needed if WatsonTranslate is configured
|
|
|
-WatsonAPIPassword= ;OPTIONAL, needed if WatsonTranslate is configured
|
|
|
+Url= ;OPTIONAL, needed if WatsonTranslate is configured
|
|
|
+Key= ;OPTIONAL, needed if WatsonTranslate is configured
|
|
|
+
|
|
|
+[Custom]
|
|
|
+Url= ;Optional, needed if CustomTranslated is configured
|
|
|
+
|
|
|
+[LecPowerTranslator15]
|
|
|
+InstallationPath= ;Optional, needed if LecPowerTranslator15 is configured
|
|
|
|
|
|
[Debug]
|
|
|
EnablePrintHierarchy=False ;Used for debugging
|
|
@@ -209,10 +225,18 @@ Resizing of a UI component does not refer to changing of it's dimensions, but ra
|
|
|
|
|
|
#### Reducing Translation Requests
|
|
|
The following aims at reducing the number of requests send to the translation endpoint:
|
|
|
- * `EnableBatching`: Batches several translation requests into a single with supported endpoints (Only GoogleTranslate and GoogleTranslateLegitimate at the moment)
|
|
|
+ * `EnableBatching`: Batches several translation requests into a single with supported endpoints.
|
|
|
* `UseStaticTranslations`: Enables usage of internal lookup dictionary of various english-to-japanese terms.
|
|
|
* `MaxCharactersPerTranslation`: Specifies the maximum length of a text to translate. Any texts longer than this is ignored by the plugin. Cannot be greater than 500.
|
|
|
|
|
|
+#### Romaji 'translation'
|
|
|
+One of the possible values as output `Language` is 'romaji'. If you choose this as language, you will find that games often has problems showing the translations because the font does not understand the special characters used, for example the [macron diacritic](https://en.wikipedia.org/wiki/Macron_(diacritic)).
|
|
|
+
|
|
|
+To rememdy this, post processing can be applied to translations when 'romaji' is chosen as `Language`. This is done through the option `RomajiPostProcessing`. This option is a ';'-seperated list of values:
|
|
|
+ * `RemoveAllDiacritics`: Remove all diacritics from the translated text
|
|
|
+ * `ReplaceMacronWithCircumflex`: Replaces the macron diacritic with a circumflex.
|
|
|
+ * `RemoveApostrophes`: Some translators might decide to include apostrophes after the 'n'-character. Applying this option removes those.
|
|
|
+
|
|
|
#### Other Options
|
|
|
* `TextGetterCompatibilityMode`: This mode fools the game into thinking that the text displayed is not translated. This is required if the game uses text displayed to the user to determine what logic to execute. You can easily determine if this is required if you can see the functionality works fine if you toggle the translation off (hotkey: ALT+T).
|
|
|
* `IgnoreTextStartingWith`: Disable translation for any texts starting with values in this ';-separated' setting. The [default value](https://www.charbase.com/180e-unicode-mongolian-vowel-separator) is an invisible character that takes up no space.
|
|
@@ -221,6 +245,7 @@ The following aims at reducing the number of requests send to the translation en
|
|
|
|
|
|
## Key Mapping
|
|
|
The following key inputs are mapped:
|
|
|
+ * 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 + 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.
|
|
@@ -240,8 +265,10 @@ REQUIRES: [BepInEx plugin manager](https://github.com/BepInEx/BepInEx) (follow i
|
|
|
The file structure should like like this:
|
|
|
```
|
|
|
{GameDirectory}/BepInEx/XUnity.AutoTranslator.Plugin.Core.dll
|
|
|
-{GameDirectory}/BepInEx/XUnity.AutoTranslator.Plugin.Core.BepInEx.dll
|
|
|
+{GameDirectory}/BepInEx/XUnity.AutoTranslator.Plugin.BepInEx.dll
|
|
|
+{GameDirectory}/BepInEx/XUnity.AutoTranslator.Plugin.ExtProtocol.dll
|
|
|
{GameDirectory}/BepInEx/ExIni.dll
|
|
|
+{GameDirectory}/BepInEx/Translators/{Translator}.dll
|
|
|
{GameDirectory}/BepInEx/Translation/AnyTranslationFile.txt (these files will be auto generated by plugin!)
|
|
|
```
|
|
|
|
|
@@ -254,9 +281,11 @@ REQUIRES: [IPA plugin manager](https://github.com/Eusth/IPA) (follow its install
|
|
|
The file structure should like like this
|
|
|
```
|
|
|
{GameDirectory}/Plugins/XUnity.AutoTranslator.Plugin.Core.dll
|
|
|
-{GameDirectory}/Plugins/XUnity.AutoTranslator.Plugin.Core.IPA.dll
|
|
|
+{GameDirectory}/Plugins/XUnity.AutoTranslator.Plugin.IPA.dll
|
|
|
+{GameDirectory}/Plugins/XUnity.AutoTranslator.Plugin.ExtProtocol.dll
|
|
|
{GameDirectory}/Plugins/0Harmony.dll
|
|
|
{GameDirectory}/Plugins/ExIni.dll
|
|
|
+{GameDirectory}/Plugins/Translators/{Translator}.dll
|
|
|
{GameDirectory}/Plugins/Translation/AnyTranslationFile.txt (these files will be auto generated by plugin!)
|
|
|
```
|
|
|
|
|
@@ -269,9 +298,11 @@ REQUIRES: UnityInjector (follow its installation instructions first!).
|
|
|
The file structure should like like this
|
|
|
```
|
|
|
{GameDirectory}/UnityInjector/XUnity.AutoTranslator.Plugin.Core.dll
|
|
|
-{GameDirectory}/UnityInjector/XUnity.AutoTranslator.Plugin.Core.UnityInjector.dll
|
|
|
+{GameDirectory}/UnityInjector/XUnity.AutoTranslator.Plugin.UnityInjector.dll
|
|
|
+{GameDirectory}/UnityInjector/XUnity.AutoTranslator.Plugin.ExtProtocol.dll
|
|
|
{GameDirectory}/UnityInjector/0Harmony.dll
|
|
|
{GameDirectory}/UnityInjector/ExIni.dll
|
|
|
+{GameDirectory}/UnityInjector/Translators/{Translator}.dll
|
|
|
{GameDirectory}/UnityInjector/Translation/AnyTranslationFile.txt (these files will be auto generated by plugin!)
|
|
|
```
|
|
|
|
|
@@ -281,7 +312,7 @@ REQUIRES: Nothing, ReiPatcher is provided by this download.
|
|
|
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.
|
|
|
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.
|
|
|
|
|
|
The file structure should like like this
|
|
@@ -296,9 +327,11 @@ The file structure should like like this
|
|
|
{GameDirectory}/ReiPatcher/ReiPatcher.exe
|
|
|
{GameDirectory}/{GameExeName}_Data/Managed/ReiPatcher.exe
|
|
|
{GameDirectory}/{GameExeName}_Data/Managed/XUnity.AutoTranslator.Plugin.Core.dll
|
|
|
+{GameDirectory}/{GameExeName}_Data/Managed/XUnity.AutoTranslator.Plugin.ExtProtocol.dll
|
|
|
{GameDirectory}/{GameExeName}_Data/Managed/0Harmony.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
|
|
@@ -354,7 +387,7 @@ TextureHashGenerationStrategy=FromImageName
|
|
|
This is done through a hash-value that is stored in square brackets in each image file name, like this: `file_name [0223B639A2-6E698E9272].png`. This configuration specifies how these hash-values are generated:
|
|
|
* `FromImageName` means that the hash is generated from the internal resource name that the game uses for the image, which may not exist for all images or even be unique. However, it is generally fairly reliable. If an image has no resource name, it will not be dumped.
|
|
|
* `FromImageData` means that the hash is generated from the data stored in the image, which is guaranteed to exist for all images. However, generating the hash comes at a performance cost, that will also be incurred by the end-users.
|
|
|
- * `FromImageNameAndScene` means that it should use the name and scene to generate a hash. The name is still required for this to work. When using this option, there is a chance the same texture could be dumped with different hashes, which is undesirable, but it could be required for some games, if the name itself is not unique and the `FromImageData` option causes performance issues.
|
|
|
+ * `FromImageNameAndScene` means that it should use the name and scene to generate a hash. The name is still required for this to work. When using this option, there is a chance the same texture could be dumped with different hashes, which is undesirable, but it could be required for some games, if the name itself is not unique and the `FromImageData` option causes performance issues. If this is used, it is recommended to enable `EnableTextureScanOnSceneLoad` as well.
|
|
|
|
|
|
There's an important catch you need to be aware when dealing with these options and that is if ANY of these options exists: `EnableTextureDumping=True`, `EnableTextureToggling=True`, `TextureHashGenerationStrategy=FromImageData`, then the game will need to read the raw data from all images it finds in game in order to replace the image and this is an expensive operation.
|
|
|
|
|
@@ -393,6 +426,8 @@ Here's how it works, and what is required:
|
|
|
3. IMGUI: public static event Func<object, string, string> OnUnableToTranslateIMGUI
|
|
|
* Also, the events can be either instance based or static.
|
|
|
|
|
|
+Be aware that if you do this, that the hooking functionality of the Auto Translator itself will be disabled. So you are entirely responsible for implementing the required hooks for the overriden text framework.
|
|
|
+
|
|
|
### Implementing a component that the Auto Translator should not interfere with
|
|
|
As a mod author, you might not want the Auto Translator to interfere with your mods UI. If this is the case there's two ways to tell Auto Translator not to perform any translation:
|
|
|
* If your UI is based on GameObjects, you can simply name your GameObjects containing the text element (for example Text class) to something that contains the string "XUAIGNORE". The Auto Translator will check for this and ignore components that contains the string.
|
|
@@ -431,3 +466,196 @@ public class MyPlugin : XPluginBase
|
|
|
```
|
|
|
|
|
|
This approach requires version 2.15.0 or later!
|
|
|
+
|
|
|
+## Implementing a Translator
|
|
|
+Since version 3.0.0, you can now also implement your own translators.
|
|
|
+
|
|
|
+In order to do so, all you have to do is implement the following interface, build the assembly and place the generated DLL in the `Translators` folder.
|
|
|
+
|
|
|
+```C#
|
|
|
+/// <summary>
|
|
|
+/// The interface that must be implemented by a translator.
|
|
|
+/// </summary>
|
|
|
+public interface ITranslateEndpoint
|
|
|
+{
|
|
|
+ /// <summary>
|
|
|
+ /// Gets the id of the ITranslateEndpoint that is used as a configuration parameter.
|
|
|
+ /// </summary>
|
|
|
+ string Id { get; }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets a friendly name that can be displayed to the user representing the plugin.
|
|
|
+ /// </summary>
|
|
|
+ string FriendlyName { get; }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets the maximum concurrency for the endpoint. This specifies how many times "Translate"
|
|
|
+ /// can be called before it returns.
|
|
|
+ /// </summary>
|
|
|
+ int MaxConcurrency { get; }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets the maximum number of translations that can be served per translation request.
|
|
|
+ /// </summary>
|
|
|
+ int MaxTranslationsPerRequest { get; }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Called during initialization. Use this to initialize plugin or throw exception if impossible.
|
|
|
+ /// </summary>
|
|
|
+ void Initialize( IInitializationContext context );
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Attempt to translated the provided untranslated text. Will be used in a "coroutine",
|
|
|
+ /// so it can be implemented in an asynchronous fashion.
|
|
|
+ /// </summary>
|
|
|
+ IEnumerator Translate( ITranslationContext context );
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+Often an implementation of this interface will access an external web service. If this is the case, you do not need to implement the entire interface yourself. Instead you can rely on a base class in the `XUnity.AutoTranslator.Plugin.Core` assembly. But more on this later.
|
|
|
+
|
|
|
+### Important Notes on Implementing a Translator based on an Online Service
|
|
|
+Whenever you implement a translator based on an online service, it is important to not use it in an abusive way. For example by:
|
|
|
+ * Establishing a large number of connections to it
|
|
|
+ * Performing web scraping instead of using an available API
|
|
|
+ * Making concurrent requests towards it
|
|
|
+ * *This is especially important if the service is not authenticated*
|
|
|
+
|
|
|
+With that in mind, consider the following:
|
|
|
+ * The `WWW` class in Unity establishes a new TCP connection on each request you make, making it extremely poor at this kind of job. Especially if SSL (https) is involved because it has to do the entire handshake procedure each time. Yuck.
|
|
|
+ * The `UnityWebRequest` class in Unity does not exist in most games, because the authors use an old engine, so it is not a good choice either.
|
|
|
+ * The `WebClient` class from .NET is capable of using persistent connections (it does so by default), but has its own problems with SSL. The version of Mono used in most Unity games rejects all certificates by default making all HTTPS connections fail. This, however, can be remedied during the initialization phase of the translator (see examples below). Another shortcoming of this API is the fact that the runtime will never release the TCP connections it has used until the process ends. The API also integrates terribly with Unity because callbacks return on a background thread.
|
|
|
+ * The `WebRequest` class from .NET is essentially the same as WebClient.
|
|
|
+ * The `HttpClient` class from .NET is also unlikely to exist in most Unity games.
|
|
|
+
|
|
|
+None of these are therefore an ideal solution.
|
|
|
+
|
|
|
+To remedy this, the plugin implements a class `XUnityWebClient`, which is based on Mono's version of WebClient. However, it adds the following features:
|
|
|
+ * Enables integration with Unity by returning result classes that can be 'yielded'.
|
|
|
+ * Properly closes connections that has not been used for 50 seconds.
|
|
|
+
|
|
|
+I recommend using this class, or in case that cannot be used, falling back to the .NET 'WebClient'.
|
|
|
+
|
|
|
+### How-To
|
|
|
+Follow these steps:
|
|
|
+ 1. Download XUnity.AutoTranslator-Developer-{VERSION}.zip from [releases](../../releases)
|
|
|
+ 2. Start a new project (.NET 3.5) 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. This makes it easier for users to know how to configure your translator
|
|
|
+ * I recommend using the "Class Library (.NET Standard)" and simply editing the generated .csproj file to use 'net35' instead of 'netstandard2.0'. This generates much cleaner .csproj files.
|
|
|
+ 3. Add a reference to the XUnity.AutoTranslator.Plugin.Core.dll that you downloaded in step 1
|
|
|
+ 4. You do not need to directly reference the UnityEngine.dll assembly. This is good, because you do not need to worry about which version of Unity is used.
|
|
|
+ * If you do need a reference to this assembly (because you need functionality from it) consider using an old version of it (if `UnityEngine.CoreModule.dll` exists in the Managed folder, it is not an old version!)
|
|
|
+ 5. Create a new class that either:
|
|
|
+ * Implements the `ITranslateEndpoint` interface
|
|
|
+ * Inherits from the `HttpEndpoint` class
|
|
|
+ * Inherits from the `WwwEndpoint` class
|
|
|
+ * Inherits from the `ExtProtocolEndpoint` class
|
|
|
+
|
|
|
+Here's an example that simply reverses the text and also reads some configuration from the configuration file the plugin uses:
|
|
|
+
|
|
|
+```C#
|
|
|
+public class ReverseTranslatorEndpoint : ITranslateEndpoint
|
|
|
+{
|
|
|
+ private bool _myConfig;
|
|
|
+
|
|
|
+ public string Id => "ReverseTranslator";
|
|
|
+
|
|
|
+ public string FriendlyName => "Reverser";
|
|
|
+
|
|
|
+ public int MaxConcurrency => 50;
|
|
|
+
|
|
|
+ public int MaxTranslationsPerRequest => 1;
|
|
|
+
|
|
|
+ public void Initialize( IInitializationContext context )
|
|
|
+ {
|
|
|
+ _myConfig = context.GetOrCreateSetting( "Reverser", "MyConfig", true );
|
|
|
+ }
|
|
|
+
|
|
|
+ public IEnumerator Translate( ITranslationContext context )
|
|
|
+ {
|
|
|
+ var reversedText = new string( context.UntranslatedText.Reverse().ToArray() );
|
|
|
+ context.Complete( reversedText );
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+Arguably, this is not a particularly interesting example, but it illustrates the basic principles of what must be done in order to implement a Translator.
|
|
|
+
|
|
|
+Let's take a look at a more advanced example that accesses the web:
|
|
|
+
|
|
|
+```C#
|
|
|
+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 string _key;
|
|
|
+
|
|
|
+ public override string Id => "YandexTranslate";
|
|
|
+
|
|
|
+ public override string FriendlyName => "Yandex Translate";
|
|
|
+
|
|
|
+ public override void Initialize( IInitializationContext context )
|
|
|
+ {
|
|
|
+ _key = context.GetOrCreateSetting( "Yandex", "YandexAPIKey", "" );
|
|
|
+ context.DisableCertificateChecksFor( "translate.yandex.net" );
|
|
|
+
|
|
|
+ // 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( !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 )
|
|
|
+ {
|
|
|
+ var request = new XUnityWebRequest(
|
|
|
+ string.Format(
|
|
|
+ HttpsServicePointTemplateUrl,
|
|
|
+ context.SourceLanguage,
|
|
|
+ context.DestinationLanguage,
|
|
|
+ WwwHelper.EscapeUrl( context.UntranslatedText ),
|
|
|
+ _key ) );
|
|
|
+
|
|
|
+ request.Headers[ HttpRequestHeader.Accept ] = "*/*";
|
|
|
+ request.Headers[ HttpRequestHeader.AcceptCharset ] = "UTF-8";
|
|
|
+
|
|
|
+ context.Complete( request );
|
|
|
+ }
|
|
|
+
|
|
|
+ public override void OnExtractTranslation( IHttpTranslationExtractionContext context )
|
|
|
+ {
|
|
|
+ var data = context.Response.Data;
|
|
|
+ var obj = JSON.Parse( data );
|
|
|
+
|
|
|
+ var code = obj.AsObject[ "code" ].ToString();
|
|
|
+ if( code != "200" ) context.Fail( "Received bad response code: " + code );
|
|
|
+
|
|
|
+ var token = obj.AsObject[ "text" ].ToString();
|
|
|
+ var translation = JsonHelper.Unescape( token.Substring( 2, token.Length - 4 ) );
|
|
|
+
|
|
|
+ if( string.IsNullOrEmpty( translation ) ) context.Fail( "Received no translation." );
|
|
|
+
|
|
|
+ context.Complete( translation );
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+This plugin extends from `HttpEndpoint`. Let's look at the three methods it overrides:
|
|
|
+ * `Initialize` is used to read the API key the user has configured. In addition it calls `context.DisableCertificateChecksFor( "translate.yandex.net" )` in order to disable the certificate check for this specific hostname. If this is neglected, SSL will fail in most versions of Unity. Finally, it throws an exception if the plugin cannot be used with the specified configuration.
|
|
|
+ * `OnCreateRequest` is used to construct the `XUnityWebRequest` object that will be sent to the external endpoint. The call to `context.Complete( request )` specifies the request to use.
|
|
|
+ * `OnExtractTranslation` is used to extract the text from the response returned from the web server.
|
|
|
+
|
|
|
+As you can see, the `XUnityWebClient` class is not even used. We simply specify a request object that the `HttpEndpoint` will use internally to perform the request.
|
|
|
+
|
|
|
+After implementing the class, simply build the project and place the generated DLL file in the "Translators" directory of the plugin folder. That's it.
|
|
|
+
|
|
|
+For more examples of implementations, you can simply take a look at this projects source code.
|
|
|
+
|
|
|
+**NOTE**: If you implement a class based on the `HttpEndpoint` and you get an error where the web request is never completed, then it is likely due to the web server requiring Tls1.2. Unity-mono has issues with this spec and it will cause the request to lock up forever. The only solutions to this for now are:
|
|
|
+ * Disable SSL, if you can. There are many situations where it is simply not possible to do this because the web server will simply redirect back to the HTTPS endoint.
|
|
|
+ * Use the `WwwEndpoint` instead. I highly advice against this though, unless it is an authenticated endpoint.
|
|
|
+
|
|
|
+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 directly, it is also possible to extend from MonoBehaviour to get access to all the normal lifecycle callbacks of Unity components.
|