123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Globalization;
- using System.IO;
- using System.Net;
- using System.Reflection;
- using System.Text;
- using SimpleJSON;
- using XUnity.AutoTranslator.Plugin.Core;
- using XUnity.AutoTranslator.Plugin.Core.Configuration;
- using XUnity.AutoTranslator.Plugin.Core.Constants;
- using XUnity.AutoTranslator.Plugin.Core.Endpoints;
- using XUnity.AutoTranslator.Plugin.Core.Endpoints.Http;
- using XUnity.AutoTranslator.Plugin.Core.Extensions;
- using XUnity.AutoTranslator.Plugin.Core.Utilities;
- using XUnity.AutoTranslator.Plugin.Core.Web;
- namespace GoogleTranslate
- {
- internal class GoogleTranslateEndpoint : HttpEndpoint
- {
- private static readonly HashSet<string> SupportedLanguages = new HashSet<string>
- {
- "romaji","af","sq","am","ar","hy","az","eu","be","bn","bs","bg","ca","ceb","zh-CN","zh-TW","co","hr","cs","da","nl","en","eo","et","fi","fr","fy","gl","ka","de","el","gu","ht","ha","haw","he","hi","hmn","hu","is","ig","id","ga","it","ja","jw","kn","kk","km","ko","ku","ky","lo","la","lv","lt","lb","mk","mg","ms","ml","mt","mi","mr","mn","my","ne","no","ny","ps","fa","pl","pt","pa","ro","ru","sm","gd","sr","st","sn","sd","si","sk","sl","so","es","su","sw","sv","tl","tg","ta","te","th","tr","uk","ur","uz","vi","cy","xh","yi","yo","zu"
- };
- private static readonly string HttpsServicePointTranslateTemplateUrl = "https://translate.googleapis.com/translate_a/single?client=webapp&sl={0}&tl={1}&dt=t&tk={2}&q={3}";
- private static readonly string HttpsServicePointRomanizeTemplateUrl = "https://translate.googleapis.com/translate_a/single?client=webapp&sl={0}&tl=en&dt=rm&tk={1}&q={2}";
- private static readonly string HttpsTranslateUserSite = "https://translate.google.com";
- private static readonly Random RandomNumbers = new Random();
- private static readonly string[] Accepts = new string[] { null, "*/*", "application/json" };
- private static readonly string[] AcceptLanguages = new string[] { null, "en-US,en;q=0.9", "en-US", "en" };
- private static readonly string[] Referers = new string[] { null, "https://translate.google.com/" };
- private static readonly string[] AcceptCharsets = new string[] { null, Encoding.UTF8.WebName };
- private static readonly string Accept = Accepts[ RandomNumbers.Next( Accepts.Length ) ];
- private static readonly string AcceptLanguage = AcceptLanguages[ RandomNumbers.Next( AcceptLanguages.Length ) ];
- private static readonly string Referer = Referers[ RandomNumbers.Next( Referers.Length ) ];
- private static readonly string AcceptCharset = AcceptCharsets[ RandomNumbers.Next( AcceptCharsets.Length ) ];
- private CookieContainer _cookieContainer;
- private bool _hasSetup = false;
- private long m = 427761;
- private long s = 1179739010;
- private int _translationsPerRequest = 10;
- public GoogleTranslateEndpoint()
- {
- _cookieContainer = new CookieContainer();
- }
- public override string Id => KnownTranslateEndpointNames.GoogleTranslate;
- public override string FriendlyName => "Google! Translate";
- public override int MaxTranslationsPerRequest => _translationsPerRequest;
- public override void Initialize( IInitializationContext context )
- {
- context.DisableCerfificateChecksFor( "translate.google.com", "translate.googleapis.com" );
- if( context.DestinationLanguage == "romaji" )
- {
- _translationsPerRequest = 1;
- }
- 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 )
- {
- if( !_hasSetup || AutoTranslatorState.TranslationCount % 100 == 0 )
- {
- _hasSetup = true;
- // Setup TKK and cookies
- var enumerator = SetupTKK();
- while( enumerator.MoveNext() )
- {
- yield return enumerator.Current;
- }
- }
- }
- public override void OnCreateRequest( IHttpRequestCreationContext context )
- {
- var allUntranslatedText = string.Join( "\n", context.UntranslatedTexts );
- XUnityWebRequest request;
- if( context.DestinationLanguage == "romaji" )
- {
- request = new XUnityWebRequest(
- string.Format(
- HttpsServicePointRomanizeTemplateUrl,
- context.SourceLanguage,
- Tk( allUntranslatedText ),
- Uri.EscapeDataString( allUntranslatedText ) ) );
- }
- else
- {
- request = new XUnityWebRequest(
- string.Format(
- HttpsServicePointTranslateTemplateUrl,
- context.SourceLanguage,
- context.DestinationLanguage,
- Tk( allUntranslatedText ),
- Uri.EscapeDataString( allUntranslatedText ) ) );
- }
- request.Cookies = _cookieContainer;
- AddHeaders( request, true );
- context.Complete( request );
- }
- public override void OnInspectResponse( IHttpResponseInspectionContext context )
- {
- InspectResponse( context.Response );
- }
- public override void OnExtractTranslation( IHttpTranslationExtractionContext context )
- {
- var dataIndex = context.DestinationLanguage == "romaji" ? 3 : 0;
- var data = context.Response.Data;
- var arr = JSON.Parse( data );
- var lineBuilder = new StringBuilder( data.Length );
- foreach( JSONNode entry in arr.AsArray[ 0 ].AsArray )
- {
- var token = entry.AsArray[ dataIndex ].ToString();
- token = JsonHelper.Unescape( token.Substring( 1, token.Length - 2 ) );
- if( !lineBuilder.EndsWithWhitespaceOrNewline() ) lineBuilder.Append( '\n' );
- lineBuilder.Append( token );
- }
- var allTranslation = lineBuilder.ToString();
- if( context.UntranslatedTexts.Length == 1 )
- {
- context.Complete( allTranslation );
- }
- else
- {
- var translatedLines = allTranslation.Split( '\n' );
- var translatedTexts = new List<string>();
- int current = 0;
- foreach( var untranslatedText in context.UntranslatedTexts )
- {
- var untranslatedLines = untranslatedText.Split( '\n' );
- var untranslatedLinesCount = untranslatedLines.Length;
- var translatedText = string.Empty;
- for( int i = 0 ; i < untranslatedLinesCount ; i++ )
- {
- if( current >= translatedLines.Length ) context.Fail( "Batch operation received incorrect number of translations." );
- var translatedLine = translatedLines[ current++ ];
- translatedText += translatedLine;
- if( i != untranslatedLinesCount - 1 ) translatedText += '\n';
- }
- translatedTexts.Add( translatedText );
- }
- if( current != translatedLines.Length ) context.Fail( "Batch operation received incorrect number of translations." );
- context.Complete( translatedTexts.ToArray() );
- }
- }
- private XUnityWebRequest CreateWebSiteRequest()
- {
- var request = new XUnityWebRequest( HttpsTranslateUserSite );
- request.Cookies = _cookieContainer;
- AddHeaders( request, false );
- return request;
- }
- private void AddHeaders( XUnityWebRequest request, bool isTranslationRequest )
- {
- request.Headers[ HttpRequestHeader.UserAgent ] = string.IsNullOrEmpty( AutoTranslatorSettings.UserAgent ) ? UserAgents.Chrome_Win10_Latest : AutoTranslatorSettings.UserAgent;
- if( AcceptLanguage != null )
- {
- request.Headers[ HttpRequestHeader.AcceptLanguage ] = AcceptLanguage;
- }
- if( Accept != null )
- {
- request.Headers[ HttpRequestHeader.Accept ] = Accept;
- }
- if( Referer != null && isTranslationRequest )
- {
- request.Headers[ HttpRequestHeader.Referer ] = Referer;
- }
- if( AcceptCharset != null )
- {
- request.Headers[ HttpRequestHeader.AcceptCharset ] = AcceptCharset;
- }
- }
- private void InspectResponse( XUnityWebResponse response )
- {
- CookieCollection cookies = response.NewCookies;
- foreach( Cookie cookie in cookies )
- {
- // redirect cookie to correct domain
- cookie.Domain = ".googleapis.com";
- }
- // FIXME: Is this needed? Should already be added
- _cookieContainer.Add( cookies );
- }
- public IEnumerator SetupTKK()
- {
- XUnityWebResponse response = null;
- _cookieContainer = new CookieContainer();
- try
- {
- var client = new XUnityWebClient();
- var request = CreateWebSiteRequest();
- response = client.Send( request );
- }
- catch( Exception e )
- {
- XuaLogger.Current.Warn( e, "An error occurred while setting up GoogleTranslate TKK. Using fallback TKK values instead." );
- yield break;
- }
- // wait for response completion
- var iterator = response.GetSupportedEnumerator();
- while( iterator.MoveNext() ) yield return iterator.Current;
- InspectResponse( response );
- // failure
- if( response.Error != null )
- {
- XuaLogger.Current.Warn( response.Error, "An error occurred while setting up GoogleTranslate TKK. Using fallback TKK values instead." );
- yield break;
- }
- // failure
- if( response.Data == null )
- {
- XuaLogger.Current.Warn( null, "An error occurred while setting up GoogleTranslate TKK. Using fallback TKK values instead." );
- yield break;
- }
- try
- {
- var html = response.Data;
- bool found = false;
- string[] lookups = new[] { "tkk:'", "TKK='" };
- foreach( var lookup in lookups )
- {
- var index = html.IndexOf( lookup );
- if( index > -1 ) // simple string approach
- {
- var startIndex = index + lookup.Length;
- var endIndex = html.IndexOf( "'", startIndex );
- var result = html.Substring( startIndex, endIndex - startIndex );
- var parts = result.Split( '.' );
- if( parts.Length == 2 )
- {
- m = long.Parse( parts[ 0 ] );
- s = long.Parse( parts[ 1 ] );
- found = true;
- break;
- }
- }
- }
- if( !found )
- {
- XuaLogger.Current.Warn( "An error occurred while setting up GoogleTranslate TKK. Could not locate TKK value. Using fallback TKK values instead." );
- }
- }
- catch( Exception e )
- {
- XuaLogger.Current.Warn( e, "An error occurred while setting up GoogleTranslate TKK. Using fallback TKK values instead." );
- }
- }
- // TKK Approach stolen from Translation Aggregator r190, all credits to Sinflower
- private long Vi( long r, string o )
- {
- for( var t = 0 ; t < o.Length ; t += 3 )
- {
- long a = o[ t + 2 ];
- a = a >= 'a' ? a - 87 : a - '0';
- a = '+' == o[ t + 1 ] ? r >> (int)a : r << (int)a;
- r = '+' == o[ t ] ? r + a & 4294967295 : r ^ a;
- }
- return r;
- }
- private string Tk( string r )
- {
- List<long> S = new List<long>();
- for( var v = 0 ; v < r.Length ; v++ )
- {
- long A = r[ v ];
- if( 128 > A )
- S.Add( A );
- else
- {
- if( 2048 > A )
- S.Add( A >> 6 | 192 );
- else if( 55296 == ( 64512 & A ) && v + 1 < r.Length && 56320 == ( 64512 & r[ v + 1 ] ) )
- {
- A = 65536 + ( ( 1023 & A ) << 10 ) + ( 1023 & r[ ++v ] );
- S.Add( A >> 18 | 240 );
- S.Add( A >> 12 & 63 | 128 );
- }
- else
- {
- S.Add( A >> 12 | 224 );
- S.Add( A >> 6 & 63 | 128 );
- }
- S.Add( 63 & A | 128 );
- }
- }
- const string F = "+-a^+6";
- const string D = "+-3^+b+-f";
- long p = m;
- for( var b = 0 ; b < S.Count ; b++ )
- {
- p += S[ b ];
- p = Vi( p, F );
- }
- p = Vi( p, D );
- p ^= s;
- if( 0 > p )
- p = ( 2147483647 & p ) + 2147483648;
- p %= (long)1e6;
- return p.ToString( CultureInfo.InvariantCulture ) + "." + ( p ^ m ).ToString( CultureInfo.InvariantCulture );
- }
- }
- }
|