@nocobase/plugin-localization 2.1.0-beta.29 → 2.1.0-beta.32
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.3a4b9b688d36da96.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.b9d793cda3c8b932.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 +16 -12
- package/dist/locale/en-US.json +15 -1
- package/dist/locale/zh-CN.json +15 -1
- package/dist/server/actions/aiTranslate.d.ts +14 -0
- package/dist/server/actions/aiTranslate.js +127 -0
- package/dist/server/actions/localization.js +30 -15
- package/dist/server/actions/localizationTexts.js +8 -9
- 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 +72 -14
- package/dist/server/tasks/localization-ai-translate.d.ts +31 -0
- package/dist/server/tasks/localization-ai-translate.js +596 -0
- package/package.json +7 -2
- package/dist/client/304.6a0dd0c975aa0b7c.js +0 -10
- package/dist/server/source-manager.d.ts +0 -35
- package/dist/server/source-manager.js +0 -77
|
@@ -0,0 +1,127 @@
|
|
|
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
|
+
const validateParams = (ctx) => {
|
|
34
|
+
const { mode, locale, employeeUsername = "lina", model, textIds } = ctx.action.params.values || {};
|
|
35
|
+
if (!["full", "incremental", "selected"].includes(mode)) {
|
|
36
|
+
ctx.throw(400, "Invalid translation mode");
|
|
37
|
+
}
|
|
38
|
+
if (mode === "selected" && (!Array.isArray(textIds) || !textIds.length)) {
|
|
39
|
+
ctx.throw(400, "Please select the records you want to translate");
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
mode,
|
|
43
|
+
locale: locale || ctx.get("X-Locale") || "en-US",
|
|
44
|
+
employeeUsername,
|
|
45
|
+
model,
|
|
46
|
+
textIds
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
const buildFindTextsOptions = (mode, locale, textIds) => {
|
|
50
|
+
const options = {};
|
|
51
|
+
if (mode === "selected") {
|
|
52
|
+
options.filter = {
|
|
53
|
+
id: {
|
|
54
|
+
$in: textIds || []
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
if (mode === "incremental") {
|
|
59
|
+
options.include = [{ association: "translations", where: { locale }, required: false }];
|
|
60
|
+
options.where = {
|
|
61
|
+
"$translations.id$": null
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return options;
|
|
65
|
+
};
|
|
66
|
+
const getAITranslatePreview = async (ctx) => {
|
|
67
|
+
const { mode, locale, employeeUsername, model, textIds } = validateParams(ctx);
|
|
68
|
+
const aiPlugin = ctx.app.pm.get("ai");
|
|
69
|
+
if (!(aiPlugin == null ? void 0 : aiPlugin.aiEmployeesManager)) {
|
|
70
|
+
ctx.throw(500, "AI plugin is not available");
|
|
71
|
+
}
|
|
72
|
+
const employee = await aiPlugin.aiEmployeesManager.getEmployee(employeeUsername);
|
|
73
|
+
if (!employee) {
|
|
74
|
+
ctx.throw(400, `AI employee "${employeeUsername}" not found`);
|
|
75
|
+
}
|
|
76
|
+
const resolvedModel = await aiPlugin.aiEmployeesManager.resolveModel(employee, model);
|
|
77
|
+
const { model: modelName, service } = await aiPlugin.aiManager.getLLMService(resolvedModel);
|
|
78
|
+
const providerMeta = aiPlugin.aiManager.llmProviders.get(service.provider);
|
|
79
|
+
const count = await ctx.db.getRepository("localizationTexts").count(buildFindTextsOptions(mode, locale, textIds));
|
|
80
|
+
return {
|
|
81
|
+
mode,
|
|
82
|
+
locale,
|
|
83
|
+
count,
|
|
84
|
+
provider: service.provider,
|
|
85
|
+
providerTitle: providerMeta == null ? void 0 : providerMeta.title,
|
|
86
|
+
llmService: service.name,
|
|
87
|
+
llmServiceTitle: service.title,
|
|
88
|
+
model: modelName
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
const aiTranslatePreview = async (ctx, next) => {
|
|
92
|
+
ctx.body = await getAITranslatePreview(ctx);
|
|
93
|
+
await next();
|
|
94
|
+
};
|
|
95
|
+
const aiTranslate = async (ctx, next) => {
|
|
96
|
+
var _a, _b, _c, _d;
|
|
97
|
+
const { mode, locale, employeeUsername, model, textIds } = validateParams(ctx);
|
|
98
|
+
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);
|
|
99
|
+
const taskManager = ctx.app.container.get("AsyncTaskManager");
|
|
100
|
+
if (!taskManager) {
|
|
101
|
+
ctx.throw(500, "AsyncTaskManager is not available");
|
|
102
|
+
}
|
|
103
|
+
const task = await taskManager.createTask(
|
|
104
|
+
{
|
|
105
|
+
origin: "localization",
|
|
106
|
+
type: import_localization_ai_translate.LOCALIZATION_AI_TRANSLATE_TASK_TYPE,
|
|
107
|
+
title: mode === "full" ? "AI full localization translation" : "AI incremental localization translation",
|
|
108
|
+
params: {
|
|
109
|
+
mode,
|
|
110
|
+
locale,
|
|
111
|
+
employeeUsername,
|
|
112
|
+
model,
|
|
113
|
+
userId: currentUserId,
|
|
114
|
+
textIds
|
|
115
|
+
},
|
|
116
|
+
createdById: currentUserId,
|
|
117
|
+
cancelable: true
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
useQueue: true,
|
|
121
|
+
context: ctx
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
ctx.body = task.toJSON();
|
|
125
|
+
await next();
|
|
126
|
+
};
|
|
127
|
+
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
|
}
|
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,18 +100,35 @@ 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`,
|
|
@@ -119,24 +148,22 @@ class PluginLocalizationServer extends import_server.Plugin {
|
|
|
119
148
|
store: "memory"
|
|
120
149
|
});
|
|
121
150
|
this.resources = new import_resources.default(this.db, cache);
|
|
122
|
-
this.
|
|
151
|
+
this.app.localeManager.registerSource("local", {
|
|
123
152
|
title: (0, import_utils2.tval)("System & Plugins", { ns: import_package.default.name }),
|
|
124
153
|
sync: async (ctx) => {
|
|
125
|
-
const resources = await ctx.app.localeManager.
|
|
154
|
+
const resources = await ctx.app.localeManager.getBuiltInResources(ctx.get("X-Locale") || "en-US");
|
|
126
155
|
const result = {};
|
|
127
156
|
Object.entries(resources).forEach(([module2, resource]) => {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
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
|
+
};
|
|
135
162
|
});
|
|
136
163
|
return result;
|
|
137
164
|
}
|
|
138
165
|
});
|
|
139
|
-
this.
|
|
166
|
+
this.app.localeManager.registerSource("db", {
|
|
140
167
|
title: (0, import_utils2.tval)("Collections & Fields", { ns: import_package.default.name }),
|
|
141
168
|
namespace: import_constants.NAMESPACE_COLLECTIONS,
|
|
142
169
|
sync: async (ctx) => {
|
|
@@ -185,6 +212,37 @@ class PluginLocalizationServer extends import_server.Plugin {
|
|
|
185
212
|
await this.addNewTexts(texts, options);
|
|
186
213
|
});
|
|
187
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
|
+
}
|
|
188
246
|
async handleSyncMessage(message) {
|
|
189
247
|
switch (message.type) {
|
|
190
248
|
case "updateCacheTexts":
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|
+
export declare const LOCALIZATION_AI_TRANSLATE_TASK_TYPE = "localization:ai-translate";
|
|
11
|
+
export declare class LocalizationAITranslateTask extends TaskType {
|
|
12
|
+
static type: string;
|
|
13
|
+
execute(): Promise<{
|
|
14
|
+
translated: number;
|
|
15
|
+
total: number;
|
|
16
|
+
}>;
|
|
17
|
+
private countTexts;
|
|
18
|
+
private buildFindTextsOptions;
|
|
19
|
+
private normalizeTextRecord;
|
|
20
|
+
private getLocaleReferences;
|
|
21
|
+
private getSystemDefaultLocale;
|
|
22
|
+
private getModuleName;
|
|
23
|
+
private isBuiltInText;
|
|
24
|
+
private getBuiltInReference;
|
|
25
|
+
private runTranslationWorker;
|
|
26
|
+
private translateText;
|
|
27
|
+
private buildProviderContext;
|
|
28
|
+
private buildTranslationTerms;
|
|
29
|
+
private getLanguageName;
|
|
30
|
+
private extractTextContent;
|
|
31
|
+
}
|