@justanarthur/payload-plugin-translator 3.0.3 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -2
- package/dist/client.d.ts +12 -2
- package/dist/client.js +93 -11
- package/dist/index.d.ts +13 -1
- package/dist/index.js +171 -42
- package/dist/jobs.d.ts +1 -1
- package/dist/jobs.js +21 -20
- package/dist/resolvers/copy.d.ts +1 -1
- package/dist/resolvers/copy.js +5 -5
- package/dist/resolvers/google.d.ts +1 -1
- package/dist/resolvers/google.js +5 -6
- package/dist/resolvers/libreTranslate.d.ts +1 -1
- package/dist/resolvers/libreTranslate.js +5 -6
- package/dist/resolvers/openAI.d.ts +4 -2
- package/dist/resolvers/openAI.js +5 -6
- package/dist/resolvers/types.d.ts +1 -1
- package/dist/rsc.d.ts +5 -0
- package/dist/rsc.js +703 -0
- package/dist/shared/chunk-15n5q7wd.js +253 -0
- package/dist/shared/chunk-31qgv1zk.js +5 -0
- package/dist/shared/chunk-9d433kmv.js +359 -0
- package/dist/shared/{chunk-zhrw0jnz.js → chunk-mvqymw6n.js} +3 -3
- package/dist/shared/chunk-nxddacte.js +243 -0
- package/dist/shared/{chunk-q51q8rd2.js → chunk-p03jz82h.js} +280 -512
- package/dist/shared/{chunk-97ktc51q.js → chunk-zenq7s47.js} +2 -2
- package/dist/shared/{chunk-fsveh29m.js → chunk-zs19h344.js} +3 -3
- package/dist/types.d.ts +8 -0
- package/package.json +18 -9
- package/dist/shared/chunk-ecnph6hw.js +0 -205
- /package/dist/shared/{chunk-frnemj69.js → chunk-e6c0qzkt.js} +0 -0
package/README.md
CHANGED
|
@@ -79,6 +79,8 @@ 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. |
|
|
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). |
|
|
82
84
|
| `disabled` | `boolean` | no | Skip the plugin entirely (no overrides, no jobs). Useful in tests. |
|
|
83
85
|
| `_options.additionalTraverseRichText` | function | no | Hook to extend rich-text traversal — see below. |
|
|
84
86
|
|
|
@@ -100,8 +102,8 @@ translator({
|
|
|
100
102
|
})
|
|
101
103
|
```
|
|
102
104
|
|
|
103
|
-
The hook receives `onText` which mutates the data tree, plus the
|
|
104
|
-
|
|
105
|
+
The hook receives `onText` which mutates the data tree, plus the current node as `siblingData`.
|
|
106
|
+
It is called for every node without a `text` property, including `block` and `inlineBlock` nodes.
|
|
105
107
|
|
|
106
108
|
## How it works under the hood
|
|
107
109
|
|
|
@@ -113,6 +115,22 @@ richText field before translation begins.
|
|
|
113
115
|
- The plugin de-duplicates via `AUTO_TRANSLATE_MARKER` — re-running it on an already-attached hook
|
|
114
116
|
is a no-op, so it's safe to wrap multiple plugins around the same collection.
|
|
115
117
|
|
|
118
|
+
## Reviewing translations
|
|
119
|
+
|
|
120
|
+
With `review` on, **Translations** appears in the admin nav (`/admin/translations`):
|
|
121
|
+
|
|
122
|
+
- **Overview**: one row per document (paged, newest first) or global, one column per target locale.
|
|
123
|
+
Each cell shows the share of translatable fields that have a translation. `404` means the locale
|
|
124
|
+
has no slug, `↻` means the source changed since the last translation, `✓` means someone reviewed
|
|
125
|
+
that locale against the current source.
|
|
126
|
+
- **Detail** (click a cell): every translatable field with the source and target text side by side,
|
|
127
|
+
marked `missing`, `same as source` (likely never translated) or `placeholders differ`, plus the
|
|
128
|
+
last auto-translate job and its error. Actions: *Translate missing fields*, *Re-translate all*,
|
|
129
|
+
*Mark reviewed*.
|
|
130
|
+
|
|
131
|
+
The view only lists collections and globals the signed-in user can read, and the endpoints behind
|
|
132
|
+
the actions require a user.
|
|
133
|
+
|
|
116
134
|
## Manual translation
|
|
117
135
|
|
|
118
136
|
Two escape hatches when you don't want the auto-hook:
|
package/dist/client.d.ts
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
|
-
import { JSX as JSX_1kxb } from "react
|
|
1
|
+
import { JSX as JSX_1kxb } from "react";
|
|
2
2
|
type Element_12xgc = JSX_1kxb["Element"];
|
|
3
3
|
declare const CustomButtonWithTranslator: ({ type }: {
|
|
4
4
|
type: "publish" | "save";
|
|
5
5
|
}) => Element_12xgc;
|
|
6
|
-
|
|
6
|
+
import { JSX as JSX_1kxb2 } from "react";
|
|
7
|
+
type Element_12xgc2 = JSX_1kxb2["Element"];
|
|
8
|
+
declare const ReviewActions: ({ entity, locale, reviewed }: {
|
|
9
|
+
entity: string;
|
|
10
|
+
locale: string;
|
|
11
|
+
reviewed: boolean;
|
|
12
|
+
}) => Element_12xgc2;
|
|
13
|
+
import { JSX as JSX_1kxb3 } from "react";
|
|
14
|
+
type Element_12xgc3 = JSX_1kxb3["Element"];
|
|
15
|
+
declare const TranslationsNavLink: () => Element_12xgc3;
|
|
16
|
+
export { CustomButtonWithTranslator, ReviewActions, TranslationsNavLink, CustomButtonWithTranslator as default };
|
package/dist/client.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import {
|
|
3
|
+
REVIEW_VIEW_PATH
|
|
4
|
+
} from "./shared/chunk-31qgv1zk.js";
|
|
5
|
+
|
|
2
6
|
// src/client/components/CustomButton/CustomButtonWithTranslator.tsx
|
|
3
7
|
import { PublishButton, SaveButton, useConfig as useConfig2, useDocumentInfo as useDocumentInfo2 } from "@payloadcms/ui";
|
|
4
8
|
|
|
@@ -85,19 +89,18 @@ var TranslatorProvider = ({ children }) => {
|
|
|
85
89
|
const resolvers = custom?.translator?.resolvers || undefined;
|
|
86
90
|
if (!resolvers)
|
|
87
91
|
return null;
|
|
88
|
-
const
|
|
89
|
-
return
|
|
92
|
+
const resolverConfig = resolvers.find((each) => each.key === resolver);
|
|
93
|
+
return resolverConfig ?? null;
|
|
90
94
|
}, [custom, resolver]);
|
|
91
95
|
if (!localization)
|
|
92
96
|
throw new Error("Localization config is not provided and PluginTranslator is used");
|
|
93
97
|
const localesOptions = localization.locales.filter((each) => each.code !== locale.code);
|
|
94
98
|
const [localeToTranslateFrom, setLocaleToTranslateFrom] = useState("");
|
|
95
99
|
useEffect(() => {
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}, [locale, localesOptions, localization.defaultLocale]);
|
|
100
|
+
const options = localization.locales.filter((each) => each.code !== locale.code);
|
|
101
|
+
const defaultFromOptions = options.find((each) => localization.defaultLocale === each.code);
|
|
102
|
+
setLocaleToTranslateFrom((defaultFromOptions ?? options[0])?.code ?? "");
|
|
103
|
+
}, [locale.code, localization.defaultLocale, localization.locales]);
|
|
101
104
|
const closeTranslator = () => modal.closeModal(modalSlug);
|
|
102
105
|
const submit = async ({ emptyOnly }) => {
|
|
103
106
|
if (!resolver)
|
|
@@ -269,12 +272,12 @@ var Content = () => {
|
|
|
269
272
|
// src/client/components/TranslatorModal/TranslatorModal.tsx
|
|
270
273
|
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
271
274
|
var TranslatorModal = () => {
|
|
272
|
-
const { closeTranslator, modalSlug
|
|
275
|
+
const { closeTranslator, modalSlug, resolver } = useTranslator();
|
|
273
276
|
if (!resolver)
|
|
274
277
|
return;
|
|
275
278
|
return /* @__PURE__ */ jsx5(Modal, {
|
|
276
279
|
className: "translator__modal",
|
|
277
|
-
slug:
|
|
280
|
+
slug: modalSlug,
|
|
278
281
|
children: /* @__PURE__ */ jsxs3("div", {
|
|
279
282
|
className: "translator__wrapper",
|
|
280
283
|
children: [
|
|
@@ -312,9 +315,88 @@ var CustomButtonWithTranslator = ({ type }) => {
|
|
|
312
315
|
});
|
|
313
316
|
};
|
|
314
317
|
|
|
318
|
+
// src/client/components/Review/ReviewActions.tsx
|
|
319
|
+
import { Button as Button3, toast as toast2, useConfig as useConfig3 } from "@payloadcms/ui";
|
|
320
|
+
import { useState as useState2 } from "react";
|
|
321
|
+
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
322
|
+
|
|
323
|
+
var ReviewActions = ({ entity, locale, reviewed }) => {
|
|
324
|
+
const { config: { routes: { api }, serverURL } } = useConfig3();
|
|
325
|
+
const [pending, setPending] = useState2(null);
|
|
326
|
+
const run = async (action) => {
|
|
327
|
+
if (action === "translate-all" && !window.confirm(`Re-translate every field in ${locale}? Existing ${locale} copy is replaced.`))
|
|
328
|
+
return;
|
|
329
|
+
setPending(action);
|
|
330
|
+
try {
|
|
331
|
+
const path = action === "mark-reviewed" ? "mark-reviewed" : "translate";
|
|
332
|
+
const res = await fetch(`${serverURL ?? ""}${api}/translator/review/${path}`, {
|
|
333
|
+
method: "POST",
|
|
334
|
+
credentials: "include",
|
|
335
|
+
headers: { "Content-Type": "application/json" },
|
|
336
|
+
body: JSON.stringify({ entity, locale, mode: action === "translate-all" ? "all" : "missing" })
|
|
337
|
+
});
|
|
338
|
+
const result = await res.json().catch(() => ({}));
|
|
339
|
+
if (!res.ok || !result.success)
|
|
340
|
+
throw new Error(result?.errors?.[0]?.message ?? "request failed");
|
|
341
|
+
toast2.success(action === "mark-reviewed" ? `${locale} marked as reviewed` : `${locale} translated`);
|
|
342
|
+
window.location.reload();
|
|
343
|
+
} catch (error) {
|
|
344
|
+
toast2.error(`Could not ${action.replace("-", " ")}: ${error instanceof Error ? error.message : String(error)}`);
|
|
345
|
+
} finally {
|
|
346
|
+
setPending(null);
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
return /* @__PURE__ */ jsxs5("div", {
|
|
350
|
+
className: "translator-review__actions",
|
|
351
|
+
children: [
|
|
352
|
+
/* @__PURE__ */ jsx7(Button3, {
|
|
353
|
+
buttonStyle: "primary",
|
|
354
|
+
disabled: Boolean(pending),
|
|
355
|
+
onClick: () => run("translate-missing"),
|
|
356
|
+
size: "small",
|
|
357
|
+
children: pending === "translate-missing" ? "Translating…" : "Translate missing fields"
|
|
358
|
+
}),
|
|
359
|
+
/* @__PURE__ */ jsx7(Button3, {
|
|
360
|
+
buttonStyle: "secondary",
|
|
361
|
+
disabled: Boolean(pending),
|
|
362
|
+
onClick: () => run("translate-all"),
|
|
363
|
+
size: "small",
|
|
364
|
+
children: pending === "translate-all" ? "Translating…" : "Re-translate all"
|
|
365
|
+
}),
|
|
366
|
+
/* @__PURE__ */ jsx7(Button3, {
|
|
367
|
+
buttonStyle: "secondary",
|
|
368
|
+
disabled: Boolean(pending) || reviewed,
|
|
369
|
+
onClick: () => run("mark-reviewed"),
|
|
370
|
+
size: "small",
|
|
371
|
+
children: reviewed ? "Reviewed" : "Mark reviewed"
|
|
372
|
+
})
|
|
373
|
+
]
|
|
374
|
+
});
|
|
375
|
+
};
|
|
376
|
+
// src/client/components/Review/TranslationsNavLink.tsx
|
|
377
|
+
import { useConfig as useConfig4 } from "@payloadcms/ui";
|
|
378
|
+
import { formatAdminURL } from "payload/shared";
|
|
379
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
380
|
+
|
|
381
|
+
var TranslationsNavLink = () => {
|
|
382
|
+
const { config: { routes: { admin } } } = useConfig4();
|
|
383
|
+
const href = formatAdminURL({ adminRoute: admin, path: REVIEW_VIEW_PATH });
|
|
384
|
+
return /* @__PURE__ */ jsx8("a", {
|
|
385
|
+
className: "nav__link",
|
|
386
|
+
href,
|
|
387
|
+
id: "nav-translations",
|
|
388
|
+
children: /* @__PURE__ */ jsx8("span", {
|
|
389
|
+
className: "nav__link-label",
|
|
390
|
+
children: "Translations"
|
|
391
|
+
})
|
|
392
|
+
});
|
|
393
|
+
};
|
|
394
|
+
|
|
315
395
|
// src/exports/client.ts
|
|
316
396
|
var client_default = CustomButtonWithTranslator;
|
|
317
397
|
export {
|
|
318
|
-
|
|
319
|
-
|
|
398
|
+
CustomButtonWithTranslator,
|
|
399
|
+
ReviewActions,
|
|
400
|
+
TranslationsNavLink,
|
|
401
|
+
client_default as default
|
|
320
402
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -51,6 +51,14 @@ type TranslatorConfig = {
|
|
|
51
51
|
globals: GlobalSlug[];
|
|
52
52
|
resolvers: TranslateResolver[];
|
|
53
53
|
autoTranslate?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* what a publish in the default locale does to the other locales.
|
|
56
|
+
* `missing` (default) fills empty fields and keeps existing translations and slugs;
|
|
57
|
+
* `all` re-translates every field.
|
|
58
|
+
*/
|
|
59
|
+
autoTranslateMode?: "all" | "missing";
|
|
60
|
+
/** adds the /admin/translations review view, its endpoints and the status collection (default true) */
|
|
61
|
+
review?: boolean;
|
|
54
62
|
_options?: {
|
|
55
63
|
additionalTraverseRichText?: (args: {
|
|
56
64
|
onText: (siblingData: Record<string, unknown>, attribute?: string) => void;
|
|
@@ -60,4 +68,8 @@ type TranslatorConfig = {
|
|
|
60
68
|
};
|
|
61
69
|
};
|
|
62
70
|
declare const translator: (pluginConfig: TranslatorConfig) => Plugin;
|
|
63
|
-
|
|
71
|
+
import { CollectionConfig } from "payload";
|
|
72
|
+
/** one row per entity and target locale: what source it was translated from, and who reviewed it */
|
|
73
|
+
declare const createTranslationStatusCollection: () => CollectionConfig;
|
|
74
|
+
declare const TRANSLATION_STATUS_SLUG = "translation-status";
|
|
75
|
+
export { TRANSLATION_STATUS_SLUG, TranslateResolver, TranslateResolverArgs, TranslateResolverResponse, createTranslationStatusCollection, translator as default, translateOperation, translator };
|
package/dist/index.js
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
import"./shared/chunk-fsveh29m.js";
|
|
2
1
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import"./shared/chunk-
|
|
12
|
-
import
|
|
2
|
+
findEntityWithConfig,
|
|
3
|
+
translateOperation2,
|
|
4
|
+
createAutoTranslateCollectionHook2,
|
|
5
|
+
createAutoTranslateGlobalHook2,
|
|
6
|
+
recordTranslationStatus,
|
|
7
|
+
createTranslateTask2,
|
|
8
|
+
createTranslateWorkflow2
|
|
9
|
+
} from "./shared/chunk-p03jz82h.js";
|
|
10
|
+
import"./shared/chunk-9d433kmv.js";
|
|
11
|
+
import {
|
|
12
|
+
readLocales,
|
|
13
|
+
parseEntityKey
|
|
14
|
+
} from "./shared/chunk-15n5q7wd.js";
|
|
15
|
+
import {
|
|
16
|
+
TRANSLATION_STATUS_SLUG2,
|
|
17
|
+
REVIEW_VIEW_PATH
|
|
18
|
+
} from "./shared/chunk-31qgv1zk.js";
|
|
13
19
|
|
|
14
20
|
// src/index.ts
|
|
15
21
|
import { deepMerge } from "payload/shared";
|
|
@@ -112,14 +118,115 @@ var translations = {
|
|
|
112
118
|
}
|
|
113
119
|
};
|
|
114
120
|
|
|
115
|
-
// src/
|
|
121
|
+
// src/review/collection.ts
|
|
122
|
+
var signedIn = ({ req }) => Boolean(req.user);
|
|
123
|
+
var createTranslationStatusCollection = () => ({
|
|
124
|
+
slug: TRANSLATION_STATUS_SLUG2,
|
|
125
|
+
admin: {
|
|
126
|
+
hidden: true
|
|
127
|
+
},
|
|
128
|
+
access: {
|
|
129
|
+
create: signedIn,
|
|
130
|
+
delete: signedIn,
|
|
131
|
+
read: signedIn,
|
|
132
|
+
update: signedIn
|
|
133
|
+
},
|
|
134
|
+
fields: [
|
|
135
|
+
{ name: "entity", type: "text", required: true, index: true },
|
|
136
|
+
{ name: "locale", type: "text", required: true, index: true },
|
|
137
|
+
{ name: "sourceHash", type: "text" },
|
|
138
|
+
{ name: "translatedAt", type: "date" },
|
|
139
|
+
{ name: "reviewedHash", type: "text" },
|
|
140
|
+
{ name: "reviewedAt", type: "date" },
|
|
141
|
+
{ name: "reviewedBy", type: "text" }
|
|
142
|
+
]
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// src/review/endpoints.ts
|
|
116
146
|
import { APIError } from "payload";
|
|
117
|
-
var
|
|
147
|
+
var readReviewRequest = async (req) => {
|
|
148
|
+
if (!req.user)
|
|
149
|
+
throw new APIError("Unauthorized", 401);
|
|
118
150
|
if (!req.json)
|
|
119
151
|
throw new APIError("Content-Type should be json");
|
|
152
|
+
const body = await req.json();
|
|
153
|
+
const { targetLocales } = readLocales(req);
|
|
154
|
+
if (!body.entity || !body.locale || !targetLocales.includes(body.locale))
|
|
155
|
+
throw new APIError("Bad Request", 400);
|
|
156
|
+
const target = parseEntityKey(body.entity);
|
|
157
|
+
const pluginConfig = req.payload.config.custom?.translator;
|
|
158
|
+
const allowed = target.globalSlug ? pluginConfig?.globals.includes(target.globalSlug) : pluginConfig?.collections.includes(target.collectionSlug);
|
|
159
|
+
if (!allowed || !pluginConfig)
|
|
160
|
+
throw new APIError("Forbidden", 403);
|
|
161
|
+
return { ...body, ...target, locale: body.locale, pluginConfig };
|
|
162
|
+
};
|
|
163
|
+
var translateHandler = async (req) => {
|
|
164
|
+
const { collectionSlug, globalSlug, id, locale, mode, pluginConfig } = await readReviewRequest(req);
|
|
165
|
+
const { defaultLocale } = readLocales(req);
|
|
166
|
+
const resolver = pluginConfig.resolvers[0]?.key;
|
|
167
|
+
if (!resolver)
|
|
168
|
+
throw new APIError("No resolver configured", 400);
|
|
169
|
+
const result = await translateOperation2({
|
|
170
|
+
req,
|
|
171
|
+
collectionSlug,
|
|
172
|
+
globalSlug,
|
|
173
|
+
id,
|
|
174
|
+
emptyOnly: mode !== "all",
|
|
175
|
+
locale,
|
|
176
|
+
localeFrom: defaultLocale,
|
|
177
|
+
overrideAccess: false,
|
|
178
|
+
resolver,
|
|
179
|
+
update: true
|
|
180
|
+
});
|
|
181
|
+
if (result.success) {
|
|
182
|
+
const { config } = await findEntityWithConfig({ collectionSlug, globalSlug, id, locale: defaultLocale, req });
|
|
183
|
+
await recordTranslationStatus({ req, collectionSlug, globalSlug, id, locale, config, dataFrom: result.dataFrom });
|
|
184
|
+
}
|
|
185
|
+
return Response.json({ success: result.success });
|
|
186
|
+
};
|
|
187
|
+
var markReviewedHandler = async (req) => {
|
|
188
|
+
const { collectionSlug, globalSlug, id, locale } = await readReviewRequest(req);
|
|
189
|
+
const { defaultLocale } = readLocales(req);
|
|
190
|
+
const { config, doc } = await findEntityWithConfig({
|
|
191
|
+
collectionSlug,
|
|
192
|
+
globalSlug,
|
|
193
|
+
id,
|
|
194
|
+
locale: defaultLocale,
|
|
195
|
+
overrideAccess: false,
|
|
196
|
+
req
|
|
197
|
+
});
|
|
198
|
+
const user = req.user;
|
|
199
|
+
await recordTranslationStatus({
|
|
200
|
+
req,
|
|
201
|
+
collectionSlug,
|
|
202
|
+
globalSlug,
|
|
203
|
+
id,
|
|
204
|
+
locale,
|
|
205
|
+
config,
|
|
206
|
+
dataFrom: doc,
|
|
207
|
+
reviewedBy: user.email ?? String(user.id)
|
|
208
|
+
});
|
|
209
|
+
return Response.json({ success: true });
|
|
210
|
+
};
|
|
211
|
+
var reviewEndpoints = [
|
|
212
|
+
{ path: "/translator/review/translate", method: "post", handler: translateHandler },
|
|
213
|
+
{ path: "/translator/review/mark-reviewed", method: "post", handler: markReviewedHandler }
|
|
214
|
+
];
|
|
215
|
+
|
|
216
|
+
// src/translate/endpoint.ts
|
|
217
|
+
import { APIError as APIError2 } from "payload";
|
|
218
|
+
var translateEndpoint = async (req) => {
|
|
219
|
+
if (!req.user)
|
|
220
|
+
throw new APIError2("Unauthorized", 401);
|
|
221
|
+
if (!req.json)
|
|
222
|
+
throw new APIError2("Content-Type should be json");
|
|
120
223
|
const args = await req.json();
|
|
121
224
|
const { collectionSlug, data, emptyOnly, globalSlug, id, locale, localeFrom, resolver } = args;
|
|
122
|
-
const
|
|
225
|
+
const pluginConfig = req.payload.config.custom?.translator;
|
|
226
|
+
const allowed = globalSlug ? pluginConfig?.globals.includes(globalSlug) : pluginConfig?.collections.includes(collectionSlug);
|
|
227
|
+
if (!allowed)
|
|
228
|
+
throw new APIError2("Forbidden", 403);
|
|
229
|
+
const result = await translateOperation2({
|
|
123
230
|
collectionSlug,
|
|
124
231
|
data,
|
|
125
232
|
emptyOnly,
|
|
@@ -141,10 +248,27 @@ var translator = (pluginConfig) => {
|
|
|
141
248
|
if (pluginConfig.disabled || !config.localization || config.localization.locales.length < 2)
|
|
142
249
|
return config;
|
|
143
250
|
const autoTranslate = pluginConfig.autoTranslate ?? true;
|
|
251
|
+
const review = pluginConfig.review ?? true;
|
|
144
252
|
const updatedConfig = {
|
|
145
253
|
...config,
|
|
146
254
|
admin: {
|
|
147
255
|
...config.admin ?? {},
|
|
256
|
+
...review ? {
|
|
257
|
+
components: {
|
|
258
|
+
...config.admin?.components ?? {},
|
|
259
|
+
afterNavLinks: [
|
|
260
|
+
...config.admin?.components?.afterNavLinks ?? [],
|
|
261
|
+
"@justanarthur/payload-plugin-translator/client#TranslationsNavLink"
|
|
262
|
+
],
|
|
263
|
+
views: {
|
|
264
|
+
...config.admin?.components?.views ?? {},
|
|
265
|
+
translations: {
|
|
266
|
+
Component: "@justanarthur/payload-plugin-translator/rsc#TranslationsView",
|
|
267
|
+
path: REVIEW_VIEW_PATH
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
} : {},
|
|
148
272
|
custom: {
|
|
149
273
|
...config.admin?.custom ?? {},
|
|
150
274
|
translator: {
|
|
@@ -152,27 +276,30 @@ var translator = (pluginConfig) => {
|
|
|
152
276
|
}
|
|
153
277
|
}
|
|
154
278
|
},
|
|
155
|
-
collections:
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
279
|
+
collections: [
|
|
280
|
+
...config.collections?.map((collection) => {
|
|
281
|
+
if (!pluginConfig.collections.includes(collection.slug))
|
|
282
|
+
return collection;
|
|
283
|
+
return {
|
|
284
|
+
...collection,
|
|
285
|
+
admin: {
|
|
286
|
+
...collection.admin ?? {},
|
|
287
|
+
components: {
|
|
288
|
+
...collection.admin?.components ?? {},
|
|
289
|
+
edit: {
|
|
290
|
+
...collection.admin?.components?.edit ?? {},
|
|
291
|
+
PublishButton: CustomButton("publish"),
|
|
292
|
+
SaveButton: CustomButton("save")
|
|
293
|
+
}
|
|
168
294
|
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
295
|
+
},
|
|
296
|
+
...autoTranslate ? {
|
|
297
|
+
hooks: attachCollectionHook(collection.hooks, createAutoTranslateCollectionHook2({ collectionSlug: String(collection.slug) }).afterChange)
|
|
298
|
+
} : {}
|
|
299
|
+
};
|
|
300
|
+
}) ?? [],
|
|
301
|
+
...review ? [createTranslationStatusCollection()] : []
|
|
302
|
+
],
|
|
176
303
|
custom: {
|
|
177
304
|
...config.custom ?? {},
|
|
178
305
|
translator: {
|
|
@@ -185,7 +312,8 @@ var translator = (pluginConfig) => {
|
|
|
185
312
|
handler: translateEndpoint,
|
|
186
313
|
method: "post",
|
|
187
314
|
path: "/translator/translate"
|
|
188
|
-
}
|
|
315
|
+
},
|
|
316
|
+
...review ? reviewEndpoints : []
|
|
189
317
|
],
|
|
190
318
|
globals: config.globals?.map((global) => {
|
|
191
319
|
if (!pluginConfig.globals.includes(global.slug))
|
|
@@ -204,7 +332,7 @@ var translator = (pluginConfig) => {
|
|
|
204
332
|
}
|
|
205
333
|
},
|
|
206
334
|
...autoTranslate ? {
|
|
207
|
-
hooks: attachGlobalHook(global.hooks,
|
|
335
|
+
hooks: attachGlobalHook(global.hooks, createAutoTranslateGlobalHook2({ globalSlug: String(global.slug) }))
|
|
208
336
|
} : {}
|
|
209
337
|
};
|
|
210
338
|
}) ?? [],
|
|
@@ -217,8 +345,8 @@ var translator = (pluginConfig) => {
|
|
|
217
345
|
...autoTranslate ? {
|
|
218
346
|
jobs: {
|
|
219
347
|
...config.jobs ?? {},
|
|
220
|
-
tasks: ensureJobBySlug(config.jobs?.tasks,
|
|
221
|
-
workflows: ensureJobBySlug(config.jobs?.workflows,
|
|
348
|
+
tasks: ensureJobBySlug(config.jobs?.tasks, createTranslateTask2()),
|
|
349
|
+
workflows: ensureJobBySlug(config.jobs?.workflows, createTranslateWorkflow2()),
|
|
222
350
|
deleteJobOnComplete: false,
|
|
223
351
|
jobsCollectionOverrides: ({ defaultJobsCollection }) => {
|
|
224
352
|
defaultJobsCollection.admin = {
|
|
@@ -270,11 +398,12 @@ function markAutoTranslate(hook) {
|
|
|
270
398
|
function isMarkedAutoTranslate(hook) {
|
|
271
399
|
return Boolean(hook?.[AUTO_TRANSLATE_MARKER]);
|
|
272
400
|
}
|
|
273
|
-
|
|
274
401
|
// src/exports/index.ts
|
|
275
402
|
var exports_default = translator;
|
|
276
403
|
export {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
exports_default as default
|
|
404
|
+
TRANSLATION_STATUS_SLUG2 as TRANSLATION_STATUS_SLUG,
|
|
405
|
+
createTranslationStatusCollection,
|
|
406
|
+
exports_default as default,
|
|
407
|
+
translateOperation2 as translateOperation,
|
|
408
|
+
translator
|
|
280
409
|
};
|
package/dist/jobs.d.ts
CHANGED
|
@@ -47,4 +47,4 @@ declare const jobs: {
|
|
|
47
47
|
createAutoTranslateCollectionHook: typeof createAutoTranslateCollectionHook;
|
|
48
48
|
createAutoTranslateGlobalHook: typeof createAutoTranslateGlobalHook;
|
|
49
49
|
};
|
|
50
|
-
export {
|
|
50
|
+
export { CreateAutoTranslateCollectionHookOptions, CreateAutoTranslateGlobalHookOptions, CreateTranslateTaskOptions, CreateTranslateWorkflowOptions, TRANSLATE_TASK_SLUG, TRANSLATE_WORKFLOW_SLUG, TranslateTaskConfig, TranslateWorkflowConfig, createAutoTranslateCollectionHook, createAutoTranslateGlobalHook, createTranslateTask, createTranslateWorkflow, jobs as default };
|
package/dist/jobs.js
CHANGED
|
@@ -1,28 +1,29 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
} from "./shared/chunk-
|
|
2
|
+
TRANSLATE_TASK_SLUG2,
|
|
3
|
+
TRANSLATE_WORKFLOW_SLUG2,
|
|
4
|
+
createAutoTranslateCollectionHook2,
|
|
5
|
+
createAutoTranslateGlobalHook2,
|
|
6
|
+
createTranslateTask2,
|
|
7
|
+
createTranslateWorkflow2
|
|
8
|
+
} from "./shared/chunk-p03jz82h.js";
|
|
9
|
+
import"./shared/chunk-9d433kmv.js";
|
|
9
10
|
|
|
10
11
|
// src/exports/jobs.ts
|
|
11
12
|
var jobs = {
|
|
12
|
-
TRANSLATE_TASK_SLUG,
|
|
13
|
-
TRANSLATE_WORKFLOW_SLUG,
|
|
14
|
-
createTranslateTask,
|
|
15
|
-
createTranslateWorkflow,
|
|
16
|
-
createAutoTranslateCollectionHook,
|
|
17
|
-
createAutoTranslateGlobalHook
|
|
13
|
+
TRANSLATE_TASK_SLUG: TRANSLATE_TASK_SLUG2,
|
|
14
|
+
TRANSLATE_WORKFLOW_SLUG: TRANSLATE_WORKFLOW_SLUG2,
|
|
15
|
+
createTranslateTask: createTranslateTask2,
|
|
16
|
+
createTranslateWorkflow: createTranslateWorkflow2,
|
|
17
|
+
createAutoTranslateCollectionHook: createAutoTranslateCollectionHook2,
|
|
18
|
+
createAutoTranslateGlobalHook: createAutoTranslateGlobalHook2
|
|
18
19
|
};
|
|
19
20
|
var jobs_default = jobs;
|
|
20
21
|
export {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
createAutoTranslateGlobalHook,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
TRANSLATE_TASK_SLUG2 as TRANSLATE_TASK_SLUG,
|
|
23
|
+
TRANSLATE_WORKFLOW_SLUG2 as TRANSLATE_WORKFLOW_SLUG,
|
|
24
|
+
createAutoTranslateCollectionHook2 as createAutoTranslateCollectionHook,
|
|
25
|
+
createAutoTranslateGlobalHook2 as createAutoTranslateGlobalHook,
|
|
26
|
+
createTranslateTask2 as createTranslateTask,
|
|
27
|
+
createTranslateWorkflow2 as createTranslateWorkflow,
|
|
28
|
+
jobs_default as default
|
|
28
29
|
};
|
package/dist/resolvers/copy.d.ts
CHANGED
|
@@ -16,4 +16,4 @@ type TranslateResolver = {
|
|
|
16
16
|
resolve: (args: TranslateResolverArgs) => Promise<TranslateResolverResponse> | TranslateResolverResponse;
|
|
17
17
|
};
|
|
18
18
|
declare const copyResolver: () => TranslateResolver;
|
|
19
|
-
export { copyResolver as default
|
|
19
|
+
export { copyResolver, copyResolver as default };
|
package/dist/resolvers/copy.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
} from "../shared/chunk-
|
|
2
|
+
copyResolver2
|
|
3
|
+
} from "../shared/chunk-zenq7s47.js";
|
|
4
4
|
|
|
5
5
|
// src/exports/resolvers/copy.ts
|
|
6
|
-
var copy_default =
|
|
6
|
+
var copy_default = copyResolver2;
|
|
7
7
|
export {
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
copyResolver2 as copyResolver,
|
|
9
|
+
copy_default as default
|
|
10
10
|
};
|
|
@@ -20,4 +20,4 @@ type GoogleResolverConfig = {
|
|
|
20
20
|
chunkLength?: number;
|
|
21
21
|
};
|
|
22
22
|
declare const googleResolver: ({ apiKey, chunkLength }: GoogleResolverConfig) => TranslateResolver;
|
|
23
|
-
export {
|
|
23
|
+
export { GoogleResolverConfig, googleResolver as default, googleResolver };
|
package/dist/resolvers/google.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
} from "../shared/chunk-
|
|
4
|
-
import"../shared/chunk-frnemj69.js";
|
|
2
|
+
googleResolver2
|
|
3
|
+
} from "../shared/chunk-zs19h344.js";
|
|
5
4
|
|
|
6
5
|
// src/exports/resolvers/google.ts
|
|
7
|
-
var google_default =
|
|
6
|
+
var google_default = googleResolver2;
|
|
8
7
|
export {
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
google_default as default,
|
|
9
|
+
googleResolver2 as googleResolver
|
|
11
10
|
};
|
|
@@ -21,4 +21,4 @@ type LibreResolverConfig = {
|
|
|
21
21
|
url?: string;
|
|
22
22
|
};
|
|
23
23
|
declare const libreResolver: ({ apiKey, chunkLength, url }: LibreResolverConfig) => TranslateResolver;
|
|
24
|
-
export {
|
|
24
|
+
export { LibreResolverConfig, libreResolver as default, libreResolver };
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
} from "../shared/chunk-
|
|
4
|
-
import"../shared/chunk-frnemj69.js";
|
|
2
|
+
libreResolver2
|
|
3
|
+
} from "../shared/chunk-mvqymw6n.js";
|
|
5
4
|
|
|
6
5
|
// src/exports/resolvers/libreTranslate.ts
|
|
7
|
-
var libreTranslate_default =
|
|
6
|
+
var libreTranslate_default = libreResolver2;
|
|
8
7
|
export {
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
libreTranslate_default as default,
|
|
9
|
+
libreResolver2 as libreResolver
|
|
11
10
|
};
|
|
@@ -24,8 +24,10 @@ type OpenAIResolverConfig = {
|
|
|
24
24
|
apiKey: string;
|
|
25
25
|
baseUrl?: string;
|
|
26
26
|
chunkLength?: number;
|
|
27
|
+
/** chunks sent at the same time */
|
|
28
|
+
concurrency?: number;
|
|
27
29
|
model?: string;
|
|
28
30
|
prompt?: OpenAIPrompt;
|
|
29
31
|
};
|
|
30
|
-
declare const openAIResolver: ({ apiKey, baseUrl, chunkLength, model, prompt }: OpenAIResolverConfig) => TranslateResolver;
|
|
31
|
-
export {
|
|
32
|
+
declare const openAIResolver: ({ apiKey, baseUrl, chunkLength, concurrency, model, prompt }: OpenAIResolverConfig) => TranslateResolver;
|
|
33
|
+
export { OpenAIPrompt, OpenAIResolverConfig, openAIResolver as default, openAIResolver };
|