@nocobase/plugin-localization 2.1.0-beta.9 → 2.2.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client-v2.d.ts +2 -0
- package/client-v2.js +1 -0
- package/dist/ai/ai-employees/lina.d.ts +10 -0
- package/dist/ai/ai-employees/lina.js +60 -0
- package/dist/client/300.1f79b801d226f40d.js +10 -0
- package/dist/client/i18n-missing-handler.d.ts +1 -17
- package/dist/client/index.js +1 -1
- package/dist/client-v2/796.0ec484505de4b04a.js +10 -0
- package/dist/client-v2/common/constants.d.ts +13 -0
- package/dist/client-v2/common/i18n-missing-handler.d.ts +23 -0
- package/dist/{client/Localization.d.ts → client-v2/i18n-missing-handler.d.ts} +1 -2
- package/dist/client-v2/index.d.ts +9 -0
- package/dist/client-v2/index.js +10 -0
- package/dist/client-v2/locale.d.ts +11 -0
- package/dist/client-v2/pages/LocalizationPage.d.ts +11 -0
- package/dist/client-v2/plugin.d.ts +13 -0
- package/dist/externalVersion.js +18 -13
- package/dist/locale/en-US.json +38 -1
- package/dist/locale/zh-CN.json +38 -1
- package/dist/server/actions/aiTranslate.d.ts +14 -0
- package/dist/server/actions/aiTranslate.js +151 -0
- package/dist/server/actions/localization.js +30 -15
- package/dist/server/actions/localizationTexts.js +8 -9
- package/dist/server/collections/localization-texts.js +1 -0
- package/dist/server/collections/localization-translations.js +1 -0
- package/dist/server/migrations/20260511230000-delete-official-plugin-package-resource-modules.d.ts +14 -0
- package/dist/server/migrations/20260511230000-delete-official-plugin-package-resource-modules.js +64 -0
- package/dist/server/plugin.d.ts +5 -2
- package/dist/server/plugin.js +76 -14
- package/dist/server/tasks/localization-ai-translate.d.ts +48 -0
- package/dist/server/tasks/localization-ai-translate.js +606 -0
- package/dist/server/translation-scope.d.ts +31 -0
- package/dist/server/translation-scope.js +107 -0
- package/package.json +7 -2
- package/dist/client/177bd849c95cf6cc.js +0 -10
- package/dist/server/source-manager.d.ts +0 -35
- package/dist/server/source-manager.js +0 -77
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import { Context, Next } from '@nocobase/actions';
|
|
10
|
+
declare const _default: {
|
|
11
|
+
aiTranslate: (ctx: Context, next: Next) => Promise<void>;
|
|
12
|
+
aiTranslatePreview: (ctx: Context, next: Next) => Promise<void>;
|
|
13
|
+
};
|
|
14
|
+
export default _default;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
var __defProp = Object.defineProperty;
|
|
11
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
|
+
var aiTranslate_exports = {};
|
|
28
|
+
__export(aiTranslate_exports, {
|
|
29
|
+
default: () => aiTranslate_default
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(aiTranslate_exports);
|
|
32
|
+
var import_localization_ai_translate = require("../tasks/localization-ai-translate");
|
|
33
|
+
var import_translation_scope = require("../translation-scope");
|
|
34
|
+
const validateParams = (ctx) => {
|
|
35
|
+
const {
|
|
36
|
+
mode,
|
|
37
|
+
locale,
|
|
38
|
+
employeeUsername = "lina",
|
|
39
|
+
model,
|
|
40
|
+
textIds,
|
|
41
|
+
scope = "all",
|
|
42
|
+
referenceLocales
|
|
43
|
+
} = ctx.action.params.values || {};
|
|
44
|
+
if (!["full", "incremental", "selected"].includes(mode)) {
|
|
45
|
+
ctx.throw(400, "Invalid translation mode");
|
|
46
|
+
}
|
|
47
|
+
if (!["all", "builtIn", "custom"].includes(scope)) {
|
|
48
|
+
ctx.throw(400, "Invalid translation scope");
|
|
49
|
+
}
|
|
50
|
+
if (mode === "selected" && (!Array.isArray(textIds) || !textIds.length)) {
|
|
51
|
+
ctx.throw(400, "Please select the records you want to translate");
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
mode,
|
|
55
|
+
locale: locale || ctx.get("X-Locale") || "en-US",
|
|
56
|
+
employeeUsername,
|
|
57
|
+
model,
|
|
58
|
+
textIds,
|
|
59
|
+
scope,
|
|
60
|
+
referenceLocales
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
const countTexts = async (ctx, mode, locale, scope, textIds) => {
|
|
64
|
+
const options = await (0, import_translation_scope.buildFindTextsOptions)({
|
|
65
|
+
app: ctx.app,
|
|
66
|
+
mode,
|
|
67
|
+
locale,
|
|
68
|
+
scope,
|
|
69
|
+
textIds
|
|
70
|
+
});
|
|
71
|
+
return await ctx.db.getRepository("localizationTexts").count(options);
|
|
72
|
+
};
|
|
73
|
+
const getScopeTitle = (scope) => {
|
|
74
|
+
return {
|
|
75
|
+
all: "All entries",
|
|
76
|
+
builtIn: "Built-in entries",
|
|
77
|
+
custom: "Custom entries"
|
|
78
|
+
}[scope];
|
|
79
|
+
};
|
|
80
|
+
const getTaskTitle = (mode, scope, locale) => {
|
|
81
|
+
if (mode === "selected") {
|
|
82
|
+
return `AI ${mode} localization translation - ${locale}`;
|
|
83
|
+
}
|
|
84
|
+
return `AI ${mode} localization translation - ${getScopeTitle(scope)} - ${locale}`;
|
|
85
|
+
};
|
|
86
|
+
const getAITranslatePreview = async (ctx) => {
|
|
87
|
+
const { mode, locale, employeeUsername, model, scope, referenceLocales, textIds } = validateParams(ctx);
|
|
88
|
+
const aiPlugin = ctx.app.pm.get("ai");
|
|
89
|
+
if (!(aiPlugin == null ? void 0 : aiPlugin.aiEmployeesManager)) {
|
|
90
|
+
ctx.throw(500, "AI plugin is not available");
|
|
91
|
+
}
|
|
92
|
+
const employee = await aiPlugin.aiEmployeesManager.getEmployee(employeeUsername);
|
|
93
|
+
if (!employee) {
|
|
94
|
+
ctx.throw(400, `AI employee "${employeeUsername}" not found`);
|
|
95
|
+
}
|
|
96
|
+
const resolvedModel = await aiPlugin.aiEmployeesManager.resolveModel(employee, model);
|
|
97
|
+
const { model: modelName, service } = await aiPlugin.aiManager.getLLMService(resolvedModel);
|
|
98
|
+
const providerMeta = aiPlugin.aiManager.llmProviders.get(service.provider);
|
|
99
|
+
const count = await countTexts(ctx, mode, locale, scope, textIds);
|
|
100
|
+
return {
|
|
101
|
+
mode,
|
|
102
|
+
locale,
|
|
103
|
+
scope,
|
|
104
|
+
referenceLocales,
|
|
105
|
+
count,
|
|
106
|
+
provider: service.provider,
|
|
107
|
+
providerTitle: providerMeta == null ? void 0 : providerMeta.title,
|
|
108
|
+
llmService: service.name,
|
|
109
|
+
llmServiceTitle: service.title,
|
|
110
|
+
model: modelName
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
const aiTranslatePreview = async (ctx, next) => {
|
|
114
|
+
ctx.body = await getAITranslatePreview(ctx);
|
|
115
|
+
await next();
|
|
116
|
+
};
|
|
117
|
+
const aiTranslate = async (ctx, next) => {
|
|
118
|
+
var _a, _b, _c, _d;
|
|
119
|
+
const { mode, locale, employeeUsername, model, textIds, scope, referenceLocales } = validateParams(ctx);
|
|
120
|
+
const currentUserId = ((_b = (_a = ctx.auth) == null ? void 0 : _a.user) == null ? void 0 : _b.id) || ((_d = (_c = ctx.state) == null ? void 0 : _c.currentUser) == null ? void 0 : _d.id);
|
|
121
|
+
const taskManager = ctx.app.container.get("AsyncTaskManager");
|
|
122
|
+
if (!taskManager) {
|
|
123
|
+
ctx.throw(500, "AsyncTaskManager is not available");
|
|
124
|
+
}
|
|
125
|
+
const task = await taskManager.createTask(
|
|
126
|
+
{
|
|
127
|
+
origin: "localization",
|
|
128
|
+
type: import_localization_ai_translate.LOCALIZATION_AI_TRANSLATE_TASK_TYPE,
|
|
129
|
+
title: getTaskTitle(mode, scope, locale),
|
|
130
|
+
params: {
|
|
131
|
+
mode,
|
|
132
|
+
locale,
|
|
133
|
+
employeeUsername,
|
|
134
|
+
model,
|
|
135
|
+
userId: currentUserId,
|
|
136
|
+
textIds,
|
|
137
|
+
scope,
|
|
138
|
+
referenceLocales
|
|
139
|
+
},
|
|
140
|
+
createdById: currentUserId,
|
|
141
|
+
cancelable: true
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
useQueue: true,
|
|
145
|
+
context: ctx
|
|
146
|
+
}
|
|
147
|
+
);
|
|
148
|
+
ctx.body = task.toJSON();
|
|
149
|
+
await next();
|
|
150
|
+
};
|
|
151
|
+
var aiTranslate_default = { aiTranslate, aiTranslatePreview };
|
|
@@ -35,15 +35,23 @@ const sync = async (ctx, next) => {
|
|
|
35
35
|
const plugin = ctx.app.pm.get("localization");
|
|
36
36
|
const resourcesInstance = plugin.resources;
|
|
37
37
|
const locale = ctx.get("X-Locale") || "en-US";
|
|
38
|
-
const { types = [] } = ctx.action.params.values || {};
|
|
38
|
+
const { types = [], resetTranslations = false } = ctx.action.params.values || {};
|
|
39
39
|
if (!types.length) {
|
|
40
40
|
ctx.throw(400, ctx.t("Please provide synchronization source."));
|
|
41
41
|
}
|
|
42
|
-
const resources = await
|
|
43
|
-
|
|
42
|
+
const resources = await ctx.app.localeManager.syncSources(ctx, types);
|
|
43
|
+
const normalizedResources = {};
|
|
44
44
|
Object.entries(resources).forEach(([module2, resource]) => {
|
|
45
|
+
const normalizedModule = plugin.normalizeResourceModule(module2).replace("resources.", "");
|
|
46
|
+
normalizedResources[normalizedModule] = {
|
|
47
|
+
...normalizedResources[normalizedModule] || {},
|
|
48
|
+
...resource
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
let textValues = [];
|
|
52
|
+
Object.entries(normalizedResources).forEach(([module2, resource]) => {
|
|
45
53
|
Object.keys(resource).forEach((text) => {
|
|
46
|
-
textValues.push({ module:
|
|
54
|
+
textValues.push({ module: plugin.normalizeResourceModule(module2), text });
|
|
47
55
|
});
|
|
48
56
|
});
|
|
49
57
|
textValues = await resourcesInstance.filterExists(textValues);
|
|
@@ -53,26 +61,34 @@ const sync = async (ctx, next) => {
|
|
|
53
61
|
});
|
|
54
62
|
const texts = await ctx.db.getModel("localizationTexts").findAll({
|
|
55
63
|
include: [{ association: "translations", where: { locale }, required: false }],
|
|
56
|
-
where: { "$translations.id$": null },
|
|
64
|
+
where: resetTranslations ? void 0 : { "$translations.id$": null },
|
|
57
65
|
transaction: t
|
|
58
66
|
});
|
|
59
67
|
const translationValues = texts.filter((text) => {
|
|
60
68
|
var _a;
|
|
61
|
-
const module2 = text.module.replace("resources.", "");
|
|
62
|
-
return (_a =
|
|
69
|
+
const module2 = plugin.normalizeResourceModule(text.module).replace("resources.", "");
|
|
70
|
+
return (_a = normalizedResources[module2]) == null ? void 0 : _a[text.text];
|
|
63
71
|
}).map((text) => {
|
|
64
72
|
var _a;
|
|
65
|
-
const module2 = text.module.replace("resources.", "");
|
|
73
|
+
const module2 = plugin.normalizeResourceModule(text.module).replace("resources.", "");
|
|
66
74
|
return {
|
|
67
75
|
locale,
|
|
68
76
|
textId: text.id,
|
|
69
|
-
translation: (_a =
|
|
77
|
+
translation: (_a = normalizedResources[module2]) == null ? void 0 : _a[text.text]
|
|
70
78
|
};
|
|
71
79
|
});
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
80
|
+
if (resetTranslations) {
|
|
81
|
+
await ctx.db.getModel("localizationTranslations").bulkCreate(translationValues, {
|
|
82
|
+
updateOnDuplicate: ["translation"],
|
|
83
|
+
transaction: t
|
|
84
|
+
});
|
|
85
|
+
} else {
|
|
86
|
+
await ctx.db.getModel("localizationTranslations").bulkCreate(translationValues, {
|
|
87
|
+
transaction: t
|
|
88
|
+
});
|
|
89
|
+
}
|
|
75
90
|
await resourcesInstance.updateCacheTexts(newTexts);
|
|
91
|
+
await resourcesInstance.reset();
|
|
76
92
|
});
|
|
77
93
|
ctx.logger.info(`Sync localization resources done, ${Date.now() - startTime}ms`);
|
|
78
94
|
await next();
|
|
@@ -82,9 +98,8 @@ const publish = async (ctx, next) => {
|
|
|
82
98
|
await next();
|
|
83
99
|
};
|
|
84
100
|
const getSources = async (ctx, next) => {
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
ctx.body = sources.map(([name, source]) => ({
|
|
101
|
+
const sources = Array.from(ctx.app.localeManager.sources.getEntities());
|
|
102
|
+
ctx.body = sources.filter(([, source]) => source.sync).map(([name, source]) => ({
|
|
88
103
|
name,
|
|
89
104
|
title: source.title
|
|
90
105
|
}));
|
|
@@ -104,18 +104,17 @@ const list = async (ctx, next) => {
|
|
|
104
104
|
const [rows, count] = await listText(ctx.db, { module: module2, keyword, hasTranslation, locale, options });
|
|
105
105
|
const cache = ctx.app.cache;
|
|
106
106
|
const pm = ctx.app.pm;
|
|
107
|
-
const plugin = pm.get("localization");
|
|
108
107
|
const plugins = await cache.wrap(`lm-plugins:${locale}`, () => pm.list({ locale }));
|
|
109
|
-
const sources = Array.from(
|
|
108
|
+
const sources = Array.from(ctx.app.localeManager.sources.getValues());
|
|
110
109
|
const extendModules = sources.filter((source) => source.namespace).map((source) => ({
|
|
111
110
|
value: source.namespace,
|
|
112
111
|
label: source.title
|
|
113
112
|
}));
|
|
114
113
|
const modules = [
|
|
115
114
|
...extendModules,
|
|
116
|
-
...plugins.map((
|
|
117
|
-
value:
|
|
118
|
-
label:
|
|
115
|
+
...plugins.map((plugin) => ({
|
|
116
|
+
value: plugin.alias || plugin.name,
|
|
117
|
+
label: plugin.displayName
|
|
119
118
|
}))
|
|
120
119
|
];
|
|
121
120
|
for (const row of rows) {
|
|
@@ -133,9 +132,9 @@ const list = async (ctx, next) => {
|
|
|
133
132
|
totalPage: Math.ceil(count / pageSize),
|
|
134
133
|
modules: [
|
|
135
134
|
...extendModules,
|
|
136
|
-
...plugins.map((
|
|
137
|
-
value:
|
|
138
|
-
label:
|
|
135
|
+
...plugins.map((plugin) => ({
|
|
136
|
+
value: plugin.alias || plugin.name,
|
|
137
|
+
label: plugin.displayName
|
|
139
138
|
}))
|
|
140
139
|
]
|
|
141
140
|
};
|
|
@@ -163,7 +162,7 @@ const missing = async (ctx, next) => {
|
|
|
163
162
|
const plugin = (_a = ctx.app.pm) == null ? void 0 : _a.get("localization");
|
|
164
163
|
const currentLocale = locale || ctx.get("X-Locale") || "en-US";
|
|
165
164
|
await (plugin == null ? void 0 : plugin.addNewTexts(
|
|
166
|
-
keys.map((key) => ({ text: key.text, module:
|
|
165
|
+
keys.map((key) => ({ text: key.text, module: plugin.normalizeResourceModule(key.ns) })),
|
|
167
166
|
{
|
|
168
167
|
locale: currentLocale
|
|
169
168
|
}
|
|
@@ -37,6 +37,7 @@ var localization_translations_default = (0, import_database.defineCollection)({
|
|
|
37
37
|
},
|
|
38
38
|
migrationRules: ["overwrite", "schema-only"],
|
|
39
39
|
name: "localizationTranslations",
|
|
40
|
+
dataCategory: "system",
|
|
40
41
|
model: "LocalizationTranslationModel",
|
|
41
42
|
createdBy: true,
|
|
42
43
|
updatedBy: true,
|
package/dist/server/migrations/20260511230000-delete-official-plugin-package-resource-modules.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import { Migration } from '@nocobase/server';
|
|
10
|
+
export default class extends Migration {
|
|
11
|
+
on: string;
|
|
12
|
+
appVersion: string;
|
|
13
|
+
up(): Promise<void>;
|
|
14
|
+
}
|
package/dist/server/migrations/20260511230000-delete-official-plugin-package-resource-modules.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
var __defProp = Object.defineProperty;
|
|
11
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
|
+
var delete_official_plugin_package_resource_modules_exports = {};
|
|
28
|
+
__export(delete_official_plugin_package_resource_modules_exports, {
|
|
29
|
+
default: () => delete_official_plugin_package_resource_modules_default
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(delete_official_plugin_package_resource_modules_exports);
|
|
32
|
+
var import_database = require("@nocobase/database");
|
|
33
|
+
var import_server = require("@nocobase/server");
|
|
34
|
+
class delete_official_plugin_package_resource_modules_default extends import_server.Migration {
|
|
35
|
+
on = "afterLoad";
|
|
36
|
+
// 'beforeLoad' or 'afterLoad'
|
|
37
|
+
appVersion = "<2.2.0";
|
|
38
|
+
async up() {
|
|
39
|
+
const textRepo = this.db.getRepository("localizationTexts");
|
|
40
|
+
const translationRepo = this.db.getRepository("localizationTranslations");
|
|
41
|
+
const texts = await textRepo.find({
|
|
42
|
+
filter: {
|
|
43
|
+
module: {
|
|
44
|
+
[import_database.Op.like]: `resources.${import_server.OFFICIAL_PLUGIN_PREFIX}%`
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
fields: ["id"]
|
|
48
|
+
});
|
|
49
|
+
const textIds = texts.map((text) => text.get("id"));
|
|
50
|
+
if (!textIds.length) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
await translationRepo.destroy({
|
|
54
|
+
filter: {
|
|
55
|
+
textId: {
|
|
56
|
+
$in: textIds
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
await textRepo.destroy({
|
|
61
|
+
filterByTk: textIds
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
package/dist/server/plugin.d.ts
CHANGED
|
@@ -9,10 +9,11 @@
|
|
|
9
9
|
/// <reference types="node" />
|
|
10
10
|
import { InstallOptions, Plugin } from '@nocobase/server';
|
|
11
11
|
import Resources from './resources';
|
|
12
|
-
import { SourceManager } from './source-manager';
|
|
13
12
|
export declare class PluginLocalizationServer extends Plugin {
|
|
14
13
|
resources: Resources;
|
|
15
|
-
|
|
14
|
+
private aiTranslateTaskRegistered;
|
|
15
|
+
private localeSourceTextHookKeys;
|
|
16
|
+
normalizeResourceModule(module: string): string;
|
|
16
17
|
addNewTexts: (texts: {
|
|
17
18
|
text: string;
|
|
18
19
|
module: string;
|
|
@@ -22,7 +23,9 @@ export declare class PluginLocalizationServer extends Plugin {
|
|
|
22
23
|
}) => Promise<void>;
|
|
23
24
|
afterAdd(): void;
|
|
24
25
|
beforeLoad(): void;
|
|
26
|
+
private registerAITranslateTaskType;
|
|
25
27
|
load(): Promise<void>;
|
|
28
|
+
private handleLocaleSourceTextsSaved;
|
|
26
29
|
handleSyncMessage(message: any): Promise<void>;
|
|
27
30
|
install(options?: InstallOptions): Promise<void>;
|
|
28
31
|
afterEnable(): Promise<void>;
|
package/dist/server/plugin.js
CHANGED
|
@@ -42,17 +42,29 @@ __export(plugin_exports, {
|
|
|
42
42
|
module.exports = __toCommonJS(plugin_exports);
|
|
43
43
|
var import_server = require("@nocobase/server");
|
|
44
44
|
var import_localization = __toESM(require("./actions/localization"));
|
|
45
|
+
var import_aiTranslate = __toESM(require("./actions/aiTranslate"));
|
|
45
46
|
var import_localizationTexts = __toESM(require("./actions/localizationTexts"));
|
|
46
47
|
var import_resources = __toESM(require("./resources"));
|
|
47
48
|
var import_utils = require("./utils");
|
|
48
49
|
var import_constants = require("./constants");
|
|
49
|
-
var import_source_manager = require("./source-manager");
|
|
50
50
|
var import_utils2 = require("@nocobase/utils");
|
|
51
|
+
var import_localization_ai_translate = require("./tasks/localization-ai-translate");
|
|
51
52
|
var import_package = __toESM(require("../../package.json"));
|
|
52
53
|
class PluginLocalizationServer extends import_server.Plugin {
|
|
53
54
|
resources;
|
|
54
|
-
|
|
55
|
+
aiTranslateTaskRegistered = false;
|
|
56
|
+
localeSourceTextHookKeys = /* @__PURE__ */ new Set();
|
|
57
|
+
normalizeResourceModule(module2) {
|
|
58
|
+
const prefix = "resources.";
|
|
59
|
+
const namespace = module2.startsWith(prefix) ? module2.slice(prefix.length) : module2;
|
|
60
|
+
const normalizedNamespace = namespace.startsWith(import_server.OFFICIAL_PLUGIN_PREFIX) ? namespace.replace(import_server.OFFICIAL_PLUGIN_PREFIX, "") : namespace;
|
|
61
|
+
return `${prefix}${normalizedNamespace}`;
|
|
62
|
+
}
|
|
55
63
|
addNewTexts = async (texts, options) => {
|
|
64
|
+
texts = texts.map(({ text, module: module2 }) => ({
|
|
65
|
+
text,
|
|
66
|
+
module: this.normalizeResourceModule(module2)
|
|
67
|
+
}));
|
|
56
68
|
texts = await this.resources.filterExists(texts, options == null ? void 0 : options.transaction);
|
|
57
69
|
await this.db.getModel("localizationTexts").bulkCreate(
|
|
58
70
|
texts.map(({ text, module: module2 }) => ({
|
|
@@ -88,23 +100,44 @@ class PluginLocalizationServer extends import_server.Plugin {
|
|
|
88
100
|
});
|
|
89
101
|
};
|
|
90
102
|
afterAdd() {
|
|
91
|
-
this.app.on("afterLoad", () => this.
|
|
103
|
+
this.app.on("afterLoad", () => this.handleLocaleSourceTextsSaved());
|
|
104
|
+
this.app.on("afterLoad", () => this.registerAITranslateTaskType());
|
|
92
105
|
}
|
|
93
106
|
beforeLoad() {
|
|
94
107
|
}
|
|
108
|
+
registerAITranslateTaskType() {
|
|
109
|
+
if (this.aiTranslateTaskRegistered) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
const taskManager = this.app.container.get("AsyncTaskManager");
|
|
114
|
+
taskManager.registerTaskType(import_localization_ai_translate.LocalizationAITranslateTask);
|
|
115
|
+
this.aiTranslateTaskRegistered = true;
|
|
116
|
+
} catch (error) {
|
|
117
|
+
this.log.warn("AsyncTaskManager is not available, skip localization AI translate task registration.");
|
|
118
|
+
}
|
|
119
|
+
}
|
|
95
120
|
async load() {
|
|
121
|
+
this.registerAITranslateTaskType();
|
|
96
122
|
this.app.resourceManager.define({
|
|
97
123
|
name: "localizationTexts",
|
|
98
124
|
actions: import_localizationTexts.default
|
|
99
125
|
});
|
|
100
126
|
this.app.resourceManager.define({
|
|
101
127
|
name: "localization",
|
|
102
|
-
actions:
|
|
128
|
+
actions: {
|
|
129
|
+
...import_localization.default,
|
|
130
|
+
...import_aiTranslate.default
|
|
131
|
+
}
|
|
103
132
|
});
|
|
104
133
|
this.app.acl.registerSnippet({
|
|
105
134
|
name: `pm.${this.name}.localization`,
|
|
106
135
|
actions: ["localization:*", "localizationTexts:*", "localizationTranslations:*"]
|
|
107
136
|
});
|
|
137
|
+
this.app.acl.registerSnippet({
|
|
138
|
+
name: "ui.localization",
|
|
139
|
+
actions: ["localizationTexts:missing"]
|
|
140
|
+
});
|
|
108
141
|
this.app.localeManager.registerResourceStorer("plugin-localization", {
|
|
109
142
|
getResources: (lang) => this.resources.getResources(lang),
|
|
110
143
|
reset: () => this.resources.reset()
|
|
@@ -115,24 +148,22 @@ class PluginLocalizationServer extends import_server.Plugin {
|
|
|
115
148
|
store: "memory"
|
|
116
149
|
});
|
|
117
150
|
this.resources = new import_resources.default(this.db, cache);
|
|
118
|
-
this.
|
|
151
|
+
this.app.localeManager.registerSource("local", {
|
|
119
152
|
title: (0, import_utils2.tval)("System & Plugins", { ns: import_package.default.name }),
|
|
120
153
|
sync: async (ctx) => {
|
|
121
|
-
const resources = await ctx.app.localeManager.
|
|
154
|
+
const resources = await ctx.app.localeManager.getBuiltInResources(ctx.get("X-Locale") || "en-US");
|
|
122
155
|
const result = {};
|
|
123
156
|
Object.entries(resources).forEach(([module2, resource]) => {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
result[module2] = resource;
|
|
157
|
+
const name = module2.startsWith(import_server.OFFICIAL_PLUGIN_PREFIX) ? module2.replace(import_server.OFFICIAL_PLUGIN_PREFIX, "") : module2;
|
|
158
|
+
result[name] = {
|
|
159
|
+
...result[name] || {},
|
|
160
|
+
...resource
|
|
161
|
+
};
|
|
131
162
|
});
|
|
132
163
|
return result;
|
|
133
164
|
}
|
|
134
165
|
});
|
|
135
|
-
this.
|
|
166
|
+
this.app.localeManager.registerSource("db", {
|
|
136
167
|
title: (0, import_utils2.tval)("Collections & Fields", { ns: import_package.default.name }),
|
|
137
168
|
namespace: import_constants.NAMESPACE_COLLECTIONS,
|
|
138
169
|
sync: async (ctx) => {
|
|
@@ -181,6 +212,37 @@ class PluginLocalizationServer extends import_server.Plugin {
|
|
|
181
212
|
await this.addNewTexts(texts, options);
|
|
182
213
|
});
|
|
183
214
|
}
|
|
215
|
+
handleLocaleSourceTextsSaved() {
|
|
216
|
+
for (const [sourceName, source] of this.app.localeManager.sources.getEntities()) {
|
|
217
|
+
if (!source.collections) {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
for (const [index, collectionOptions] of source.collections.entries()) {
|
|
221
|
+
const hookKey = `${sourceName}:${index}:${collectionOptions.collection}`;
|
|
222
|
+
if (this.localeSourceTextHookKeys.has(hookKey)) {
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
this.localeSourceTextHookKeys.add(hookKey);
|
|
226
|
+
this.db.on(`${collectionOptions.collection}.afterSave`, async (instance, options) => {
|
|
227
|
+
let texts = [];
|
|
228
|
+
if (collectionOptions.getTexts) {
|
|
229
|
+
texts = await collectionOptions.getTexts(instance, options);
|
|
230
|
+
} else {
|
|
231
|
+
const fields = collectionOptions.fields || [];
|
|
232
|
+
const changedFields = fields.filter((field) => instance["_changed"].has(field));
|
|
233
|
+
if (!changedFields.length) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
texts = changedFields.map((field) => instance.get(field)).filter(Boolean).map((text) => ({ text, module: `resources.${source.namespace}` }));
|
|
237
|
+
}
|
|
238
|
+
if (!(texts == null ? void 0 : texts.length)) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
await this.addNewTexts(texts, options);
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
184
246
|
async handleSyncMessage(message) {
|
|
185
247
|
switch (message.type) {
|
|
186
248
|
case "updateCacheTexts":
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import { TaskType } from '@nocobase/plugin-async-task-manager';
|
|
10
|
+
import type { LocalizationTextRecord } from '../translation-scope';
|
|
11
|
+
export declare const LOCALIZATION_AI_TRANSLATE_TASK_TYPE = "localization:ai-translate";
|
|
12
|
+
export declare const pickBuiltInResourceReference: (row: LocalizationTextRecord, references: Map<string, Map<string, string>>, locales: ReferenceLocaleConfig) => {
|
|
13
|
+
locale: string;
|
|
14
|
+
translation: string;
|
|
15
|
+
} | {
|
|
16
|
+
locale?: undefined;
|
|
17
|
+
translation?: undefined;
|
|
18
|
+
};
|
|
19
|
+
type ReferenceLocaleConfig = {
|
|
20
|
+
primary?: string;
|
|
21
|
+
fallback?: string;
|
|
22
|
+
};
|
|
23
|
+
export type TranslationReferenceLocales = {
|
|
24
|
+
builtIn?: ReferenceLocaleConfig;
|
|
25
|
+
custom?: ReferenceLocaleConfig;
|
|
26
|
+
};
|
|
27
|
+
export declare class LocalizationAITranslateTask extends TaskType {
|
|
28
|
+
static type: string;
|
|
29
|
+
execute(): Promise<{
|
|
30
|
+
translated: number;
|
|
31
|
+
total: number;
|
|
32
|
+
}>;
|
|
33
|
+
private countTexts;
|
|
34
|
+
private getLocaleReferences;
|
|
35
|
+
private getReferenceMaps;
|
|
36
|
+
private getBuiltInReferenceMaps;
|
|
37
|
+
private pickDbReference;
|
|
38
|
+
private getSystemDefaultLocale;
|
|
39
|
+
private resolveReferenceLocales;
|
|
40
|
+
private buildTranslationItems;
|
|
41
|
+
private translateItem;
|
|
42
|
+
private translateText;
|
|
43
|
+
private getEmployeeSystemPrompt;
|
|
44
|
+
private buildProviderContext;
|
|
45
|
+
private getLanguageName;
|
|
46
|
+
private extractTextContent;
|
|
47
|
+
}
|
|
48
|
+
export {};
|