@justanarthur/payload-plugin-translator 1.3.21 → 3.1.0

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.
@@ -0,0 +1,623 @@
1
+ import {
2
+ samePlaceholders,
3
+ traverseFields,
4
+ collectTranslatableFields,
5
+ entityKey,
6
+ sourceHash
7
+ } from "./chunk-9d433kmv.js";
8
+ import {
9
+ TRANSLATION_STATUS_SLUG2
10
+ } from "./chunk-31qgv1zk.js";
11
+
12
+ // src/translate/operation.ts
13
+ import he from "he";
14
+ import { APIError as APIError3 } from "payload";
15
+
16
+ // src/translate/findEntityWithConfig.ts
17
+ import { APIError } from "payload";
18
+ var findConfigBySlug = (slug, enities) => enities.find((entity) => entity.slug === slug);
19
+ var findEntityWithConfig = async (args) => {
20
+ const { collectionSlug, globalSlug, id, locale, overrideAccess, req } = args;
21
+ if (!collectionSlug && !globalSlug)
22
+ throw new APIError("Bad Request", 400);
23
+ const { payload } = req;
24
+ const { config } = payload;
25
+ const isGlobal = !!globalSlug;
26
+ if (!isGlobal && !id)
27
+ throw new APIError("Bad Request", 400);
28
+ const entityConfig = isGlobal ? findConfigBySlug(globalSlug, config.globals) : findConfigBySlug(collectionSlug, config.collections);
29
+ if (!entityConfig)
30
+ throw new APIError("Bad Request", 400);
31
+ const docPromise = isGlobal ? payload.findGlobal({
32
+ depth: 0,
33
+ fallbackLocale: false,
34
+ locale,
35
+ overrideAccess,
36
+ req,
37
+ slug: args.globalSlug
38
+ }) : payload.findByID({
39
+ collection: collectionSlug,
40
+ depth: 0,
41
+ fallbackLocale: false,
42
+ id,
43
+ locale,
44
+ overrideAccess,
45
+ req
46
+ });
47
+ const doc = await docPromise;
48
+ return {
49
+ config: entityConfig,
50
+ doc
51
+ };
52
+ };
53
+
54
+ // src/translate/updateEntity.ts
55
+ import { APIError as APIError2 } from "payload";
56
+ var updateEntity = ({
57
+ collectionSlug,
58
+ data,
59
+ depth: incomingDepth,
60
+ globalSlug,
61
+ id,
62
+ locale,
63
+ overrideAccess,
64
+ req
65
+ }) => {
66
+ if (!collectionSlug && !globalSlug)
67
+ throw new APIError2("Bad Request", 400);
68
+ const isGlobal = !!globalSlug;
69
+ if (!isGlobal && !id)
70
+ throw new APIError2("Bad Request", 400);
71
+ const depth = incomingDepth ?? req.payload.config.defaultDepth;
72
+ const promise = isGlobal ? req.payload.updateGlobal({
73
+ data,
74
+ depth,
75
+ context: { disableAutoTranslate: true },
76
+ locale,
77
+ overrideAccess,
78
+ req,
79
+ slug: globalSlug
80
+ }) : req.payload.update({
81
+ collection: collectionSlug,
82
+ context: { disableAutoTranslate: true },
83
+ data,
84
+ depth,
85
+ id,
86
+ locale,
87
+ overrideAccess,
88
+ req
89
+ });
90
+ return promise;
91
+ };
92
+
93
+ // src/translate/operation.ts
94
+ var preview = (value) => {
95
+ const flat = (typeof value === "string" ? value : String(value ?? "")).replace(/\s+/g, " ").trim();
96
+ return JSON.stringify(flat.length > 60 ? `${flat.slice(0, 57)}…` : flat);
97
+ };
98
+ var translateOperation2 = async (args) => {
99
+ const req = "req" in args ? args.req : {
100
+ payload: args.payload
101
+ };
102
+ const { collectionSlug, globalSlug, id, locale, localeFrom, overrideAccess } = args;
103
+ const { config, doc: dataFrom } = await findEntityWithConfig({
104
+ collectionSlug,
105
+ globalSlug,
106
+ id,
107
+ locale: localeFrom,
108
+ overrideAccess,
109
+ req
110
+ });
111
+ const resolver = (req.payload.config.custom?.translator?.resolvers ?? []).find((each) => each.key === args.resolver);
112
+ if (!resolver)
113
+ throw new APIError3(`Resolver with the key ${args.resolver} was not found`);
114
+ const valuesToTranslate = [];
115
+ let translatedData = args.data;
116
+ if (!translatedData) {
117
+ const { doc } = await findEntityWithConfig({
118
+ collectionSlug,
119
+ globalSlug,
120
+ id,
121
+ locale,
122
+ overrideAccess,
123
+ req
124
+ });
125
+ translatedData = doc;
126
+ }
127
+ traverseFields({
128
+ dataFrom,
129
+ emptyOnly: args.emptyOnly,
130
+ fields: config.fields,
131
+ translatedData,
132
+ valuesToTranslate,
133
+ _options: req.payload.config.custom?.translator?._options
134
+ });
135
+ const entityLabel = `${collectionSlug || globalSlug}#${id ?? ""}`;
136
+ const direction = `${args.localeFrom}→${args.locale}`;
137
+ req.payload.logger.info({
138
+ msg: `[translate] ${entityLabel} ${direction}: traversed ${valuesToTranslate.length} translatable value(s)`
139
+ });
140
+ const resolveResult = valuesToTranslate.length === 0 ? { success: true, translatedTexts: [] } : await resolver.resolve({
141
+ localeFrom: args.localeFrom,
142
+ localeTo: args.locale,
143
+ req,
144
+ texts: valuesToTranslate.map((each) => each.value)
145
+ });
146
+ let result;
147
+ if (!resolveResult.success) {
148
+ req.payload.logger.warn({
149
+ msg: `[translate] ${entityLabel} ${direction}: resolver failed — ${valuesToTranslate.length} value(s) traversed but NOT translated` + (valuesToTranslate.length ? `
150
+ ${valuesToTranslate.map((v) => ` ${v.path ?? "(unknown)"}: ${preview(v.value)}`).join(`
151
+ `)}` : "")
152
+ });
153
+ result = {
154
+ success: false
155
+ };
156
+ } else if (resolveResult.translatedTexts.length !== valuesToTranslate.length) {
157
+ req.payload.logger.error({
158
+ msg: `[translate] ${entityLabel} ${direction}: resolver returned ${resolveResult.translatedTexts.length} value(s) for ${valuesToTranslate.length} — nothing applied`
159
+ });
160
+ result = {
161
+ success: false
162
+ };
163
+ } else {
164
+ const summary = [];
165
+ resolveResult.translatedTexts.forEach((translated, index) => {
166
+ const entry = valuesToTranslate[index];
167
+ const formattedValue = typeof entry.value === "string" && /&[#\w]+;/.test(entry.value) ? translated : he.decode(translated);
168
+ if (typeof entry.value === "string" && !samePlaceholders(entry.value, formattedValue)) {
169
+ summary.push(` ${entry.path ?? "(unknown)"}: placeholders changed, kept source ${preview(entry.value)}`);
170
+ entry.onTranslate(entry.value);
171
+ return;
172
+ }
173
+ summary.push(` ${entry.path ?? "(unknown)"}: ${preview(entry.value)} → ${preview(formattedValue)}`);
174
+ entry.onTranslate(formattedValue);
175
+ });
176
+ req.payload.logger.info({
177
+ msg: `[translate] ${entityLabel} ${direction}: translated ${resolveResult.translatedTexts.length} value(s)` + (summary.length ? `
178
+ ${summary.join(`
179
+ `)}` : "")
180
+ });
181
+ if (args.update) {
182
+ const { _locale, _parent_id, createdAt, updatedAt, ...data } = translatedData;
183
+ await updateEntity({
184
+ collectionSlug,
185
+ data,
186
+ depth: 0,
187
+ globalSlug,
188
+ id,
189
+ locale,
190
+ overrideAccess,
191
+ req
192
+ });
193
+ }
194
+ result = {
195
+ success: true,
196
+ translatedData,
197
+ dataFrom
198
+ };
199
+ }
200
+ return result;
201
+ };
202
+
203
+ // src/jobs/constants.ts
204
+ var TRANSLATE_TASK_SLUG2 = "translateEntityToLocale";
205
+ var TRANSLATE_WORKFLOW_SLUG2 = "translateEntityToLocales";
206
+
207
+ // src/jobs/createAutoTranslateCollectionHook.ts
208
+ function createAutoTranslateCollectionHook2(options) {
209
+ const {
210
+ collectionSlug,
211
+ defaultLocale: defaultLocaleOption,
212
+ targetLocales: targetLocalesOption,
213
+ onlyOnPublished = true,
214
+ workflowSlug = TRANSLATE_WORKFLOW_SLUG2
215
+ } = options;
216
+ const afterChange = async ({ doc, req }) => {
217
+ if (shouldSkipAutoTranslate(req.context))
218
+ return doc;
219
+ const typed = doc;
220
+ const requestLocale = readRequestLocale(req);
221
+ const defaultLocale = defaultLocaleOption ?? readConfigDefaultLocale(req) ?? "";
222
+ if (!requestLocale || !defaultLocale || requestLocale !== defaultLocale)
223
+ return doc;
224
+ if (!req.user)
225
+ return doc;
226
+ if (onlyOnPublished && typed._status && typed._status !== "published")
227
+ return doc;
228
+ const targetLocales = targetLocalesOption ?? readTargetLocales(req, defaultLocale);
229
+ if (targetLocales.length === 0)
230
+ return doc;
231
+ const resolverKey = options.resolverKey ?? readFirstResolverKey(req);
232
+ if (!resolverKey) {
233
+ req.payload.logger.error({
234
+ msg: `auto-translate: no resolver key available for collection ${collectionSlug} — pass \`resolverKey\` to \`createAutoTranslateCollectionHook\` or configure \`translator.resolvers\` in \`translator({...})\``
235
+ });
236
+ return doc;
237
+ }
238
+ const updatedAt = typed.updatedAt ?? new Date().toISOString();
239
+ const toLocales = targetLocales.filter((toLocale) => toLocale !== defaultLocale);
240
+ if (toLocales.length === 0)
241
+ return doc;
242
+ try {
243
+ const job = await req.payload.jobs.queue({
244
+ req,
245
+ workflow: workflowSlug,
246
+ input: {
247
+ id: typed.id,
248
+ updatedAt,
249
+ collection: collectionSlug,
250
+ fromLocale: defaultLocale,
251
+ resolver: resolverKey || undefined,
252
+ toLocales
253
+ }
254
+ });
255
+ req.payload.logger.info({
256
+ msg: `auto-translate: queued translation of ${collectionSlug}#${typed.id} → [${toLocales.join(", ")}] (job ${job.id})`
257
+ });
258
+ } catch (error) {
259
+ req.payload.logger.error({
260
+ msg: `auto-translate: failed to queue ${collectionSlug}#${typed.id}: ${String(error)}`
261
+ });
262
+ }
263
+ return doc;
264
+ };
265
+ return { afterChange };
266
+ }
267
+ function shouldSkipAutoTranslate(context) {
268
+ return Boolean(context?.disableAutoTranslate);
269
+ }
270
+ function readRequestLocale(req) {
271
+ if (!req || typeof req !== "object")
272
+ return "";
273
+ const locale = req.locale;
274
+ return typeof locale === "string" && locale.length > 0 ? locale : "";
275
+ }
276
+ function readConfigDefaultLocale(req) {
277
+ if (!req || typeof req !== "object")
278
+ return "";
279
+ const config = req.payload?.config;
280
+ const def = config?.localization?.defaultLocale;
281
+ return typeof def === "string" && def.length > 0 ? def : "";
282
+ }
283
+ function readTargetLocales(req, defaultLocale) {
284
+ if (!req || typeof req !== "object")
285
+ return [];
286
+ const list = req.payload?.config?.localization?.locales;
287
+ if (!Array.isArray(list) || list.length === 0)
288
+ return [];
289
+ const out = [];
290
+ for (const entry of list) {
291
+ const code = typeof entry === "string" ? entry : entry?.code;
292
+ if (typeof code === "string" && code.length > 0 && code !== defaultLocale) {
293
+ out.push(code);
294
+ }
295
+ }
296
+ return out;
297
+ }
298
+ function readFirstResolverKey(req) {
299
+ if (!req || typeof req !== "object")
300
+ return "";
301
+ const custom = req.payload?.config?.custom?.translator?.resolvers;
302
+ if (!Array.isArray(custom) || custom.length === 0)
303
+ return "";
304
+ const first = custom[0];
305
+ return typeof first?.key === "string" ? first.key : "";
306
+ }
307
+
308
+ // src/jobs/createAutoTranslateGlobalHook.ts
309
+ function createAutoTranslateGlobalHook2(options) {
310
+ const {
311
+ globalSlug,
312
+ defaultLocale: defaultLocaleOption,
313
+ targetLocales: targetLocalesOption,
314
+ workflowSlug = TRANSLATE_WORKFLOW_SLUG2
315
+ } = options;
316
+ return async ({ doc, req }) => {
317
+ if (shouldSkipAutoTranslate2(req.context))
318
+ return doc;
319
+ const typed = doc;
320
+ const requestLocale = readRequestLocale2(req);
321
+ const defaultLocale = defaultLocaleOption ?? readConfigDefaultLocale2(req) ?? "";
322
+ if (!requestLocale || !defaultLocale || requestLocale !== defaultLocale)
323
+ return doc;
324
+ if (!req.user)
325
+ return doc;
326
+ if (typed._status && typed._status !== "published")
327
+ return doc;
328
+ const targetLocales = targetLocalesOption ?? readTargetLocales2(req, defaultLocale);
329
+ if (targetLocales.length === 0)
330
+ return doc;
331
+ const resolverKey = options.resolverKey ?? readFirstResolverKey2(req);
332
+ if (!resolverKey) {
333
+ req.payload.logger.error({
334
+ msg: `auto-translate: no resolver key available for global ${globalSlug} — pass \`resolverKey\` to \`createAutoTranslateGlobalHook\` or configure \`translator.resolvers\` in \`translator({...})\``
335
+ });
336
+ return doc;
337
+ }
338
+ const updatedAt = typed.updatedAt ?? new Date().toISOString();
339
+ const toLocales = targetLocales.filter((toLocale) => toLocale !== defaultLocale);
340
+ if (toLocales.length === 0)
341
+ return doc;
342
+ try {
343
+ const job = await req.payload.jobs.queue({
344
+ req,
345
+ workflow: workflowSlug,
346
+ input: {
347
+ updatedAt,
348
+ global: globalSlug,
349
+ fromLocale: defaultLocale,
350
+ resolver: resolverKey || undefined,
351
+ toLocales
352
+ }
353
+ });
354
+ req.payload.logger.info({
355
+ msg: `auto-translate: queued translation of ${globalSlug} → [${toLocales.join(", ")}] (job ${job.id})`
356
+ });
357
+ } catch (error) {
358
+ req.payload.logger.error({
359
+ msg: `auto-translate: failed to queue ${globalSlug}: ${String(error)}`
360
+ });
361
+ }
362
+ return doc;
363
+ };
364
+ }
365
+ function shouldSkipAutoTranslate2(context) {
366
+ return Boolean(context?.disableAutoTranslate);
367
+ }
368
+ function readRequestLocale2(req) {
369
+ if (!req || typeof req !== "object")
370
+ return "";
371
+ const locale = req.locale;
372
+ return typeof locale === "string" && locale.length > 0 ? locale : "";
373
+ }
374
+ function readConfigDefaultLocale2(req) {
375
+ if (!req || typeof req !== "object")
376
+ return "";
377
+ const config = req.payload?.config;
378
+ const def = config?.localization?.defaultLocale;
379
+ return typeof def === "string" && def.length > 0 ? def : "";
380
+ }
381
+ function readTargetLocales2(req, defaultLocale) {
382
+ if (!req || typeof req !== "object")
383
+ return [];
384
+ const list = req.payload?.config?.localization?.locales;
385
+ if (!Array.isArray(list) || list.length === 0)
386
+ return [];
387
+ const out = [];
388
+ for (const entry of list) {
389
+ const code = typeof entry === "string" ? entry : entry?.code;
390
+ if (typeof code === "string" && code.length > 0 && code !== defaultLocale) {
391
+ out.push(code);
392
+ }
393
+ }
394
+ return out;
395
+ }
396
+ function readFirstResolverKey2(req) {
397
+ if (!req || typeof req !== "object")
398
+ return "";
399
+ const custom = req.payload?.config?.custom?.translator?.resolvers;
400
+ if (!Array.isArray(custom) || custom.length === 0)
401
+ return "";
402
+ const first = custom[0];
403
+ return typeof first?.key === "string" ? first.key : "";
404
+ }
405
+
406
+ // src/review/recordTranslationStatus.ts
407
+ var upsertTranslationStatus = async (req, entity, locale, data) => {
408
+ if (!req.payload.collections[TRANSLATION_STATUS_SLUG2])
409
+ return;
410
+ const existing = await req.payload.find({
411
+ collection: TRANSLATION_STATUS_SLUG2,
412
+ where: { and: [{ entity: { equals: entity } }, { locale: { equals: locale } }] },
413
+ limit: 1,
414
+ depth: 0,
415
+ overrideAccess: true,
416
+ req
417
+ });
418
+ const row = existing.docs[0];
419
+ if (row)
420
+ await req.payload.update({ collection: TRANSLATION_STATUS_SLUG2, id: row.id, data, overrideAccess: true, req });
421
+ else
422
+ await req.payload.create({ collection: TRANSLATION_STATUS_SLUG2, data: { entity, locale, ...data }, overrideAccess: true, req });
423
+ };
424
+ var recordTranslationStatus = async ({ req, collectionSlug, globalSlug, id, locale, config, dataFrom, reviewedBy }) => {
425
+ const hash = sourceHash(collectTranslatableFields({
426
+ config,
427
+ dataFrom,
428
+ dataTarget: {},
429
+ options: req.payload.config.custom?.translator?._options
430
+ }));
431
+ const now = new Date().toISOString();
432
+ await upsertTranslationStatus(req, entityKey({ collectionSlug, globalSlug, id }), locale, reviewedBy ? { reviewedHash: hash, reviewedAt: now, reviewedBy } : { sourceHash: hash, translatedAt: now });
433
+ };
434
+
435
+ // src/jobs/createTranslateTask.ts
436
+ function createTranslateTask2(options = {}) {
437
+ const { slug = "translateEntityToLocale" } = options;
438
+ return {
439
+ slug,
440
+ inputSchema: [
441
+ { name: "id", type: "number", required: false },
442
+ { name: "collection", type: "text", required: false },
443
+ { name: "global", type: "text", required: false },
444
+ { name: "fromLocale", type: "text", required: true },
445
+ { name: "toLocale", type: "text", required: true },
446
+ { name: "resolver", type: "text", required: false },
447
+ { name: "mode", type: "text", required: false }
448
+ ],
449
+ outputSchema: [],
450
+ retries: 3,
451
+ handler: async (args) => {
452
+ const { input, job, req } = args;
453
+ const { id, collection, global, fromLocale, toLocale, resolver: inputResolver } = input;
454
+ const mode = input.mode ?? readAutoTranslateMode(req);
455
+ if (!collection && !global) {
456
+ throw new Error("translateTask: either `collection` or `global` must be provided");
457
+ }
458
+ const resolverKey = options.resolverKey ?? inputResolver ?? readFirstResolverKey3(req);
459
+ if (!resolverKey) {
460
+ throw new Error(`translateTask: no resolver key available — pass \`resolverKey\` to \`createTranslateTask\` or queue the workflow with a \`resolver\` input. Did you forget to pass \`translator.resolvers\` to \`translator({...})\`?`);
461
+ }
462
+ const entityLabel = collection || global;
463
+ req.payload.logger.info({
464
+ jobId: job.id,
465
+ msg: `translating ${entityLabel} to locale ${toLocale}`
466
+ });
467
+ let result;
468
+ try {
469
+ result = await translateOperation2({
470
+ req,
471
+ collectionSlug: collection,
472
+ globalSlug: global,
473
+ emptyOnly: mode !== "all",
474
+ id,
475
+ locale: toLocale,
476
+ localeFrom: fromLocale,
477
+ overrideAccess: true,
478
+ resolver: resolverKey,
479
+ update: false
480
+ });
481
+ } catch (error) {
482
+ req.payload.logger.error({
483
+ jobId: job.id,
484
+ msg: `translateOperation threw for ${entityLabel} → ${toLocale}: ${String(error)}`
485
+ });
486
+ throw error;
487
+ }
488
+ if (!result.success) {
489
+ req.payload.logger.error({
490
+ jobId: job.id,
491
+ msg: `translation for ${entityLabel} to ${toLocale} failed (resolver returned success=false)`
492
+ });
493
+ throw new Error(`translateTask: resolver returned success=false for ${entityLabel} → ${toLocale}`);
494
+ }
495
+ const latest = await findEntityWithConfig({
496
+ collectionSlug: collection,
497
+ globalSlug: global,
498
+ id,
499
+ locale: fromLocale,
500
+ overrideAccess: true,
501
+ req
502
+ });
503
+ if (String(latest.doc?.updatedAt) !== String(result.dataFrom?.updatedAt)) {
504
+ req.payload.logger.warn({
505
+ jobId: job.id,
506
+ msg: `[translate] ${entityLabel}#${id ?? global} changed during translation — skipping ${toLocale}, the newer save re-queues it`
507
+ });
508
+ return { output: { success: true } };
509
+ }
510
+ const translated = result.translatedData ?? {};
511
+ const { _locale: _dropLocale, _parent_id: _dropParent, updatedAt: _dropUpdatedAt, createdAt: _dropCreatedAt, ...data } = translated;
512
+ req.payload.logger.info({
513
+ jobId: job.id,
514
+ msg: `[translate] persisting ${entityLabel}#${id ?? global} → ${toLocale}`
515
+ });
516
+ try {
517
+ await updateEntity({
518
+ collectionSlug: collection,
519
+ data,
520
+ depth: 0,
521
+ globalSlug: global,
522
+ id,
523
+ locale: toLocale,
524
+ overrideAccess: true,
525
+ req
526
+ });
527
+ await recordTranslationStatus({
528
+ req,
529
+ collectionSlug: collection,
530
+ globalSlug: global,
531
+ id,
532
+ locale: toLocale,
533
+ config: latest.config,
534
+ dataFrom: latest.doc
535
+ });
536
+ } catch (error) {
537
+ req.payload.logger.error({
538
+ jobId: job.id,
539
+ msg: `persist failed for ${entityLabel} (id=${id ?? global}) at locale ${toLocale}: ${String(error)}`
540
+ });
541
+ throw error;
542
+ }
543
+ req.payload.logger.info({
544
+ jobId: job.id,
545
+ msg: `translation complete for ${entityLabel} → ${toLocale}`
546
+ });
547
+ return { output: { success: true } };
548
+ }
549
+ };
550
+ }
551
+ function readFirstResolverKey3(req) {
552
+ if (!req || typeof req !== "object")
553
+ return "";
554
+ const custom = req.payload?.config?.custom?.translator?.resolvers;
555
+ if (!Array.isArray(custom) || custom.length === 0)
556
+ return "";
557
+ const first = custom[0];
558
+ return typeof first?.key === "string" ? first.key : "";
559
+ }
560
+ function readAutoTranslateMode(req) {
561
+ return req.payload.config.custom?.translator?.autoTranslateMode === "all" ? "all" : "missing";
562
+ }
563
+
564
+ // src/jobs/createTranslateWorkflow.ts
565
+ function createTranslateWorkflow2(options = {}) {
566
+ const { slug = "translateEntityToLocales", taskSlug = "translateEntityToLocale" } = options;
567
+ return {
568
+ slug,
569
+ inputSchema: [
570
+ { name: "id", type: "number", required: false },
571
+ { name: "updatedAt", type: "date", required: true },
572
+ { name: "collection", type: "text", required: false },
573
+ { name: "global", type: "text", required: false },
574
+ { name: "fromLocale", type: "text", required: true },
575
+ { name: "toLocales", type: "json", required: false },
576
+ { name: "toLocale", type: "text", required: false },
577
+ { name: "resolver", type: "text", required: false }
578
+ ],
579
+ handler: async (args) => {
580
+ const { job, req, tasks } = args;
581
+ const { id, collection, global, fromLocale, toLocales, toLocale, updatedAt, resolver } = job.input;
582
+ if (!collection && !global) {
583
+ throw new Error("translateWorkflow: either `collection` or `global` must be provided");
584
+ }
585
+ if (typeof req.payload.config.localization !== "object") {
586
+ req.payload.logger.error({
587
+ jobId: job.id,
588
+ msg: "localization is not enabled — skipping auto-translation workflow"
589
+ });
590
+ return;
591
+ }
592
+ const entityKey = collection ? `${collection}-${id}` : global;
593
+ const updatedAtIso = updatedAt instanceof Date ? updatedAt.toISOString() : String(updatedAt);
594
+ const requested = Array.isArray(toLocales) && toLocales.length > 0 ? toLocales : toLocale ? [toLocale] : [];
595
+ const targets = requested.filter((locale) => typeof locale === "string" && locale && locale !== fromLocale);
596
+ if (targets.length === 0) {
597
+ req.payload.logger.warn({
598
+ jobId: job.id,
599
+ msg: `translateWorkflow: no target locales for ${entityKey} — nothing to translate`
600
+ });
601
+ return;
602
+ }
603
+ req.payload.logger.info({
604
+ jobId: job.id,
605
+ msg: `scheduling translation of ${entityKey}: ${fromLocale} → [${targets.join(", ")}]`
606
+ });
607
+ for (const target of targets) {
608
+ await tasks[taskSlug](`${entityKey}-${fromLocale}-${target}-${updatedAtIso}`, {
609
+ input: {
610
+ id,
611
+ collection,
612
+ global,
613
+ fromLocale,
614
+ toLocale: target,
615
+ resolver
616
+ }
617
+ });
618
+ }
619
+ }
620
+ };
621
+ }
622
+
623
+ export { findEntityWithConfig, translateOperation2, TRANSLATE_TASK_SLUG2, TRANSLATE_WORKFLOW_SLUG2, createAutoTranslateCollectionHook2, createAutoTranslateGlobalHook2, recordTranslationStatus, createTranslateTask2, createTranslateWorkflow2 };
@@ -0,0 +1,15 @@
1
+ // src/resolvers/copy.ts
2
+ var copyResolver2 = () => {
3
+ return {
4
+ key: "copy",
5
+ resolve: (args) => {
6
+ const { texts } = args;
7
+ return {
8
+ success: true,
9
+ translatedTexts: texts
10
+ };
11
+ }
12
+ };
13
+ };
14
+
15
+ export { copyResolver2 };
@@ -0,0 +1,55 @@
1
+ import {
2
+ chunkArray
3
+ } from "./chunk-e6c0qzkt.js";
4
+
5
+ // src/resolvers/google.ts
6
+ var localeToCountryCodeMapper = {
7
+ ua: "uk"
8
+ };
9
+ var mapLocale = (incoming) => (incoming in localeToCountryCodeMapper) ? localeToCountryCodeMapper[incoming] : incoming;
10
+ var googleResolver2 = ({
11
+ apiKey,
12
+ chunkLength = 100
13
+ }) => {
14
+ return {
15
+ key: "google",
16
+ resolve: async (args) => {
17
+ const { localeFrom, localeTo, req, texts } = args;
18
+ const apiUrl = `https://translation.googleapis.com/language/translate/v2?key=${apiKey}`;
19
+ const responses = await Promise.all(chunkArray(texts, chunkLength).map((q) => fetch(apiUrl, {
20
+ body: JSON.stringify({
21
+ q,
22
+ source: mapLocale(localeFrom),
23
+ target: mapLocale(localeTo)
24
+ }),
25
+ headers: {
26
+ "Content-Type": "application/json"
27
+ },
28
+ method: "POST"
29
+ }).then(async (res) => {
30
+ const data = await res.json();
31
+ if (!res.ok)
32
+ req.payload.logger.info({
33
+ googleResponse: data,
34
+ message: "An error occurred when trying to translate the data using Google API"
35
+ });
36
+ return {
37
+ data,
38
+ success: res.ok
39
+ };
40
+ })));
41
+ if (responses.some((res) => !res.success)) {
42
+ return {
43
+ success: false
44
+ };
45
+ }
46
+ const translatedTexts = responses.flatMap((chunk) => chunk.data.data.translations).map((translation) => translation.translatedText);
47
+ return {
48
+ success: true,
49
+ translatedTexts
50
+ };
51
+ }
52
+ };
53
+ };
54
+
55
+ export { googleResolver2 };