@roxxel/payload-multilang 0.0.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 +165 -0
- package/dist/components/LanguageListToolbar.d.ts +6 -0
- package/dist/components/LanguageListToolbar.js +69 -0
- package/dist/components/LanguageListToolbar.js.map +1 -0
- package/dist/components/LanguageMetabox.d.ts +2 -0
- package/dist/components/LanguageMetabox.js +275 -0
- package/dist/components/LanguageMetabox.js.map +1 -0
- package/dist/components/TranslationActionsClient.d.ts +10 -0
- package/dist/components/TranslationActionsClient.js +166 -0
- package/dist/components/TranslationActionsClient.js.map +1 -0
- package/dist/components/TranslationColumnCell.d.ts +2 -0
- package/dist/components/TranslationColumnCell.js +69 -0
- package/dist/components/TranslationColumnCell.js.map +1 -0
- package/dist/components/TranslationColumnCellClient.d.ts +12 -0
- package/dist/components/TranslationColumnCellClient.js +107 -0
- package/dist/components/TranslationColumnCellClient.js.map +1 -0
- package/dist/components/TranslationsTab.d.ts +2 -0
- package/dist/components/TranslationsTab.js +118 -0
- package/dist/components/TranslationsTab.js.map +1 -0
- package/dist/components/config.d.ts +40 -0
- package/dist/components/config.js +31 -0
- package/dist/components/config.js.map +1 -0
- package/dist/constants.d.ts +5 -0
- package/dist/constants.js +24 -0
- package/dist/constants.js.map +1 -0
- package/dist/endpoints/translations.d.ts +19 -0
- package/dist/endpoints/translations.js +301 -0
- package/dist/endpoints/translations.js.map +1 -0
- package/dist/exports/client.d.ts +4 -0
- package/dist/exports/client.js +6 -0
- package/dist/exports/client.js.map +1 -0
- package/dist/exports/rsc.d.ts +2 -0
- package/dist/exports/rsc.js +4 -0
- package/dist/exports/rsc.js.map +1 -0
- package/dist/hooks/translatedCollection.d.ts +26 -0
- package/dist/hooks/translatedCollection.js +290 -0
- package/dist/hooks/translatedCollection.js.map +1 -0
- package/dist/hooks/translatedGlobal.d.ts +16 -0
- package/dist/hooks/translatedGlobal.js +71 -0
- package/dist/hooks/translatedGlobal.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +63 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/config.d.ts +14 -0
- package/dist/lib/config.js +96 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/data.d.ts +107 -0
- package/dist/lib/data.js +307 -0
- package/dist/lib/data.js.map +1 -0
- package/dist/payload-config.d.js +2 -0
- package/dist/payload-config.d.js.map +1 -0
- package/dist/styles/admin.css +316 -0
- package/dist/types.d.ts +96 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/docs/assets/admin-ui/collection-list-language-shortcuts.png +0 -0
- package/docs/assets/admin-ui/english-post-translations.png +0 -0
- package/docs/assets/admin-ui/ukrainian-post-translations.png +0 -0
- package/docs/configuration.md +192 -0
- package/docs/helpers.md +231 -0
- package/docs/usage.md +269 -0
- package/package.json +95 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export const DEFAULT_FIELD_NAMES = {
|
|
2
|
+
group: '_multilangGroup',
|
|
3
|
+
language: '_multilangLanguage',
|
|
4
|
+
meta: '_multilangMeta'
|
|
5
|
+
};
|
|
6
|
+
export const DEFAULT_GLOBAL_TABS_LABEL = 'Localized content';
|
|
7
|
+
export const DEFAULT_DUPLICATE_EXCLUDE_FIELDS = [
|
|
8
|
+
'id',
|
|
9
|
+
'createdAt',
|
|
10
|
+
'updatedAt',
|
|
11
|
+
'deletedAt',
|
|
12
|
+
'_status',
|
|
13
|
+
'sizes',
|
|
14
|
+
'filename',
|
|
15
|
+
'filesize',
|
|
16
|
+
'height',
|
|
17
|
+
'mimeType',
|
|
18
|
+
'thumbnailURL',
|
|
19
|
+
'url',
|
|
20
|
+
'width'
|
|
21
|
+
];
|
|
22
|
+
export const MULTILANG_SKIP_HOOK = 'payloadMultilangSkip';
|
|
23
|
+
|
|
24
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts"],"sourcesContent":["import type { MultilangFieldNames } from './types.js'\n\nexport const DEFAULT_FIELD_NAMES: MultilangFieldNames = {\n group: '_multilangGroup',\n language: '_multilangLanguage',\n meta: '_multilangMeta',\n}\n\nexport const DEFAULT_GLOBAL_TABS_LABEL = 'Localized content'\n\nexport const DEFAULT_DUPLICATE_EXCLUDE_FIELDS = [\n 'id',\n 'createdAt',\n 'updatedAt',\n 'deletedAt',\n '_status',\n 'sizes',\n 'filename',\n 'filesize',\n 'height',\n 'mimeType',\n 'thumbnailURL',\n 'url',\n 'width',\n]\n\nexport const MULTILANG_SKIP_HOOK = 'payloadMultilangSkip'\n"],"names":["DEFAULT_FIELD_NAMES","group","language","meta","DEFAULT_GLOBAL_TABS_LABEL","DEFAULT_DUPLICATE_EXCLUDE_FIELDS","MULTILANG_SKIP_HOOK"],"mappings":"AAEA,OAAO,MAAMA,sBAA2C;IACtDC,OAAO;IACPC,UAAU;IACVC,MAAM;AACR,EAAC;AAED,OAAO,MAAMC,4BAA4B,oBAAmB;AAE5D,OAAO,MAAMC,mCAAmC;IAC9C;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACD,CAAA;AAED,OAAO,MAAMC,sBAAsB,uBAAsB"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { CollectionConfig, Endpoint } from 'payload';
|
|
2
|
+
import type { ResolvedMultilangCollection } from '../types.js';
|
|
3
|
+
export declare const createTranslationStateEndpoint: ({ collection, }: {
|
|
4
|
+
collection: ResolvedMultilangCollection;
|
|
5
|
+
}) => Endpoint;
|
|
6
|
+
export declare const createCreateTranslationEndpoint: ({ collection, collectionConfig, }: {
|
|
7
|
+
collection: ResolvedMultilangCollection;
|
|
8
|
+
collectionConfig: CollectionConfig;
|
|
9
|
+
}) => Endpoint;
|
|
10
|
+
export declare const createConnectTranslationEndpoint: ({ collection, }: {
|
|
11
|
+
collection: ResolvedMultilangCollection;
|
|
12
|
+
}) => Endpoint;
|
|
13
|
+
export declare const createDisconnectTranslationEndpoint: ({ collection, }: {
|
|
14
|
+
collection: ResolvedMultilangCollection;
|
|
15
|
+
}) => Endpoint;
|
|
16
|
+
export declare const createTranslationEndpoints: ({ collection, collectionConfig, }: {
|
|
17
|
+
collection: ResolvedMultilangCollection;
|
|
18
|
+
collectionConfig: CollectionConfig;
|
|
19
|
+
}) => Endpoint[];
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import { randomUUID } from 'crypto';
|
|
2
|
+
import { MULTILANG_SKIP_HOOK } from '../constants.js';
|
|
3
|
+
import { asDocument, duplicateDocumentData, getDocumentTranslationsWithPayload, getGroupTranslationsWithPayload, getID, getStringValue } from '../lib/data.js';
|
|
4
|
+
const json = (body, init)=>Response.json(body, init);
|
|
5
|
+
const parseBody = async (req)=>{
|
|
6
|
+
if (typeof req.json !== 'function') {
|
|
7
|
+
return {};
|
|
8
|
+
}
|
|
9
|
+
return asDocument(await req.json());
|
|
10
|
+
};
|
|
11
|
+
const getLanguageCode = (value)=>{
|
|
12
|
+
const language = getStringValue(value)?.trim().toLowerCase();
|
|
13
|
+
return language || undefined;
|
|
14
|
+
};
|
|
15
|
+
const validateLanguage = ({ collection, language })=>{
|
|
16
|
+
if (collection.languages.some((candidate)=>candidate.code === language)) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
return json({
|
|
20
|
+
message: `Language "${language}" is not configured for ${collection.slug}.`
|
|
21
|
+
}, {
|
|
22
|
+
status: 400
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
const ensureGroup = async ({ collection, doc, req })=>{
|
|
26
|
+
const existingGroup = getStringValue(doc[collection.fieldNames.group]);
|
|
27
|
+
if (existingGroup) {
|
|
28
|
+
return existingGroup;
|
|
29
|
+
}
|
|
30
|
+
const id = getID(doc.id);
|
|
31
|
+
if (!id) {
|
|
32
|
+
return randomUUID();
|
|
33
|
+
}
|
|
34
|
+
const group = randomUUID();
|
|
35
|
+
await req.payload.update({
|
|
36
|
+
id,
|
|
37
|
+
collection: collection.slug,
|
|
38
|
+
context: {
|
|
39
|
+
[MULTILANG_SKIP_HOOK]: true
|
|
40
|
+
},
|
|
41
|
+
data: {
|
|
42
|
+
[collection.fieldNames.group]: group
|
|
43
|
+
},
|
|
44
|
+
overrideAccess: false,
|
|
45
|
+
req
|
|
46
|
+
});
|
|
47
|
+
return group;
|
|
48
|
+
};
|
|
49
|
+
const checkDuplicateLanguage = async ({ collection, excludeID, group, language, req })=>{
|
|
50
|
+
const and = [
|
|
51
|
+
{
|
|
52
|
+
[collection.fieldNames.group]: {
|
|
53
|
+
equals: group
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
[collection.fieldNames.language]: {
|
|
58
|
+
equals: language
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
];
|
|
62
|
+
if (excludeID) {
|
|
63
|
+
and.push({
|
|
64
|
+
id: {
|
|
65
|
+
not_equals: excludeID
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
const { totalDocs } = await req.payload.count({
|
|
70
|
+
collection: collection.slug,
|
|
71
|
+
overrideAccess: false,
|
|
72
|
+
req,
|
|
73
|
+
where: {
|
|
74
|
+
and
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
if (totalDocs > 0) {
|
|
78
|
+
return json({
|
|
79
|
+
message: `A translation already exists for language "${language}".`
|
|
80
|
+
}, {
|
|
81
|
+
status: 409
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
export const createTranslationStateEndpoint = ({ collection })=>({
|
|
86
|
+
handler: async (req)=>{
|
|
87
|
+
const id = getID(req.query?.id);
|
|
88
|
+
const group = getStringValue(req.query?.group);
|
|
89
|
+
if (!id && !group) {
|
|
90
|
+
return json({
|
|
91
|
+
message: 'Missing document id or translation group.'
|
|
92
|
+
}, {
|
|
93
|
+
status: 400
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
const state = id ? await getDocumentTranslationsWithPayload({
|
|
97
|
+
id,
|
|
98
|
+
collection: collection.slug,
|
|
99
|
+
fieldNames: collection.fieldNames,
|
|
100
|
+
payload: req.payload,
|
|
101
|
+
overrideAccess: false,
|
|
102
|
+
req
|
|
103
|
+
}) : await getGroupTranslationsWithPayload({
|
|
104
|
+
collection: collection.slug,
|
|
105
|
+
fieldNames: collection.fieldNames,
|
|
106
|
+
group: group,
|
|
107
|
+
payload: req.payload,
|
|
108
|
+
overrideAccess: false,
|
|
109
|
+
req
|
|
110
|
+
});
|
|
111
|
+
return json(state);
|
|
112
|
+
},
|
|
113
|
+
method: 'get',
|
|
114
|
+
path: '/multilang/state'
|
|
115
|
+
});
|
|
116
|
+
export const createCreateTranslationEndpoint = ({ collection, collectionConfig })=>({
|
|
117
|
+
handler: async (req)=>{
|
|
118
|
+
const body = await parseBody(req);
|
|
119
|
+
const sourceID = getID(body.sourceId);
|
|
120
|
+
const targetLanguage = getLanguageCode(body.targetLanguage);
|
|
121
|
+
if (!sourceID || !targetLanguage) {
|
|
122
|
+
return json({
|
|
123
|
+
message: 'sourceId and targetLanguage are required.'
|
|
124
|
+
}, {
|
|
125
|
+
status: 400
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
const languageError = validateLanguage({
|
|
129
|
+
collection,
|
|
130
|
+
language: targetLanguage
|
|
131
|
+
});
|
|
132
|
+
if (languageError) {
|
|
133
|
+
return languageError;
|
|
134
|
+
}
|
|
135
|
+
const source = asDocument(await req.payload.findByID({
|
|
136
|
+
id: sourceID,
|
|
137
|
+
collection: collection.slug,
|
|
138
|
+
depth: 0,
|
|
139
|
+
overrideAccess: false,
|
|
140
|
+
req
|
|
141
|
+
}));
|
|
142
|
+
const group = await ensureGroup({
|
|
143
|
+
collection,
|
|
144
|
+
doc: source,
|
|
145
|
+
req
|
|
146
|
+
});
|
|
147
|
+
const duplicateError = await checkDuplicateLanguage({
|
|
148
|
+
collection,
|
|
149
|
+
group,
|
|
150
|
+
language: targetLanguage,
|
|
151
|
+
req
|
|
152
|
+
});
|
|
153
|
+
if (duplicateError) {
|
|
154
|
+
return duplicateError;
|
|
155
|
+
}
|
|
156
|
+
const shouldDuplicate = body.duplicate !== false && collection.duplicate;
|
|
157
|
+
const data = shouldDuplicate ? duplicateDocumentData({
|
|
158
|
+
collection: collectionConfig,
|
|
159
|
+
doc: source,
|
|
160
|
+
duplicateExcludeFields: collection.duplicateExcludeFields,
|
|
161
|
+
fieldNames: collection.fieldNames,
|
|
162
|
+
targetLanguage
|
|
163
|
+
}) : {};
|
|
164
|
+
const doc = await req.payload.create({
|
|
165
|
+
collection: collection.slug,
|
|
166
|
+
data: {
|
|
167
|
+
...data,
|
|
168
|
+
[collection.fieldNames.group]: group,
|
|
169
|
+
[collection.fieldNames.language]: targetLanguage,
|
|
170
|
+
[collection.fieldNames.meta]: {
|
|
171
|
+
createdFrom: sourceID
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
overrideAccess: false,
|
|
175
|
+
req
|
|
176
|
+
});
|
|
177
|
+
return json({
|
|
178
|
+
doc
|
|
179
|
+
});
|
|
180
|
+
},
|
|
181
|
+
method: 'post',
|
|
182
|
+
path: '/multilang/create'
|
|
183
|
+
});
|
|
184
|
+
export const createConnectTranslationEndpoint = ({ collection })=>({
|
|
185
|
+
handler: async (req)=>{
|
|
186
|
+
const body = await parseBody(req);
|
|
187
|
+
const sourceID = getID(body.sourceId);
|
|
188
|
+
const targetID = getID(body.targetId);
|
|
189
|
+
const targetLanguage = getLanguageCode(body.targetLanguage);
|
|
190
|
+
if (!sourceID || !targetID || sourceID === targetID) {
|
|
191
|
+
return json({
|
|
192
|
+
message: 'sourceId and a different targetId are required.'
|
|
193
|
+
}, {
|
|
194
|
+
status: 400
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
const source = asDocument(await req.payload.findByID({
|
|
198
|
+
id: sourceID,
|
|
199
|
+
collection: collection.slug,
|
|
200
|
+
depth: 0,
|
|
201
|
+
overrideAccess: false,
|
|
202
|
+
req
|
|
203
|
+
}));
|
|
204
|
+
const target = asDocument(await req.payload.findByID({
|
|
205
|
+
id: targetID,
|
|
206
|
+
collection: collection.slug,
|
|
207
|
+
depth: 0,
|
|
208
|
+
overrideAccess: false,
|
|
209
|
+
req
|
|
210
|
+
}));
|
|
211
|
+
const group = await ensureGroup({
|
|
212
|
+
collection,
|
|
213
|
+
doc: source,
|
|
214
|
+
req
|
|
215
|
+
});
|
|
216
|
+
const language = targetLanguage || getLanguageCode(target[collection.fieldNames.language]) || getLanguageCode(source[collection.fieldNames.language]);
|
|
217
|
+
if (!language) {
|
|
218
|
+
return json({
|
|
219
|
+
message: 'Target language is required.'
|
|
220
|
+
}, {
|
|
221
|
+
status: 400
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
const languageError = validateLanguage({
|
|
225
|
+
collection,
|
|
226
|
+
language
|
|
227
|
+
});
|
|
228
|
+
if (languageError) {
|
|
229
|
+
return languageError;
|
|
230
|
+
}
|
|
231
|
+
const duplicateError = await checkDuplicateLanguage({
|
|
232
|
+
collection,
|
|
233
|
+
excludeID: targetID,
|
|
234
|
+
group,
|
|
235
|
+
language,
|
|
236
|
+
req
|
|
237
|
+
});
|
|
238
|
+
if (duplicateError) {
|
|
239
|
+
return duplicateError;
|
|
240
|
+
}
|
|
241
|
+
const doc = await req.payload.update({
|
|
242
|
+
id: targetID,
|
|
243
|
+
collection: collection.slug,
|
|
244
|
+
data: {
|
|
245
|
+
[collection.fieldNames.group]: group,
|
|
246
|
+
[collection.fieldNames.language]: language
|
|
247
|
+
},
|
|
248
|
+
overrideAccess: false,
|
|
249
|
+
req
|
|
250
|
+
});
|
|
251
|
+
return json({
|
|
252
|
+
doc
|
|
253
|
+
});
|
|
254
|
+
},
|
|
255
|
+
method: 'post',
|
|
256
|
+
path: '/multilang/connect'
|
|
257
|
+
});
|
|
258
|
+
export const createDisconnectTranslationEndpoint = ({ collection })=>({
|
|
259
|
+
handler: async (req)=>{
|
|
260
|
+
const body = await parseBody(req);
|
|
261
|
+
const id = getID(body.id);
|
|
262
|
+
if (!id) {
|
|
263
|
+
return json({
|
|
264
|
+
message: 'Missing document id.'
|
|
265
|
+
}, {
|
|
266
|
+
status: 400
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
const doc = await req.payload.update({
|
|
270
|
+
id,
|
|
271
|
+
collection: collection.slug,
|
|
272
|
+
data: {
|
|
273
|
+
[collection.fieldNames.group]: randomUUID()
|
|
274
|
+
},
|
|
275
|
+
overrideAccess: false,
|
|
276
|
+
req
|
|
277
|
+
});
|
|
278
|
+
return json({
|
|
279
|
+
doc
|
|
280
|
+
});
|
|
281
|
+
},
|
|
282
|
+
method: 'post',
|
|
283
|
+
path: '/multilang/disconnect'
|
|
284
|
+
});
|
|
285
|
+
export const createTranslationEndpoints = ({ collection, collectionConfig })=>[
|
|
286
|
+
createTranslationStateEndpoint({
|
|
287
|
+
collection
|
|
288
|
+
}),
|
|
289
|
+
createCreateTranslationEndpoint({
|
|
290
|
+
collection,
|
|
291
|
+
collectionConfig
|
|
292
|
+
}),
|
|
293
|
+
createConnectTranslationEndpoint({
|
|
294
|
+
collection
|
|
295
|
+
}),
|
|
296
|
+
createDisconnectTranslationEndpoint({
|
|
297
|
+
collection
|
|
298
|
+
})
|
|
299
|
+
];
|
|
300
|
+
|
|
301
|
+
//# sourceMappingURL=translations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/endpoints/translations.ts"],"sourcesContent":["import type { CollectionConfig, Endpoint, PayloadRequest, Where } from 'payload'\n\nimport { randomUUID } from 'crypto'\n\nimport type { ResolvedMultilangCollection } from '../types.js'\n\nimport { MULTILANG_SKIP_HOOK } from '../constants.js'\nimport {\n asDocument,\n duplicateDocumentData,\n getDocumentTranslationsWithPayload,\n getGroupTranslationsWithPayload,\n getID,\n getStringValue,\n} from '../lib/data.js'\n\nconst json = (body: unknown, init?: ResponseInit): Response =>\n Response.json(body, init)\n\nconst parseBody = async (req: PayloadRequest): Promise<Record<string, unknown>> => {\n if (typeof req.json !== 'function') {\n return {}\n }\n\n return asDocument(await req.json())\n}\n\nconst getLanguageCode = (value: unknown): string | undefined => {\n const language = getStringValue(value)?.trim().toLowerCase()\n\n return language || undefined\n}\n\nconst validateLanguage = ({\n collection,\n language,\n}: {\n collection: ResolvedMultilangCollection\n language: string\n}): Response | undefined => {\n if (collection.languages.some((candidate) => candidate.code === language)) {\n return undefined\n }\n\n return json(\n {\n message: `Language \"${language}\" is not configured for ${collection.slug}.`,\n },\n { status: 400 },\n )\n}\n\nconst ensureGroup = async ({\n collection,\n doc,\n req,\n}: {\n collection: ResolvedMultilangCollection\n doc: Record<string, unknown>\n req: PayloadRequest\n}): Promise<string> => {\n const existingGroup = getStringValue(doc[collection.fieldNames.group])\n\n if (existingGroup) {\n return existingGroup\n }\n\n const id = getID(doc.id)\n\n if (!id) {\n return randomUUID()\n }\n\n const group = randomUUID()\n\n await req.payload.update({\n id,\n collection: collection.slug,\n context: {\n [MULTILANG_SKIP_HOOK]: true,\n },\n data: {\n [collection.fieldNames.group]: group,\n },\n overrideAccess: false,\n req,\n })\n\n return group\n}\n\nconst checkDuplicateLanguage = async ({\n collection,\n excludeID,\n group,\n language,\n req,\n}: {\n collection: ResolvedMultilangCollection\n excludeID?: number | string\n group: string\n language: string\n req: PayloadRequest\n}): Promise<Response | undefined> => {\n const and: Where[] = [\n {\n [collection.fieldNames.group]: {\n equals: group,\n },\n },\n {\n [collection.fieldNames.language]: {\n equals: language,\n },\n },\n ]\n\n if (excludeID) {\n and.push({\n id: {\n not_equals: excludeID,\n },\n })\n }\n\n const { totalDocs } = await req.payload.count({\n collection: collection.slug,\n overrideAccess: false,\n req,\n where: {\n and,\n },\n })\n\n if (totalDocs > 0) {\n return json(\n {\n message: `A translation already exists for language \"${language}\".`,\n },\n { status: 409 },\n )\n }\n}\n\nexport const createTranslationStateEndpoint = ({\n collection,\n}: {\n collection: ResolvedMultilangCollection\n}): Endpoint => ({\n handler: async (req) => {\n const id = getID(req.query?.id)\n const group = getStringValue(req.query?.group)\n\n if (!id && !group) {\n return json({ message: 'Missing document id or translation group.' }, { status: 400 })\n }\n\n const state = id\n ? await getDocumentTranslationsWithPayload({\n id,\n collection: collection.slug,\n fieldNames: collection.fieldNames,\n payload: req.payload,\n overrideAccess: false,\n req,\n })\n : await getGroupTranslationsWithPayload({\n collection: collection.slug,\n fieldNames: collection.fieldNames,\n group: group!,\n payload: req.payload,\n overrideAccess: false,\n req,\n })\n\n return json(state)\n },\n method: 'get',\n path: '/multilang/state',\n})\n\nexport const createCreateTranslationEndpoint = ({\n collection,\n collectionConfig,\n}: {\n collection: ResolvedMultilangCollection\n collectionConfig: CollectionConfig\n}): Endpoint => ({\n handler: async (req) => {\n const body = await parseBody(req)\n const sourceID = getID(body.sourceId)\n const targetLanguage = getLanguageCode(body.targetLanguage)\n\n if (!sourceID || !targetLanguage) {\n return json(\n { message: 'sourceId and targetLanguage are required.' },\n { status: 400 },\n )\n }\n\n const languageError = validateLanguage({\n collection,\n language: targetLanguage,\n })\n\n if (languageError) {\n return languageError\n }\n\n const source = asDocument(\n await req.payload.findByID({\n id: sourceID,\n collection: collection.slug,\n depth: 0,\n overrideAccess: false,\n req,\n }),\n )\n const group = await ensureGroup({ collection, doc: source, req })\n const duplicateError = await checkDuplicateLanguage({\n collection,\n group,\n language: targetLanguage,\n req,\n })\n\n if (duplicateError) {\n return duplicateError\n }\n\n const shouldDuplicate = body.duplicate !== false && collection.duplicate\n const data = shouldDuplicate\n ? duplicateDocumentData({\n collection: collectionConfig,\n doc: source,\n duplicateExcludeFields: collection.duplicateExcludeFields,\n fieldNames: collection.fieldNames,\n targetLanguage,\n })\n : {}\n\n const doc = await req.payload.create({\n collection: collection.slug,\n data: {\n ...data,\n [collection.fieldNames.group]: group,\n [collection.fieldNames.language]: targetLanguage,\n [collection.fieldNames.meta]: {\n createdFrom: sourceID,\n },\n },\n overrideAccess: false,\n req,\n })\n\n return json({\n doc,\n })\n },\n method: 'post',\n path: '/multilang/create',\n})\n\nexport const createConnectTranslationEndpoint = ({\n collection,\n}: {\n collection: ResolvedMultilangCollection\n}): Endpoint => ({\n handler: async (req) => {\n const body = await parseBody(req)\n const sourceID = getID(body.sourceId)\n const targetID = getID(body.targetId)\n const targetLanguage = getLanguageCode(body.targetLanguage)\n\n if (!sourceID || !targetID || sourceID === targetID) {\n return json({ message: 'sourceId and a different targetId are required.' }, { status: 400 })\n }\n\n const source = asDocument(\n await req.payload.findByID({\n id: sourceID,\n collection: collection.slug,\n depth: 0,\n overrideAccess: false,\n req,\n }),\n )\n const target = asDocument(\n await req.payload.findByID({\n id: targetID,\n collection: collection.slug,\n depth: 0,\n overrideAccess: false,\n req,\n }),\n )\n const group = await ensureGroup({ collection, doc: source, req })\n const language =\n targetLanguage ||\n getLanguageCode(target[collection.fieldNames.language]) ||\n getLanguageCode(source[collection.fieldNames.language])\n\n if (!language) {\n return json({ message: 'Target language is required.' }, { status: 400 })\n }\n\n const languageError = validateLanguage({\n collection,\n language,\n })\n\n if (languageError) {\n return languageError\n }\n\n const duplicateError = await checkDuplicateLanguage({\n collection,\n excludeID: targetID,\n group,\n language,\n req,\n })\n\n if (duplicateError) {\n return duplicateError\n }\n\n const doc = await req.payload.update({\n id: targetID,\n collection: collection.slug,\n data: {\n [collection.fieldNames.group]: group,\n [collection.fieldNames.language]: language,\n },\n overrideAccess: false,\n req,\n })\n\n return json({ doc })\n },\n method: 'post',\n path: '/multilang/connect',\n})\n\nexport const createDisconnectTranslationEndpoint = ({\n collection,\n}: {\n collection: ResolvedMultilangCollection\n}): Endpoint => ({\n handler: async (req) => {\n const body = await parseBody(req)\n const id = getID(body.id)\n\n if (!id) {\n return json({ message: 'Missing document id.' }, { status: 400 })\n }\n\n const doc = await req.payload.update({\n id,\n collection: collection.slug,\n data: {\n [collection.fieldNames.group]: randomUUID(),\n },\n overrideAccess: false,\n req,\n })\n\n return json({ doc })\n },\n method: 'post',\n path: '/multilang/disconnect',\n})\n\nexport const createTranslationEndpoints = ({\n collection,\n collectionConfig,\n}: {\n collection: ResolvedMultilangCollection\n collectionConfig: CollectionConfig\n}): Endpoint[] => [\n createTranslationStateEndpoint({ collection }),\n createCreateTranslationEndpoint({ collection, collectionConfig }),\n createConnectTranslationEndpoint({ collection }),\n createDisconnectTranslationEndpoint({ collection }),\n]\n"],"names":["randomUUID","MULTILANG_SKIP_HOOK","asDocument","duplicateDocumentData","getDocumentTranslationsWithPayload","getGroupTranslationsWithPayload","getID","getStringValue","json","body","init","Response","parseBody","req","getLanguageCode","value","language","trim","toLowerCase","undefined","validateLanguage","collection","languages","some","candidate","code","message","slug","status","ensureGroup","doc","existingGroup","fieldNames","group","id","payload","update","context","data","overrideAccess","checkDuplicateLanguage","excludeID","and","equals","push","not_equals","totalDocs","count","where","createTranslationStateEndpoint","handler","query","state","method","path","createCreateTranslationEndpoint","collectionConfig","sourceID","sourceId","targetLanguage","languageError","source","findByID","depth","duplicateError","shouldDuplicate","duplicate","duplicateExcludeFields","create","meta","createdFrom","createConnectTranslationEndpoint","targetID","targetId","target","createDisconnectTranslationEndpoint","createTranslationEndpoints"],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { LanguageListToolbar } from '../components/LanguageListToolbar.js';
|
|
2
|
+
export { LanguageMetabox } from '../components/LanguageMetabox.js';
|
|
3
|
+
export { TranslationActionsClient } from '../components/TranslationActionsClient.js';
|
|
4
|
+
export { TranslationColumnCellClient } from '../components/TranslationColumnCellClient.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { LanguageListToolbar } from '../components/LanguageListToolbar.js';
|
|
2
|
+
export { LanguageMetabox } from '../components/LanguageMetabox.js';
|
|
3
|
+
export { TranslationActionsClient } from '../components/TranslationActionsClient.js';
|
|
4
|
+
export { TranslationColumnCellClient } from '../components/TranslationColumnCellClient.js';
|
|
5
|
+
|
|
6
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/exports/client.ts"],"sourcesContent":["export { LanguageListToolbar } from '../components/LanguageListToolbar.js'\nexport { LanguageMetabox } from '../components/LanguageMetabox.js'\nexport { TranslationActionsClient } from '../components/TranslationActionsClient.js'\nexport { TranslationColumnCellClient } from '../components/TranslationColumnCellClient.js'\n"],"names":["LanguageListToolbar","LanguageMetabox","TranslationActionsClient","TranslationColumnCellClient"],"mappings":"AAAA,SAASA,mBAAmB,QAAQ,uCAAsC;AAC1E,SAASC,eAAe,QAAQ,mCAAkC;AAClE,SAASC,wBAAwB,QAAQ,4CAA2C;AACpF,SAASC,2BAA2B,QAAQ,+CAA8C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/exports/rsc.ts"],"sourcesContent":["export { TranslationColumnCell } from '../components/TranslationColumnCell.js'\nexport { TranslationsTab } from '../components/TranslationsTab.js'\n"],"names":["TranslationColumnCell","TranslationsTab"],"mappings":"AAAA,SAASA,qBAAqB,QAAQ,yCAAwC;AAC9E,SAASC,eAAe,QAAQ,mCAAkC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { CollectionAfterChangeHook, CollectionBeforeChangeHook, CollectionConfig, Field } from 'payload';
|
|
2
|
+
import type { ResolvedMultilangCollection } from '../types.js';
|
|
3
|
+
export declare const createTranslatedCollectionBeforeChangeHook: ({ collection }: {
|
|
4
|
+
collection: ResolvedMultilangCollection;
|
|
5
|
+
}) => CollectionBeforeChangeHook;
|
|
6
|
+
export declare const createSynchronizedFieldsAfterChangeHook: ({ collection }: {
|
|
7
|
+
collection: ResolvedMultilangCollection;
|
|
8
|
+
}) => CollectionAfterChangeHook;
|
|
9
|
+
export declare const createHiddenFields: ({ collection, }: {
|
|
10
|
+
collection: ResolvedMultilangCollection;
|
|
11
|
+
}) => Field[];
|
|
12
|
+
export declare const getLanguageColumnName: () => string;
|
|
13
|
+
export declare const createLanguageColumnFields: ({ collection, }: {
|
|
14
|
+
collection: ResolvedMultilangCollection;
|
|
15
|
+
}) => Field[];
|
|
16
|
+
export declare const translatedCollectionMeta: ({ collection, }: {
|
|
17
|
+
collection: ResolvedMultilangCollection;
|
|
18
|
+
}) => {
|
|
19
|
+
defaultLanguage: import("../types.js").MultilangLanguage;
|
|
20
|
+
fieldNames: import("../types.js").MultilangFieldNames;
|
|
21
|
+
languages: import("../types.js").MultilangLanguage[];
|
|
22
|
+
};
|
|
23
|
+
export declare const withTranslatedCollection: ({ collection, config, }: {
|
|
24
|
+
collection: ResolvedMultilangCollection;
|
|
25
|
+
config: CollectionConfig;
|
|
26
|
+
}) => CollectionConfig;
|