The following implementation demonstrates how to fetch translations from Google Translate using direct HTTP requests, dynamic token validation, and server-side JavaScript evaluation. The approach relies on extracting the internal tkk seed, computing a valid signature through a custom JS routine, and parsing the resulting JSON payload.
public class TranslationService
{
private readonly HttpClient _httpClient;
private readonly CookieContainer _cookieJar = new();
public TranslationService()
{
_httpClient = new HttpClient(new SocketsHttpHandler
{
CookieContainer = _cookieJar,
AllowAutoRedirect = true
});
_httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0)");
}
[HttpPost]
public async Task<string> TranslateAsync(string sourceText, string targetLang, string sourceLang = "auto")
{
var baseUrl = "https://translate.google.cn/";
var initialPage = await FetchPageAsync(baseUrl, baseUrl);
var tkkMatch = Regex.Match(initialPage, @"(?<=tkk:')[^']+(?='')");
if (!tkkMatch.Success) throw new InvalidOperationException("Failed to extract TKK seed.");
var jsToken = ComputeTranslationToken(sourceText, tkkMatch.Value);
var apiUrl = $"https://translate.google.cn/translate_a/single?client=t&sl={sourceLang}&tl={targetLang}&hl=en&dt=at&dt=bd&dt=ex&dt=ld&dt=md&dt=qca&dt=rw&dt=rm&dt=ss&dt=t&ie=UTF-8&oe=UTF-8&otf=1&ssel=0&tsel=0&kc=1&tk={jsToken}&q={Uri.EscapeDataString(sourceText)}";
var responsePayload = await FetchPageAsync(apiUrl, baseUrl);
var parsedData = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(responsePayload);
return parsedData[0][0][0]?.ToString() ?? string.Empty;
}
private async Task<string> FetchPageAsync(string uri, string referer)
{
using var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Referrer = new Uri(referer);
request.Headers.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
private string ComputeTranslationToken(string text, string seed)
{
// Load and execute the external JS file containing the token generation algorithm
var jsEngine = new MSScriptControl.ScriptControl();
jsEngine.UseSafeSubset = true;
jsEngine.Language = "JScript";
jsEngine.AddCode(File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "content", "js", "gettk.js")));
var expression = $"tk(\"{text}\",\"{seed}\")";
return jsEngine.Eval(expression)?.ToString() ?? string.Empty;
}
}
The core token generation logic resides in an embedded JavaScript file. Below is a refactored version of the algorithm with standardized naming conventions and improved readability while preserving the original bitwise operations:
const applyBitwiseOps = (currentVal, operations) => {{
for (let i = 0; i < operations.length - 2; i += 3) {{
const opChar = operations[i + 1];
let shiftArg = operations[i + 2].charCodeAt(0) - 87;
if (isNaN(shiftArg)) shiftArg = parseInt(operations[i + 2], 10);
const shifted = opChar === '+' ? (currentVal >>> shiftArg) : (currentVal << shiftArg);
currentVal = operations[i] === '+' ? ((currentVal + shifted) & 4294967295) : (currentVal ^ shifted);
}}
return currentVal;
}};
const computeSignature = (inputText, configSeed) => {{
const parts = configSeed.split('.');
const baseVal = parseInt(parts[0], 10) || 0;
const charCodes = [];
for (let i = 0; i < inputText.length; i++) {{
const charCode = inputText.charCodeAt(i);
if (charCode < 128) {{
charCodes.push(charCode);
}} else if (charCode < 2048) {{
charCodes.push((charCode >> 6) | 192);
}} else if (
(charCode & 64512) === 55296 &&
i + 1 < inputText.length &&
(inputText.charCodeAt(i + 1) & 64512) === 56320
) {{
const combinedCode = 65536 + (((charCode & 1023) << 10) | (inputText.charCodeAt(++i) & 1023));
charCodes.push((combinedCode >> 18) | 240);
charCodes.push(((combinedCode >> 12) & 63) | 128);
}} else {{
charCodes.push((charCode >> 12) | 224);
charCodes.push(((charCode >> 6) & 63) | 128);
}}
charCodes.push((charCode & 63) | 128);
}}
let accumulator = baseVal;
for (const val of charCodes) {{
accumulator += val;
accumulator = applyBitwiseOps(accumulator, '+-a^+6');
}}
accumulator = applyBitwiseOps(accumulator, '+-3^+b+-f');
accumulator ^= parseInt(parts[1], 10) || 0;
if (accumulator < 0) accumulator = (accumulator & 2147483647) + 2147483648;
accumulator %= 1000000;
return `${{accumulator}}.${{accumulator ^ baseVal}}`;
}};
When hosting this service under IIS 7+, the legacy MSScriptControl component may trigger a REGDB_E_CLASSNOTREG failure during runtime. This occurs because the ActiveX scripting engine lacks native support in 64-bit processes. To resolve this constraint, navigate to Appplication Pools, select the targeted pool, open Advanced Settings, and toggle Enable 32-Bit Applications to True. This configuration forces the worker process to load the 32-bit DLL registry, allowing the JavaScript evaluaotr to initialize correct.