@focus-reactive/payload-plugin-translator 0.4.0 → 0.5.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 +44 -29
- package/dist/client/shared/ui/Button/Button.d.ts +4 -4
- package/dist/client/shared/ui/Button/Button.js +18 -18
- package/dist/client/shared/ui/Button/styles.module.scss +1 -1
- package/dist/client/shared/ui/Popup/Popup.d.ts +10 -4
- package/dist/client/shared/ui/Popup/Popup.js +4 -3
- package/dist/client/shared/ui/Select/Select.d.ts +9 -7
- package/dist/client/shared/ui/Select/Select.js +18 -17
- package/dist/client/shared/ui/Select/styles.module.scss +5 -0
- package/dist/index.d.ts +16 -14
- package/dist/index.js +9 -7
- package/dist/plugin.d.ts +15 -4
- package/dist/plugin.js +34 -57
- package/dist/server/features/cancel/handler.d.ts +2 -2
- package/dist/server/features/cancel/handler.js +3 -1
- package/dist/server/features/cancel/route.d.ts +4 -4
- package/dist/server/features/cancel/route.js +4 -4
- package/dist/server/features/cancel-by-collection/handler.d.ts +3 -3
- package/dist/server/features/cancel-by-collection/handler.js +1 -1
- package/dist/server/features/cancel-by-collection/route.d.ts +5 -5
- package/dist/server/features/cancel-by-collection/route.js +4 -4
- package/dist/server/features/createTranslationRoutes.d.ts +23 -0
- package/dist/server/features/createTranslationRoutes.js +27 -0
- package/dist/server/features/enqueue-translation/handler.d.ts +3 -3
- package/dist/server/features/enqueue-translation/handler.js +1 -1
- package/dist/server/features/enqueue-translation/route.d.ts +5 -5
- package/dist/server/features/enqueue-translation/route.js +4 -4
- package/dist/server/features/get-collection-status/handler.d.ts +3 -3
- package/dist/server/features/get-collection-status/handler.js +1 -1
- package/dist/server/features/get-collection-status/route.d.ts +5 -5
- package/dist/server/features/get-collection-status/route.js +4 -4
- package/dist/server/features/get-document-status/handler.d.ts +3 -3
- package/dist/server/features/get-document-status/handler.js +1 -1
- package/dist/server/features/get-document-status/route.d.ts +5 -5
- package/dist/server/features/get-document-status/route.js +4 -4
- package/dist/server/features/index.d.ts +7 -6
- package/dist/server/features/index.js +7 -6
- package/dist/server/features/run-translation/handler.d.ts +2 -2
- package/dist/server/features/run-translation/route.d.ts +4 -4
- package/dist/server/features/run-translation/route.js +4 -4
- package/dist/server/features/translate-document/handler.js +7 -11
- package/dist/server/features/translate-field/resolveFieldSubtree.d.ts +31 -0
- package/dist/server/features/translate-field/resolveFieldSubtree.js +45 -0
- package/dist/server/modules/task-runner/TaskRunnerProvider.interface.d.ts +18 -1
- package/dist/server/modules/task-runner/index.d.ts +6 -6
- package/dist/server/modules/task-runner/index.js +2 -2
- package/dist/server/modules/task-runner/payload-jobs-runner/PayloadJobsRunnerProvider.d.ts +16 -1
- package/dist/server/modules/task-runner/payload-jobs-runner/PayloadJobsRunnerProvider.js +16 -1
- package/dist/server/modules/task-runner/sync-runner/SyncRunnerProvider.d.ts +18 -10
- package/dist/server/modules/task-runner/sync-runner/SyncRunnerProvider.js +24 -14
- package/dist/server/modules/translation-levels/PluginConfigBuilder.d.ts +46 -0
- package/dist/server/modules/translation-levels/PluginConfigBuilder.js +108 -0
- package/dist/server/modules/translation-levels/collectionLevel.d.ts +15 -0
- package/dist/server/modules/translation-levels/collectionLevel.js +24 -0
- package/dist/server/modules/translation-levels/documentLevel.d.ts +19 -0
- package/dist/server/modules/translation-levels/documentLevel.js +28 -0
- package/dist/server/modules/translation-levels/index.d.ts +3 -0
- package/dist/server/modules/translation-levels/index.js +4 -0
- package/dist/server/modules/translation-levels/types.d.ts +42 -0
- package/dist/server/modules/translation-levels/types.js +10 -0
- package/dist/server/modules/translation-levels/useDocTranslationApi.d.ts +8 -0
- package/dist/server/modules/translation-levels/useDocTranslationApi.js +18 -0
- package/dist/server/modules/translation-pipeline/index.d.ts +5 -3
- package/dist/server/modules/translation-pipeline/index.js +3 -2
- package/dist/server/modules/translation-pipeline/stages/data-reconciler/DataReconciler.d.ts +3 -9
- package/dist/server/modules/translation-pipeline/stages/data-reconciler/DataReconciler.js +71 -78
- package/dist/server/modules/translation-pipeline/stages/field-collector/FieldChunkCollector.d.ts +11 -22
- package/dist/server/modules/translation-pipeline/stages/field-collector/FieldChunkCollector.js +85 -106
- package/dist/server/modules/translation-pipeline/translateContent.d.ts +35 -0
- package/dist/server/modules/translation-pipeline/translateContent.js +30 -0
- package/dist/server/modules/translation-providers/OpenAITranslation.provider.d.ts +4 -3
- package/dist/server/modules/translation-providers/OpenAITranslation.provider.js +15 -15
- package/dist/server/shared/field-traversal/findFieldByPath.d.ts +40 -0
- package/dist/server/shared/field-traversal/findFieldByPath.js +88 -0
- package/dist/server/shared/field-traversal/index.d.ts +5 -0
- package/dist/server/shared/field-traversal/index.js +5 -0
- package/dist/server/shared/field-traversal/kernel.d.ts +85 -0
- package/dist/server/shared/field-traversal/kernel.js +145 -0
- package/dist/server/shared/field-traversal/types.d.ts +191 -0
- package/dist/server/shared/field-traversal/types.js +41 -0
- package/dist/server/shared/field-traversal/walkFields.d.ts +51 -0
- package/dist/server/shared/field-traversal/walkFields.js +164 -0
- package/dist/server/shared/utils/filterLocalizedFields.d.ts +3 -3
- package/dist/server/shared/utils/filterLocalizedFields.js +52 -63
- package/dist/types/AccessGuard.d.ts +24 -1
- package/dist/types/AccessGuard.js +12 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -12,6 +12,7 @@ Translation plugin for Payload CMS 3.x. Automatically translate your localized c
|
|
|
12
12
|
- **Pluggable providers** — use OpenAI or create custom translation providers
|
|
13
13
|
- **Field exclusion** — exclude specific fields from translation via `withFieldTranslation`
|
|
14
14
|
- **Translation strategies** — choose between overwrite all or skip existing translations
|
|
15
|
+
- **Configurable surfaces** — enable the per-document popup and/or the bulk-collection dashboard via the `levels` option _(since v0.5.0)_
|
|
15
16
|
|
|
16
17
|
## Installation
|
|
17
18
|
|
|
@@ -33,11 +34,7 @@ yarn add @focus-reactive/payload-plugin-translator
|
|
|
33
34
|
|
|
34
35
|
```typescript
|
|
35
36
|
import { buildConfig } from "payload";
|
|
36
|
-
import {
|
|
37
|
-
translatorPlugin,
|
|
38
|
-
createOpenAIProvider,
|
|
39
|
-
createPayloadJobsRunner,
|
|
40
|
-
} from "@focus-reactive/payload-plugin-translator";
|
|
37
|
+
import { translatorPlugin, createOpenAIProvider, createPayloadJobsRunner } from "@focus-reactive/payload-plugin-translator";
|
|
41
38
|
import { Posts } from "./collections/Posts";
|
|
42
39
|
import { Pages } from "./collections/Pages";
|
|
43
40
|
|
|
@@ -65,13 +62,14 @@ export default buildConfig({
|
|
|
65
62
|
|
|
66
63
|
Configuration for `translatorPlugin()`.
|
|
67
64
|
|
|
68
|
-
| Property | Type | Required | Default
|
|
69
|
-
| --------------------- | --------------------- | -------- |
|
|
70
|
-
| `collections` | `CollectionConfig[]` | Yes | —
|
|
71
|
-
| `translationProvider` | `TranslationProvider` | Yes | —
|
|
72
|
-
| `runner` | `TaskRunnerProvider` | Yes | —
|
|
73
|
-
| `access` | `AccessGuard` | No | `undefined`
|
|
74
|
-
| `basePath` | `string` | No | `'/translate'`
|
|
65
|
+
| Property | Type | Required | Default | Description |
|
|
66
|
+
| --------------------- | --------------------- | -------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
67
|
+
| `collections` | `CollectionConfig[]` | Yes | — | Original collection configs to enable translation for. Must be the same objects passed to `buildConfig`, not slugs. |
|
|
68
|
+
| `translationProvider` | `TranslationProvider` | Yes | — | Translation provider instance (e.g., `createOpenAIProvider(...)`) |
|
|
69
|
+
| `runner` | `TaskRunnerProvider` | Yes | — | Task runner provider for background processing (e.g., `createPayloadJobsRunner()`) |
|
|
70
|
+
| `access` | `AccessGuard` | No | `undefined` | Access guard (`{ check }`) for the translation endpoints; omit to leave them open |
|
|
71
|
+
| `basePath` | `string` | No | `'/translate'` | Base path for all API endpoints |
|
|
72
|
+
| `levels` | `TranslationLevel[]` | No | `[documentLevel(), collectionLevel()]` | Which translation surfaces to enable. See [Translation Levels](#translation-levels) |
|
|
75
73
|
|
|
76
74
|
```typescript
|
|
77
75
|
translatorPlugin({
|
|
@@ -80,11 +78,37 @@ translatorPlugin({
|
|
|
80
78
|
apiKey: process.env.OPENAI_API_KEY,
|
|
81
79
|
}),
|
|
82
80
|
runner: createPayloadJobsRunner(),
|
|
83
|
-
access:
|
|
81
|
+
access: { check: ({ req }) => req.user?.role === "admin" },
|
|
84
82
|
basePath: "/translate",
|
|
85
83
|
});
|
|
86
84
|
```
|
|
87
85
|
|
|
86
|
+
### Translation Levels
|
|
87
|
+
|
|
88
|
+
_Since v0.5.0._
|
|
89
|
+
|
|
90
|
+
The `levels` option controls which translation surfaces the plugin exposes. Each entry is a factory you import and list:
|
|
91
|
+
|
|
92
|
+
- `documentLevel()` — a **Translate** popup on the document edit view (translate one document).
|
|
93
|
+
- `collectionLevel()` — a **bulk dashboard** on the collection list view (translate many at once).
|
|
94
|
+
|
|
95
|
+
Both run through the configured `runner` (async Payload Jobs by default, or synchronous via `createSyncRunner()`) and share the same translation REST API.
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
import { translatorPlugin, documentLevel, collectionLevel, createOpenAIProvider, createPayloadJobsRunner } from "@focus-reactive/payload-plugin-translator";
|
|
99
|
+
|
|
100
|
+
translatorPlugin({
|
|
101
|
+
collections: [Posts],
|
|
102
|
+
translationProvider: createOpenAIProvider({
|
|
103
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
104
|
+
}),
|
|
105
|
+
runner: createPayloadJobsRunner(),
|
|
106
|
+
levels: [collectionLevel()], // bulk dashboard only — no per-document popup
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Omit `levels` for the default `[documentLevel(), collectionLevel()]`, which is identical to the previous behaviour — so adopting this option is non-breaking. A synchronous `fieldLevel()` (in-place field translation) is planned for a later release.
|
|
111
|
+
|
|
88
112
|
### OpenAIProviderConfig
|
|
89
113
|
|
|
90
114
|
Configuration for `createOpenAIProvider()`.
|
|
@@ -100,8 +124,7 @@ Configuration for `createOpenAIProvider()`.
|
|
|
100
124
|
createOpenAIProvider({
|
|
101
125
|
apiKey: process.env.OPENAI_API_KEY,
|
|
102
126
|
model: "gpt-4o-mini",
|
|
103
|
-
systemPrompt: ({ sourceLang, targetLang, defaultPrompt }) =>
|
|
104
|
-
`${defaultPrompt}\nUse formal language. Keep brand names unchanged.`,
|
|
127
|
+
systemPrompt: ({ sourceLang, targetLang, defaultPrompt }) => `${defaultPrompt}\nUse formal language. Keep brand names unchanged.`,
|
|
105
128
|
dryRun: false,
|
|
106
129
|
});
|
|
107
130
|
```
|
|
@@ -165,10 +188,7 @@ Configuration for `withFieldTranslation()` helper or `field.custom.translateKit`
|
|
|
165
188
|
```typescript
|
|
166
189
|
import { withFieldTranslation } from "@focus-reactive/payload-plugin-translator";
|
|
167
190
|
|
|
168
|
-
withFieldTranslation(
|
|
169
|
-
{ name: "sku", type: "text", localized: true },
|
|
170
|
-
{ exclude: true },
|
|
171
|
-
);
|
|
191
|
+
withFieldTranslation({ name: "sku", type: "text", localized: true }, { exclude: true });
|
|
172
192
|
```
|
|
173
193
|
|
|
174
194
|
## Translation Strategies
|
|
@@ -310,20 +330,12 @@ type TranslationOutput = Record<TranslationIndex, string>;
|
|
|
310
330
|
#### Example Implementation
|
|
311
331
|
|
|
312
332
|
```typescript
|
|
313
|
-
import type {
|
|
314
|
-
TranslationProvider,
|
|
315
|
-
TranslationInput,
|
|
316
|
-
TranslationOutput,
|
|
317
|
-
} from "@focus-reactive/payload-plugin-translator";
|
|
333
|
+
import type { TranslationProvider, TranslationInput, TranslationOutput } from "@focus-reactive/payload-plugin-translator";
|
|
318
334
|
|
|
319
335
|
class DeepLProvider implements TranslationProvider {
|
|
320
336
|
constructor(private apiKey: string) {}
|
|
321
337
|
|
|
322
|
-
async translate(
|
|
323
|
-
content: TranslationInput,
|
|
324
|
-
sourceLng: string,
|
|
325
|
-
targetLng: string,
|
|
326
|
-
): Promise<TranslationOutput | null> {
|
|
338
|
+
async translate(content: TranslationInput, sourceLng: string, targetLng: string): Promise<TranslationOutput | null> {
|
|
327
339
|
try {
|
|
328
340
|
// content example: { "0": "Hello", "1": "World" }
|
|
329
341
|
const texts = Object.values(content);
|
|
@@ -401,6 +413,9 @@ import type {
|
|
|
401
413
|
|
|
402
414
|
// Field config
|
|
403
415
|
FieldTranslationConfig,
|
|
416
|
+
|
|
417
|
+
// Translation levels
|
|
418
|
+
TranslationLevel,
|
|
404
419
|
} from "@focus-reactive/payload-plugin-translator";
|
|
405
420
|
```
|
|
406
421
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { ComponentPropsWithRef, ReactNode } from
|
|
2
|
-
type ButtonProps = ComponentPropsWithRef<
|
|
1
|
+
import type { ComponentPropsWithRef, ReactNode } from "react";
|
|
2
|
+
type ButtonProps = ComponentPropsWithRef<"button"> & {
|
|
3
3
|
$fullWidth?: boolean;
|
|
4
|
-
$variant?:
|
|
4
|
+
$variant?: "outlined" | "filled" | "unstyled" | "transparent" | "light";
|
|
5
5
|
$startContent?: ReactNode;
|
|
6
|
-
$size?:
|
|
6
|
+
$size?: "sm" | "md" | "lg";
|
|
7
7
|
$isLoading?: boolean;
|
|
8
8
|
$isIconButton?: boolean;
|
|
9
9
|
};
|
|
@@ -1,31 +1,31 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import classNames from
|
|
3
|
-
import { forwardRef } from
|
|
4
|
-
import Loading from
|
|
5
|
-
import styles from
|
|
2
|
+
import classNames from "classnames";
|
|
3
|
+
import { forwardRef } from "react";
|
|
4
|
+
import Loading from "../Loading";
|
|
5
|
+
import styles from "./styles.module.scss";
|
|
6
6
|
const sizes = {
|
|
7
7
|
sm: {
|
|
8
|
-
height:
|
|
9
|
-
paddingInline:
|
|
8
|
+
height: "24px",
|
|
9
|
+
paddingInline: "0.25rem"
|
|
10
10
|
},
|
|
11
11
|
md: {
|
|
12
|
-
height:
|
|
13
|
-
paddingInline:
|
|
12
|
+
height: "32px",
|
|
13
|
+
paddingInline: "0.5rem"
|
|
14
14
|
},
|
|
15
15
|
lg: {
|
|
16
|
-
height:
|
|
17
|
-
paddingInline:
|
|
16
|
+
height: "40px",
|
|
17
|
+
paddingInline: "0.75rem"
|
|
18
18
|
}
|
|
19
19
|
};
|
|
20
|
-
const Button = /*#__PURE__*/ forwardRef(function Button({ className =
|
|
20
|
+
const Button = /*#__PURE__*/ forwardRef(function Button({ className = "", children, $fullWidth, style, $startContent, $variant, $isLoading, $size = "md", $isIconButton, ...props }, ref) {
|
|
21
21
|
const _style = {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
"--button-width": $fullWidth ? "100%" : "auto",
|
|
23
|
+
"--height": sizes[$size].height,
|
|
24
|
+
"--padding-inline": $isIconButton ? "0" : sizes[$size].paddingInline,
|
|
25
|
+
"--width": $isIconButton ? sizes[$size].height : "auto",
|
|
26
26
|
...style
|
|
27
27
|
};
|
|
28
|
-
const buttonClassName = $variant ===
|
|
28
|
+
const buttonClassName = $variant === "unstyled" ? classNames(styles["unstyled"], className, $isIconButton && styles["icon-button"]) : classNames(styles["button"], className, $variant ? styles[$variant] : undefined, $isIconButton && styles["icon-button"]);
|
|
29
29
|
return /*#__PURE__*/ _jsxs("button", {
|
|
30
30
|
...props,
|
|
31
31
|
style: _style,
|
|
@@ -33,11 +33,11 @@ const Button = /*#__PURE__*/ forwardRef(function Button({ className = '', childr
|
|
|
33
33
|
className: buttonClassName,
|
|
34
34
|
children: [
|
|
35
35
|
$startContent && /*#__PURE__*/ _jsx("div", {
|
|
36
|
-
className: styles[
|
|
36
|
+
className: styles["start-content"],
|
|
37
37
|
children: $startContent
|
|
38
38
|
}),
|
|
39
39
|
$isLoading ? /*#__PURE__*/ _jsx(Loading, {
|
|
40
|
-
size: $isIconButton ?
|
|
40
|
+
size: $isIconButton ? "small" : "medium"
|
|
41
41
|
}) : children
|
|
42
42
|
]
|
|
43
43
|
});
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
justify-content: center;
|
|
8
8
|
align-items: center;
|
|
9
9
|
text-decoration: none;
|
|
10
|
-
transition: background 0.
|
|
10
|
+
transition: background 0.1s ease-in-out, opacity 0.2s ease-in-out, border-color 0.2s ease-in-out;
|
|
11
11
|
background: none;
|
|
12
12
|
padding-block: 0.25rem;
|
|
13
13
|
padding-inline: var(--padding-inline);
|
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
import type { PropsWithChildren, ReactElement } from
|
|
1
|
+
import type { PropsWithChildren, ReactElement } from "react";
|
|
2
2
|
type PopupProps = PropsWithChildren<{
|
|
3
3
|
$trigger: ReactElement;
|
|
4
|
-
$align?:
|
|
5
|
-
$side?:
|
|
4
|
+
$align?: "center" | "end" | "start";
|
|
5
|
+
$side?: "top" | "right" | "bottom" | "left";
|
|
6
6
|
open: boolean;
|
|
7
7
|
onOpenChange: (open: boolean) => void;
|
|
8
|
+
/**
|
|
9
|
+
* Forwarded to Radix `Popover.Content`. Call `event.preventDefault()` to stop the popover
|
|
10
|
+
* from auto-focusing its first focusable child on open — useful when that child is a
|
|
11
|
+
* tooltip trigger (Radix tooltips open on focus, so autofocus would flash the tooltip).
|
|
12
|
+
*/
|
|
13
|
+
onOpenAutoFocus?: (event: Event) => void;
|
|
8
14
|
}>;
|
|
9
|
-
declare function Popup({ open, children, onOpenChange, $trigger, $align, $side }: PopupProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
declare function Popup({ open, children, onOpenChange, $trigger, $align, $side, onOpenAutoFocus }: PopupProps): import("react/jsx-runtime").JSX.Element;
|
|
10
16
|
export default Popup;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import * as Popover from
|
|
3
|
-
import style from
|
|
4
|
-
function Popup({ open, children, onOpenChange, $trigger, $align, $side }) {
|
|
2
|
+
import * as Popover from "@radix-ui/react-popover";
|
|
3
|
+
import style from "./styles.module.scss";
|
|
4
|
+
function Popup({ open, children, onOpenChange, $trigger, $align, $side, onOpenAutoFocus }) {
|
|
5
5
|
return /*#__PURE__*/ _jsxs(Popover.Root, {
|
|
6
6
|
open: open,
|
|
7
7
|
onOpenChange: onOpenChange,
|
|
@@ -16,6 +16,7 @@ function Popup({ open, children, onOpenChange, $trigger, $align, $side }) {
|
|
|
16
16
|
side: $side,
|
|
17
17
|
align: $align,
|
|
18
18
|
className: style.popover,
|
|
19
|
+
onOpenAutoFocus: onOpenAutoFocus,
|
|
19
20
|
children: children
|
|
20
21
|
})
|
|
21
22
|
})
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import type { ComponentPropsWithoutRef, ComponentPropsWithRef } from
|
|
2
|
-
type SelectProps = ComponentPropsWithRef<
|
|
1
|
+
import type { ComponentPropsWithoutRef, ComponentPropsWithRef } from "react";
|
|
2
|
+
type SelectProps = ComponentPropsWithRef<"select"> & {
|
|
3
3
|
emptyOption?: boolean;
|
|
4
4
|
hasError?: boolean;
|
|
5
|
-
$size?:
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
$size?: "sm" | "md" | "lg";
|
|
6
|
+
/** Stretch to fill the container (default). Set `false` to size to content — useful inline. */
|
|
7
|
+
$fullWidth?: boolean;
|
|
8
|
+
"aria-describedby"?: string;
|
|
9
|
+
"aria-invalid"?: boolean;
|
|
8
10
|
};
|
|
9
|
-
type SelectOptionProps = ComponentPropsWithoutRef<
|
|
10
|
-
type SelectEmptyOptionProps = Omit<SelectOptionProps,
|
|
11
|
+
type SelectOptionProps = ComponentPropsWithoutRef<"option">;
|
|
12
|
+
type SelectEmptyOptionProps = Omit<SelectOptionProps, "value" | "disabled" | "hidden">;
|
|
11
13
|
declare const SelectOption: ({ children, ...props }: SelectOptionProps) => import("react/jsx-runtime").JSX.Element;
|
|
12
14
|
declare const SelectEmptyOption: ({ children, ...props }: SelectEmptyOptionProps) => import("react/jsx-runtime").JSX.Element;
|
|
13
15
|
declare const Select: import("react").ForwardRefExoticComponent<Omit<SelectProps, "ref"> & import("react").RefAttributes<HTMLSelectElement>>;
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import classNames from
|
|
3
|
-
import { forwardRef } from
|
|
4
|
-
import styles from
|
|
2
|
+
import classNames from "classnames";
|
|
3
|
+
import { forwardRef } from "react";
|
|
4
|
+
import styles from "./styles.module.scss";
|
|
5
5
|
const sizes = {
|
|
6
6
|
sm: {
|
|
7
|
-
height:
|
|
8
|
-
paddingInline:
|
|
9
|
-
fontSize:
|
|
7
|
+
height: "24px",
|
|
8
|
+
paddingInline: "0.25rem",
|
|
9
|
+
fontSize: "0.875rem"
|
|
10
10
|
},
|
|
11
11
|
md: {
|
|
12
|
-
height:
|
|
13
|
-
paddingInline:
|
|
14
|
-
fontSize:
|
|
12
|
+
height: "32px",
|
|
13
|
+
paddingInline: "0.5rem",
|
|
14
|
+
fontSize: "1rem"
|
|
15
15
|
},
|
|
16
16
|
lg: {
|
|
17
|
-
height:
|
|
18
|
-
paddingInline:
|
|
19
|
-
fontSize:
|
|
17
|
+
height: "40px",
|
|
18
|
+
paddingInline: "0.75rem",
|
|
19
|
+
fontSize: "1rem"
|
|
20
20
|
}
|
|
21
21
|
};
|
|
22
22
|
const SelectOption = ({ children, ...props })=>/*#__PURE__*/ _jsx("option", {
|
|
@@ -28,15 +28,16 @@ const SelectEmptyOption = ({ children, ...props })=>/*#__PURE__*/ _jsx("option",
|
|
|
28
28
|
...props,
|
|
29
29
|
children: children
|
|
30
30
|
});
|
|
31
|
-
const Select = /*#__PURE__*/ forwardRef(function Select({ children, className =
|
|
31
|
+
const Select = /*#__PURE__*/ forwardRef(function Select({ children, className = "", hasError = false, $size = "lg", $fullWidth = true, style, "aria-invalid": ariaInvalid, ...props }, ref) {
|
|
32
32
|
const _style = {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
"--height": sizes[$size].height,
|
|
34
|
+
"--padding-inline": sizes[$size].paddingInline,
|
|
35
|
+
"--font-size": sizes[$size].fontSize,
|
|
36
36
|
...style
|
|
37
37
|
};
|
|
38
38
|
const selectClassName = classNames(styles.select, styles[`size-${$size}`], {
|
|
39
|
-
[styles.selectError]: hasError
|
|
39
|
+
[styles.selectError]: hasError,
|
|
40
|
+
[styles.auto]: !$fullWidth
|
|
40
41
|
}, className);
|
|
41
42
|
return /*#__PURE__*/ _jsx("select", {
|
|
42
43
|
ref: ref,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
export { translatorPlugin } from
|
|
2
|
-
export type { TranslatorPluginConfig } from
|
|
3
|
-
export type { AccessGuard, AccessGuardRequest } from
|
|
4
|
-
export { createOpenAIProvider } from
|
|
5
|
-
export type { TranslationProvider, TranslationInput, TranslationOutput, OpenAIProviderConfig, DryRunConfig
|
|
6
|
-
export { createPayloadJobsRunner, createSyncRunner } from
|
|
7
|
-
export type { TaskRunnerProvider, PayloadJobsRunnerOptions } from
|
|
8
|
-
export {
|
|
9
|
-
export type {
|
|
10
|
-
export {
|
|
11
|
-
export type {
|
|
12
|
-
export {
|
|
13
|
-
export {
|
|
14
|
-
export
|
|
1
|
+
export { translatorPlugin } from "./plugin";
|
|
2
|
+
export type { TranslatorPluginConfig } from "./plugin";
|
|
3
|
+
export type { AccessGuard, AccessGuardRequest } from "./types/AccessGuard";
|
|
4
|
+
export { createOpenAIProvider } from "./server/modules/translation-providers";
|
|
5
|
+
export type { TranslationProvider, TranslationInput, TranslationOutput, OpenAIProviderConfig, DryRunConfig } from "./server/modules/translation-providers";
|
|
6
|
+
export { createPayloadJobsRunner, createSyncRunner } from "./server/modules/task-runner";
|
|
7
|
+
export type { TaskRunnerProvider, PayloadJobsRunnerOptions } from "./server/modules/task-runner";
|
|
8
|
+
export { documentLevel, collectionLevel } from "./server/modules/translation-levels";
|
|
9
|
+
export type { TranslationLevel } from "./server/modules/translation-levels";
|
|
10
|
+
export { withFieldTranslation } from "./field-config";
|
|
11
|
+
export type { FieldTranslationConfig } from "./field-config";
|
|
12
|
+
export { createTranslatePlugin, TranslateCollectionPlugin } from "./plugin";
|
|
13
|
+
export type { TranslateCollectionPluginConfig } from "./plugin";
|
|
14
|
+
export { OpenAITranslationProvider } from "./server/modules/translation-providers";
|
|
15
|
+
export { translateKitField } from "./field-config";
|
|
16
|
+
export type { TranslateKitFieldConfig } from "./field-config";
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
// Main plugin
|
|
2
|
-
export { translatorPlugin } from
|
|
2
|
+
export { translatorPlugin } from "./plugin";
|
|
3
3
|
// Translation providers
|
|
4
|
-
export { createOpenAIProvider } from
|
|
4
|
+
export { createOpenAIProvider } from "./server/modules/translation-providers";
|
|
5
5
|
// Task runners
|
|
6
|
-
export { createPayloadJobsRunner, createSyncRunner } from
|
|
6
|
+
export { createPayloadJobsRunner, createSyncRunner } from "./server/modules/task-runner";
|
|
7
|
+
// Translation levels
|
|
8
|
+
export { documentLevel, collectionLevel } from "./server/modules/translation-levels";
|
|
7
9
|
// Field config
|
|
8
|
-
export { withFieldTranslation } from
|
|
10
|
+
export { withFieldTranslation } from "./field-config";
|
|
9
11
|
// Deprecated exports (for backwards compatibility)
|
|
10
|
-
export { createTranslatePlugin, TranslateCollectionPlugin } from
|
|
11
|
-
export { OpenAITranslationProvider } from
|
|
12
|
-
export { translateKitField } from
|
|
12
|
+
export { createTranslatePlugin, TranslateCollectionPlugin } from "./plugin";
|
|
13
|
+
export { OpenAITranslationProvider } from "./server/modules/translation-providers";
|
|
14
|
+
export { translateKitField } from "./field-config";
|
|
13
15
|
|
|
14
16
|
//# sourceMappingURL=index.js.map
|
package/dist/plugin.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type { CollectionConfig, Config } from
|
|
2
|
-
import type { AccessGuard } from
|
|
3
|
-
import type { TranslationProvider } from
|
|
4
|
-
import type { TaskRunnerProvider } from
|
|
1
|
+
import type { CollectionConfig, Config } from "payload";
|
|
2
|
+
import type { AccessGuard } from "./types/AccessGuard";
|
|
3
|
+
import type { TranslationProvider } from "./server/modules/translation-providers";
|
|
4
|
+
import type { TaskRunnerProvider } from "./server/modules/task-runner";
|
|
5
|
+
import type { TranslationLevel } from "./server/modules/translation-levels";
|
|
5
6
|
export type TranslatorPluginConfig = {
|
|
6
7
|
/**
|
|
7
8
|
* Original collection configs (same objects passed to buildConfig).
|
|
@@ -32,6 +33,16 @@ export type TranslatorPluginConfig = {
|
|
|
32
33
|
* @default '/translate'
|
|
33
34
|
*/
|
|
34
35
|
basePath?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Which translation surfaces to enable, as level factories — `documentLevel()`,
|
|
38
|
+
* `collectionLevel()` (and, from a later phase, `fieldLevel()`). Omit for the
|
|
39
|
+
* default `[documentLevel(), collectionLevel()]`, which is exactly today's behaviour.
|
|
40
|
+
* Each level should appear at most once: endpoints are deduplicated, but admin
|
|
41
|
+
* components are not, so a duplicated level renders a duplicated control.
|
|
42
|
+
* @default [documentLevel(), collectionLevel()]
|
|
43
|
+
* @since 0.5.0
|
|
44
|
+
*/
|
|
45
|
+
levels?: TranslationLevel[];
|
|
35
46
|
};
|
|
36
47
|
/** @deprecated Use `TranslatorPluginConfig` instead */
|
|
37
48
|
export type TranslateCollectionPluginConfig = TranslatorPluginConfig;
|
package/dist/plugin.js
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
|
-
import { CacheProviderExport } from
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { createRunRoute } from './server/features/run-translation';
|
|
7
|
-
import { createCancelRoute } from './server/features/cancel';
|
|
8
|
-
import { createCancelByCollectionRoute } from './server/features/cancel-by-collection';
|
|
9
|
-
import { createGetDocumentStatusRoute } from './server/features/get-document-status';
|
|
10
|
-
import { createGetCollectionStatusRoute } from './server/features/get-collection-status';
|
|
11
|
-
import { normalizePath, pipe } from './server/shared';
|
|
1
|
+
import { CacheProviderExport } from "./client/app/cache/CacheProvider.export";
|
|
2
|
+
import { TranslateDocumentHandler } from "./server/features/translate-document";
|
|
3
|
+
import { documentLevel, collectionLevel } from "./server/modules/translation-levels";
|
|
4
|
+
import { PluginConfigBuilder } from "./server/modules/translation-levels/PluginConfigBuilder";
|
|
5
|
+
import { normalizePath } from "./server/shared";
|
|
12
6
|
/** @deprecated Use `translatorPlugin` function instead */ export class TranslateCollectionPlugin {
|
|
13
7
|
pluginConfig;
|
|
14
8
|
constructor(pluginConfig){
|
|
@@ -16,7 +10,7 @@ import { normalizePath, pipe } from './server/shared';
|
|
|
16
10
|
}
|
|
17
11
|
init() {
|
|
18
12
|
return async (config)=>{
|
|
19
|
-
const { access, translationProvider, basePath: rawBasePath =
|
|
13
|
+
const { access, translationProvider, runner, collections, levels, basePath: rawBasePath = "/translate" } = this.pluginConfig;
|
|
20
14
|
// Build schema map from deep-cloned collections
|
|
21
15
|
// Deep clone is required because Payload mutates the original collection objects,
|
|
22
16
|
// removing `localized: true` from nested fields during sanitization.
|
|
@@ -25,14 +19,14 @@ import { normalizePath, pipe } from './server/shared';
|
|
|
25
19
|
// TODO: Consider introducing a FieldLike interface with only the properties
|
|
26
20
|
// used by the pipeline (name, type, localized, fields, blocks, tabs, custom)
|
|
27
21
|
// to make the contract explicit and avoid reliance on JSON round-trip.
|
|
28
|
-
const schemaMap = new Map(
|
|
22
|
+
const schemaMap = new Map(collections.map((col)=>[
|
|
29
23
|
col.slug,
|
|
30
24
|
JSON.parse(JSON.stringify(col.fields))
|
|
31
25
|
]));
|
|
32
26
|
const collectionSlugs = new Set(schemaMap.keys());
|
|
33
27
|
const basePath = normalizePath(rawBasePath);
|
|
34
28
|
const translateHandler = new TranslateDocumentHandler(translationProvider, schemaMap);
|
|
35
|
-
const
|
|
29
|
+
const runnerContext = {
|
|
36
30
|
handler: async (payload, input)=>{
|
|
37
31
|
await translateHandler.handle(payload, {
|
|
38
32
|
collection: input.collection,
|
|
@@ -44,50 +38,33 @@ import { normalizePath, pipe } from './server/shared';
|
|
|
44
38
|
});
|
|
45
39
|
},
|
|
46
40
|
collections: Array.from(collectionSlugs)
|
|
41
|
+
};
|
|
42
|
+
const runnerConfigModifier = runner.configure(runnerContext);
|
|
43
|
+
// Bind the context once so routes receive a self-sufficient factory: the
|
|
44
|
+
// runner needs no mutable per-instance handler state and create() has no
|
|
45
|
+
// "configure() must run first" ordering coupling (translator plan, 0c).
|
|
46
|
+
const taskRunnerFactory = {
|
|
47
|
+
create: (payload)=>runner.create(payload, runnerContext.handler)
|
|
48
|
+
};
|
|
49
|
+
const activeLevels = levels ?? [
|
|
50
|
+
documentLevel(),
|
|
51
|
+
collectionLevel()
|
|
52
|
+
];
|
|
53
|
+
const builder = new PluginConfigBuilder({
|
|
54
|
+
collections,
|
|
55
|
+
basePath,
|
|
56
|
+
access,
|
|
57
|
+
taskRunnerFactory
|
|
47
58
|
});
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
(
|
|
57
|
-
cfg.collections?.forEach((collection)=>{
|
|
58
|
-
if (!collectionSlugs.has(collection.slug)) {
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
if (!collection.admin) collection.admin = {};
|
|
62
|
-
if (!collection.admin.components) collection.admin.components = {};
|
|
63
|
-
if (!collection.admin.components.edit) collection.admin.components.edit = {};
|
|
64
|
-
if (!collection.admin.components.edit.beforeDocumentControls) {
|
|
65
|
-
collection.admin.components.edit.beforeDocumentControls = [];
|
|
66
|
-
}
|
|
67
|
-
if (!collection.admin.components.beforeListTable) {
|
|
68
|
-
collection.admin.components.beforeListTable = [];
|
|
69
|
-
}
|
|
70
|
-
collection.admin.components.edit.beforeDocumentControls.push(new TranslateDocumentExport(collection, access));
|
|
71
|
-
collection.admin.components.beforeListTable.push(new BulkDocumentTranslationDashboard(access));
|
|
72
|
-
});
|
|
73
|
-
return cfg;
|
|
74
|
-
}, // 3. Runner configures jobs/tasks/autorun
|
|
75
|
-
runnerConfigModifier, // 4. Add global endpoints
|
|
76
|
-
(cfg)=>{
|
|
77
|
-
if (!cfg.endpoints) cfg.endpoints = [];
|
|
78
|
-
const collectionConfig = {
|
|
79
|
-
availableCollections: collectionSlugs
|
|
80
|
-
};
|
|
81
|
-
cfg.endpoints.push(...[
|
|
82
|
-
createEnqueueRoute(this.pluginConfig.runner, collectionConfig, access, basePath),
|
|
83
|
-
createRunRoute(this.pluginConfig.runner, access, basePath),
|
|
84
|
-
createCancelRoute(this.pluginConfig.runner, access, basePath),
|
|
85
|
-
createCancelByCollectionRoute(collectionConfig, this.pluginConfig.runner, access, basePath),
|
|
86
|
-
createGetDocumentStatusRoute(collectionConfig, this.pluginConfig.runner, access, basePath),
|
|
87
|
-
createGetCollectionStatusRoute(collectionConfig, this.pluginConfig.runner, access, basePath)
|
|
88
|
-
]);
|
|
89
|
-
return cfg;
|
|
90
|
-
})(config);
|
|
59
|
+
for (const level of activeLevels)level.extend(builder);
|
|
60
|
+
// Plugin-level contributions, routed through the same single config-writer:
|
|
61
|
+
// - the runner's jobs/autorun/onInit modifier (the builder applies it first,
|
|
62
|
+
// so a modifier returning a fresh config object doesn't drop later writes),
|
|
63
|
+
// - the always-on client cache provider.
|
|
64
|
+
builder.addConfigModifier(runnerConfigModifier);
|
|
65
|
+
builder.addAdminProvider(new CacheProviderExport(basePath));
|
|
66
|
+
// The single place the Payload config is mutated.
|
|
67
|
+
return builder.applyTo(config);
|
|
91
68
|
};
|
|
92
69
|
}
|
|
93
70
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { PayloadRequest } from "payload";
|
|
2
|
-
import type {
|
|
2
|
+
import type { TaskRunnerFactory } from "../../modules/task-runner";
|
|
3
3
|
/**
|
|
4
4
|
* Cancels and deletes translation tasks by IDs
|
|
5
5
|
*/
|
|
6
6
|
export declare class CancelHandler {
|
|
7
7
|
private readonly taskRunnerFactory;
|
|
8
|
-
constructor(taskRunnerFactory:
|
|
8
|
+
constructor(taskRunnerFactory: TaskRunnerFactory);
|
|
9
9
|
handle(req: PayloadRequest): Promise<Response>;
|
|
10
10
|
}
|
|
@@ -9,7 +9,9 @@ import { CancelInputSchema } from "./model";
|
|
|
9
9
|
}
|
|
10
10
|
async handle(req) {
|
|
11
11
|
const validationResult = CancelInputSchema.safeParse(await req.json?.());
|
|
12
|
-
if (validationResult.error)
|
|
12
|
+
if (validationResult.error) {
|
|
13
|
+
return ServerResponse.validationError(validationResult.error.issues);
|
|
14
|
+
}
|
|
13
15
|
const { ids } = validationResult.data;
|
|
14
16
|
const runner = this.taskRunnerFactory.create(req.payload);
|
|
15
17
|
await runner.cancel(ids);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { Endpoint } from
|
|
2
|
-
import type { AccessGuard } from
|
|
3
|
-
import type {
|
|
1
|
+
import type { Endpoint } from "payload";
|
|
2
|
+
import type { AccessGuard } from "../../shared";
|
|
3
|
+
import type { TaskRunnerFactory } from "../../modules/task-runner";
|
|
4
4
|
/**
|
|
5
5
|
* Creates the batch cancel endpoint
|
|
6
6
|
*/
|
|
7
|
-
export declare function createCancelRoute(taskRunnerFactory:
|
|
7
|
+
export declare function createCancelRoute(taskRunnerFactory: TaskRunnerFactory, access?: AccessGuard, basePath?: string): Endpoint;
|