From c53888c65d1aaf73692b354233a0175f60203952 Mon Sep 17 00:00:00 2001 From: penginn-net <121443048+penginn-net@users.noreply.github.com> Date: Sat, 21 Dec 2024 17:46:31 +0000 Subject: [PATCH 1/6] Libretranslate --- compose.local-db.yml | 6 +++ compose_example.yml | 6 +++ .../migration/1734793052000-LibreTranslate.js | 18 ++++++++ packages/backend/src/models/Meta.ts | 12 +++++ .../src/server/api/endpoints/admin/meta.ts | 10 +++++ .../server/api/endpoints/admin/update-meta.ts | 10 +++++ .../api/endpoints/notes/polls/translate.ts | 45 +++++++++++++++++++ .../server/api/endpoints/notes/translate.ts | 38 ++++++++++++++++ .../server/api/endpoints/users/translate.ts | 39 ++++++++++++++++ .../src/pages/admin/external-services.vue | 19 ++++++++ 10 files changed, 203 insertions(+) create mode 100644 packages/backend/migration/1734793052000-LibreTranslate.js diff --git a/compose.local-db.yml b/compose.local-db.yml index 3835cb23db..f0efdacc7b 100644 --- a/compose.local-db.yml +++ b/compose.local-db.yml @@ -38,3 +38,9 @@ services: # volumes: # - ./meili_data:/meili_data +# translator: +# restart: always +# image: libretranslate/libretranslate:latest +# healthcheck: +# test: ['CMD-SHELL', './venv/bin/python scripts/healthcheck.py'] + diff --git a/compose_example.yml b/compose_example.yml index 011a397124..11eadeae00 100644 --- a/compose_example.yml +++ b/compose_example.yml @@ -92,6 +92,12 @@ services: # volumes: # - ./meili_data:/meili_data +# translator: +# restart: always +# image: libretranslate/libretranslate:latest +# healthcheck: +# test: ['CMD-SHELL', './venv/bin/python scripts/healthcheck.py'] + networks: internal_network: internal: true diff --git a/packages/backend/migration/1734793052000-LibreTranslate.js b/packages/backend/migration/1734793052000-LibreTranslate.js new file mode 100644 index 0000000000..6714defc84 --- /dev/null +++ b/packages/backend/migration/1734793052000-LibreTranslate.js @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class LibreTranslate1734793052000 { + name = 'LibreTranslate1734793052000' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "libreTranslateEndPoint" character varying(1024)`); + await queryRunner.query(`ALTER TABLE "meta" ADD "libreTranslateApiKey" character varying(1024)`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "libreTranslateEndPoint"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "libreTranslateApiKey"`); + } +} diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts index 50d8a7e5cb..a44a38ff96 100644 --- a/packages/backend/src/models/Meta.ts +++ b/packages/backend/src/models/Meta.ts @@ -389,6 +389,18 @@ export class MiMeta { }) public ctav3Location: string | null; + @Column('varchar', { + length: 1024, + nullable: true, + }) + public libreTranslateEndPoint: string | null; + + @Column('varchar', { + length: 1024, + nullable: true, + }) + public libreTranslateApiKey: string | null; + @Column('varchar', { length: 1024, nullable: true, diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index 74cb16dc0e..7a4955d3af 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -468,6 +468,14 @@ export const meta = { type: 'boolean', optional: false, nullable: false, }, + libreTranslateEndPoint: { + type: 'string', + optional: false, nullable: true, + }, + libreTranslateApiKey: { + type: 'boolean', + optional: false, nullable: false, + }, defaultDarkTheme: { type: 'string', optional: false, nullable: true, @@ -763,6 +771,8 @@ export default class extends Endpoint { // eslint- remoteObjectStorageS3ForcePathStyle: instance.remoteObjectStorageS3ForcePathStyle, deeplAuthKey: instance.deeplAuthKey, deeplIsPro: instance.deeplIsPro, + libreTranslateEndPoint: instance.libreTranslateEndPoint, + libreTranslateApiKey: instance.libreTranslateApiKey, ctav3SaKey: instance.ctav3SaKey, ctav3ProjectId: instance.ctav3ProjectId, ctav3Location: instance.ctav3Location, diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index 5666754af9..efaa69dfff 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -107,6 +107,8 @@ export const paramDef = { ctav3Location: { type: 'string', nullable: true }, ctav3Model: { type: 'string', nullable: true }, ctav3Glossary: { type: 'string', nullable: true }, + libreTranslateEndPoint: { type: 'string', nullable: true }, + libreTranslateApiKey: { type: 'string', nullable: true }, enableEmail: { type: 'boolean' }, email: { type: 'string', nullable: true }, smtpSecure: { type: 'boolean' }, @@ -657,6 +659,14 @@ export default class extends Endpoint { // eslint- set.ctav3Glossary = ps.ctav3Glossary; } + if (ps.libreTranslateEndPoint !== undefined) { + set.libreTranslateEndPoint = ps.libreTranslateEndPoint; + } + + if (ps.libreTranslateApiKey !== undefined) { + set.libreTranslateApiKey = ps.libreTranslateApiKey; + } + if (ps.enableIpLogging !== undefined) { set.enableIpLogging = ps.enableIpLogging; } diff --git a/packages/backend/src/server/api/endpoints/notes/polls/translate.ts b/packages/backend/src/server/api/endpoints/notes/polls/translate.ts index e30ec2a156..9f8237a048 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/translate.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/translate.ts @@ -119,6 +119,10 @@ export default class extends Endpoint { // eslint- else if (this.serverSettings.ctav3ProjectId == null) return Promise.resolve(204); else if (this.serverSettings.ctav3Location == null) return Promise.resolve(204); translationResult = await this.apiCloudTranslationAdvanced(poll.choices, targetLang, this.serverSettings.ctav3SaKey, this.serverSettings.ctav3ProjectId, this.serverSettings.ctav3Location, this.serverSettings.ctav3Model, this.serverSettings.ctav3Glossary, this.serverSettings.translatorType); + } else if (this.serverSettings.translatorType === 'Libretranslate') { + const endPoint = this.serverSettings.libreTranslateEndPoint; + if (endPoint === null) throw new Error('libreTranslateEndPoint is null'); + translationResult = await this.translateLibretranslate(poll.choices, targetLang, endPoint, this.serverSettings.libreTranslateApiKey); } else { throw new Error('Unsupported translator type'); } @@ -217,4 +221,45 @@ export default class extends Endpoint { // eslint- translator: provider, }; } + + private async translateLibretranslate(texts: string[], targetLang: string, endpoint: string, apiKey:string | null ) { + const translations = []; + const target = targetLang.split('-')[0]; + for (const text of texts) { + const detectLangRes = await this.httpRequestService.send(endpoint + '/detect', { + method: 'POST', + body: JSON.stringify({ + q: text, + ...(apiKey ? { api_key: apiKey } : {}), + }), + headers: { 'Content-Type': 'application/json' }, + }); + + const detectedLang = (await detectLangRes.json() as any); + const res = await this.httpRequestService.send(endpoint + '/translate', { + method: 'POST', + body: JSON.stringify({ + q: text, + source: detectedLang[0].language, + target: target, + ...(apiKey ? { api_key: apiKey } : {}), + }), + headers: { 'Content-Type': 'application/json' }, + }); + const json = (await res.json()) as { + translatedText: string, + error: string, + }; + translations.push({ + translatedText: json.translatedText || '', + sourceLang: detectedLang[0].language || '', + }); + } + + return { + sourceLang: translations[0]?.sourceLang || '', + text: translations.map(choice => choice.translatedText), + translator: 'Libretranslate', + }; + } } diff --git a/packages/backend/src/server/api/endpoints/notes/translate.ts b/packages/backend/src/server/api/endpoints/notes/translate.ts index 1b10507e42..6ec21dfd89 100644 --- a/packages/backend/src/server/api/endpoints/notes/translate.ts +++ b/packages/backend/src/server/api/endpoints/notes/translate.ts @@ -100,6 +100,7 @@ export default class extends Endpoint { // eslint- 'deepl', 'google_no_api', 'ctav3', + 'Libretranslate', ]; if (this.serverSettings.translatorType == null || !translatorServices.includes(this.serverSettings.translatorType)) { @@ -131,6 +132,10 @@ export default class extends Endpoint { // eslint- else if (this.serverSettings.ctav3ProjectId == null) return Promise.resolve(204); else if (this.serverSettings.ctav3Location == null) return Promise.resolve(204); translationResult = await this.apiCloudTranslationAdvanced((note.cw ? note.cw + '\n' : '') + note.text, targetLang, this.serverSettings.ctav3SaKey, this.serverSettings.ctav3ProjectId, this.serverSettings.ctav3Location, this.serverSettings.ctav3Model, this.serverSettings.ctav3Glossary, this.serverSettings.translatorType); + } else if (this.serverSettings.translatorType === 'Libretranslate') { + const endPoint = this.serverSettings.libreTranslateEndPoint; + if (endPoint === null) throw new Error('libreTranslateEndPoint is null'); + translationResult = await this.translateLibretranslate((note.cw ? note.cw + '\n' : '') + note.text, targetLang, endPoint, this.serverSettings.libreTranslateApiKey); } else { throw new Error('Unsupported translator type'); } @@ -221,4 +226,37 @@ export default class extends Endpoint { // eslint- translator: provider, }; } + private async translateLibretranslate(text: string, targetLang: string, endpoint: string, apiKey:string | null ) { + const detectLangRes = await this.httpRequestService.send(endpoint + '/detect', { + method: 'POST', + body: JSON.stringify({ + q: text, + ...(apiKey ? { api_key: apiKey } : { }), + }), + headers: { 'Content-Type': 'application/json' }, + }); + + const detectedLang = ( await detectLangRes.json() as any); + const res = await this.httpRequestService.send(endpoint + '/translate', { + method: 'POST', + body: JSON.stringify({ + q: text, + source: detectedLang[0].language, + target: targetLang.split('-')[0], + ...(apiKey ? { api_key: apiKey } : { }), + }), + headers: { 'Content-Type': 'application/json' }, + }); + + const json = (await res.json()) as { + translatedText: string, + error: string, + }; + + return { + sourceLang: detectedLang[0].language, + text: json.translatedText, + translator: 'Libretranslate', + }; + } } diff --git a/packages/backend/src/server/api/endpoints/users/translate.ts b/packages/backend/src/server/api/endpoints/users/translate.ts index 02de4238d6..e17406a45b 100644 --- a/packages/backend/src/server/api/endpoints/users/translate.ts +++ b/packages/backend/src/server/api/endpoints/users/translate.ts @@ -89,6 +89,7 @@ export default class extends Endpoint { // eslint- 'deepl', 'google_no_api', 'ctav3', + 'Libretranslate', ]; if (this.serverSettings.translatorType == null || !translatorServices.includes(this.serverSettings.translatorType)) { @@ -120,6 +121,10 @@ export default class extends Endpoint { // eslint- else if (this.serverSettings.ctav3ProjectId == null) return Promise.resolve(204); else if (this.serverSettings.ctav3Location == null) return Promise.resolve(204); translationResult = await this.apiCloudTranslationAdvanced(target.description, targetLang, this.serverSettings.ctav3SaKey, this.serverSettings.ctav3ProjectId, this.serverSettings.ctav3Location, this.serverSettings.ctav3Model, this.serverSettings.ctav3Glossary, this.serverSettings.translatorType); + } else if (this.serverSettings.translatorType === 'Libretranslate') { + const endPoint = this.serverSettings.libreTranslateEndPoint; + if (endPoint === null) throw new Error('libreTranslateEndPoint is null'); + translationResult = await this.translateLibretranslate(target.description, targetLang, endPoint, this.serverSettings.libreTranslateApiKey); } else { throw new Error('Unsupported translator type'); } @@ -210,4 +215,38 @@ export default class extends Endpoint { // eslint- translator: provider, }; } + + private async translateLibretranslate(text: string, targetLang: string, endpoint: string, apiKey:string | null ) { + const detectLangRes = await this.httpRequestService.send(endpoint + '/detect', { + method: 'POST', + body: JSON.stringify({ + q: text, + ...(apiKey ? { api_key: apiKey } : { }), + }), + headers: { 'Content-Type': 'application/json' }, + }); + + const detectedLang = ( await detectLangRes.json() as any); + const res = await this.httpRequestService.send(endpoint + '/translate', { + method: 'POST', + body: JSON.stringify({ + q: text, + source: detectedLang[0].language, + target: targetLang.split('-')[0], + ...(apiKey ? { api_key: apiKey } : { }), + }), + headers: { 'Content-Type': 'application/json' }, + }); + + const json = (await res.json()) as { + translatedText: string, + error: string, + }; + + return { + sourceLang: detectedLang[0].language, + text: json.translatedText, + translator: 'Libretranslate', + }; + } } diff --git a/packages/frontend/src/pages/admin/external-services.vue b/packages/frontend/src/pages/admin/external-services.vue index e841d2f668..bc13ea52cc 100644 --- a/packages/frontend/src/pages/admin/external-services.vue +++ b/packages/frontend/src/pages/admin/external-services.vue @@ -32,6 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only + + {{ i18n.ts.save }} @@ -95,6 +108,8 @@ const ctav3ProjectId = ref(''); const ctav3Location = ref(''); const ctav3Model = ref(''); const ctav3Glossary = ref(''); +const libreTranslateEndPoint = ref(''); +const libreTranslateApiKey = ref(''); async function init() { const meta = await misskeyApi('admin/meta'); @@ -106,6 +121,8 @@ async function init() { ctav3Location.value = meta.ctav3Location; ctav3Model.value = meta.ctav3Model; ctav3Glossary.value = meta.ctav3Glossary; + libreTranslateEndPoint.value = meta.libreTranslateEndPoint; + libreTranslateApiKey.value = meta.libreTranslateApiKey; } function save_deepl() { @@ -118,6 +135,8 @@ function save_deepl() { ctav3Location: ctav3Location.value, ctav3Model: ctav3Model.value, ctav3Glossary: ctav3Glossary.value, + libreTranslateEndPoint: libreTranslateEndPoint.value, + libreTranslateApiKey: libreTranslateApiKey.value, }).then(() => { fetchInstance(true); }); From 6fb1c365fb7ad9c0cc1eef1da9bb34cd76e85afb Mon Sep 17 00:00:00 2001 From: penginn-net <121443048+penginn-net@users.noreply.github.com> Date: Sat, 21 Dec 2024 17:53:21 +0000 Subject: [PATCH 2/6] pnpm run build-cherrypick-js-with-types --- packages/cherrypick-js/src/autogen/types.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/cherrypick-js/src/autogen/types.ts b/packages/cherrypick-js/src/autogen/types.ts index a573b6afa8..e34f94c7bc 100644 --- a/packages/cherrypick-js/src/autogen/types.ts +++ b/packages/cherrypick-js/src/autogen/types.ts @@ -5622,6 +5622,8 @@ export type operations = { backgroundImageUrl: string | null; deeplAuthKey: string | null; deeplIsPro: boolean; + libreTranslateEndPoint: string | null; + libreTranslateApiKey: boolean; defaultDarkTheme: string | null; defaultLightTheme: string | null; description: string | null; @@ -10401,6 +10403,8 @@ export type operations = { ctav3Location?: string | null; ctav3Model?: string | null; ctav3Glossary?: string | null; + libreTranslateEndPoint?: string | null; + libreTranslateApiKey?: string | null; enableEmail?: boolean; email?: string | null; smtpSecure?: boolean; From 194081b6fffc13f589692441a70e3f10603f05b9 Mon Sep 17 00:00:00 2001 From: penginn-net <121443048+penginn-net@users.noreply.github.com> Date: Sat, 21 Dec 2024 18:15:03 +0000 Subject: [PATCH 3/6] fix type(bool => string) --- packages/backend/src/server/api/endpoints/admin/meta.ts | 2 +- packages/cherrypick-js/src/autogen/types.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index 7a4955d3af..26868b6632 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -473,7 +473,7 @@ export const meta = { optional: false, nullable: true, }, libreTranslateApiKey: { - type: 'boolean', + type: 'string', optional: false, nullable: false, }, defaultDarkTheme: { diff --git a/packages/cherrypick-js/src/autogen/types.ts b/packages/cherrypick-js/src/autogen/types.ts index e34f94c7bc..40188fe527 100644 --- a/packages/cherrypick-js/src/autogen/types.ts +++ b/packages/cherrypick-js/src/autogen/types.ts @@ -5623,7 +5623,7 @@ export type operations = { deeplAuthKey: string | null; deeplIsPro: boolean; libreTranslateEndPoint: string | null; - libreTranslateApiKey: boolean; + libreTranslateApiKey: string; defaultDarkTheme: string | null; defaultLightTheme: string | null; description: string | null; From e060e7a8e1906f4947ad7aeac6d8777f3bcfd553 Mon Sep 17 00:00:00 2001 From: penginn-net <121443048+penginn-net@users.noreply.github.com> Date: Sat, 21 Dec 2024 18:17:22 +0000 Subject: [PATCH 4/6] fix nullable --- packages/backend/src/server/api/endpoints/admin/meta.ts | 2 +- packages/cherrypick-js/src/autogen/types.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index 26868b6632..791391d374 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -474,7 +474,7 @@ export const meta = { }, libreTranslateApiKey: { type: 'string', - optional: false, nullable: false, + optional: false, nullable: true, }, defaultDarkTheme: { type: 'string', diff --git a/packages/cherrypick-js/src/autogen/types.ts b/packages/cherrypick-js/src/autogen/types.ts index 40188fe527..af80d30769 100644 --- a/packages/cherrypick-js/src/autogen/types.ts +++ b/packages/cherrypick-js/src/autogen/types.ts @@ -5623,7 +5623,7 @@ export type operations = { deeplAuthKey: string | null; deeplIsPro: boolean; libreTranslateEndPoint: string | null; - libreTranslateApiKey: string; + libreTranslateApiKey: string | null; defaultDarkTheme: string | null; defaultLightTheme: string | null; description: string | null; From 64736e7aee57390db4380cbb74202b6157919ede Mon Sep 17 00:00:00 2001 From: penginn-net <121443048+penginn-net@users.noreply.github.com> Date: Sat, 21 Dec 2024 18:21:31 +0000 Subject: [PATCH 5/6] fix polls-translate --- .../backend/src/server/api/endpoints/notes/polls/translate.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/backend/src/server/api/endpoints/notes/polls/translate.ts b/packages/backend/src/server/api/endpoints/notes/polls/translate.ts index 9f8237a048..d438c5f726 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/translate.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/translate.ts @@ -83,6 +83,7 @@ export default class extends Endpoint { // eslint- 'deepl', 'google_no_api', 'ctav3', + 'Libretranslate', ]; if (this.serverSettings.translatorType == null || !translatorServices.includes(this.serverSettings.translatorType)) { From bdedaae7d55d51443a41fc067c8a9cddfac3cbb6 Mon Sep 17 00:00:00 2001 From: penginn-net <121443048+penginn-net@users.noreply.github.com> Date: Tue, 31 Dec 2024 03:00:50 +0900 Subject: [PATCH 6/6] fix --- .../api/endpoints/notes/polls/translate.ts | 22 ++++++++----------- .../server/api/endpoints/notes/translate.ts | 19 ++++++---------- .../server/api/endpoints/users/translate.ts | 19 ++++++---------- .../src/pages/admin/external-services.vue | 2 +- 4 files changed, 24 insertions(+), 38 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/notes/polls/translate.ts b/packages/backend/src/server/api/endpoints/notes/polls/translate.ts index d438c5f726..3509cd6480 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/translate.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/translate.ts @@ -227,33 +227,29 @@ export default class extends Endpoint { // eslint- const translations = []; const target = targetLang.split('-')[0]; for (const text of texts) { - const detectLangRes = await this.httpRequestService.send(endpoint + '/detect', { - method: 'POST', - body: JSON.stringify({ - q: text, - ...(apiKey ? { api_key: apiKey } : {}), - }), - headers: { 'Content-Type': 'application/json' }, - }); - - const detectedLang = (await detectLangRes.json() as any); const res = await this.httpRequestService.send(endpoint + '/translate', { method: 'POST', body: JSON.stringify({ q: text, - source: detectedLang[0].language, + source: 'auto', + format: 'text', target: target, - ...(apiKey ? { api_key: apiKey } : {}), + ...(apiKey ? { api_key: apiKey } : { }), }), headers: { 'Content-Type': 'application/json' }, }); + const json = (await res.json()) as { translatedText: string, + detectedLanguage: { + confidence: number, + language: string, + } error: string, }; translations.push({ translatedText: json.translatedText || '', - sourceLang: detectedLang[0].language || '', + sourceLang: json.detectedLanguage.language || '', }); } diff --git a/packages/backend/src/server/api/endpoints/notes/translate.ts b/packages/backend/src/server/api/endpoints/notes/translate.ts index 6ec21dfd89..23abaf1127 100644 --- a/packages/backend/src/server/api/endpoints/notes/translate.ts +++ b/packages/backend/src/server/api/endpoints/notes/translate.ts @@ -227,21 +227,12 @@ export default class extends Endpoint { // eslint- }; } private async translateLibretranslate(text: string, targetLang: string, endpoint: string, apiKey:string | null ) { - const detectLangRes = await this.httpRequestService.send(endpoint + '/detect', { - method: 'POST', - body: JSON.stringify({ - q: text, - ...(apiKey ? { api_key: apiKey } : { }), - }), - headers: { 'Content-Type': 'application/json' }, - }); - - const detectedLang = ( await detectLangRes.json() as any); const res = await this.httpRequestService.send(endpoint + '/translate', { method: 'POST', body: JSON.stringify({ q: text, - source: detectedLang[0].language, + source: 'auto', + format: 'text', target: targetLang.split('-')[0], ...(apiKey ? { api_key: apiKey } : { }), }), @@ -250,11 +241,15 @@ export default class extends Endpoint { // eslint- const json = (await res.json()) as { translatedText: string, + detectedLanguage: { + confidence: number, + language: string, + } error: string, }; return { - sourceLang: detectedLang[0].language, + sourceLang: json.detectedLanguage.language, text: json.translatedText, translator: 'Libretranslate', }; diff --git a/packages/backend/src/server/api/endpoints/users/translate.ts b/packages/backend/src/server/api/endpoints/users/translate.ts index e17406a45b..8f878ec4d2 100644 --- a/packages/backend/src/server/api/endpoints/users/translate.ts +++ b/packages/backend/src/server/api/endpoints/users/translate.ts @@ -217,21 +217,12 @@ export default class extends Endpoint { // eslint- } private async translateLibretranslate(text: string, targetLang: string, endpoint: string, apiKey:string | null ) { - const detectLangRes = await this.httpRequestService.send(endpoint + '/detect', { - method: 'POST', - body: JSON.stringify({ - q: text, - ...(apiKey ? { api_key: apiKey } : { }), - }), - headers: { 'Content-Type': 'application/json' }, - }); - - const detectedLang = ( await detectLangRes.json() as any); const res = await this.httpRequestService.send(endpoint + '/translate', { method: 'POST', body: JSON.stringify({ q: text, - source: detectedLang[0].language, + source: 'auto', + format: 'text', target: targetLang.split('-')[0], ...(apiKey ? { api_key: apiKey } : { }), }), @@ -240,11 +231,15 @@ export default class extends Endpoint { // eslint- const json = (await res.json()) as { translatedText: string, + detectedLanguage: { + confidence: number, + language: string, + } error: string, }; return { - sourceLang: detectedLang[0].language, + sourceLang: json.detectedLanguage.language, text: json.translatedText, translator: 'Libretranslate', }; diff --git a/packages/frontend/src/pages/admin/external-services.vue b/packages/frontend/src/pages/admin/external-services.vue index bc13ea52cc..e94229c61c 100644 --- a/packages/frontend/src/pages/admin/external-services.vue +++ b/packages/frontend/src/pages/admin/external-services.vue @@ -67,7 +67,7 @@ SPDX-License-Identifier: AGPL-3.0-only