@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.
Files changed (33) hide show
  1. package/client-v2.d.ts +2 -0
  2. package/client-v2.js +1 -0
  3. package/dist/ai/ai-employees/lina.d.ts +10 -0
  4. package/dist/ai/ai-employees/lina.js +60 -0
  5. package/dist/client/300.3a4b9b688d36da96.js +10 -0
  6. package/dist/client/i18n-missing-handler.d.ts +1 -17
  7. package/dist/client/index.js +1 -1
  8. package/dist/client-v2/796.b9d793cda3c8b932.js +10 -0
  9. package/dist/client-v2/common/constants.d.ts +13 -0
  10. package/dist/client-v2/common/i18n-missing-handler.d.ts +23 -0
  11. package/dist/{client/Localization.d.ts → client-v2/i18n-missing-handler.d.ts} +1 -2
  12. package/dist/client-v2/index.d.ts +9 -0
  13. package/dist/client-v2/index.js +10 -0
  14. package/dist/client-v2/locale.d.ts +11 -0
  15. package/dist/client-v2/pages/LocalizationPage.d.ts +11 -0
  16. package/dist/client-v2/plugin.d.ts +13 -0
  17. package/dist/externalVersion.js +16 -12
  18. package/dist/locale/en-US.json +15 -1
  19. package/dist/locale/zh-CN.json +15 -1
  20. package/dist/server/actions/aiTranslate.d.ts +14 -0
  21. package/dist/server/actions/aiTranslate.js +127 -0
  22. package/dist/server/actions/localization.js +30 -15
  23. package/dist/server/actions/localizationTexts.js +8 -9
  24. package/dist/server/migrations/20260511230000-delete-official-plugin-package-resource-modules.d.ts +14 -0
  25. package/dist/server/migrations/20260511230000-delete-official-plugin-package-resource-modules.js +64 -0
  26. package/dist/server/plugin.d.ts +5 -2
  27. package/dist/server/plugin.js +72 -14
  28. package/dist/server/tasks/localization-ai-translate.d.ts +31 -0
  29. package/dist/server/tasks/localization-ai-translate.js +596 -0
  30. package/package.json +7 -2
  31. package/dist/client/304.6a0dd0c975aa0b7c.js +0 -10
  32. package/dist/server/source-manager.d.ts +0 -35
  33. 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 plugin.sourceManager.sync(ctx, types);
43
- let textValues = [];
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: `resources.${module2}`, text });
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 = resources[module2]) == null ? void 0 : _a[text.text];
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 = resources[module2]) == null ? void 0 : _a[text.text]
77
+ translation: (_a = normalizedResources[module2]) == null ? void 0 : _a[text.text]
70
78
  };
71
79
  });
72
- await ctx.db.getModel("localizationTranslations").bulkCreate(translationValues, {
73
- transaction: t
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 plugin = ctx.app.pm.get("localization");
86
- const sources = Array.from(plugin.sourceManager.sources.getEntities());
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(plugin.sourceManager.sources.getValues());
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((plugin2) => ({
117
- value: plugin2.alias || plugin2.name,
118
- label: plugin2.displayName
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((plugin2) => ({
137
- value: plugin2.alias || plugin2.name,
138
- label: plugin2.displayName
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: `resources.${key.ns}` })),
165
+ keys.map((key) => ({ text: key.text, module: plugin.normalizeResourceModule(key.ns) })),
167
166
  {
168
167
  locale: currentLocale
169
168
  }
@@ -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
+ }
@@ -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
+ }
@@ -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
- sourceManager: SourceManager;
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>;
@@ -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
- sourceManager = new import_source_manager.SourceManager();
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.sourceManager.handleTextsSaved(this.db, this.addNewTexts));
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: import_localization.default
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.sourceManager.registerSource("local", {
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.getCacheResources(ctx.get("X-Locale") || "en-US");
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
- if (module2.startsWith(import_server.OFFICIAL_PLUGIN_PREFIX)) {
129
- const name = module2.replace(import_server.OFFICIAL_PLUGIN_PREFIX, "");
130
- if (resources[name]) {
131
- return;
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.sourceManager.registerSource("db", {
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
+ }