Merge pull request #3219 from SillyTavern/pt-br Translate: Split Portuguese languages
Signed| @@ -18,7 +18,7 @@ | |||
| 18 | "@popperjs/core": "^2.11.8", | 18 | "@popperjs/core": "^2.11.8", |
| 19 | "@zeldafan0225/ai_horde": "^5.1.0", | 19 | "@zeldafan0225/ai_horde": "^5.1.0", |
| 20 | "archiver": "^7.0.1", | 20 | "archiver": "^7.0.1", |
| 21 | "bing-translate-api": "^2.9.1", | 21 | "bing-translate-api": "^4.0.2", |
| 22 | "body-parser": "^1.20.2", | 22 | "body-parser": "^1.20.2", |
| 23 | "bowser": "^2.11.0", | 23 | "bowser": "^2.11.0", |
| 24 | "command-exists": "^1.2.9", | 24 | "command-exists": "^1.2.9", |
| @@ -2161,9 +2161,9 @@ | |||
| 2161 | } | 2161 | } |
| 2162 | }, | 2162 | }, |
| 2163 | "node_modules/bing-translate-api": { | 2163 | "node_modules/bing-translate-api": { |
| 2164 | "version": "2.9.1", | 2164 | "version": "4.0.2", |
| 2165 | "resolved": "https://registry.npmjs.org/bing-translate-api/-/bing-translate-api-2.9.1.tgz", | 2165 | "resolved": "https://registry.npmjs.org/bing-translate-api/-/bing-translate-api-4.0.2.tgz", |
| 2166 | "integrity": "sha512-DaYqa7iupfj+fj/KeaeZSp5FUY/ZR4sZ6jqoTP0RHkYOUfo7wwoxlhYDkh4VcvBBzuVORnBEgdXBVQrfM4kk7g==", | 2166 | "integrity": "sha512-JJ8XUehnxzOhHU91oy86xEtp8OOMjVEjCZJX042fKxoO19NNvxJ5omeCcxQNFoPbDqVpBJwqiGVquL0oPdQm1Q==", |
| 2167 | "license": "MIT", | 2167 | "license": "MIT", |
| 2168 | "dependencies": { | 2168 | "dependencies": { |
| 2169 | "got": "^11.8.6" | 2169 | "got": "^11.8.6" |
| @@ -8,7 +8,7 @@ | |||
| 8 | "@popperjs/core": "^2.11.8", | 8 | "@popperjs/core": "^2.11.8", |
| 9 | "@zeldafan0225/ai_horde": "^5.1.0", | 9 | "@zeldafan0225/ai_horde": "^5.1.0", |
| 10 | "archiver": "^7.0.1", | 10 | "archiver": "^7.0.1", |
| 11 | "bing-translate-api": "^2.9.1", | 11 | "bing-translate-api": "^4.0.2", |
| 12 | "body-parser": "^1.20.2", | 12 | "body-parser": "^1.20.2", |
| 13 | "bowser": "^2.11.0", | 13 | "bowser": "^2.11.0", |
| 14 | "command-exists": "^1.2.9", | 14 | "command-exists": "^1.2.9", |
| @@ -106,7 +106,8 @@ const languageCodes = { | |||
| 106 | 'Pashto': 'ps', | 106 | 'Pashto': 'ps', |
| 107 | 'Persian': 'fa', | 107 | 'Persian': 'fa', |
| 108 | 'Polish': 'pl', | 108 | 'Polish': 'pl', |
| 109 | 'Portuguese (Portugal, Brazil)': 'pt', | 109 | 'Portuguese (Portugal)': 'pt-PT', |
| 110 | 'Portuguese (Brazil)': 'pt-BR', | ||
| 110 | 'Punjabi': 'pa', | 111 | 'Punjabi': 'pa', |
| 111 | 'Romanian': 'ro', | 112 | 'Romanian': 'ro', |
| 112 | 'Russian': 'ru', | 113 | 'Russian': 'ru', |
| @@ -1,10 +1,10 @@ | |||
| 1 | import https from 'node:https'; | ||
| 2 | import { createRequire } from 'node:module'; | 1 | import { createRequire } from 'node:module'; |
| 3 | 2 | ||
| 4 | import fetch from 'node-fetch'; | 3 | import fetch from 'node-fetch'; |
| 5 | import express from 'express'; | 4 | import express from 'express'; |
| 6 | import bingTranslateApi from 'bing-translate-api'; | 5 | import { translate as bingTranslate } from 'bing-translate-api'; |
| 7 | import iconv from 'iconv-lite'; | 6 | import iconv from 'iconv-lite'; |
| 7 | import urlJoin from 'url-join'; | ||
| 8 | 8 | ||
| 9 | import { readSecret, SECRET_KEYS } from './secrets.js'; | 9 | import { readSecret, SECRET_KEYS } from './secrets.js'; |
| 10 | import { getConfigValue, uuidv4 } from '../util.js'; | 10 | import { getConfigValue, uuidv4 } from '../util.js'; |
| @@ -12,6 +12,7 @@ import { jsonParser } from '../express-common.js'; | |||
| 12 | 12 | ||
| 13 | const DEEPLX_URL_DEFAULT = 'http://127.0.0.1:1188/translate'; | 13 | const DEEPLX_URL_DEFAULT = 'http://127.0.0.1:1188/translate'; |
| 14 | const ONERING_URL_DEFAULT = 'http://127.0.0.1:4990/translate'; | 14 | const ONERING_URL_DEFAULT = 'http://127.0.0.1:4990/translate'; |
| 15 | const LINGVA_DEFAULT = 'https://lingva.ml/api/v1'; | ||
| 15 | 16 | ||
| 16 | export const router = express.Router(); | 17 | export const router = express.Router(); |
| 17 | 18 | ||
| @@ -56,6 +57,10 @@ router.post('/libre', jsonParser, async (request, response) => { | |||
| 56 | request.body.lang = 'zt'; | 57 | request.body.lang = 'zt'; |
| 57 | } | 58 | } |
| 58 | 59 | ||
| 60 | if (request.body.lang === 'pt-BR' || request.body.lang === 'pt-PT') { | ||
| 61 | request.body.lang = 'pt'; | ||
| 62 | } | ||
| 63 | |||
| 59 | const text = request.body.text; | 64 | const text = request.body.text; |
| 60 | const lang = request.body.lang; | 65 | const lang = request.body.lang; |
| 61 | 66 | ||
| @@ -81,7 +86,7 @@ router.post('/libre', jsonParser, async (request, response) => { | |||
| 81 | if (!result.ok) { | 86 | if (!result.ok) { |
| 82 | const error = await result.text(); | 87 | const error = await result.text(); |
| 83 | console.log('LibreTranslate error: ', result.statusText, error); | 88 | console.log('LibreTranslate error: ', result.statusText, error); |
| 84 | return response.sendStatus(result.status); | 89 | return response.sendStatus(500); |
| 85 | } | 90 | } |
| 86 | 91 | ||
| 87 | /** @type {any} */ | 92 | /** @type {any} */ |
| @@ -130,6 +135,14 @@ router.post('/google', jsonParser, async (request, response) => { | |||
| 130 | 135 | ||
| 131 | router.post('/yandex', jsonParser, async (request, response) => { | 136 | router.post('/yandex', jsonParser, async (request, response) => { |
| 132 | try { | 137 | try { |
| 138 | if (request.body.lang === 'pt-PT') { | ||
| 139 | request.body.lang = 'pt'; | ||
| 140 | } | ||
| 141 | |||
| 142 | if (request.body.lang === 'zh-CN' || request.body.lang === 'zh-TW') { | ||
| 143 | request.body.lang = 'zh'; | ||
| 144 | } | ||
| 145 | |||
| 133 | const chunks = request.body.chunks; | 146 | const chunks = request.body.chunks; |
| 134 | const lang = request.body.lang; | 147 | const lang = request.body.lang; |
| 135 | 148 | ||
| @@ -178,13 +191,26 @@ router.post('/yandex', jsonParser, async (request, response) => { | |||
| 178 | 191 | ||
| 179 | router.post('/lingva', jsonParser, async (request, response) => { | 192 | router.post('/lingva', jsonParser, async (request, response) => { |
| 180 | try { | 193 | try { |
| 181 | const baseUrl = readSecret(request.user.directories, SECRET_KEYS.LINGVA_URL); | 194 | const secretUrl = readSecret(request.user.directories, SECRET_KEYS.LINGVA_URL); |
| 195 | const baseUrl = secretUrl || LINGVA_DEFAULT; | ||
| 196 | |||
| 197 | if (!secretUrl && baseUrl === LINGVA_DEFAULT) { | ||
| 198 | console.log('Lingva URL is using default value.', LINGVA_DEFAULT); | ||
| 199 | } | ||
| 182 | 200 | ||
| 183 | if (!baseUrl) { | 201 | if (!baseUrl) { |
| 184 | console.log('Lingva URL is not configured.'); | 202 | console.log('Lingva URL is not configured.'); |
| 185 | return response.sendStatus(400); | 203 | return response.sendStatus(400); |
| 186 | } | 204 | } |
| 187 | 205 | ||
| 206 | if (request.body.lang === 'zh-CN' || request.body.lang === 'zh-TW') { | ||
| 207 | request.body.lang = 'zh'; | ||
| 208 | } | ||
| 209 | |||
| 210 | if (request.body.lang === 'pt-BR' || request.body.lang === 'pt-PT') { | ||
| 211 | request.body.lang = 'pt'; | ||
| 212 | } | ||
| 213 | |||
| 188 | const text = request.body.text; | 214 | const text = request.body.text; |
| 189 | const lang = request.body.lang; | 215 | const lang = request.body.lang; |
| 190 | 216 | ||
| @@ -193,29 +219,24 @@ router.post('/lingva', jsonParser, async (request, response) => { | |||
| 193 | } | 219 | } |
| 194 | 220 | ||
| 195 | console.log('Input text: ' + text); | 221 | console.log('Input text: ' + text); |
| 196 | const url = `${baseUrl}/auto/${lang}/${encodeURIComponent(text)}`; | 222 | |
| 197 | 223 | try { | |
| 198 | https.get(url, (resp) => { | 224 | const url = urlJoin(baseUrl, 'auto', lang, encodeURIComponent(text)); |
| 199 | let data = ''; | 225 | const result = await fetch(url); |
| 200 | 226 | ||
| 201 | resp.on('data', (chunk) => { | 227 | if (!result.ok) { |
| 202 | data += chunk; | 228 | const error = await result.text(); |
| 203 | }); | 229 | console.log('Lingva error: ', result.statusText, error); |
| 204 | 230 | } | |
| 205 | resp.on('end', () => { | 231 | |
| 206 | try { | 232 | /** @type {any} */ |
| 207 | const result = JSON.parse(data); | 233 | const data = await result.json(); |
| 208 | console.log('Translated text: ' + result.translation); | 234 | console.log('Translated text: ' + data.translation); |
| 209 | return response.send(result.translation); | 235 | return response.send(data.translation); |
| 210 | } catch (error) { | 236 | } catch (error) { |
| 211 | console.log('Translation error', error); | 237 | console.log('Translation error:', error); |
| 212 | return response.sendStatus(500); | ||
| 213 | } | ||
| 214 | }); | ||
| 215 | }).on('error', (err) => { | ||
| 216 | console.log('Translation error: ' + err.message); | ||
| 217 | return response.sendStatus(500); | 238 | return response.sendStatus(500); |
| 218 | }); | 239 | } |
| 219 | } catch (error) { | 240 | } catch (error) { |
| 220 | console.log('Translation error', error); | 241 | console.log('Translation error', error); |
| 221 | return response.sendStatus(500); | 242 | return response.sendStatus(500); |
| @@ -248,8 +269,7 @@ router.post('/deepl', jsonParser, async (request, response) => { | |||
| 248 | params.append('text', text); | 269 | params.append('text', text); |
| 249 | params.append('target_lang', lang); | 270 | params.append('target_lang', lang); |
| 250 | 271 | ||
| 251 | if (['de', 'fr', 'it', 'es', 'nl', 'ja', 'ru'].includes(lang)) { | 272 | if (['de', 'fr', 'it', 'es', 'nl', 'ja', 'ru', 'pt-BR', 'pt-PT'].includes(lang)) { |
| 252 | // We don't specify a Portuguese variant, so ignore formality for it. | ||
| 253 | params.append('formality', formality); | 273 | params.append('formality', formality); |
| 254 | } | 274 | } |
| 255 | 275 | ||
| @@ -267,7 +287,7 @@ router.post('/deepl', jsonParser, async (request, response) => { | |||
| 267 | if (!result.ok) { | 287 | if (!result.ok) { |
| 268 | const error = await result.text(); | 288 | const error = await result.text(); |
| 269 | console.log('DeepL error: ', result.statusText, error); | 289 | console.log('DeepL error: ', result.statusText, error); |
| 270 | return response.sendStatus(result.status); | 290 | return response.sendStatus(500); |
| 271 | } | 291 | } |
| 272 | 292 | ||
| 273 | /** @type {any} */ | 293 | /** @type {any} */ |
| @@ -294,6 +314,10 @@ router.post('/onering', jsonParser, async (request, response) => { | |||
| 294 | console.log('OneRing URL is using default value.', ONERING_URL_DEFAULT); | 314 | console.log('OneRing URL is using default value.', ONERING_URL_DEFAULT); |
| 295 | } | 315 | } |
| 296 | 316 | ||
| 317 | if (request.body.lang === 'pt-BR' || request.body.lang === 'pt-PT') { | ||
| 318 | request.body.lang = 'pt'; | ||
| 319 | } | ||
| 320 | |||
| 297 | const text = request.body.text; | 321 | const text = request.body.text; |
| 298 | const from_lang = request.body.from_lang; | 322 | const from_lang = request.body.from_lang; |
| 299 | const to_lang = request.body.to_lang; | 323 | const to_lang = request.body.to_lang; |
| @@ -320,7 +344,7 @@ router.post('/onering', jsonParser, async (request, response) => { | |||
| 320 | if (!result.ok) { | 344 | if (!result.ok) { |
| 321 | const error = await result.text(); | 345 | const error = await result.text(); |
| 322 | console.log('OneRing error: ', result.statusText, error); | 346 | console.log('OneRing error: ', result.statusText, error); |
| 323 | return response.sendStatus(result.status); | 347 | return response.sendStatus(500); |
| 324 | } | 348 | } |
| 325 | 349 | ||
| 326 | /** @type {any} */ | 350 | /** @type {any} */ |
| @@ -376,7 +400,7 @@ router.post('/deeplx', jsonParser, async (request, response) => { | |||
| 376 | if (!result.ok) { | 400 | if (!result.ok) { |
| 377 | const error = await result.text(); | 401 | const error = await result.text(); |
| 378 | console.log('DeepLX error: ', result.statusText, error); | 402 | console.log('DeepLX error: ', result.statusText, error); |
| 379 | return response.sendStatus(result.status); | 403 | return response.sendStatus(500); |
| 380 | } | 404 | } |
| 381 | 405 | ||
| 382 | /** @type {any} */ | 406 | /** @type {any} */ |
| @@ -391,24 +415,34 @@ router.post('/deeplx', jsonParser, async (request, response) => { | |||
| 391 | }); | 415 | }); |
| 392 | 416 | ||
| 393 | router.post('/bing', jsonParser, async (request, response) => { | 417 | router.post('/bing', jsonParser, async (request, response) => { |
| 394 | const text = request.body.text; | 418 | try { |
| 395 | let lang = request.body.lang; | 419 | const text = request.body.text; |
| 420 | let lang = request.body.lang; | ||
| 396 | 421 | ||
| 397 | if (request.body.lang === 'zh-CN') { | 422 | if (request.body.lang === 'zh-CN') { |
| 398 | lang = 'zh-Hans'; | 423 | lang = 'zh-Hans'; |
| 399 | } | 424 | } |
| 400 | 425 | ||
| 401 | if (!text || !lang) { | 426 | if (request.body.lang === 'zh-TW') { |
| 402 | return response.sendStatus(400); | 427 | lang = 'zh-Hant'; |
| 403 | } | 428 | } |
| 404 | 429 | ||
| 405 | console.log('Input text: ' + text); | 430 | if (request.body.lang === 'pt-BR') { |
| 431 | lang = 'pt'; | ||
| 432 | } | ||
| 406 | 433 | ||
| 407 | bingTranslateApi.translate(text, null, lang).then(result => { | 434 | if (!text || !lang) { |
| 408 | console.log('Translated text: ' + result.translation); | 435 | return response.sendStatus(400); |
| 409 | return response.send(result.translation); | 436 | } |
| 410 | }).catch(err => { | 437 | |
| 411 | console.log('Translation error: ' + err.message); | 438 | console.log('Input text: ' + text); |
| 439 | |||
| 440 | const result = await bingTranslate(text, null, lang); | ||
| 441 | const translatedText = result?.translation; | ||
| 442 | console.log('Translated text: ' + translatedText); | ||
| 443 | return response.send(translatedText); | ||
| 444 | } catch (error) { | ||
| 445 | console.log('Translation error', error); | ||
| 412 | return response.sendStatus(500); | 446 | return response.sendStatus(500); |
| 413 | }); | 447 | } |
| 414 | }); | 448 | }); |