diff --git a/packages/backend/migration/1682777547198-LibreTranslate.js b/packages/backend/migration/1682777547198-LibreTranslate.js
new file mode 100644
index 0000000000..dbaf483e6c
--- /dev/null
+++ b/packages/backend/migration/1682777547198-LibreTranslate.js
@@ -0,0 +1,23 @@
+export class LibreTranslate1682777547198 {
+ name = "LibreTranslate1682777547198";
+
+ async up(queryRunner) {
+ await queryRunner.query(`
+ ALTER TABLE "meta"
+ ADD "libreTranslateApiUrl" character varying(512)
+ `);
+ await queryRunner.query(`
+ ALTER TABLE "meta"
+ ADD "libreTranslateApiKey" character varying(128)
+ `);
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query(`
+ ALTER TABLE "meta" DROP COLUMN "libreTranslateApiKey"
+ `);
+ await queryRunner.query(`
+ ALTER TABLE "meta" DROP COLUMN "libreTranslateApiUrl"
+ `);
+ }
+}
diff --git a/packages/backend/src/config/types.ts b/packages/backend/src/config/types.ts
index 4f367debe0..0cd8c02adc 100644
--- a/packages/backend/src/config/types.ts
+++ b/packages/backend/src/config/types.ts
@@ -89,6 +89,11 @@ export type Source = {
authKey?: string;
isPro?: boolean;
};
+ libreTranslate: {
+ managed?: boolean;
+ apiUrl?: string;
+ apiKey?: string;
+ };
email: {
managed?: boolean;
address?: string;
diff --git a/packages/backend/src/models/entities/meta.ts b/packages/backend/src/models/entities/meta.ts
index 26a7c9c193..2f77796c4b 100644
--- a/packages/backend/src/models/entities/meta.ts
+++ b/packages/backend/src/models/entities/meta.ts
@@ -386,6 +386,18 @@ export class Meta {
})
public deeplIsPro: boolean;
+ @Column('varchar', {
+ length: 512,
+ nullable: true,
+ })
+ public libreTranslateApiUrl: string | null;
+
+ @Column('varchar', {
+ length: 128,
+ nullable: true,
+ })
+ public libreTranslateApiKey: string | null;
+
@Column('varchar', {
length: 512,
nullable: true,
diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/hosted.ts b/packages/backend/src/server/api/endpoints/admin/accounts/hosted.ts
index 15ad1f9a17..a7b6e95c28 100644
--- a/packages/backend/src/server/api/endpoints/admin/accounts/hosted.ts
+++ b/packages/backend/src/server/api/endpoints/admin/accounts/hosted.ts
@@ -30,6 +30,17 @@ export default define(meta, paramDef, async (ps, me) => {
set.deeplIsPro = config.deepl.isPro;
}
}
+ if (
+ config.libreTranslate.managed != null &&
+ config.libreTranslate.managed === true
+ ) {
+ if (typeof config.libreTranslate.apiUrl === "string") {
+ set.libreTranslateApiUrl = config.libreTranslate.apiUrl;
+ }
+ if (typeof config.libreTranslate.apiKey === "string") {
+ set.libreTranslateApiKey = config.libreTranslate.apiKey;
+ }
+ }
if (config.email.managed != null && config.email.managed === true) {
set.enableEmail = true;
if (typeof config.email.address === "string") {
diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts
index c8c639f504..f0ac57892d 100644
--- a/packages/backend/src/server/api/endpoints/admin/meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/meta.ts
@@ -512,7 +512,8 @@ 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 || instance.libreTranslateApiUrl != null,
pinnedPages: instance.pinnedPages,
pinnedClipId: instance.pinnedClipId,
cacheRemoteFiles: instance.cacheRemoteFiles,
@@ -564,6 +565,8 @@ export default define(meta, paramDef, async (ps, me) => {
objectStorageS3ForcePathStyle: instance.objectStorageS3ForcePathStyle,
deeplAuthKey: instance.deeplAuthKey,
deeplIsPro: instance.deeplIsPro,
+ libreTranslateApiUrl: instance.libreTranslateApiUrl,
+ libreTranslateApiKey: instance.libreTranslateApiKey,
enableIpLogging: instance.enableIpLogging,
enableActiveEmailValidation: instance.enableActiveEmailValidation,
};
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 f7e79b64b5..a230007323 100644
--- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
@@ -124,6 +124,8 @@ export const paramDef = {
summalyProxy: { type: "string", nullable: true },
deeplAuthKey: { type: "string", nullable: true },
deeplIsPro: { type: "boolean" },
+ libreTranslateApiUrl: { type: "string", nullable: true },
+ libreTranslateApiKey: { type: "string", nullable: true },
enableTwitterIntegration: { type: "boolean" },
twitterConsumerKey: { type: "string", nullable: true },
twitterConsumerSecret: { type: "string", nullable: true },
@@ -515,6 +517,22 @@ export default define(meta, paramDef, async (ps, me) => {
set.deeplIsPro = ps.deeplIsPro;
}
+ if (ps.libreTranslateApiUrl !== undefined) {
+ if (ps.libreTranslateApiUrl === "") {
+ set.libreTranslateApiUrl = null;
+ } else {
+ set.libreTranslateApiUrl = ps.libreTranslateApiUrl;
+ }
+ }
+
+ if (ps.libreTranslateApiKey !== undefined) {
+ if (ps.libreTranslateApiKey === "") {
+ set.libreTranslateApiKey = null;
+ } else {
+ set.libreTranslateApiKey = ps.libreTranslateApiKey;
+ }
+ }
+
if (ps.enableIpLogging !== undefined) {
set.enableIpLogging = ps.enableIpLogging;
}
diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts
index 4dc1c941e3..23989750fc 100644
--- a/packages/backend/src/server/api/endpoints/meta.ts
+++ b/packages/backend/src/server/api/endpoints/meta.ts
@@ -482,7 +482,8 @@ export default define(meta, paramDef, async (ps, me) => {
enableServiceWorker: instance.enableServiceWorker,
- translatorAvailable: instance.deeplAuthKey != null,
+ translatorAvailable:
+ instance.deeplAuthKey != null || instance.libreTranslateApiUrl != null,
defaultReaction: instance.defaultReaction,
...(ps.detail
diff --git a/packages/backend/src/server/api/endpoints/notes/translate.ts b/packages/backend/src/server/api/endpoints/notes/translate.ts
index c6415ceef2..d86fc12a2e 100644
--- a/packages/backend/src/server/api/endpoints/notes/translate.ts
+++ b/packages/backend/src/server/api/endpoints/notes/translate.ts
@@ -51,15 +51,54 @@ export default define(meta, paramDef, async (ps, user) => {
const instance = await fetchMeta();
- if (instance.deeplAuthKey == null) {
+ if (instance.deeplAuthKey == null && instance.libreTranslateApiUrl == null) {
return 204; // TODO: 良い感じのエラー返す
}
let targetLang = ps.targetLang;
if (targetLang.includes("-")) targetLang = targetLang.split("-")[0];
+ if (instance.libreTranslateApiUrl != null) {
+ const jsonBody = {
+ q: note.text,
+ source: "auto",
+ target: targetLang,
+ format: "text",
+ api_key: instance.libreTranslateApiKey ?? "",
+ };
+
+ const url = new URL(instance.libreTranslateApiUrl);
+ if (url.pathname.endsWith("/")) {
+ url.pathname = url.pathname.slice(0, -1);
+ }
+ if (!url.pathname.endsWith("/translate")) {
+ url.pathname += "/translate";
+ }
+ const res = await fetch(url.toString(), {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(jsonBody),
+ agent: getAgentByUrl,
+ });
+
+ const json = (await res.json()) as {
+ detectedLanguage?: {
+ confidence: number;
+ language: string;
+ };
+ translatedText: string;
+ };
+
+ return {
+ sourceLang: json.detectedLanguage?.language,
+ text: json.translatedText,
+ };
+ }
+
const params = new URLSearchParams();
- params.append("auth_key", instance.deeplAuthKey);
+ params.append("auth_key", instance.deeplAuthKey ?? "");
params.append("text", note.text);
params.append("target_lang", targetLang);
diff --git a/packages/calckey-js/.eslintignore b/packages/calckey-js/.eslintignore
deleted file mode 100644
index f22128f047..0000000000
--- a/packages/calckey-js/.eslintignore
+++ /dev/null
@@ -1,7 +0,0 @@
-node_modules
-/built
-/coverage
-/.eslintrc.js
-/jest.config.ts
-/test
-/test-d
diff --git a/packages/calckey-js/.eslintrc.js b/packages/calckey-js/.eslintrc.js
deleted file mode 100644
index 164cf1fbe8..0000000000
--- a/packages/calckey-js/.eslintrc.js
+++ /dev/null
@@ -1,65 +0,0 @@
-module.exports = {
- root: true,
- parser: "@typescript-eslint/parser",
- parserOptions: {
- tsconfigRootDir: __dirname,
- project: ["./tsconfig.json"],
- },
- plugins: ["@typescript-eslint"],
- extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
- rules: {
- indent: [
- "error",
- "tab",
- {
- SwitchCase: 1,
- MemberExpression: "off",
- flatTernaryExpressions: true,
- ArrayExpression: "first",
- ObjectExpression: "first",
- },
- ],
- "eol-last": ["error", "always"],
- semi: ["error", "always"],
- quotes: ["error", "single"],
- "comma-dangle": ["error", "always-multiline"],
- "keyword-spacing": [
- "error",
- {
- before: true,
- after: true,
- },
- ],
- "key-spacing": [
- "error",
- {
- beforeColon: false,
- afterColon: true,
- },
- ],
- "space-infix-ops": ["error"],
- "space-before-blocks": ["error", "always"],
- "object-curly-spacing": ["error", "always"],
- "nonblock-statement-body-position": ["error", "beside"],
- eqeqeq: ["error", "always", { null: "ignore" }],
- "no-multiple-empty-lines": ["error", { max: 1 }],
- "no-multi-spaces": ["error"],
- "no-var": ["error"],
- "prefer-arrow-callback": ["error"],
- "no-throw-literal": ["error"],
- "no-param-reassign": ["warn"],
- "no-constant-condition": ["warn"],
- "no-empty-pattern": ["warn"],
- "@typescript-eslint/no-unnecessary-condition": ["error"],
- "@typescript-eslint/no-inferrable-types": ["warn"],
- "@typescript-eslint/no-non-null-assertion": ["warn"],
- "@typescript-eslint/explicit-function-return-type": ["warn"],
- "@typescript-eslint/no-misused-promises": [
- "error",
- {
- checksVoidReturn: false,
- },
- ],
- "@typescript-eslint/consistent-type-imports": "error",
- },
-};
diff --git a/packages/calckey-js/package.json b/packages/calckey-js/package.json
index d68f241752..598dd1cdbc 100644
--- a/packages/calckey-js/package.json
+++ b/packages/calckey-js/package.json
@@ -9,9 +9,8 @@
"tsd": "tsd",
"api": "pnpm api-extractor run --local --verbose",
"api-prod": "pnpm api-extractor run --verbose",
- "eslint": "eslint . --ext .js,.jsx,.ts,.tsx",
"typecheck": "tsc --noEmit",
- "lint": "pnpm typecheck && pnpm eslint",
+ "lint": "pnpm typecheck && pnpm rome check \"src/*.ts\"",
"jest": "jest --coverage --detectOpenHandles",
"test": "pnpm jest && pnpm tsd"
},
diff --git a/packages/client/src/pages/admin/settings.vue b/packages/client/src/pages/admin/settings.vue
index 5349df805a..feedaff6d5 100644
--- a/packages/client/src/pages/admin/settings.vue
+++ b/packages/client/src/pages/admin/settings.vue
@@ -371,6 +371,34 @@
Pro account
+
+
+ Libre Translate
+
+
+
+ Libre Translate API URL
+
+
+
+
+ Libre Translate API Key
+
+
@@ -422,6 +450,8 @@ let swPublicKey: any = $ref(null);
let swPrivateKey: any = $ref(null);
let deeplAuthKey: string = $ref("");
let deeplIsPro: boolean = $ref(false);
+let libreTranslateApiUrl: string = $ref("");
+let libreTranslateApiKey: string = $ref("");
let defaultReaction: string = $ref("");
let defaultReactionCustom: string = $ref("");
@@ -456,6 +486,8 @@ async function init() {
swPrivateKey = meta.swPrivateKey;
deeplAuthKey = meta.deeplAuthKey;
deeplIsPro = meta.deeplIsPro;
+ libreTranslateApiUrl = meta.libreTranslateApiUrl;
+ libreTranslateApiKey = meta.libreTranslateApiKey;
defaultReaction = ["⭐", "👍", "❤️"].includes(meta.defaultReaction)
? meta.defaultReaction
: "custom";
@@ -498,6 +530,8 @@ function save() {
swPrivateKey,
deeplAuthKey,
deeplIsPro,
+ libreTranslateApiUrl,
+ libreTranslateApiKey,
defaultReaction,
}).then(() => {
fetchInstance();