const fetch = require('node-fetch').default; const https = require('https'); const express = require('express'); const iconv = require('iconv-lite'); const { readSecret, SECRET_KEYS } = require('./secrets'); const { getConfigValue, uuidv4 } = require('../util'); const { jsonParser } = require('../express-common'); const DEEPLX_URL_DEFAULT = 'http://127.0.0.1:1188/translate'; const ONERING_URL_DEFAULT = 'http://127.0.0.1:4990/translate'; const router = express.Router(); router.post('/libre', jsonParser, async (request, response) => { const key = readSecret(request.user.directories, SECRET_KEYS.LIBRE); const url = readSecret(request.user.directories, SECRET_KEYS.LIBRE_URL); if (!url) { console.log('LibreTranslate URL is not configured.'); return response.sendStatus(400); } if (request.body.lang === 'zh-CN') { request.body.lang = 'zh'; } if (request.body.lang === 'zh-TW') { request.body.lang = 'zt'; } const text = request.body.text; const lang = request.body.lang; if (!text || !lang) { return response.sendStatus(400); } console.log('Input text: ' + text); try { const result = await fetch(url, { method: 'POST', body: JSON.stringify({ q: text, source: 'auto', target: lang, format: 'text', api_key: key, }), headers: { 'Content-Type': 'application/json' }, }); if (!result.ok) { const error = await result.text(); console.log('LibreTranslate error: ', result.statusText, error); return response.sendStatus(result.status); } const json = await result.json(); console.log('Translated text: ' + json.translatedText); return response.send(json.translatedText); } catch (error) { console.log('Translation error: ' + error.message); return response.sendStatus(500); } }); router.post('/google', jsonParser, async (request, response) => { try { const { generateRequestUrl, normaliseResponse } = require('google-translate-api-browser'); const text = request.body.text; const lang = request.body.lang; if (!text || !lang) { return response.sendStatus(400); } console.log('Input text: ' + text); const url = generateRequestUrl(text, { to: lang }); https.get(url, (resp) => { const data = []; resp.on('data', (chunk) => { data.push(chunk); }); resp.on('end', () => { try { const decodedData = iconv.decode(Buffer.concat(data), 'utf-8'); const result = normaliseResponse(JSON.parse(decodedData)); console.log('Translated text: ' + result.text); response.setHeader('Content-Type', 'text/plain; charset=utf-8'); return response.send(result.text); } catch (error) { console.log('Translation error', error); return response.sendStatus(500); } }); }).on('error', (err) => { console.log('Translation error: ' + err.message); return response.sendStatus(500); }); } catch (error) { console.log('Translation error', error); return response.sendStatus(500); } }); router.post('/yandex', jsonParser, async (request, response) => { const chunks = request.body.chunks; const lang = request.body.lang; if (!chunks || !lang) { return response.sendStatus(400); } // reconstruct original text to log let inputText = ''; const params = new URLSearchParams(); for (const chunk of chunks) { params.append('text', chunk); inputText += chunk; } params.append('lang', lang); const ucid = uuidv4().replaceAll('-', ''); console.log('Input text: ' + inputText); try { const result = await fetch(`https://translate.yandex.net/api/v1/tr.json/translate?ucid=${ucid}&srv=android&format=text`, { method: 'POST', body: params, headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, timeout: 0, }); if (!result.ok) { const error = await result.text(); console.log('Yandex error: ', result.statusText, error); return response.sendStatus(500); } const json = await result.json(); const translated = json.text.join(); console.log('Translated text: ' + translated); return response.send(translated); } catch (error) { console.log('Translation error: ' + error.message); return response.sendStatus(500); } }); router.post('/lingva', jsonParser, async (request, response) => { try { const baseUrl = readSecret(request.user.directories, SECRET_KEYS.LINGVA_URL); if (!baseUrl) { console.log('Lingva URL is not configured.'); return response.sendStatus(400); } const text = request.body.text; const lang = request.body.lang; if (!text || !lang) { return response.sendStatus(400); } console.log('Input text: ' + text); const url = `${baseUrl}/auto/${lang}/${encodeURIComponent(text)}`; https.get(url, (resp) => { let data = ''; resp.on('data', (chunk) => { data += chunk; }); resp.on('end', () => { try { const result = JSON.parse(data); console.log('Translated text: ' + result.translation); return response.send(result.translation); } catch (error) { console.log('Translation error', error); return response.sendStatus(500); } }); }).on('error', (err) => { console.log('Translation error: ' + err.message); return response.sendStatus(500); }); } catch (error) { console.log('Translation error', error); return response.sendStatus(500); } }); router.post('/deepl', jsonParser, async (request, response) => { const key = readSecret(request.user.directories, SECRET_KEYS.DEEPL); if (!key) { console.log('DeepL key is not configured.'); return response.sendStatus(400); } if (request.body.lang === 'zh-CN' || request.body.lang === 'zh-TW') { request.body.lang = 'ZH'; } const text = request.body.text; const lang = request.body.lang; const formality = getConfigValue('deepl.formality', 'default'); if (!text || !lang) { return response.sendStatus(400); } console.log('Input text: ' + text); const params = new URLSearchParams(); params.append('text', text); params.append('target_lang', lang); if (['de', 'fr', 'it', 'es', 'nl', 'ja', 'ru'].includes(lang)) { // We don't specify a Portuguese variant, so ignore formality for it. params.append('formality', formality); } try { const result = await fetch('https://api-free.deepl.com/v2/translate', { method: 'POST', body: params, headers: { 'Accept': 'application/json', 'Authorization': `DeepL-Auth-Key ${key}`, 'Content-Type': 'application/x-www-form-urlencoded', }, timeout: 0, }); if (!result.ok) { const error = await result.text(); console.log('DeepL error: ', result.statusText, error); return response.sendStatus(result.status); } const json = await result.json(); console.log('Translated text: ' + json.translations[0].text); return response.send(json.translations[0].text); } catch (error) { console.log('Translation error: ' + error.message); return response.sendStatus(500); } }); router.post('/onering', jsonParser, async (request, response) => { const secretUrl = readSecret(request.user.directories, SECRET_KEYS.ONERING_URL); const url = secretUrl || ONERING_URL_DEFAULT; if (!url) { console.log('OneRing URL is not configured.'); return response.sendStatus(400); } if (!secretUrl && url === ONERING_URL_DEFAULT) { console.log('OneRing URL is using default value.', ONERING_URL_DEFAULT); } const text = request.body.text; const from_lang = request.body.from_lang; const to_lang = request.body.to_lang; if (!text || !from_lang || !to_lang) { return response.sendStatus(400); } const params = new URLSearchParams(); params.append('text', text); params.append('from_lang', from_lang); params.append('to_lang', to_lang); console.log('Input text: ' + text); try { const fetchUrl = new URL(url); fetchUrl.search = params.toString(); const result = await fetch(fetchUrl, { method: 'GET', timeout: 0, }); if (!result.ok) { const error = await result.text(); console.log('OneRing error: ', result.statusText, error); return response.sendStatus(result.status); } const data = await result.json(); console.log('Translated text: ' + data.result); return response.send(data.result); } catch (error) { console.log('Translation error: ' + error.message); return response.sendStatus(500); } }); router.post('/deeplx', jsonParser, async (request, response) => { const secretUrl = readSecret(request.user.directories, SECRET_KEYS.DEEPLX_URL); const url = secretUrl || DEEPLX_URL_DEFAULT; if (!url) { console.log('DeepLX URL is not configured.'); return response.sendStatus(400); } if (!secretUrl && url === DEEPLX_URL_DEFAULT) { console.log('DeepLX URL is using default value.', DEEPLX_URL_DEFAULT); } const text = request.body.text; let lang = request.body.lang; if (request.body.lang === 'zh-CN' || request.body.lang === 'zh-TW') { lang = 'ZH'; } if (!text || !lang) { return response.sendStatus(400); } console.log('Input text: ' + text); try { const result = await fetch(url, { method: 'POST', body: JSON.stringify({ text: text, source_lang: 'auto', target_lang: lang, }), headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, timeout: 0, }); if (!result.ok) { const error = await result.text(); console.log('DeepLX error: ', result.statusText, error); return response.sendStatus(result.status); } const json = await result.json(); console.log('Translated text: ' + json.data); return response.send(json.data); } catch (error) { console.log('DeepLX translation error: ' + error.message); return response.sendStatus(500); } }); router.post('/bing', jsonParser, async (request, response) => { const bingTranslateApi = require('bing-translate-api'); const text = request.body.text; let lang = request.body.lang; if (request.body.lang === 'zh-CN') { lang = 'zh-Hans'; } if (!text || !lang) { return response.sendStatus(400); } console.log('Input text: ' + text); bingTranslateApi.translate(text, null, lang).then(result => { console.log('Translated text: ' + result.translation); return response.send(result.translation); }).catch(err => { console.log('Translation error: ' + err.message); return response.sendStatus(500); }); }); module.exports = { router };