@extravirgin/payload-enchants-translator 1.0.8 → 1.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 +231 -21
- package/dist/client/components/CustomButton/CustomButtonWithTranslator.d.ts +2 -2
- package/dist/client/components/CustomButton/CustomButtonWithTranslator.d.ts.map +1 -1
- package/dist/client/components/CustomButton/CustomButtonWithTranslator.js +1 -1
- package/dist/client/components/CustomButton/CustomButtonWithTranslator.js.map +1 -1
- package/dist/client/components/CustomButton/index.js +2 -2
- package/dist/client/components/CustomButton/index.js.map +1 -1
- package/dist/client/components/GlossaryControls/index.d.ts +4 -0
- package/dist/client/components/GlossaryControls/index.d.ts.map +1 -0
- package/dist/client/components/GlossaryControls/index.js +42 -0
- package/dist/client/components/GlossaryControls/index.js.map +1 -0
- package/dist/client/providers/Translator/TranslatorProvider.d.ts.map +1 -1
- package/dist/client/providers/Translator/TranslatorProvider.js +2 -1
- package/dist/client/providers/Translator/TranslatorProvider.js.map +1 -1
- package/dist/exports/client.d.ts +1 -0
- package/dist/exports/client.d.ts.map +1 -1
- package/dist/exports/client.js +1 -0
- package/dist/exports/client.js.map +1 -1
- package/dist/glossary.d.ts +17 -0
- package/dist/glossary.d.ts.map +1 -0
- package/dist/glossary.js +52 -0
- package/dist/glossary.js.map +1 -0
- package/dist/glossaryCollection.d.ts +5 -0
- package/dist/glossaryCollection.d.ts.map +1 -0
- package/dist/glossaryCollection.js +114 -0
- package/dist/glossaryCollection.js.map +1 -0
- package/dist/i18n-translations.d.ts +84 -0
- package/dist/i18n-translations.d.ts.map +1 -1
- package/dist/i18n-translations.js +83 -0
- package/dist/i18n-translations.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +53 -8
- package/dist/index.js.map +1 -1
- package/dist/resolvers/deepl.d.ts +12 -0
- package/dist/resolvers/deepl.d.ts.map +1 -0
- package/dist/resolvers/deepl.js +82 -0
- package/dist/resolvers/deepl.js.map +1 -0
- package/dist/resolvers/deeplGlossary.d.ts +19 -0
- package/dist/resolvers/deeplGlossary.d.ts.map +1 -0
- package/dist/resolvers/deeplGlossary.js +117 -0
- package/dist/resolvers/deeplGlossary.js.map +1 -0
- package/dist/resolvers/types.d.ts +3 -0
- package/dist/resolvers/types.d.ts.map +1 -1
- package/dist/resolvers/types.js.map +1 -1
- package/dist/translate/endpoint.d.ts.map +1 -1
- package/dist/translate/endpoint.js +2 -1
- package/dist/translate/endpoint.js.map +1 -1
- package/dist/translate/findEntityWithConfig.d.ts.map +1 -1
- package/dist/translate/findEntityWithConfig.js +6 -5
- package/dist/translate/findEntityWithConfig.js.map +1 -1
- package/dist/translate/operation.d.ts.map +1 -1
- package/dist/translate/operation.js +5 -1
- package/dist/translate/operation.js.map +1 -1
- package/dist/translate/traverseFields.d.ts.map +1 -1
- package/dist/translate/traverseFields.js +16 -1
- package/dist/translate/traverseFields.js.map +1 -1
- package/dist/translate/updateEntity.d.ts.map +1 -1
- package/dist/translate/updateEntity.js +3 -2
- package/dist/translate/updateEntity.js.map +1 -1
- package/dist/types.d.ts +10 -8
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +53 -22
package/README.md
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
|
-
# Translator plugin for Payload 3
|
|
1
|
+
# Translator plugin for Payload 3
|
|
2
2
|
|
|
3
3
|
## Install
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
```sh
|
|
6
|
+
pnpm add @extravirgin/payload-enchants-translator
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
When using the DeepL resolver, also install its optional peer dependency:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
pnpm add deepl-node@^1.27.0
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Use Payload 3 with localization enabled and at least two content locales. Register the collections and globals listed in the plugin options in your Payload config.
|
|
6
16
|
|
|
7
17
|
## Video
|
|
8
18
|
|
|
@@ -10,25 +20,38 @@ https://github.com/r1tsuu/payload-plugin-translator/assets/64744993/d39aeba4-baf
|
|
|
10
20
|
|
|
11
21
|
## Features:
|
|
12
22
|
|
|
13
|
-
1. A flexible structure with [resolvers](
|
|
23
|
+
1. A flexible structure with [resolvers](./src/resolvers) that allows you to apply any kind of transformation to your localized data.
|
|
14
24
|
2. Can be used not only from the admin panel, but within Local API as well. [Example of the hook](#example-of-the-hook-that-uses-local-operation-to-copy-the-doc-data-to-other-locales) that automatically fills the other locales data on create
|
|
15
|
-
3.
|
|
25
|
+
3. Includes Copy, DeepL, Google Translate, LibreTranslate, and OpenAI resolvers, with support for custom resolvers.
|
|
16
26
|
4. Works with any nested document structure and 2 Rich Text editor adapters - Lexical and Slate.
|
|
17
|
-
5. You can omit
|
|
27
|
+
5. You can omit fields from translation. [Documentation](#omitting-fields)
|
|
28
|
+
6. Optional DeepL glossary: synchronize individual entries on save and synchronize the full list manually.
|
|
29
|
+
7. English and German admin UI messages, following the current UI language.
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
18
32
|
|
|
19
|
-
|
|
33
|
+
Enable the resolvers your project needs. The glossary is optional; the following configuration does not register a glossary collection or sync endpoint.
|
|
20
34
|
|
|
21
35
|
```ts
|
|
22
|
-
import { buildConfig } from 'payload
|
|
36
|
+
import { buildConfig } from 'payload';
|
|
37
|
+
import { en } from '@payloadcms/translations/languages/en';
|
|
38
|
+
import { de } from '@payloadcms/translations/languages/de';
|
|
23
39
|
import {
|
|
24
40
|
translator,
|
|
25
41
|
copyResolver,
|
|
26
42
|
googleResolver,
|
|
27
43
|
openAIResolver,
|
|
28
44
|
libreResolver,
|
|
29
|
-
} from '@payload-enchants
|
|
45
|
+
} from '@extravirgin/payload-enchants-translator';
|
|
30
46
|
|
|
31
47
|
export default buildConfig({
|
|
48
|
+
localization: {
|
|
49
|
+
defaultLocale: 'en',
|
|
50
|
+
locales: ['en', 'de'],
|
|
51
|
+
},
|
|
52
|
+
i18n: {
|
|
53
|
+
supportedLanguages: { en, de },
|
|
54
|
+
},
|
|
32
55
|
plugins: [
|
|
33
56
|
translator({
|
|
34
57
|
// collections with the enabled translator in the admin UI
|
|
@@ -55,6 +78,131 @@ export default buildConfig({
|
|
|
55
78
|
|
|
56
79
|
## Resolvers
|
|
57
80
|
|
|
81
|
+
### DeepL without a glossary
|
|
82
|
+
|
|
83
|
+
For translation only, omit `glossary` from both `translator()` and `deeplResolver()`, and omit `glossaryId`. Only `DEEPL_API_KEY` is needed; `DEEPL_GLOSSARY_ID` is not required.
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import { deeplResolver, translator } from '@extravirgin/payload-enchants-translator';
|
|
87
|
+
|
|
88
|
+
export const translatorPlugin = translator({
|
|
89
|
+
collections: ['pages', 'news'],
|
|
90
|
+
globals: ['global-settings'],
|
|
91
|
+
resolvers: [
|
|
92
|
+
deeplResolver({
|
|
93
|
+
apiKey: process.env.DEEPL_API_KEY!,
|
|
94
|
+
targetLocaleMap: { en: 'en-GB' },
|
|
95
|
+
}),
|
|
96
|
+
],
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Add `translatorPlugin` to your Payload `plugins` array. Normal translation works without registering glossary fields, collections, controls, or sync endpoints.
|
|
101
|
+
|
|
102
|
+
The DeepL resolver accepts:
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
export type DeepLResolverConfig = {
|
|
106
|
+
apiKey: string;
|
|
107
|
+
chunkLength?: number; // Concurrent text batch size; default: 100
|
|
108
|
+
glossary?: GlossaryConfig;
|
|
109
|
+
glossaryId?: string; // Existing DeepL multilingual glossary ID
|
|
110
|
+
sourceLocaleMap?: Record<string, string>;
|
|
111
|
+
targetLocaleMap?: Record<string, string>;
|
|
112
|
+
};
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Locale maps convert CMS locale codes to provider codes. For example, `en: 'en-GB'` selects British English for text translation; glossary language pairs use the base language `en`.
|
|
116
|
+
|
|
117
|
+
### DeepL with an optional glossary
|
|
118
|
+
|
|
119
|
+
Configure `DEEPL_API_KEY` and `DEEPL_GLOSSARY_ID` on the server. The glossary ID must identify an existing DeepL multilingual glossary accessible with that API key. The plugin does not create one automatically.
|
|
120
|
+
|
|
121
|
+
Pass the same glossary configuration to the plugin and resolver:
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
import type { Access } from 'payload';
|
|
125
|
+
import type { GlossaryConfig } from '@extravirgin/payload-enchants-translator';
|
|
126
|
+
import { deeplResolver, translator } from '@extravirgin/payload-enchants-translator';
|
|
127
|
+
|
|
128
|
+
// Replace this example with your project's admin/editor access policy.
|
|
129
|
+
const authenticated: Access = ({ req }) => Boolean(req.user);
|
|
130
|
+
|
|
131
|
+
const glossary: GlossaryConfig = {
|
|
132
|
+
slug: 'translation-glossary',
|
|
133
|
+
sourceLocale: 'de',
|
|
134
|
+
targetLocales: ['fr', 'it', 'en'],
|
|
135
|
+
access: { read: authenticated, update: authenticated },
|
|
136
|
+
localeLabels: {
|
|
137
|
+
de: { en: 'German (source)', de: 'Deutsch (Quelle)' },
|
|
138
|
+
fr: { en: 'French', de: 'Französisch' },
|
|
139
|
+
it: { en: 'Italian', de: 'Italienisch' },
|
|
140
|
+
en: { en: 'English', de: 'Englisch' },
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export const translatorPlugin = translator({
|
|
145
|
+
collections: ['pages', 'news'],
|
|
146
|
+
globals: ['global-settings'],
|
|
147
|
+
glossary,
|
|
148
|
+
resolvers: [
|
|
149
|
+
deeplResolver({
|
|
150
|
+
apiKey: process.env.DEEPL_API_KEY!,
|
|
151
|
+
glossary,
|
|
152
|
+
glossaryId: process.env.DEEPL_GLOSSARY_ID!,
|
|
153
|
+
targetLocaleMap: { en: 'en-GB' },
|
|
154
|
+
}),
|
|
155
|
+
],
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Configure the corresponding content locales in Payload. After adding the plugin or changing its schema, run your project's Payload migration, type-generation, and import-map workflow.
|
|
160
|
+
|
|
161
|
+
#### Storage and validation
|
|
162
|
+
|
|
163
|
+
- Entries use a collection named `<slug>-records`, or `translation-glossary-records` with the default slug. There is no glossary global or sync-state collection.
|
|
164
|
+
- The source language is required. In the example, German is required and French, Italian, and English are optional.
|
|
165
|
+
- Terms are trimmed, must be single-line without tabs, and may contain at most 1,024 UTF-8 bytes.
|
|
166
|
+
- Source terms must be unique after trimming, Unicode normalization, and case normalization.
|
|
167
|
+
- `access.read` controls reading. `access.update` controls creating, editing, and deleting entries. Synchronization requires both callbacks to return `true`.
|
|
168
|
+
|
|
169
|
+
#### Saving an entry
|
|
170
|
+
|
|
171
|
+
Creating or editing an entry sends only that item's populated translations to DeepL. Blank target languages are skipped. Other Payload glossary entries are neither read nor uploaded.
|
|
172
|
+
|
|
173
|
+
Every save resends the item's populated translations, including unchanged ones. A German-only entry sends no dictionary updates. Saves wait for DeepL; synchronization errors are reported as save failures. There is no background worker, job queue, or automatic synchronization retry.
|
|
174
|
+
|
|
175
|
+
#### Manual full synchronization
|
|
176
|
+
|
|
177
|
+
The **Sync all to DeepL** button appears at the top-right of the glossary list. Its tooltip explains when full synchronization is needed.
|
|
178
|
+
|
|
179
|
+
| Action | On save | After Sync all |
|
|
180
|
+
| ---------------------------------------- | ---------------------------------------------------------------- | ----------------------------------------- |
|
|
181
|
+
| Add an item or edit a target translation | Merges that item's populated translations | Replaces the full configured dictionaries |
|
|
182
|
+
| Rename the source term | Adds the new term; the old term remains in DeepL | Removes the old term |
|
|
183
|
+
| Clear a target translation | Skips the empty value; the previous translation remains in DeepL | Removes the previous translation |
|
|
184
|
+
| Delete an item | No DeepL request | Removes the deleted item from DeepL |
|
|
185
|
+
|
|
186
|
+
Full synchronization reads all saved entries in pages of 1,000, regardless of the list's current filters or page. It replaces each configured source/target dictionary and removes configured dictionaries that have no remaining entries. Unrelated language pairs in the same DeepL glossary are preserved. The uploaded glossary data is limited to 10 MiB.
|
|
187
|
+
|
|
188
|
+
The button is disabled while syncing, then displays a success or error notification. Avoid editing entries while full synchronization runs. DeepL updates are not atomic across languages: a failed operation may already have updated some dictionaries. Retry the item save or use Sync all to reconcile DeepL with the saved Payload entries.
|
|
189
|
+
|
|
190
|
+
The manual action uses `POST <basePath><apiRoute>/translator/glossary/sync`, where the default Payload API route is `/api`.
|
|
191
|
+
|
|
192
|
+
#### Using the glossary during translation
|
|
193
|
+
|
|
194
|
+
The resolver attaches the configured glossary when translating from its source locale to a configured target locale and at least one saved entry has a translation for that target. If there are no populated terms for that target, translation proceeds without a glossary. Other language directions also translate without it.
|
|
195
|
+
|
|
196
|
+
Translating content does not synchronize the glossary. Run Sync all once to upload existing entries, and again after source renames, cleared translations, or deletions. In the admin editor, translated content still needs to be saved; both “Translate all” and “Translate only empty fields” are available.
|
|
197
|
+
|
|
198
|
+
To omit the glossary feature entirely, remove `glossary` from both configurations and remove `glossaryId`. Setting `disabled: true` alone does not remove an explicitly configured glossary collection.
|
|
199
|
+
|
|
200
|
+
### Admin UI language
|
|
201
|
+
|
|
202
|
+
The plugin includes English and German labels, buttons, tooltips, validation messages, and user-facing errors. They follow the current admin UI language, independently of the document's content locale. Enable `en` and `de` under Payload's `i18n.supportedLanguages`, as shown above.
|
|
203
|
+
|
|
204
|
+
Project translations under `i18n.translations[language]['plugin-translator']` override the plugin defaults. Detailed provider failures are logged on the server; glossary controls display localized error messages.
|
|
205
|
+
|
|
58
206
|
### OpenAI
|
|
59
207
|
|
|
60
208
|
#### Config:
|
|
@@ -62,9 +210,10 @@ export default buildConfig({
|
|
|
62
210
|
```ts
|
|
63
211
|
export type OpenAIResolverConfig = {
|
|
64
212
|
apiKey: string; // API key
|
|
213
|
+
baseUrl?: string; // Optional API base URL
|
|
65
214
|
chunkLength?: number; // How many texts to include into 1 request, default: 100
|
|
66
215
|
model?: string; // model, default: 'gpt-3.5-turbo'
|
|
67
|
-
|
|
216
|
+
prompt?: OpenAIPrompt; // custom prompt
|
|
68
217
|
};
|
|
69
218
|
```
|
|
70
219
|
|
|
@@ -78,7 +227,7 @@ export type OpenAIPrompt = (args: {
|
|
|
78
227
|
}) => string;
|
|
79
228
|
|
|
80
229
|
// Default
|
|
81
|
-
const
|
|
230
|
+
const defaultPrompt: OpenAIPrompt = ({ localeFrom, localeTo, texts }) => {
|
|
82
231
|
return `Translate me the following array: ${JSON.stringify(texts)} in locale=${localeFrom} to locale ${localeTo}, respond me with the same array structure`;
|
|
83
232
|
};
|
|
84
233
|
```
|
|
@@ -90,14 +239,17 @@ const defaultPromt: OpenAIPrompt = ({ localeFrom, localeTo, texts }) => {
|
|
|
90
239
|
```ts
|
|
91
240
|
export type GoogleResolverConfig = {
|
|
92
241
|
apiKey: string; // API key
|
|
93
|
-
chunkLength?: number;
|
|
242
|
+
chunkLength?: number; // How many texts to include into 1 request, default: 100
|
|
94
243
|
};
|
|
95
244
|
```
|
|
96
245
|
|
|
97
246
|
### Writing your own
|
|
98
247
|
|
|
99
248
|
```ts
|
|
100
|
-
import
|
|
249
|
+
import { buildConfig } from 'payload';
|
|
250
|
+
import { en } from '@payloadcms/translations/languages/en';
|
|
251
|
+
import { translator } from '@extravirgin/payload-enchants-translator';
|
|
252
|
+
import type { TranslateResolver } from '@extravirgin/payload-enchants-translator/resolvers/types';
|
|
101
253
|
|
|
102
254
|
const myResolver: TranslateResolver = {
|
|
103
255
|
key: 'my',
|
|
@@ -115,7 +267,7 @@ const myResolver: TranslateResolver = {
|
|
|
115
267
|
|
|
116
268
|
export default buildConfig({
|
|
117
269
|
plugins: [
|
|
118
|
-
|
|
270
|
+
translator({
|
|
119
271
|
collections: ['posts', 'small-posts'],
|
|
120
272
|
globals: [],
|
|
121
273
|
resolvers: [myResolver],
|
|
@@ -127,7 +279,7 @@ export default buildConfig({
|
|
|
127
279
|
translations: {
|
|
128
280
|
en: {
|
|
129
281
|
'plugin-translator': {
|
|
130
|
-
resolver_my_buttonLabel: '
|
|
282
|
+
resolver_my_buttonLabel: 'Custom translation',
|
|
131
283
|
resolver_my_errorMessage: 'An error occurred when trying to translate the data',
|
|
132
284
|
resolver_my_modalTitle: 'Choose the locale to translate from',
|
|
133
285
|
resolver_my_submitButtonLabelEmpty: 'Translate only empty fields',
|
|
@@ -143,7 +295,7 @@ export default buildConfig({
|
|
|
143
295
|
## Using the Local API
|
|
144
296
|
|
|
145
297
|
```ts
|
|
146
|
-
import { translateOperation } from '@payload-enchants
|
|
298
|
+
import { translateOperation } from '@extravirgin/payload-enchants-translator';
|
|
147
299
|
|
|
148
300
|
const translateResult = await translateOperation({
|
|
149
301
|
collectionSlug: 'posts', // or globalSlug if globals,
|
|
@@ -151,20 +303,21 @@ const translateResult = await translateOperation({
|
|
|
151
303
|
id: postDefaultLocale.id, // pass the doc id if it's a collection
|
|
152
304
|
locale: 'de', // locale to translate to
|
|
153
305
|
localeFrom: 'en', // locale to translate from
|
|
154
|
-
|
|
155
|
-
req, // PayloadRequest, can be used instead of payload, more appreciable than payload if you have it
|
|
306
|
+
req, // Prefer the current PayloadRequest to preserve user, UI language, and transaction context
|
|
156
307
|
overrideAccess: false, //
|
|
157
308
|
resolver: 'copy', // pass resolver key
|
|
158
309
|
update: true, // optional, should update immediately or just return the translated result, default false
|
|
159
|
-
data:
|
|
310
|
+
// data: destinationFormData, // Optional destination data; the source is read from localeFrom
|
|
160
311
|
});
|
|
161
312
|
```
|
|
162
313
|
|
|
314
|
+
For calls without an existing request, `payload` can be passed instead of `req`. When using glossary access rules based on the current user, pass an authenticated `req`.
|
|
315
|
+
|
|
163
316
|
### Example of the hook that uses local operation to copy the doc data to other locales
|
|
164
317
|
|
|
165
318
|
```ts
|
|
166
|
-
import type { CollectionAfterChangeHook } from 'payload
|
|
167
|
-
import { translateOperation } from '@payload-enchants
|
|
319
|
+
import type { CollectionAfterChangeHook } from 'payload';
|
|
320
|
+
import { translateOperation } from '@extravirgin/payload-enchants-translator';
|
|
168
321
|
|
|
169
322
|
export const copyOtherLocales: CollectionAfterChangeHook = async ({
|
|
170
323
|
collection,
|
|
@@ -201,7 +354,7 @@ export const copyOtherLocales: CollectionAfterChangeHook = async ({
|
|
|
201
354
|
|
|
202
355
|
### Omitting fields
|
|
203
356
|
|
|
204
|
-
To omit a specific field for translation,
|
|
357
|
+
To omit a specific field for translation, add `custom.translatorSkip = true` to the field's config. This applies to every field type, including rich text. On groups, arrays, blocks or layout fields, it excludes the entire subtree. Skipped content is neither copied nor collected for the translation resolver.
|
|
205
358
|
|
|
206
359
|
```ts
|
|
207
360
|
const field = {
|
|
@@ -213,3 +366,60 @@ const field = {
|
|
|
213
366
|
type: 'text',
|
|
214
367
|
};
|
|
215
368
|
```
|
|
369
|
+
|
|
370
|
+
## Standalone development
|
|
371
|
+
|
|
372
|
+
This repository contains only the translator package extracted from
|
|
373
|
+
[Kliqs/payload-enchants](https://github.com/Kliqs/payload-enchants), originally
|
|
374
|
+
by [r1tsuu](https://github.com/r1tsuu/payload-enchants). The npm name and public
|
|
375
|
+
entry points are unchanged. See [MIGRATION.md](./MIGRATION.md) for provenance
|
|
376
|
+
and the extraction review.
|
|
377
|
+
|
|
378
|
+
Use Node.js 22 and pnpm 10.11.0 (via Corepack):
|
|
379
|
+
|
|
380
|
+
```sh
|
|
381
|
+
corepack enable
|
|
382
|
+
pnpm install --frozen-lockfile
|
|
383
|
+
pnpm lint
|
|
384
|
+
pnpm typecheck
|
|
385
|
+
pnpm test
|
|
386
|
+
pnpm clean && pnpm build
|
|
387
|
+
pnpm pack --pack-destination artifacts
|
|
388
|
+
pnpm --dir playground install --frozen-lockfile
|
|
389
|
+
pnpm --dir playground test
|
|
390
|
+
pnpm --dir playground generate:importmap
|
|
391
|
+
pnpm --dir playground typecheck
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
The playground installs the packed tarball, rather than importing source
|
|
395
|
+
through workspace aliases. After rebuilding and repacking a changed plugin,
|
|
396
|
+
run `pnpm --dir playground update @extravirgin/payload-enchants-translator`
|
|
397
|
+
to refresh the installed archive and its integrity in the consumer lockfile. Its smoke test starts a temporary MongoDB with generated sample
|
|
398
|
+
content and removes the database when finished; first run downloads MongoDB.
|
|
399
|
+
It requires no translation-provider credentials.
|
|
400
|
+
|
|
401
|
+
For manual admin testing, start a local MongoDB on port 27017 and run
|
|
402
|
+
`pnpm dev`. Open `http://localhost:3000/admin`, create a local test account,
|
|
403
|
+
create an English post, save it, switch to German and try both copy actions.
|
|
404
|
+
Also test the Settings global. The development server binds to localhost.
|
|
405
|
+
The default session secret is generated per process; sessions expire when
|
|
406
|
+
the server restarts. Use configured environment variables for a persistent
|
|
407
|
+
secret or a different MongoDB URL; do not commit credentials.
|
|
408
|
+
|
|
409
|
+
The playground deliberately enables only the Copy resolver. Live DeepL,
|
|
410
|
+
Google, OpenAI and glossary synchronization require explicit provider setup
|
|
411
|
+
and separate manual checks. Glossary validation is covered by unit tests.
|
|
412
|
+
|
|
413
|
+
### Releases
|
|
414
|
+
|
|
415
|
+
CI checks the package but never publishes it. After review, update the
|
|
416
|
+
package version and the playground tarball dependency together, rebuild,
|
|
417
|
+
repack and regenerate both lockfiles. Publish with `pnpm publish` only when
|
|
418
|
+
a release is explicitly approved. The extraction baseline was version 1.1.0; use a new version for each npm release.
|
|
419
|
+
|
|
420
|
+
### Upstream updates
|
|
421
|
+
|
|
422
|
+
GitHub's fork sync does not apply to this extracted repository. Inspect
|
|
423
|
+
changes under `packages/translator` in the original monorepo, apply relevant
|
|
424
|
+
changes on a feature branch, and rerun the package and playground checks.
|
|
425
|
+
Root-level dependency and tooling changes must be assessed separately.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import './styles.scss';
|
|
2
|
-
export declare const CustomButtonWithTranslator: ({
|
|
3
|
-
type: "publish" | "save";
|
|
2
|
+
export declare const CustomButtonWithTranslator: ({ disabledLocales, type }: {
|
|
4
3
|
disabledLocales: string[];
|
|
4
|
+
type: "publish" | "save";
|
|
5
5
|
}) => import("react").JSX.Element;
|
|
6
6
|
//# sourceMappingURL=CustomButtonWithTranslator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CustomButtonWithTranslator.d.ts","sourceRoot":"","sources":["../../../../src/client/components/CustomButton/CustomButtonWithTranslator.tsx"],"names":[],"mappings":"AAEA,OAAO,eAAe,CAAC;AASvB,eAAO,MAAM,0BAA0B,8BAA+B;IAAE,
|
|
1
|
+
{"version":3,"file":"CustomButtonWithTranslator.d.ts","sourceRoot":"","sources":["../../../../src/client/components/CustomButton/CustomButtonWithTranslator.tsx"],"names":[],"mappings":"AAEA,OAAO,eAAe,CAAC;AASvB,eAAO,MAAM,0BAA0B,8BAA+B;IAAE,eAAe,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAAA;CAAE,gCAyB5H,CAAC"}
|
|
@@ -5,7 +5,7 @@ import { PublishButton, SaveButton, useConfig, useDocumentInfo, useLocale } from
|
|
|
5
5
|
import { TranslatorProvider } from '../../providers/Translator/TranslatorProvider';
|
|
6
6
|
import { ResolverButton } from '../ResolverButton';
|
|
7
7
|
import { TranslatorModal } from '../TranslatorModal';
|
|
8
|
-
export const CustomButtonWithTranslator = ({
|
|
8
|
+
export const CustomButtonWithTranslator = ({ disabledLocales, type })=>{
|
|
9
9
|
const { config } = useConfig();
|
|
10
10
|
const locale = useLocale();
|
|
11
11
|
const DefaultButton = type === 'publish' ? PublishButton : SaveButton;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/client/components/CustomButton/CustomButtonWithTranslator.tsx"],"sourcesContent":["'use client';\n\nimport './styles.scss';\n\nimport { PublishButton, SaveButton, useConfig, useDocumentInfo, useLocale } from '@payloadcms/ui';\n\nimport type { TranslateResolver } from '../../../resolvers/types';\nimport { TranslatorProvider } from '../../providers/Translator/TranslatorProvider';\nimport { ResolverButton } from '../ResolverButton';\nimport { TranslatorModal } from '../TranslatorModal';\n\nexport const CustomButtonWithTranslator = ({
|
|
1
|
+
{"version":3,"sources":["../../../../src/client/components/CustomButton/CustomButtonWithTranslator.tsx"],"sourcesContent":["'use client';\n\nimport './styles.scss';\n\nimport { PublishButton, SaveButton, useConfig, useDocumentInfo, useLocale } from '@payloadcms/ui';\n\nimport type { TranslateResolver } from '../../../resolvers/types';\nimport { TranslatorProvider } from '../../providers/Translator/TranslatorProvider';\nimport { ResolverButton } from '../ResolverButton';\nimport { TranslatorModal } from '../TranslatorModal';\n\nexport const CustomButtonWithTranslator = ({ disabledLocales, type }: { disabledLocales: string[], type: 'publish' | 'save' }) => {\n const { config } = useConfig();\n const locale = useLocale();\n\n const DefaultButton = type === 'publish' ? PublishButton : SaveButton;\n\n const { globalSlug, id } = useDocumentInfo();\n\n const resolvers = (config.admin?.custom?.translator?.resolvers as TranslateResolver[]) ?? [];\n\n if (!id && !globalSlug) return <DefaultButton />;\n\n if(disabledLocales.includes(locale.code)) return <DefaultButton />;\n\n return (\n <TranslatorProvider>\n <div className={'translator__custom-save-button'}>\n <TranslatorModal disabledLocales={disabledLocales} />\n {resolvers.map((resolver) => (\n <ResolverButton key={resolver.key} resolver={resolver} />\n ))}\n {<DefaultButton />}\n </div>\n </TranslatorProvider>\n );\n};\n"],"names":["PublishButton","SaveButton","useConfig","useDocumentInfo","useLocale","TranslatorProvider","ResolverButton","TranslatorModal","CustomButtonWithTranslator","disabledLocales","type","config","locale","DefaultButton","globalSlug","id","resolvers","admin","custom","translator","includes","code","div","className","map","resolver","key"],"mappings":"AAAA;;AAEA,OAAO,gBAAgB;AAEvB,SAASA,aAAa,EAAEC,UAAU,EAAEC,SAAS,EAAEC,eAAe,EAAEC,SAAS,QAAQ,iBAAiB;AAGlG,SAASC,kBAAkB,QAAQ,gDAAgD;AACnF,SAASC,cAAc,QAAQ,oBAAoB;AACnD,SAASC,eAAe,QAAQ,qBAAqB;AAErD,OAAO,MAAMC,6BAA6B,CAAC,EAAEC,eAAe,EAAEC,IAAI,EAA2D;IAC3H,MAAM,EAAEC,MAAM,EAAE,GAAGT;IACnB,MAAMU,SAASR;IAEf,MAAMS,gBAAgBH,SAAS,YAAYV,gBAAgBC;IAE3D,MAAM,EAAEa,UAAU,EAAEC,EAAE,EAAE,GAAGZ;IAE3B,MAAMa,YAAY,AAACL,OAAOM,KAAK,EAAEC,QAAQC,YAAYH,aAAqC,EAAE;IAE5F,IAAI,CAACD,MAAM,CAACD,YAAY,qBAAO,KAACD;IAEhC,IAAGJ,gBAAgBW,QAAQ,CAACR,OAAOS,IAAI,GAAG,qBAAO,KAACR;IAElD,qBACE,KAACR;kBACC,cAAA,MAACiB;YAAIC,WAAW;;8BACd,KAAChB;oBAAgBE,iBAAiBA;;gBACjCO,UAAUQ,GAAG,CAAC,CAACC,yBACd,KAACnB;wBAAkCmB,UAAUA;uBAAxBA,SAASC,GAAG;8BAElC,KAACb;;;;AAIV,EAAE"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export const CustomButton = (type, disabledLocales)=>{
|
|
2
2
|
return {
|
|
3
3
|
clientProps: {
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
disabledLocales: disabledLocales || [],
|
|
5
|
+
type
|
|
6
6
|
},
|
|
7
7
|
path: '@extravirgin/payload-enchants-translator/client#CustomButtonWithTranslator'
|
|
8
8
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/client/components/CustomButton/index.tsx"],"sourcesContent":["import type { CustomPublishButton, CustomSaveButton } from 'payload';\n\nexport const CustomButton = (type: 'publish' | 'save', disabledLocales?: string[]): CustomPublishButton | CustomSaveButton => {\n return {\n clientProps: {\n
|
|
1
|
+
{"version":3,"sources":["../../../../src/client/components/CustomButton/index.tsx"],"sourcesContent":["import type { CustomPublishButton, CustomSaveButton } from 'payload';\n\nexport const CustomButton = (type: 'publish' | 'save', disabledLocales?: string[]): CustomPublishButton | CustomSaveButton => {\n return {\n clientProps: {\n disabledLocales: disabledLocales || [],\n type,\n },\n path: '@extravirgin/payload-enchants-translator/client#CustomButtonWithTranslator',\n };\n};\n"],"names":["CustomButton","type","disabledLocales","clientProps","path"],"mappings":"AAEA,OAAO,MAAMA,eAAe,CAACC,MAA0BC;IACrD,OAAO;QACLC,aAAa;YACXD,iBAAiBA,mBAAmB,EAAE;YACtCD;QACF;QACAG,MAAM;IACR;AACF,EAAE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/client/components/GlossaryControls/index.tsx"],"names":[],"mappings":"AAOA,eAAO,MAAM,gBAAgB,iBAAkB;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,gCAyClE,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Button, toast, useConfig, useTranslation } from '@payloadcms/ui';
|
|
4
|
+
import { useState } from 'react';
|
|
5
|
+
import { translatorT } from '../../../i18n-translations';
|
|
6
|
+
export const GlossaryControls = ({ basePath })=>{
|
|
7
|
+
const { config } = useConfig();
|
|
8
|
+
const { i18n, t } = useTranslation();
|
|
9
|
+
const [syncing, setSyncing] = useState(false);
|
|
10
|
+
const sync = async ()=>{
|
|
11
|
+
setSyncing(true);
|
|
12
|
+
try {
|
|
13
|
+
const response = await fetch(`${config.serverURL}${basePath}${config.routes.api}/translator/glossary/sync`, {
|
|
14
|
+
credentials: 'include',
|
|
15
|
+
headers: {
|
|
16
|
+
'Accept-Language': i18n.language
|
|
17
|
+
},
|
|
18
|
+
method: 'POST'
|
|
19
|
+
});
|
|
20
|
+
const result = await response.json();
|
|
21
|
+
if (!response.ok) {
|
|
22
|
+
toast.error(result.error || translatorT(t, 'glossary_syncError'));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
toast.success(translatorT(t, 'glossary_syncSuccess'));
|
|
26
|
+
} catch {
|
|
27
|
+
toast.error(translatorT(t, 'glossary_syncError'));
|
|
28
|
+
} finally{
|
|
29
|
+
setSyncing(false);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
return /*#__PURE__*/ _jsx(Button, {
|
|
33
|
+
disabled: syncing,
|
|
34
|
+
onClick: sync,
|
|
35
|
+
size: "small",
|
|
36
|
+
tooltip: translatorT(t, 'glossary_syncHint'),
|
|
37
|
+
type: "button",
|
|
38
|
+
children: translatorT(t, syncing ? 'glossary_syncing' : 'glossary_syncAll')
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/client/components/GlossaryControls/index.tsx"],"sourcesContent":["'use client';\n\nimport { Button, toast, useConfig, useTranslation } from '@payloadcms/ui';\nimport { useState } from 'react';\n\nimport { translatorT } from '../../../i18n-translations';\n\nexport const GlossaryControls = ({ basePath }: { basePath: string }) => {\n const { config } = useConfig();\n\n const { i18n, t } = useTranslation();\n\n const [syncing, setSyncing] = useState(false);\n\n const sync = async () => {\n setSyncing(true);\n try {\n const response = await fetch(\n `${config.serverURL}${basePath}${config.routes.api}/translator/glossary/sync`,\n { credentials: 'include', headers: { 'Accept-Language': i18n.language }, method: 'POST' },\n );\n\n const result = await response.json();\n\n if (!response.ok) {\n toast.error(result.error || translatorT(t, 'glossary_syncError'));\n\n return;\n }\n toast.success(translatorT(t, 'glossary_syncSuccess'));\n } catch {\n toast.error(translatorT(t, 'glossary_syncError'));\n } finally {\n setSyncing(false);\n }\n };\n\n return (\n <Button\n disabled={syncing}\n onClick={sync}\n size='small'\n tooltip={translatorT(t, 'glossary_syncHint')}\n type='button'\n >\n {translatorT(t, syncing ? 'glossary_syncing' : 'glossary_syncAll')}\n </Button>\n );\n};\n"],"names":["Button","toast","useConfig","useTranslation","useState","translatorT","GlossaryControls","basePath","config","i18n","t","syncing","setSyncing","sync","response","fetch","serverURL","routes","api","credentials","headers","language","method","result","json","ok","error","success","disabled","onClick","size","tooltip","type"],"mappings":"AAAA;;AAEA,SAASA,MAAM,EAAEC,KAAK,EAAEC,SAAS,EAAEC,cAAc,QAAQ,iBAAiB;AAC1E,SAASC,QAAQ,QAAQ,QAAQ;AAEjC,SAASC,WAAW,QAAQ,6BAA6B;AAEzD,OAAO,MAAMC,mBAAmB,CAAC,EAAEC,QAAQ,EAAwB;IACjE,MAAM,EAAEC,MAAM,EAAE,GAAGN;IAEnB,MAAM,EAAEO,IAAI,EAAEC,CAAC,EAAE,GAAGP;IAEpB,MAAM,CAACQ,SAASC,WAAW,GAAGR,SAAS;IAEvC,MAAMS,OAAO;QACXD,WAAW;QACX,IAAI;YACF,MAAME,WAAW,MAAMC,MACrB,GAAGP,OAAOQ,SAAS,GAAGT,WAAWC,OAAOS,MAAM,CAACC,GAAG,CAAC,yBAAyB,CAAC,EAC7E;gBAAEC,aAAa;gBAAWC,SAAS;oBAAE,mBAAmBX,KAAKY,QAAQ;gBAAC;gBAAGC,QAAQ;YAAO;YAG1F,MAAMC,SAAS,MAAMT,SAASU,IAAI;YAElC,IAAI,CAACV,SAASW,EAAE,EAAE;gBAChBxB,MAAMyB,KAAK,CAACH,OAAOG,KAAK,IAAIrB,YAAYK,GAAG;gBAE3C;YACF;YACAT,MAAM0B,OAAO,CAACtB,YAAYK,GAAG;QAC/B,EAAE,OAAM;YACNT,MAAMyB,KAAK,CAACrB,YAAYK,GAAG;QAC7B,SAAU;YACRE,WAAW;QACb;IACF;IAEA,qBACE,KAACZ;QACC4B,UAAUjB;QACVkB,SAAShB;QACTiB,MAAK;QACLC,SAAS1B,YAAYK,GAAG;QACxBsB,MAAK;kBAEJ3B,YAAYK,GAAGC,UAAU,qBAAqB;;AAGrD,EAAE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TranslatorProvider.d.ts","sourceRoot":"","sources":["../../../../src/client/providers/Translator/TranslatorProvider.tsx"],"names":[],"mappings":"AAYA,OAAO,EAAE,KAAK,SAAS,EAAgC,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"TranslatorProvider.d.ts","sourceRoot":"","sources":["../../../../src/client/providers/Translator/TranslatorProvider.tsx"],"names":[],"mappings":"AAYA,OAAO,EAAE,KAAK,SAAS,EAAgC,MAAM,OAAO,CAAC;AAUrE,eAAO,MAAM,kBAAkB,iBAAkB;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,gCAmJvE,CAAC"}
|
|
@@ -3,6 +3,7 @@ import { toast, useAllFormFields, useConfig, useDocumentInfo, useForm, useLocale
|
|
|
3
3
|
import { reduceFieldsToValues } from 'payload/shared';
|
|
4
4
|
import { useEffect, useMemo, useState } from 'react';
|
|
5
5
|
import { createClient } from '../../api';
|
|
6
|
+
import { translatorT } from '../../../i18n-translations';
|
|
6
7
|
import { TranslatorContext } from './context';
|
|
7
8
|
const modalSlug = 'translator-modal';
|
|
8
9
|
export const TranslatorProvider = ({ children })=>{
|
|
@@ -35,7 +36,7 @@ export const TranslatorProvider = ({ children })=>{
|
|
|
35
36
|
custom,
|
|
36
37
|
resolver
|
|
37
38
|
]);
|
|
38
|
-
if (!localization) throw new Error(
|
|
39
|
+
if (!localization) throw new Error(translatorT(t, 'error_missingLocalization'));
|
|
39
40
|
const localesOptions = localization.locales.filter((each)=>each.code !== locale.code);
|
|
40
41
|
const [localeToTranslateFrom, setLocaleToTranslateFrom] = useState('');
|
|
41
42
|
useEffect(()=>{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/client/providers/Translator/TranslatorProvider.tsx"],"sourcesContent":["import {\n toast,\n useAllFormFields,\n useConfig,\n useDocumentInfo,\n useForm,\n useLocale,\n useModal,\n useServerFunctions,\n useTranslation,\n} from '@payloadcms/ui';\nimport { reduceFieldsToValues } from 'payload/shared';\nimport { type ReactNode, useEffect, useMemo, useState } from 'react';\n\nimport type { TranslateResolver } from '../../../resolvers/types';\nimport type { TranslateArgs } from '../../../translate/types';\nimport { createClient } from '../../api';\nimport { TranslatorContext } from './context';\n\nconst modalSlug = 'translator-modal';\n\nexport const TranslatorProvider = ({ children }: { children: ReactNode }) => {\n const [resolver, setResolver] = useState<null | string>(null);\n\n const [data, dispatch] = useAllFormFields();\n\n const { getFormState } = useServerFunctions();\n\n const { collectionSlug, getDocPreferences, globalSlug, id } = useDocumentInfo();\n\n const { setModified } = useForm();\n\n const modal = useModal();\n\n const { t } = useTranslation();\n\n const resolverT = (\n key:\n | 'buttonLabel'\n | 'errorMessage'\n | 'modalTitle'\n | 'submitButtonLabelEmpty'\n | 'submitButtonLabelFull'\n | 'successMessage',\n ) => {\n if (!resolver) return '';\n\n return t(`plugin-translator:resolver_${resolver}_${key}` as Parameters<typeof t>[0]);\n };\n\n const locale = useLocale();\n\n const {\n config: {\n admin: { custom },\n localization,\n routes: { api },\n serverURL,\n },\n } = useConfig();\n\n const basePath = (custom?.translator?.basePath as string | undefined) ?? '';\n const apiClient = createClient({ api, basePath, serverURL });\n\n const resolverConfig = useMemo(() => {\n if (!resolver) return null;\n\n const resolvers = (custom?.translator?.resolvers as TranslateResolver[]) || undefined;\n\n if (!resolvers) return null;\n\n const resolverConfig = resolvers.find((each) => each.key === resolver);\n\n return resolverConfig ?? null;\n }, [custom, resolver]);\n\n if (!localization)\n throw new Error(
|
|
1
|
+
{"version":3,"sources":["../../../../src/client/providers/Translator/TranslatorProvider.tsx"],"sourcesContent":["import {\n toast,\n useAllFormFields,\n useConfig,\n useDocumentInfo,\n useForm,\n useLocale,\n useModal,\n useServerFunctions,\n useTranslation,\n} from '@payloadcms/ui';\nimport { reduceFieldsToValues } from 'payload/shared';\nimport { type ReactNode, useEffect, useMemo, useState } from 'react';\n\nimport type { TranslateResolver } from '../../../resolvers/types';\nimport type { TranslateArgs } from '../../../translate/types';\nimport { createClient } from '../../api';\nimport { translatorT } from '../../../i18n-translations';\nimport { TranslatorContext } from './context';\n\nconst modalSlug = 'translator-modal';\n\nexport const TranslatorProvider = ({ children }: { children: ReactNode }) => {\n const [resolver, setResolver] = useState<null | string>(null);\n\n const [data, dispatch] = useAllFormFields();\n\n const { getFormState } = useServerFunctions();\n\n const { collectionSlug, getDocPreferences, globalSlug, id } = useDocumentInfo();\n\n const { setModified } = useForm();\n\n const modal = useModal();\n\n const { t } = useTranslation();\n\n const resolverT = (\n key:\n | 'buttonLabel'\n | 'errorMessage'\n | 'modalTitle'\n | 'submitButtonLabelEmpty'\n | 'submitButtonLabelFull'\n | 'successMessage',\n ) => {\n if (!resolver) return '';\n\n return t(`plugin-translator:resolver_${resolver}_${key}` as Parameters<typeof t>[0]);\n };\n\n const locale = useLocale();\n\n const {\n config: {\n admin: { custom },\n localization,\n routes: { api },\n serverURL,\n },\n } = useConfig();\n\n const basePath = (custom?.translator?.basePath as string | undefined) ?? '';\n const apiClient = createClient({ api, basePath, serverURL });\n\n const resolverConfig = useMemo(() => {\n if (!resolver) return null;\n\n const resolvers = (custom?.translator?.resolvers as TranslateResolver[]) || undefined;\n\n if (!resolvers) return null;\n\n const resolverConfig = resolvers.find((each) => each.key === resolver);\n\n return resolverConfig ?? null;\n }, [custom, resolver]);\n\n if (!localization)\n throw new Error(translatorT(t, 'error_missingLocalization'));\n\n const localesOptions = localization.locales.filter((each) => each.code !== locale.code);\n\n const [localeToTranslateFrom, setLocaleToTranslateFrom] = useState<string>('');\n\n useEffect(()=>{\n const localeToTranslateFromExists = localesOptions.find((each)=>localeToTranslateFrom === each.code);\n if (localeToTranslateFrom && localeToTranslateFromExists) return;\n const defaultFromOptions = localesOptions.find((each)=>localization.defaultLocale === each.code);\n if (defaultFromOptions) {\n setLocaleToTranslateFrom(defaultFromOptions.code);\n return;\n }\n setLocaleToTranslateFrom(localesOptions[0].code);\n}, [\n locale,\n localesOptions,\n localization.defaultLocale\n]);\n\n const closeTranslator = () => modal.closeModal(modalSlug);\n\n const submit = async ({ emptyOnly }: { emptyOnly: boolean }) => {\n if (!resolver) return;\n\n const args: TranslateArgs = {\n collectionSlug,\n data: reduceFieldsToValues(data, true),\n emptyOnly,\n globalSlug,\n id: id === null ? undefined : id,\n locale: locale.code,\n localeFrom: localeToTranslateFrom,\n resolver,\n };\n\n const result = await apiClient.translate(args);\n\n if (!result.success) {\n toast.error(resolverT('errorMessage'));\n\n return;\n }\n\n const { state } = await getFormState({\n collectionSlug,\n data: result.translatedData,\n docPermissions: {\n fields: true,\n update: true,\n },\n docPreferences: await getDocPreferences(),\n globalSlug,\n locale: locale.code,\n operation: 'update',\n renderAllFields: true,\n schemaPath: collectionSlug || globalSlug || '',\n });\n\n if (state) {\n dispatch({\n state,\n type: 'REPLACE_STATE',\n });\n setModified(true);\n toast.success(resolverT('successMessage'));\n }\n closeTranslator();\n };\n\n return (\n <TranslatorContext.Provider\n value={{\n closeTranslator,\n localeToTranslateFrom,\n localesOptions,\n modalSlug,\n openTranslator: ({ resolverKey }) => {\n setResolver(resolverKey);\n modal.openModal(modalSlug);\n },\n resolver: resolverConfig,\n resolverT,\n setLocaleToTranslateFrom,\n submit,\n }}\n >\n {children}\n </TranslatorContext.Provider>\n );\n};\n"],"names":["toast","useAllFormFields","useConfig","useDocumentInfo","useForm","useLocale","useModal","useServerFunctions","useTranslation","reduceFieldsToValues","useEffect","useMemo","useState","createClient","translatorT","TranslatorContext","modalSlug","TranslatorProvider","children","resolver","setResolver","data","dispatch","getFormState","collectionSlug","getDocPreferences","globalSlug","id","setModified","modal","t","resolverT","key","locale","config","admin","custom","localization","routes","api","serverURL","basePath","translator","apiClient","resolverConfig","resolvers","undefined","find","each","Error","localesOptions","locales","filter","code","localeToTranslateFrom","setLocaleToTranslateFrom","localeToTranslateFromExists","defaultFromOptions","defaultLocale","closeTranslator","closeModal","submit","emptyOnly","args","localeFrom","result","translate","success","error","state","translatedData","docPermissions","fields","update","docPreferences","operation","renderAllFields","schemaPath","type","Provider","value","openTranslator","resolverKey","openModal"],"mappings":"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"}
|
package/dist/exports/client.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/exports/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,8DAA8D,CAAC"}
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/exports/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,8DAA8D,CAAC;AAE1G,OAAO,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC"}
|
package/dist/exports/client.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/exports/client.ts"],"sourcesContent":["export { CustomButtonWithTranslator } from '../client/components/CustomButton/CustomButtonWithTranslator';\n"],"names":["CustomButtonWithTranslator"],"mappings":"AAAA,SAASA,0BAA0B,QAAQ,+DAA+D"}
|
|
1
|
+
{"version":3,"sources":["../../src/exports/client.ts"],"sourcesContent":["export { CustomButtonWithTranslator } from '../client/components/CustomButton/CustomButtonWithTranslator';\n\nexport { GlossaryControls } from '../client/components/GlossaryControls';\n"],"names":["CustomButtonWithTranslator","GlossaryControls"],"mappings":"AAAA,SAASA,0BAA0B,QAAQ,+DAA+D;AAE1G,SAASC,gBAAgB,QAAQ,wCAAwC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { TFunction } from '@payloadcms/translations';
|
|
2
|
+
import type { Access, CollectionConfig } from 'payload';
|
|
3
|
+
export type GlossaryConfig = {
|
|
4
|
+
access: {
|
|
5
|
+
read: Access;
|
|
6
|
+
update: Access;
|
|
7
|
+
};
|
|
8
|
+
label?: NonNullable<CollectionConfig['labels']>['plural'];
|
|
9
|
+
localeLabels?: Record<string, Record<string, string> | string>;
|
|
10
|
+
slug?: string;
|
|
11
|
+
sourceLocale: string;
|
|
12
|
+
targetLocales: string[];
|
|
13
|
+
};
|
|
14
|
+
export declare const getGlossarySlug: (config: GlossaryConfig) => string;
|
|
15
|
+
export declare const validateGlossaryEntries: (value: unknown, config: GlossaryConfig, t?: TFunction) => string | true;
|
|
16
|
+
export declare const validateGlossaryConfig: (config: GlossaryConfig) => void;
|
|
17
|
+
//# sourceMappingURL=glossary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glossary.d.ts","sourceRoot":"","sources":["../src/glossary.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAIxD,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,KAAK,CAAC,EAAE,WAAW,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC1D,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB,CAAC;AAEF,eAAO,MAAM,eAAe,WAAY,cAAc,WAA0C,CAAC;AAEjG,eAAO,MAAM,uBAAuB,UAC3B,OAAO,UACN,cAAc,MAClB,SAAS,KACZ,MAAM,GAAG,IAkCX,CAAC;AAEF,eAAO,MAAM,sBAAsB,WAAY,cAAc,KAAG,IAyB/D,CAAC"}
|