@justanarthur/payload-plugin-translator 3.1.0 → 3.2.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.
package/README.md CHANGED
@@ -79,7 +79,7 @@ export const myResolver: TranslateResolver = {
79
79
  | `globals` | `GlobalSlug[]` | yes | Globals to auto-translate. |
80
80
  | `resolvers` | `TranslateResolver[]` | yes | Tried in order; first to succeed wins per chunk. |
81
81
  | `autoTranslate` | `boolean` | no | Default `true`. Set `false` to opt out of the auto-translate afterChange hook — useful when you drive translation manually via the `translateOperation` export. |
82
- | `autoTranslateMode` | `'missing' \| 'all'` | no | Default `'missing'`: a publish in the default locale fills empty fields in other locales and keeps existing translations and slugs. `'all'` re-translates everything. |
82
+ | `autoTranslateMode` | `'missing' \| 'untranslated' \| 'all'` | no | Default `'missing'`: a publish in the default locale fills empty fields in other locales and keeps existing translations and slugs. `'untranslated'` also replaces fields that still hold the source copy. `'all'` re-translates everything. |
83
83
  | `review` | `boolean` | no | Default `true`. Adds the `/admin/translations` review view, its endpoints and the hidden `translation-status` collection (needs a migration on postgres). |
84
84
  | `disabled` | `boolean` | no | Skip the plugin entirely (no overrides, no jobs). Useful in tests. |
85
85
  | `_options.additionalTraverseRichText` | function | no | Hook to extend rich-text traversal — see below. |
@@ -128,6 +128,12 @@ With `review` on, **Translations** appears in the admin nav (`/admin/translation
128
128
  last auto-translate job and its error. Actions: *Translate missing fields*, *Re-translate all*,
129
129
  *Mark reviewed*.
130
130
 
131
+ - **Bulk fix**: *Translate untranslated* queues one translate workflow per incomplete document,
132
+ for all locales or one, in `untranslated` mode (empty fields and fields still holding the source
133
+ copy; existing translations and slugs stay). Locales a pending job already covers are skipped.
134
+ The overview shows queued, running and failed jobs, and *Run a batch now* processes a few without
135
+ waiting for the job runner. Needs `autoTranslate` on, since that registers the jobs.
136
+
131
137
  The view only lists collections and globals the signed-in user can read, and the endpoints behind
132
138
  the actions require a user.
133
139
 
package/dist/client.css CHANGED
@@ -39,187 +39,3 @@
39
39
  flex-wrap: wrap;
40
40
  gap: .5rem;
41
41
  }
42
-
43
- /* src/client/components/Review/styles.css */
44
- .translator-review {
45
- padding-bottom: calc(var(--base) * 3);
46
- }
47
-
48
- .translator-review__header {
49
- display: flex;
50
- justify-content: space-between;
51
- align-items: baseline;
52
- gap: var(--base);
53
- margin: calc(var(--base) * 1.5) 0;
54
- flex-wrap: wrap;
55
- }
56
-
57
- .translator-review__header h1 {
58
- margin: 0;
59
- }
60
-
61
- .translator-review__tabs {
62
- display: flex;
63
- gap: calc(var(--base) / 2);
64
- margin-bottom: var(--base);
65
- flex-wrap: wrap;
66
- }
67
-
68
- .translator-review__tab {
69
- border: 1px solid var(--theme-elevation-150);
70
- color: var(--theme-elevation-800);
71
- text-decoration: none;
72
- border-radius: 999px;
73
- padding: 4px 12px;
74
- font-size: 13px;
75
- }
76
-
77
- .translator-review__tab[aria-current="true"] {
78
- background: var(--theme-elevation-900);
79
- border-color: var(--theme-elevation-900);
80
- color: var(--theme-elevation-0);
81
- }
82
-
83
- .translator-review__scroll {
84
- overflow-x: auto;
85
- border: 1px solid var(--theme-elevation-100);
86
- border-radius: var(--style-radius-m);
87
- }
88
-
89
- .translator-review table {
90
- border-collapse: collapse;
91
- width: 100%;
92
- font-size: 13px;
93
- }
94
-
95
- .translator-review th, .translator-review td {
96
- border-bottom: 1px solid var(--theme-elevation-100);
97
- text-align: left;
98
- vertical-align: top;
99
- padding: 8px 10px;
100
- }
101
-
102
- .translator-review th {
103
- background: var(--theme-elevation-50);
104
- white-space: nowrap;
105
- font-weight: 600;
106
- }
107
-
108
- .translator-review tr:last-child td {
109
- border-bottom: 0;
110
- }
111
-
112
- .translator-review__doc {
113
- min-width: 220px;
114
- }
115
-
116
- .translator-review__doc small {
117
- display: block;
118
- color: var(--theme-elevation-500);
119
- }
120
-
121
- .translator-review__cell {
122
- display: inline-flex;
123
- font-variant-numeric: tabular-nums;
124
- text-decoration: none;
125
- color: var(--theme-elevation-1000);
126
- border: 1px solid #0000;
127
- border-radius: 999px;
128
- align-items: center;
129
- gap: 4px;
130
- min-width: 64px;
131
- padding: 2px 8px;
132
- }
133
-
134
- .translator-review__cell--ok {
135
- background: var(--theme-success-100);
136
- }
137
-
138
- .translator-review__cell--partial {
139
- background: var(--theme-warning-100);
140
- }
141
-
142
- .translator-review__cell--missing {
143
- background: var(--theme-error-100);
144
- }
145
-
146
- .translator-review__cell--stale {
147
- border-color: var(--theme-warning-500);
148
- }
149
-
150
- .translator-review__legend {
151
- display: flex;
152
- gap: var(--base);
153
- color: var(--theme-elevation-600);
154
- margin-top: calc(var(--base) / 2);
155
- flex-wrap: wrap;
156
- font-size: 12px;
157
- }
158
-
159
- .translator-review__pager {
160
- display: flex;
161
- gap: var(--base);
162
- margin-top: var(--base);
163
- align-items: center;
164
- }
165
-
166
- .translator-review__state {
167
- white-space: nowrap;
168
- border-radius: 999px;
169
- padding: 1px 8px;
170
- font-size: 12px;
171
- }
172
-
173
- .translator-review__state--ok {
174
- background: var(--theme-success-100);
175
- }
176
-
177
- .translator-review__state--missing {
178
- background: var(--theme-error-100);
179
- }
180
-
181
- .translator-review__state--identical, .translator-review__state--placeholders {
182
- background: var(--theme-warning-100);
183
- }
184
-
185
- .translator-review__text {
186
- white-space: pre-wrap;
187
- word-break: break-word;
188
- max-width: 420px;
189
- }
190
-
191
- .translator-review__text--empty {
192
- color: var(--theme-elevation-400);
193
- font-style: italic;
194
- }
195
-
196
- .translator-review__path {
197
- font-family: var(--font-mono, monospace);
198
- color: var(--theme-elevation-600);
199
- word-break: break-all;
200
- max-width: 240px;
201
- font-size: 12px;
202
- }
203
-
204
- .translator-review__meta {
205
- display: flex;
206
- gap: calc(var(--base) * 1.5);
207
- color: var(--theme-elevation-600);
208
- margin-bottom: var(--base);
209
- flex-wrap: wrap;
210
- font-size: 13px;
211
- }
212
-
213
- .translator-review__actions {
214
- display: flex;
215
- gap: calc(var(--base) / 2);
216
- flex-wrap: wrap;
217
- }
218
-
219
- .translator-review__actions .btn {
220
- margin: 0;
221
- }
222
-
223
- .translator-review__error {
224
- color: var(--theme-error-500);
225
- }
package/dist/client.d.ts CHANGED
@@ -4,15 +4,25 @@ declare const CustomButtonWithTranslator: ({ type }: {
4
4
  type: "publish" | "save";
5
5
  }) => Element_12xgc;
6
6
  import { JSX as JSX_1kxb2 } from "react";
7
+ type Progress = {
8
+ pending: number;
9
+ running: number;
10
+ failed: number;
11
+ } | null;
7
12
  type Element_12xgc2 = JSX_1kxb2["Element"];
13
+ declare const BulkActions: ({ tab, locales, progress }: {
14
+ tab: string;
15
+ locales: string[];
16
+ progress: Progress;
17
+ }) => Element_12xgc2;
18
+ import { JSX as JSX_1kxb3 } from "react";
19
+ type Element_12xgc3 = JSX_1kxb3["Element"];
8
20
  declare const ReviewActions: ({ entity, locale, reviewed }: {
9
21
  entity: string;
10
22
  locale: string;
11
23
  reviewed: boolean;
12
- }) => Element_12xgc2;
13
- /** loads the review view styles; the view itself is a server component */
14
- declare const ReviewStyles: () => null;
15
- import { JSX as JSX_1kxb3 } from "react";
16
- type Element_12xgc3 = JSX_1kxb3["Element"];
17
- declare const TranslationsNavLink: () => Element_12xgc3;
18
- export { CustomButtonWithTranslator, ReviewActions, ReviewStyles, TranslationsNavLink, CustomButtonWithTranslator as default };
24
+ }) => Element_12xgc3;
25
+ import { JSX as JSX_1kxb4 } from "react";
26
+ type Element_12xgc4 = JSX_1kxb4["Element"];
27
+ declare const TranslationsNavLink: () => Element_12xgc4;
28
+ export { BulkActions, CustomButtonWithTranslator, ReviewActions, TranslationsNavLink, CustomButtonWithTranslator as default };
package/dist/client.js CHANGED
@@ -315,14 +315,125 @@ var CustomButtonWithTranslator = ({ type }) => {
315
315
  });
316
316
  };
317
317
 
318
- // src/client/components/Review/ReviewActions.tsx
318
+ // src/client/components/Review/BulkActions.tsx
319
319
  import { Button as Button3, toast as toast2, useConfig as useConfig3 } from "@payloadcms/ui";
320
320
  import { useState as useState2 } from "react";
321
321
  import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
322
322
 
323
- var ReviewActions = ({ entity, locale, reviewed }) => {
323
+ var BulkActions = ({ tab, locales, progress }) => {
324
324
  const { config: { routes: { api }, serverURL } } = useConfig3();
325
+ const [locale, setLocale] = useState2("");
325
326
  const [pending, setPending] = useState2(null);
327
+ const post = async (path, body) => {
328
+ const res = await fetch(`${serverURL ?? ""}${api}/translator/review/${path}`, {
329
+ method: "POST",
330
+ credentials: "include",
331
+ headers: { "Content-Type": "application/json" },
332
+ body: JSON.stringify(body)
333
+ });
334
+ const result = await res.json().catch(() => ({}));
335
+ if (!res.ok || !result.success)
336
+ throw new Error(result?.errors?.[0]?.message ?? "request failed");
337
+ return result;
338
+ };
339
+ const queue = async () => {
340
+ const scope = `${locale ? locale.toUpperCase() : "every locale"} of all ${tab}`;
341
+ if (!window.confirm(`Queue translation of empty and untranslated fields in ${scope}? Existing translations are kept.`))
342
+ return;
343
+ setPending("bulk");
344
+ try {
345
+ const { queuedDocuments, queuedLocales, skippedPending } = await post("bulk", { tab, locale, mode: "untranslated" });
346
+ toast2.success(queuedDocuments ? `Queued ${queuedDocuments} documents (${queuedLocales} locales)${skippedPending ? `, ${skippedPending} already queued` : ""}` : skippedPending ? "Everything incomplete is already queued" : "Nothing to translate");
347
+ window.location.reload();
348
+ } catch (error) {
349
+ toast2.error(`Could not queue: ${error instanceof Error ? error.message : String(error)}`);
350
+ } finally {
351
+ setPending(null);
352
+ }
353
+ };
354
+ const runNow = async () => {
355
+ setPending("run");
356
+ try {
357
+ await post("run-jobs", {});
358
+ toast2.success("Ran a batch of queued translations");
359
+ window.location.reload();
360
+ } catch (error) {
361
+ toast2.error(`Could not run jobs: ${error instanceof Error ? error.message : String(error)}`);
362
+ } finally {
363
+ setPending(null);
364
+ }
365
+ };
366
+ return /* @__PURE__ */ jsxs5("div", {
367
+ className: "tr__bulk",
368
+ children: [
369
+ /* @__PURE__ */ jsxs5("div", {
370
+ className: "tr__bulk-main",
371
+ children: [
372
+ /* @__PURE__ */ jsxs5("select", {
373
+ "aria-label": "Locale",
374
+ className: "tr__select",
375
+ onChange: (event) => setLocale(event.target.value),
376
+ value: locale,
377
+ children: [
378
+ /* @__PURE__ */ jsx7("option", {
379
+ value: "",
380
+ children: "All locales"
381
+ }),
382
+ locales.map((each) => /* @__PURE__ */ jsx7("option", {
383
+ value: each,
384
+ children: each.toUpperCase()
385
+ }, each))
386
+ ]
387
+ }),
388
+ /* @__PURE__ */ jsx7(Button3, {
389
+ buttonStyle: "primary",
390
+ disabled: Boolean(pending),
391
+ onClick: queue,
392
+ size: "small",
393
+ children: pending === "bulk" ? "Queuing…" : "Translate untranslated"
394
+ })
395
+ ]
396
+ }),
397
+ progress && (progress.pending > 0 || progress.failed > 0) && /* @__PURE__ */ jsxs5("div", {
398
+ className: "tr__bulk-progress",
399
+ children: [
400
+ progress.pending > 0 && /* @__PURE__ */ jsxs5("span", {
401
+ className: "tr__pill",
402
+ "data-tone": "warn",
403
+ children: [
404
+ progress.pending,
405
+ " queued",
406
+ progress.running ? `, ${progress.running} running` : ""
407
+ ]
408
+ }),
409
+ progress.failed > 0 && /* @__PURE__ */ jsxs5("span", {
410
+ className: "tr__pill",
411
+ "data-tone": "bad",
412
+ children: [
413
+ progress.failed,
414
+ " failed today"
415
+ ]
416
+ }),
417
+ progress.pending > 0 && /* @__PURE__ */ jsx7(Button3, {
418
+ buttonStyle: "secondary",
419
+ disabled: Boolean(pending),
420
+ onClick: runNow,
421
+ size: "small",
422
+ children: pending === "run" ? "Running…" : "Run a batch now"
423
+ })
424
+ ]
425
+ })
426
+ ]
427
+ });
428
+ };
429
+ // src/client/components/Review/ReviewActions.tsx
430
+ import { Button as Button4, toast as toast3, useConfig as useConfig4 } from "@payloadcms/ui";
431
+ import { useState as useState3 } from "react";
432
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
433
+
434
+ var ReviewActions = ({ entity, locale, reviewed }) => {
435
+ const { config: { routes: { api }, serverURL } } = useConfig4();
436
+ const [pending, setPending] = useState3(null);
326
437
  const run = async (action) => {
327
438
  if (action === "translate-all" && !window.confirm(`Re-translate every field in ${locale}? Existing ${locale} copy is replaced.`))
328
439
  return;
@@ -333,37 +444,37 @@ var ReviewActions = ({ entity, locale, reviewed }) => {
333
444
  method: "POST",
334
445
  credentials: "include",
335
446
  headers: { "Content-Type": "application/json" },
336
- body: JSON.stringify({ entity, locale, mode: action === "translate-all" ? "all" : "missing" })
447
+ body: JSON.stringify({ entity, locale, mode: action === "translate-all" ? "all" : "untranslated" })
337
448
  });
338
449
  const result = await res.json().catch(() => ({}));
339
450
  if (!res.ok || !result.success)
340
451
  throw new Error(result?.errors?.[0]?.message ?? "request failed");
341
- toast2.success(action === "mark-reviewed" ? `${locale} marked as reviewed` : `${locale} translated`);
452
+ toast3.success(action === "mark-reviewed" ? `${locale} marked as reviewed` : `${locale} translated`);
342
453
  window.location.reload();
343
454
  } catch (error) {
344
- toast2.error(`Could not ${action.replace("-", " ")}: ${error instanceof Error ? error.message : String(error)}`);
455
+ toast3.error(`Could not ${action.replace("-", " ")}: ${error instanceof Error ? error.message : String(error)}`);
345
456
  } finally {
346
457
  setPending(null);
347
458
  }
348
459
  };
349
- return /* @__PURE__ */ jsxs5("div", {
460
+ return /* @__PURE__ */ jsxs6("div", {
350
461
  className: "translator-review__actions",
351
462
  children: [
352
- /* @__PURE__ */ jsx7(Button3, {
463
+ /* @__PURE__ */ jsx8(Button4, {
353
464
  buttonStyle: "primary",
354
465
  disabled: Boolean(pending),
355
- onClick: () => run("translate-missing"),
466
+ onClick: () => run("translate-untranslated"),
356
467
  size: "small",
357
- children: pending === "translate-missing" ? "Translating…" : "Translate missing fields"
468
+ children: pending === "translate-untranslated" ? "Translating…" : "Translate untranslated fields"
358
469
  }),
359
- /* @__PURE__ */ jsx7(Button3, {
470
+ /* @__PURE__ */ jsx8(Button4, {
360
471
  buttonStyle: "secondary",
361
472
  disabled: Boolean(pending),
362
473
  onClick: () => run("translate-all"),
363
474
  size: "small",
364
475
  children: pending === "translate-all" ? "Translating…" : "Re-translate all"
365
476
  }),
366
- /* @__PURE__ */ jsx7(Button3, {
477
+ /* @__PURE__ */ jsx8(Button4, {
367
478
  buttonStyle: "secondary",
368
479
  disabled: Boolean(pending) || reviewed,
369
480
  onClick: () => run("mark-reviewed"),
@@ -373,22 +484,19 @@ var ReviewActions = ({ entity, locale, reviewed }) => {
373
484
  ]
374
485
  });
375
486
  };
376
- // src/client/components/Review/ReviewStyles.tsx
377
-
378
- var ReviewStyles = () => null;
379
487
  // src/client/components/Review/TranslationsNavLink.tsx
380
- import { useConfig as useConfig4 } from "@payloadcms/ui";
488
+ import { useConfig as useConfig5 } from "@payloadcms/ui";
381
489
  import { formatAdminURL } from "payload/shared";
382
- import { jsx as jsx8 } from "react/jsx-runtime";
490
+ import { jsx as jsx9 } from "react/jsx-runtime";
383
491
 
384
492
  var TranslationsNavLink = () => {
385
- const { config: { routes: { admin } } } = useConfig4();
493
+ const { config: { routes: { admin } } } = useConfig5();
386
494
  const href = formatAdminURL({ adminRoute: admin, path: REVIEW_VIEW_PATH });
387
- return /* @__PURE__ */ jsx8("a", {
495
+ return /* @__PURE__ */ jsx9("a", {
388
496
  className: "nav__link",
389
497
  href,
390
498
  id: "nav-translations",
391
- children: /* @__PURE__ */ jsx8("span", {
499
+ children: /* @__PURE__ */ jsx9("span", {
392
500
  className: "nav__link-label",
393
501
  children: "Translations"
394
502
  })
@@ -398,9 +506,9 @@ var TranslationsNavLink = () => {
398
506
  // src/exports/client.ts
399
507
  var client_default = CustomButtonWithTranslator;
400
508
  export {
509
+ BulkActions,
401
510
  CustomButtonWithTranslator,
402
511
  ReviewActions,
403
- ReviewStyles,
404
512
  TranslationsNavLink,
405
513
  client_default as default
406
514
  };
package/dist/index.d.ts CHANGED
@@ -6,6 +6,8 @@ type TranslateArgs = {
6
6
  data?: Record<string, any>;
7
7
  emptyOnly?: boolean;
8
8
  globalSlug?: string;
9
+ /** with `emptyOnly`, also replace fields that still hold the source copy */
10
+ retranslateIdentical?: boolean;
9
11
  id?: number | string;
10
12
  locale: string;
11
13
  localeFrom: string;
@@ -18,6 +20,8 @@ type TranslateResult = {
18
20
  } | {
19
21
  success: true;
20
22
  translatedData: Record<string, any>;
23
+ /** values sent to the resolver; 0 means the target needed nothing */
24
+ translatedCount: number;
21
25
  dataFrom: Doc;
22
26
  };
23
27
  type TranslateOperationArgs = ({
@@ -45,6 +49,11 @@ type TranslateResolver = {
45
49
  key: string;
46
50
  resolve: (args: TranslateResolverArgs) => Promise<TranslateResolverResponse> | TranslateResolverResponse;
47
51
  };
52
+ /**
53
+ * `missing` fills empty fields, `untranslated` also replaces fields that still hold the
54
+ * source copy, `all` re-translates everything.
55
+ */
56
+ type TranslateMode = "all" | "missing" | "untranslated";
48
57
  type TranslatorConfig = {
49
58
  collections: CollectionSlug[];
50
59
  disabled?: boolean;
@@ -56,7 +65,7 @@ type TranslatorConfig = {
56
65
  * `missing` (default) fills empty fields and keeps existing translations and slugs;
57
66
  * `all` re-translates every field.
58
67
  */
59
- autoTranslateMode?: "all" | "missing";
68
+ autoTranslateMode?: TranslateMode;
60
69
  /** adds the /admin/translations review view, its endpoints and the status collection (default true) */
61
70
  review?: boolean;
62
71
  _options?: {
package/dist/index.js CHANGED
@@ -4,14 +4,19 @@ import {
4
4
  createAutoTranslateCollectionHook2,
5
5
  createAutoTranslateGlobalHook2,
6
6
  recordTranslationStatus,
7
+ translateModeArgs,
8
+ isTranslateMode,
7
9
  createTranslateTask2,
8
10
  createTranslateWorkflow2
9
- } from "./shared/chunk-p03jz82h.js";
10
- import"./shared/chunk-9d433kmv.js";
11
+ } from "./shared/chunk-hkx0bzb6.js";
12
+ import"./shared/chunk-d0n3f8ad.js";
11
13
  import {
12
14
  readLocales,
13
- parseEntityKey
14
- } from "./shared/chunk-15n5q7wd.js";
15
+ parseEntityKey,
16
+ hasTranslateWorkflow,
17
+ queueBulkTranslation,
18
+ runQueuedTranslations
19
+ } from "./shared/chunk-4ftfmxgs.js";
15
20
  import {
16
21
  TRANSLATION_STATUS_SLUG2,
17
22
  REVIEW_VIEW_PATH
@@ -171,7 +176,7 @@ var translateHandler = async (req) => {
171
176
  collectionSlug,
172
177
  globalSlug,
173
178
  id,
174
- emptyOnly: mode !== "all",
179
+ ...translateModeArgs(isTranslateMode(mode) ? mode : "untranslated"),
175
180
  locale,
176
181
  localeFrom: defaultLocale,
177
182
  overrideAccess: false,
@@ -208,7 +213,39 @@ var markReviewedHandler = async (req) => {
208
213
  });
209
214
  return Response.json({ success: true });
210
215
  };
216
+ var bulkHandler = async (req) => {
217
+ if (!req.user)
218
+ throw new APIError("Unauthorized", 401);
219
+ if (!req.json)
220
+ throw new APIError("Content-Type should be json");
221
+ if (!hasTranslateWorkflow(req))
222
+ throw new APIError("Bulk translation needs autoTranslate enabled, which registers the translate jobs", 400);
223
+ const body = await req.json();
224
+ const pluginConfig = req.payload.config.custom?.translator;
225
+ const { targetLocales } = readLocales(req);
226
+ const tabAllowed = body.tab === "globals" ? Boolean(pluginConfig?.globals.length) : pluginConfig?.collections.includes(body.tab);
227
+ if (!body.tab || !tabAllowed)
228
+ throw new APIError("Forbidden", 403);
229
+ if (body.locale && !targetLocales.includes(body.locale))
230
+ throw new APIError("Bad Request", 400);
231
+ const result = await queueBulkTranslation({
232
+ req,
233
+ tab: body.tab,
234
+ locale: body.locale || undefined,
235
+ mode: isTranslateMode(body.mode) ? body.mode : "untranslated"
236
+ });
237
+ return Response.json({ success: true, ...result });
238
+ };
239
+ var runJobsHandler = async (req) => {
240
+ if (!req.user)
241
+ throw new APIError("Unauthorized", 401);
242
+ if (!hasTranslateWorkflow(req))
243
+ throw new APIError("Translate jobs are not registered", 400);
244
+ return Response.json({ success: true, progress: await runQueuedTranslations(req) });
245
+ };
211
246
  var reviewEndpoints = [
247
+ { path: "/translator/review/bulk", method: "post", handler: bulkHandler },
248
+ { path: "/translator/review/run-jobs", method: "post", handler: runJobsHandler },
212
249
  { path: "/translator/review/translate", method: "post", handler: translateHandler },
213
250
  { path: "/translator/review/mark-reviewed", method: "post", handler: markReviewedHandler }
214
251
  ];
package/dist/jobs.js CHANGED
@@ -1,12 +1,13 @@
1
1
  import {
2
- TRANSLATE_TASK_SLUG2,
3
- TRANSLATE_WORKFLOW_SLUG2,
4
2
  createAutoTranslateCollectionHook2,
5
3
  createAutoTranslateGlobalHook2,
6
4
  createTranslateTask2,
7
5
  createTranslateWorkflow2
8
- } from "./shared/chunk-p03jz82h.js";
9
- import"./shared/chunk-9d433kmv.js";
6
+ } from "./shared/chunk-hkx0bzb6.js";
7
+ import {
8
+ TRANSLATE_TASK_SLUG2,
9
+ TRANSLATE_WORKFLOW_SLUG2
10
+ } from "./shared/chunk-d0n3f8ad.js";
10
11
 
11
12
  // src/exports/jobs.ts
12
13
  var jobs = {