@deeplx/core
Version:
An unofficial but powerful and easy-to-use yet free DeepL API client for Node.js using [DeepL](https://www.deepl.com) by porting [OwO-Network/DeepLX](https://github.com/OwO-Network/DeepLX).
330 lines (323 loc) • 11 kB
JavaScript
;
var proxy = require('node-fetch-native/proxy');
var whatlangNode = require('whatlang-node');
var xFetch = require('x-fetch');
const SUPPORTED_LANGUAGES = [
{ code: "BG", language: "Bulgarian" },
{ code: "ZH", language: "Chinese" },
{ code: "CS", language: "Czech" },
{ code: "DA", language: "Danish" },
{ code: "NL", language: "Dutch" },
{ code: "EN", language: "English" },
{ code: "ET", language: "Estonian" },
{ code: "FI", language: "Finnish" },
{ code: "FR", language: "French" },
{ code: "DE", language: "German" },
{ code: "EL", language: "Greek" },
{ code: "HU", language: "Hungarian" },
{ code: "IT", language: "Italian" },
{ code: "JA", language: "Japanese" },
{ code: "LV", language: "Latvian" },
{ code: "LT", language: "Lithuanian" },
{ code: "PL", language: "Polish" },
{ code: "PT", language: "Portuguese" },
{ code: "RO", language: "Romanian" },
{ code: "RU", language: "Russian" },
{ code: "SK", language: "Slovak" },
{ code: "SL", language: "Slovenian" },
{ code: "ES", language: "Spanish" },
{ code: "SV", language: "Swedish" },
{ code: "TR", language: "Turkish" },
{ code: "ID", language: "Indonesian" },
{ code: "UK", language: "Ukrainian" }
];
const FORMALITY_TONES = /* @__PURE__ */ new Set([
"formal",
"informal",
"undefined"
]);
const API_URL = "https://www2.deepl.com/jsonrpc";
const HTTP_STATUS_OK = 200;
const HTTP_STATUS_BAD_REQUEST = 400;
const HTTP_STATUS_NOT_FOUND = 404;
const HTTP_STATUS_NOT_ALLOWED = 405;
const HTTP_STATUS_INTERNAL_ERROR = 500;
const HTTP_STATUS_SERVICE_UNAVAILABLE = 503;
const COMMON_HEADERS = {
"Content-Type": "application/json",
"User-Agent": "DeepL/1627620 CFNetwork/3826.500.62.2.1 Darwin/24.4.0",
Accept: "*/*",
"X-App-Os-Name": "iOS",
"X-App-Os-Version": "18.4.0",
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
// Keep this!
"X-App-Device": "iPhone16,2",
Referer: "https://www.deepl.com/",
"X-Product": "translator",
"X-App-Build": "1627620",
"X-App-Version": "25.1"
};
const getICount = (translateText) => {
return (translateText.match(/i/g) || []).length;
};
const getRandomNumber = () => {
const base = Math.floor(Math.random() * 99999) + 83e5;
return base * 1e3;
};
const getTimeStamp = (iCount) => {
const ts = Date.now();
if (iCount !== 0) {
const adjustedCount = iCount + 1;
return ts - ts % adjustedCount + adjustedCount;
}
return ts;
};
const formatPostString = (postData) => {
const postStr = JSON.stringify(postData);
const shouldAddSpace = (
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
(postData.id + 5) % 29 === 0 || (postData.id + 3) % 13 === 0
);
return postStr.replaceAll(
'"method":"',
shouldAddSpace ? `"method" : "` : `"method": "`
);
};
let abbreviateLanguageDictionary;
function getAbbreviateLanguages() {
return abbreviateLanguageDictionary != null ? abbreviateLanguageDictionary : abbreviateLanguageDictionary = SUPPORTED_LANGUAGES.reduce(
(acc, lang) => {
acc[lang.code.toLowerCase()] = lang.code;
acc[lang.language.toLowerCase()] = lang.code;
return acc;
},
{}
);
}
function abbreviateLanguage(language) {
return getAbbreviateLanguages()[language.split("-")[0].toLowerCase()];
}
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __async$1 = (__this, __arguments, generator) => {
return new Promise((resolve, reject) => {
var fulfilled = (value) => {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
};
var rejected = (value) => {
try {
step(generator.throw(value));
} catch (e) {
reject(e);
}
};
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
step((generator = generator.apply(__this, __arguments)).next());
});
};
const makeRequest = (postData, proxyUrl, dlSession) => __async$1(void 0, null, function* () {
return xFetch.xfetch(API_URL, __spreadValues({
method: "POST",
body: formatPostString(postData),
headers: dlSession ? __spreadProps(__spreadValues({}, COMMON_HEADERS), { Cookie: `dl_session=${dlSession}` }) : COMMON_HEADERS
}, proxy.createProxy({ url: proxyUrl })));
});
const splitAndProcess = (text) => text.split("\n").map((line) => line.trim() === "" ? "\n" : line);
const translateByDeepLX = (sourceLang, targetLang, text, formal, tagHandling = "plaintext", proxyUrl, dlSession) => __async$1(void 0, null, function* () {
var _a, _b;
if (!text) {
return { code: HTTP_STATUS_NOT_FOUND, message: "No text to translate" };
}
const textParts = splitAndProcess(text);
const translatedParts = [];
const allAlternatives = [];
for (const part of textParts) {
if (!part.trim()) {
translatedParts.push("");
allAlternatives.push([""]);
continue;
}
if (!sourceLang || sourceLang === "auto") {
sourceLang = whatlangNode.detectLang(part, true);
}
const sourceLangCode = (_a = abbreviateLanguage(sourceLang)) != null ? _a : sourceLang.toUpperCase();
const jobs = [
{
kind: "default",
preferred_num_beams: 4,
raw_en_context_before: [],
raw_en_context_after: [],
sentences: [{ prefix: "", text: part, id: 0 }]
}
];
let hasRegionalVariant = false;
let targetLangCode = (_b = abbreviateLanguage(targetLang)) != null ? _b : targetLang.toUpperCase();
const targetLangParts = targetLang.split("-");
if (targetLangParts.length > 1) {
targetLangCode = targetLangParts[0];
hasRegionalVariant = true;
}
const id = getRandomNumber();
const postData = {
jsonrpc: "2.0",
method: "LMT_handle_jobs",
id,
params: {
commonJobParams: __spreadValues({
mode: "translate",
formality: (
// eslint-disable-next-line sonarjs/no-nested-conditional
formal == null ? "undefined" : formal ? "formal" : "informal"
),
transcribe_as: "romanize",
advancedMode: false,
textType: tagHandling,
wasSpoken: false
}, hasRegionalVariant && { regionalVariant: targetLang }),
lang: {
source_lang_user_selected: "auto",
target_lang: targetLangCode,
source_lang_computed: sourceLangCode
},
jobs,
timestamp: getTimeStamp(getICount(part))
}
};
let translations;
try {
const { result } = yield makeRequest(postData, proxyUrl, dlSession);
translations = result.translations;
} catch (error) {
return {
code: HTTP_STATUS_SERVICE_UNAVAILABLE,
message: String(error)
};
}
let partTranslation = "";
const partAlternatives = [];
if (translations.length > 0) {
for (const translation of translations) {
partTranslation += translation.beams[0].sentences[0].text + " ";
}
partTranslation = partTranslation.trim();
const numBeams = translations[0].beams.length;
for (let i = 1; i < numBeams; i++) {
let altText = "";
for (const translation of translations) {
const beams = translation.beams;
if (i < beams.length) {
altText += beams[i].sentences[0].text + " ";
}
}
if (altText) {
partAlternatives.push(altText.trim());
}
}
}
if (!partTranslation) {
return {
code: HTTP_STATUS_SERVICE_UNAVAILABLE,
message: "Translation failed"
};
}
translatedParts.push(partTranslation);
allAlternatives.push(partAlternatives);
}
const translatedText = translatedParts.join("\n");
const combinedAlternatives = [];
let maxAlts = 0;
for (const alts of allAlternatives) {
if (alts.length > maxAlts) {
maxAlts = alts.length;
}
}
for (let i = 0; i < maxAlts; i++) {
const altParts = [];
for (const [j, alts] of allAlternatives.entries()) {
if (i < alts.length) {
altParts.push(alts[i]);
} else if (translatedParts[j].length === 0) {
altParts.push("");
} else {
altParts.push(translatedParts[j]);
}
}
combinedAlternatives.push(altParts.join("\n"));
}
return {
code: HTTP_STATUS_OK,
id: getRandomNumber(),
// Using new ID for the complete translation
data: translatedText,
alternatives: combinedAlternatives,
sourceLang,
targetLang,
method: dlSession ? "Pro" : "Free"
};
});
var __async = (__this, __arguments, generator) => {
return new Promise((resolve, reject) => {
var fulfilled = (value) => {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
};
var rejected = (value) => {
try {
step(generator.throw(value));
} catch (e) {
reject(e);
}
};
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
step((generator = generator.apply(__this, __arguments)).next());
});
};
const translate = (text, targetLang, sourceLang, formal) => __async(void 0, null, function* () {
const result = yield translateByDeepLX(sourceLang, targetLang, text, formal);
if ("message" in result) {
throw new Error(result.message, { cause: result });
}
return result.data;
});
exports.API_URL = API_URL;
exports.COMMON_HEADERS = COMMON_HEADERS;
exports.FORMALITY_TONES = FORMALITY_TONES;
exports.HTTP_STATUS_BAD_REQUEST = HTTP_STATUS_BAD_REQUEST;
exports.HTTP_STATUS_INTERNAL_ERROR = HTTP_STATUS_INTERNAL_ERROR;
exports.HTTP_STATUS_NOT_ALLOWED = HTTP_STATUS_NOT_ALLOWED;
exports.HTTP_STATUS_NOT_FOUND = HTTP_STATUS_NOT_FOUND;
exports.HTTP_STATUS_OK = HTTP_STATUS_OK;
exports.HTTP_STATUS_SERVICE_UNAVAILABLE = HTTP_STATUS_SERVICE_UNAVAILABLE;
exports.SUPPORTED_LANGUAGES = SUPPORTED_LANGUAGES;
exports.abbreviateLanguage = abbreviateLanguage;
exports.formatPostString = formatPostString;
exports.getICount = getICount;
exports.getRandomNumber = getRandomNumber;
exports.getTimeStamp = getTimeStamp;
exports.translate = translate;
exports.translateByDeepLX = translateByDeepLX;