Translate with Google(NoAPI)

This commit is contained in:
ltlapy 2022-08-11 12:03:17 +09:00
parent ba14fdc2f4
commit 737868f309
8 changed files with 125 additions and 42 deletions

View file

@ -30,6 +30,7 @@
"cleanall": "npm run clean-all"
},
"dependencies": {
"@vitalets/google-translate-api": "8.0.0",
"execa": "5.1.1",
"gulp": "4.0.2",
"gulp-cssnano": "2.1.3",

View file

@ -0,0 +1,17 @@
export class multipleTranslationServices1660183643857 {
name = 'multipleTranslationServices1660183643857'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ADD "translatorType" character varying(32)`);
await queryRunner.query('SELECT "deeplAuthKey" FROM "meta" where "deeplAuthKey" is not null')
.then(deeplAuthKey => {
if (deeplAuthKey.length > 0) {
return queryRunner.query('UPDATE "meta" SET "translatorType" = "DeepL"');
}
})
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "translatorType"`);
}
}

View file

@ -335,6 +335,12 @@ export class Meta {
})
public discordClientSecret: string | null;
@Column('varchar', {
length: 32,
nullable: true,
})
public translatorType: string | null;
@Column('varchar', {
length: 128,
nullable: true,

View file

@ -155,6 +155,10 @@ export const meta = {
type: 'boolean',
optional: false, nullable: false,
},
translatorType: {
type: 'string',
optional: false, nullable: true,
},
proxyAccountName: {
type: 'string',
optional: false, nullable: true,
@ -380,7 +384,9 @@ export default define(meta, paramDef, async (ps, me) => {
enableGithubIntegration: instance.enableGithubIntegration,
enableDiscordIntegration: instance.enableDiscordIntegration,
enableServiceWorker: instance.enableServiceWorker,
translatorAvailable: instance.deeplAuthKey != null,
// translatorAvailable: instance.deeplAuthKey != null,
translatorAvailable: instance.translatorType != null,
translatorType: instance.translatorType,
pinnedPages: instance.pinnedPages,
pinnedClipId: instance.pinnedClipId,
cacheRemoteFiles: instance.cacheRemoteFiles,

View file

@ -63,6 +63,7 @@ export const paramDef = {
type: 'string',
} },
summalyProxy: { type: 'string', nullable: true },
translatorType: { type: 'string', nullable: true },
deeplAuthKey: { type: 'string', nullable: true },
deeplIsPro: { type: 'boolean' },
enableTwitterIntegration: { type: 'boolean' },
@ -406,6 +407,14 @@ export default define(meta, paramDef, async (ps, me) => {
set.objectStorageS3ForcePathStyle = ps.objectStorageS3ForcePathStyle;
}
if (ps.translatorType !== undefined) {
if (ps.translatorType === '') {
set.translatorType = null;
} else {
set.translatorType = ps.translatorType;
}
}
if (ps.deeplAuthKey !== undefined) {
if (ps.deeplAuthKey === '') {
set.deeplAuthKey = null;

View file

@ -377,7 +377,8 @@ export default define(meta, paramDef, async (ps, me) => {
enableServiceWorker: instance.enableServiceWorker,
translatorAvailable: instance.deeplAuthKey != null,
// translatorAvailable: instance.deeplAuthKey != null,
translatorAvailable: instance.translatorType != null,
...(ps.detail ? {
pinnedPages: instance.pinnedPages,

View file

@ -1,4 +1,5 @@
import { URLSearchParams } from 'node:url';
import googletr from '@vitalets/google-translate-api';
import fetch from 'node-fetch';
import config from '@/config/index.js';
import { getAgentByUrl } from '@/misc/fetch.js';
@ -24,6 +25,11 @@ export const meta = {
code: 'NO_SUCH_NOTE',
id: 'bea9b03f-36e0-49c5-a4db-627a029f8971',
},
noTranslateService: {
message: 'Translate service is not available.',
code: 'NO_TRANSLATE_SERVICE',
id: 'bef6e895-c05d-4499-9815-035ed18b0e31',
},
},
} as const;
@ -42,6 +48,10 @@ export default define(meta, paramDef, async (ps, user) => {
if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
throw e;
});
const translatorServices = [
'DeepL',
'GoogleNoAPI',
];
if (!(await Notes.isVisibleForMe(note, user ? user.id : null))) {
return 204; // TODO: 良い感じのエラー返す
@ -53,42 +63,62 @@ export default define(meta, paramDef, async (ps, user) => {
const instance = await fetchMeta();
if (instance.deeplAuthKey == null) {
return 204; // TODO: 良い感じのエラー返す
if (instance.translatorType == null || !translatorServices.includes(instance.translatorType)) {
throw new ApiError(meta.errors.noTranslateService);
}
let targetLang = ps.targetLang;
if (targetLang.includes('-')) targetLang = targetLang.split('-')[0];
if (instance.translatorType === 'DeepL') {
if (instance.deeplAuthKey == null) {
return 204; // TODO: 良い感じのエラー返す
}
const params = new URLSearchParams();
params.append('auth_key', instance.deeplAuthKey);
params.append('text', note.text);
params.append('target_lang', targetLang);
let targetLang = ps.targetLang;
if (targetLang.includes('-')) targetLang = targetLang.split('-')[0];
const endpoint = instance.deeplIsPro ? 'https://api.deepl.com/v2/translate' : 'https://api-free.deepl.com/v2/translate';
const params = new URLSearchParams();
params.append('auth_key', instance.deeplAuthKey);
params.append('text', note.text);
params.append('target_lang', targetLang);
const res = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': config.userAgent,
Accept: 'application/json, */*',
},
body: params,
// TODO
//timeout: 10000,
agent: getAgentByUrl,
});
const endpoint = instance.deeplIsPro ? 'https://api.deepl.com/v2/translate' : 'https://api-free.deepl.com/v2/translate';
const json = (await res.json()) as {
translations: {
detected_source_language: string;
text: string;
}[];
};
const res = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': config.userAgent,
Accept: 'application/json, */*',
},
body: params,
// TODO
//timeout: 10000,
agent: getAgentByUrl,
});
return {
sourceLang: json.translations[0].detected_source_language,
text: json.translations[0].text,
};
const json = (await res.json()) as {
translations: {
detected_source_language: string;
text: string;
}[];
};
return {
sourceLang: json.translations[0].detected_source_language,
text: json.translations[0].text,
translator: translatorServices,
};
} else if (instance.translatorType === 'GoogleNoAPI') {
let targetLang = ps.targetLang;
if (targetLang.includes('-')) targetLang = targetLang.split('-')[0];
const json = await googletr(note.text, { to: targetLang });
return {
sourceLang: json.from.language.iso,
text: json.text,
translator: translatorServices,
};
}
return 204; // TODO: 良い感じのエラー返す
});

View file

@ -130,15 +130,24 @@
</FormSection>
<FormSection>
<template #label>DeepL Translation</template>
<template #label>Translation</template>
<FormInput v-model="deeplAuthKey" class="_formBlock">
<template #prefix><i class="fas fa-key"></i></template>
<template #label>DeepL Auth Key</template>
</FormInput>
<FormSwitch v-model="deeplIsPro" class="_formBlock">
<template #label>Pro account</template>
</FormSwitch>
<FormRadios v-model="translatorType" class="_formBlock">
<template #label>Translator type</template>
<option :value="null">{{ i18n.ts.none }}</option>
<option value="DeepL">DeepL</option>
<option value="GoogleNoAPI">Google Translate(without API)</option>
</FormRadios>
<template v-if="translatorType === 'DeepL'">
<FormInput v-model="deeplAuthKey" class="_formBlock">
<template #prefix><i class="fas fa-key"></i></template>
<template #label>DeepL Auth Key</template>
</FormInput>
<FormSwitch v-model="deeplIsPro" class="_formBlock">
<template #label>Pro account</template>
</FormSwitch>
</template>
</FormSection>
</div>
</FormSuspense>
@ -157,6 +166,7 @@ import FormInfo from '@/components/ui/info.vue';
import FormSection from '@/components/form/section.vue';
import FormSplit from '@/components/form/split.vue';
import FormSuspense from '@/components/form/suspense.vue';
import FormRadios from '@/components/form/radios.vue'
import * as os from '@/os';
import { fetchInstance } from '@/instance';
import { i18n } from '@/i18n';
@ -184,6 +194,7 @@ let emailRequiredForSignup: boolean = $ref(false);
let enableServiceWorker: boolean = $ref(false);
let swPublicKey: any = $ref(null);
let swPrivateKey: any = $ref(null);
let translatorType: string | null = $ref(null);
let deeplAuthKey: string = $ref('');
let deeplIsPro: boolean = $ref(false);
@ -211,6 +222,7 @@ async function init() {
enableServiceWorker = meta.enableServiceWorker;
swPublicKey = meta.swPublickey;
swPrivateKey = meta.swPrivateKey;
translatorType = meta.translatorType;
deeplAuthKey = meta.deeplAuthKey;
deeplIsPro = meta.deeplIsPro;
}
@ -239,6 +251,7 @@ function save() {
enableServiceWorker,
swPublicKey,
swPrivateKey,
translatorType,
deeplAuthKey,
deeplIsPro,
}).then(() => {