google-translate-api-x
Version:
A free and unlimited API for Google Translate
92 lines (84 loc) • 3.72 kB
JavaScript
;
const { DEFAULT_OPTIONS, TRANSLATE_PATH } = require('./defaults.cjs');
const { getCode } = require('./languages.cjs');
module.exports = function (input, options) {
options = {...DEFAULT_OPTIONS, ...options};
// jQ1olc - is the specific id for spoken requests
const rpcids = 'jQ1olc';
const queryParams = new URLSearchParams({
'rpcids': rpcids,
'source-path': '/',
'f.sid': '',
'bl': '',
'hl': 'en-US',
'soc-app': 1,
'soc-platform': 1,
'soc-device': 1,
'_reqid': Math.floor(1000 + (Math.random() * 9000)),
'rt': 'c'
});
const url = TRANSLATE_PATH + options.tld + '/_/TranslateWebserverUi/data/batchexecute?' + queryParams.toString();
const requestOptions = {...options.requestOptions, ...DEFAULT_OPTIONS.requestOptions};
requestOptions.method = 'POST';
requestOptions.headers['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
const sourceArray = Array.isArray(input) ? input : (typeof input === 'object' ? Object.values(input) : [input]);
const freq = [];
for (let i = 0; i < sourceArray.length; i++) {
const text = sourceArray[i].text ?? sourceArray[i];
if (text.length > 200) {
return new Promise(() => {
throw new Error('At least one of the inputs exceeded 200 characters, which is rejected by Google translate. You should split it into a batch input with arrays/objects.', {cause: {input: {text}}});
});
}
const forceTo = sourceArray[i].forceTo ?? options.forceTo;
const to = sourceArray[i].to ?? options.to;
const toIso = forceTo ? to : getCode(to);
if (toIso === null) {
return new Promise(() => {
throw new Error(`To language ${to} unsupported, bypass this with setting forceTo to true if you're certain the iso is correct`, {cause: {options: {to}}});
});
}
// i (converted to base 36 to minimize request length) is used as a unique indentifier to order responses
const freqPart = [rpcids, JSON.stringify([text, toIso, true]), null, i.toString(36)];
freq.push(freqPart);
}
requestOptions.body = 'f.req=' + encodeURIComponent(JSON.stringify([freq])) + '&';
return options.requestFunction(url, requestOptions).then( async res => {
if (!res.ok) {
throw new Error(res.statusText, {cause: {options, url, response: res}});
}
res = await res.text();
res = res.slice(6);
let finalResult = Array.isArray(input) ? [] : (typeof input === 'object' ? {} : undefined);
for (let chunk of res.split('\n')) {
if (chunk[0] !== '[' || chunk[3] === 'e') {
continue;
}
chunk = JSON.parse(chunk);
for (let translation of chunk) {
if (translation[0] !== 'wrb.fr') {
continue;
}
const id = parseInt(translation[translation.length - 1], 36);
if (translation[2] === null && options.rejectOnPartialFail) {
throw new Error(
'Partial TTS Request Fail: at least one TTS request failed, it was either invalid or more likely- rejected by the server. ' +
'You can try the request again and if it persists try a proxy, spacing out requests, and/or using a different tld. ' +
'If you would like to still speak other requests in a batch speak request even if one fails(the failed TTS will be ' +
'set to `null`) pass the option `rejectOnPartialFail: false`',
{cause: {input, url, options, requestOptions}}
);
}
const result = (translation[2] !== null) ? JSON.parse(translation[2])[0] : null;
if (Array.isArray(input)) {
finalResult[id] = result;
} else if (typeof input === 'object') {
finalResult[Object.keys(input)[id]] = result;
} else {
finalResult = result;
}
}
}
return finalResult;
});
};