UNPKG

@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
'use strict'; 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;