@roxxel/payload-multilang 0.0.5 → 0.0.7

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.
Files changed (39) hide show
  1. package/README.md +16 -3
  2. package/dist/components/LanguageListToolbar.js +4 -3
  3. package/dist/components/LanguageListToolbar.js.map +1 -1
  4. package/dist/components/LanguageMetabox.js +18 -17
  5. package/dist/components/LanguageMetabox.js.map +1 -1
  6. package/dist/components/TranslationActionsClient.js +16 -14
  7. package/dist/components/TranslationActionsClient.js.map +1 -1
  8. package/dist/components/TranslationColumnCell.js +4 -1
  9. package/dist/components/TranslationColumnCell.js.map +1 -1
  10. package/dist/components/TranslationColumnCellClient.js +16 -7
  11. package/dist/components/TranslationColumnCellClient.js.map +1 -1
  12. package/dist/components/TranslationsTab.js +9 -8
  13. package/dist/components/TranslationsTab.js.map +1 -1
  14. package/dist/constants.d.ts +1 -1
  15. package/dist/constants.js +1 -1
  16. package/dist/constants.js.map +1 -1
  17. package/dist/endpoints/translations.js +4 -6
  18. package/dist/endpoints/translations.js.map +1 -1
  19. package/dist/hooks/translatedCollection.d.ts +2 -1
  20. package/dist/hooks/translatedCollection.js +321 -16
  21. package/dist/hooks/translatedCollection.js.map +1 -1
  22. package/dist/hooks/translatedGlobal.js +5 -1
  23. package/dist/hooks/translatedGlobal.js.map +1 -1
  24. package/dist/index.d.ts +1 -0
  25. package/dist/index.js +22 -2
  26. package/dist/index.js.map +1 -1
  27. package/dist/lib/config.js +2 -10
  28. package/dist/lib/config.js.map +1 -1
  29. package/dist/lib/data.d.ts +1 -2
  30. package/dist/lib/data.js +32 -4
  31. package/dist/lib/data.js.map +1 -1
  32. package/dist/translations.d.ts +72 -0
  33. package/dist/translations.js +72 -0
  34. package/dist/translations.js.map +1 -0
  35. package/dist/types.d.ts +0 -19
  36. package/dist/types.js.map +1 -1
  37. package/docs/configuration.md +51 -9
  38. package/docs/usage.md +17 -35
  39. package/package.json +1 -1
@@ -1,13 +1,298 @@
1
1
  import { randomUUID } from 'crypto';
2
2
  import { MULTILANG_SKIP_HOOK } from '../constants.js';
3
3
  import { asDocument, getID, getStringValue } from '../lib/data.js';
4
+ const LANGUAGE_LABEL = {
5
+ en: 'Language',
6
+ uk: 'Мова'
7
+ };
8
+ const TRANSLATIONS_LABEL = {
9
+ en: 'Translations',
10
+ uk: 'Переклади'
11
+ };
4
12
  const hasSkipContext = (context)=>context?.[MULTILANG_SKIP_HOOK] === true;
13
+ const isObject = (value)=>typeof value === 'object' && value !== null && !Array.isArray(value);
14
+ const toArray = (value)=>Array.isArray(value) ? value.filter(isObject) : [];
5
15
  const valuesAreEqual = (left, right)=>{
6
16
  if (left === right) {
7
17
  return true;
8
18
  }
9
19
  return JSON.stringify(left) === JSON.stringify(right);
10
20
  };
21
+ const fieldAffectsData = (field)=>'name' in field && typeof field.name === 'string' && field.name !== 'id' && field.type !== 'ui';
22
+ const fieldSynchronizes = (field)=>{
23
+ const custom = isObject(field.custom) ? field.custom : {};
24
+ const multilang = isObject(custom.multilang) ? custom.multilang : {};
25
+ return multilang.synchronize === true;
26
+ };
27
+ const fieldSynchronizesPosition = (field)=>{
28
+ const custom = isObject(field.custom) ? field.custom : {};
29
+ const multilang = isObject(custom.multilang) ? custom.multilang : {};
30
+ return multilang.synchronizePosition === true;
31
+ };
32
+ const hasName = (value)=>isObject(value) && typeof value.name === 'string';
33
+ const getSynchronizedBlocks = (field, inheritedSynchronize)=>{
34
+ if (field.type !== 'blocks') {
35
+ return [];
36
+ }
37
+ return field.blocks.flatMap((block)=>{
38
+ const fields = getSynchronizedFields(block.fields, inheritedSynchronize);
39
+ if (fields.length === 0) {
40
+ return [];
41
+ }
42
+ return [
43
+ {
44
+ slug: block.slug,
45
+ fields
46
+ }
47
+ ];
48
+ });
49
+ };
50
+ const getSynchronizedTabFields = (field, inheritedSynchronize)=>field.tabs.flatMap((tab)=>{
51
+ const fields = getSynchronizedFields(tab.fields, inheritedSynchronize);
52
+ if (fields.length === 0) {
53
+ return [];
54
+ }
55
+ if (hasName(tab)) {
56
+ return [
57
+ {
58
+ name: tab.name,
59
+ fields,
60
+ kind: 'group'
61
+ }
62
+ ];
63
+ }
64
+ return fields;
65
+ });
66
+ const getSynchronizedFields = (fields = [], inheritedSynchronize = false)=>fields.flatMap((field)=>{
67
+ if (field.type === 'row' || field.type === 'collapsible') {
68
+ return getSynchronizedFields(field.fields, inheritedSynchronize || fieldSynchronizes(field));
69
+ }
70
+ if (field.type === 'tabs') {
71
+ return getSynchronizedTabFields(field, inheritedSynchronize);
72
+ }
73
+ if (!fieldAffectsData(field)) {
74
+ return [];
75
+ }
76
+ const synchronize = inheritedSynchronize || fieldSynchronizes(field);
77
+ if (field.type === 'array') {
78
+ const fields = getSynchronizedFields(field.fields, synchronize);
79
+ const synchronizePosition = synchronize || fieldSynchronizesPosition(field);
80
+ if (!synchronizePosition && fields.length === 0) {
81
+ return [];
82
+ }
83
+ return [
84
+ {
85
+ name: field.name,
86
+ fields,
87
+ kind: 'array',
88
+ synchronizePosition
89
+ }
90
+ ];
91
+ }
92
+ if (field.type === 'blocks') {
93
+ const blocks = getSynchronizedBlocks(field, synchronize);
94
+ const synchronizePosition = synchronize || fieldSynchronizesPosition(field);
95
+ if (!synchronizePosition && blocks.length === 0) {
96
+ return [];
97
+ }
98
+ return [
99
+ {
100
+ name: field.name,
101
+ blocks,
102
+ kind: 'blocks',
103
+ synchronizePosition
104
+ }
105
+ ];
106
+ }
107
+ if (field.type === 'group') {
108
+ const fields = getSynchronizedFields(field.fields, synchronize);
109
+ if (fields.length === 0) {
110
+ return [];
111
+ }
112
+ return [
113
+ {
114
+ name: field.name,
115
+ fields,
116
+ kind: 'group'
117
+ }
118
+ ];
119
+ }
120
+ if (!synchronize) {
121
+ return [];
122
+ }
123
+ return [
124
+ {
125
+ name: field.name,
126
+ kind: 'value'
127
+ }
128
+ ];
129
+ });
130
+ const getRowKey = (row, index)=>{
131
+ const id = getID(row.id);
132
+ return id === undefined ? `index:${index}` : `id:${String(id)}`;
133
+ };
134
+ const getShellSignature = (value, field)=>JSON.stringify(toArray(value).map((row, index)=>({
135
+ blockName: field.kind === 'blocks' ? getStringValue(row.blockName) : undefined,
136
+ blockType: field.kind === 'blocks' ? getStringValue(row.blockType) : undefined,
137
+ key: getRowKey(row, index)
138
+ })));
139
+ const positionChanged = (field, source, previous)=>{
140
+ if (field.kind !== 'array' && field.kind !== 'blocks' || !field.synchronizePosition || !(field.name in source)) {
141
+ return false;
142
+ }
143
+ return getShellSignature(source[field.name], field) !== getShellSignature(previous[field.name], field);
144
+ };
145
+ const getBlockFieldsForRow = (field, sourceRow)=>{
146
+ if (field.kind !== 'blocks') {
147
+ return [];
148
+ }
149
+ const blockType = getStringValue(sourceRow.blockType);
150
+ return field.blocks?.find((block)=>block.slug === blockType)?.fields || [];
151
+ };
152
+ const createSynchronizedRow = (sourceRow, previousRow, targetRow, field)=>{
153
+ const row = targetRow ? {
154
+ ...targetRow
155
+ } : {};
156
+ if (field.kind === 'blocks') {
157
+ row.id = randomUUID();
158
+ }
159
+ if (field.synchronizePosition && field.kind === 'blocks' && 'blockType' in sourceRow) {
160
+ row.blockType = sourceRow.blockType;
161
+ }
162
+ if (field.synchronizePosition && field.kind === 'blocks') {
163
+ if ('blockName' in sourceRow) {
164
+ row.blockName = sourceRow.blockName;
165
+ } else {
166
+ delete row.blockName;
167
+ }
168
+ }
169
+ const fields = field.kind === 'array' ? field.fields : getBlockFieldsForRow(field, sourceRow);
170
+ return synchronizeData({
171
+ fields,
172
+ previous: previousRow || {},
173
+ source: sourceRow,
174
+ target: row
175
+ });
176
+ };
177
+ const synchronizeContainerValue = ({ field, previousSourceValue, sourceValue, targetValue })=>{
178
+ const targetRows = toArray(targetValue);
179
+ const previousSourceRows = toArray(previousSourceValue);
180
+ const sourceRows = toArray(sourceValue);
181
+ if (!field.synchronizePosition) {
182
+ const sourceRowsByPreviousKey = new Map(sourceRows.map((row, index)=>{
183
+ const previousRow = previousSourceRows[index];
184
+ const key = previousRow ? getRowKey(previousRow, index) : getRowKey(row, index);
185
+ return [
186
+ key,
187
+ row
188
+ ];
189
+ }));
190
+ return targetRows.map((targetRow, index)=>{
191
+ const previousRow = previousSourceRows[index];
192
+ const sourceRow = previousRow && sourceRowsByPreviousKey.get(getRowKey(previousRow, index)) || sourceRows[index];
193
+ return sourceRow ? createSynchronizedRow(sourceRow, previousRow, targetRow, field) : {
194
+ ...targetRow
195
+ };
196
+ });
197
+ }
198
+ const previousSourceIndexesByKey = new Map(previousSourceRows.map((row, index)=>[
199
+ getRowKey(row, index),
200
+ index
201
+ ]));
202
+ return sourceRows.map((sourceRow, index)=>{
203
+ const previousIndex = previousSourceIndexesByKey.get(getRowKey(sourceRow, index));
204
+ return createSynchronizedRow(sourceRow, previousIndex === undefined ? undefined : previousSourceRows[previousIndex], previousIndex === undefined ? undefined : targetRows[previousIndex], field);
205
+ });
206
+ };
207
+ const descriptorChanged = ({ field, previous, source })=>{
208
+ if (field.kind === 'value') {
209
+ return !valuesAreEqual(source[field.name], previous[field.name]);
210
+ }
211
+ if (field.kind === 'group') {
212
+ return descriptorsChanged({
213
+ fields: field.fields,
214
+ previous: asDocument(previous[field.name]),
215
+ source: asDocument(source[field.name])
216
+ });
217
+ }
218
+ if (!(field.name in source)) {
219
+ return false;
220
+ }
221
+ if (positionChanged(field, source, previous)) {
222
+ return true;
223
+ }
224
+ const previousRows = toArray(previous[field.name]);
225
+ const previousRowsByKey = new Map(previousRows.map((row, index)=>[
226
+ getRowKey(row, index),
227
+ row
228
+ ]));
229
+ return toArray(source[field.name]).some((sourceRow, index)=>{
230
+ const previousRow = previousRowsByKey.get(getRowKey(sourceRow, index)) || previousRows[index];
231
+ const fields = field.kind === 'array' ? field.fields : getBlockFieldsForRow(field, sourceRow);
232
+ return descriptorsChanged({
233
+ fields,
234
+ previous: previousRow || {},
235
+ source: sourceRow
236
+ });
237
+ });
238
+ };
239
+ const descriptorsChanged = ({ fields, previous, source })=>fields.some((field)=>descriptorChanged({
240
+ field,
241
+ previous,
242
+ source
243
+ }));
244
+ const synchronizeData = ({ fields, previous, source, target })=>fields.reduce((acc, field)=>{
245
+ if (field.kind === 'value') {
246
+ if (field.name in source) {
247
+ acc[field.name] = source[field.name];
248
+ } else {
249
+ delete acc[field.name];
250
+ }
251
+ return acc;
252
+ }
253
+ if (field.kind === 'group') {
254
+ if (field.name in source) {
255
+ const value = synchronizeData({
256
+ fields: field.fields,
257
+ previous: asDocument(previous[field.name]),
258
+ source: asDocument(source[field.name]),
259
+ target: asDocument(acc[field.name])
260
+ });
261
+ if (!valuesAreEqual(value, acc[field.name])) {
262
+ acc[field.name] = value;
263
+ }
264
+ } else {
265
+ delete acc[field.name];
266
+ }
267
+ return acc;
268
+ }
269
+ const value = synchronizeContainerValue({
270
+ field,
271
+ previousSourceValue: previous[field.name],
272
+ sourceValue: source[field.name],
273
+ targetValue: target[field.name]
274
+ });
275
+ if (!valuesAreEqual(value, target[field.name])) {
276
+ acc[field.name] = value;
277
+ }
278
+ return acc;
279
+ }, {
280
+ ...target
281
+ });
282
+ const getSynchronizedDataForTranslation = ({ fields, nextDoc, previous, translation })=>{
283
+ const synchronized = synchronizeData({
284
+ fields,
285
+ previous,
286
+ source: nextDoc,
287
+ target: translation
288
+ });
289
+ return fields.reduce((acc, field)=>{
290
+ if (!valuesAreEqual(synchronized[field.name], translation[field.name])) {
291
+ acc[field.name] = synchronized[field.name];
292
+ }
293
+ return acc;
294
+ }, {});
295
+ };
11
296
  const isConfiguredLanguage = (collection, language)=>Boolean(language && collection.languages.some((configuredLanguage)=>configuredLanguage.code === language));
12
297
  const getDuplicateLanguageIDs = async ({ collection, excludeID, group, language, req })=>{
13
298
  const and = [
@@ -85,8 +370,9 @@ export const createTranslatedCollectionBeforeChangeHook = ({ collection })=>asyn
85
370
  }
86
371
  return nextData;
87
372
  };
88
- export const createSynchronizedFieldsAfterChangeHook = ({ collection })=>async ({ context, doc, operation, previousDoc, req })=>{
89
- if (hasSkipContext(context) || operation !== 'update' || collection.synchronizedFields.length === 0) {
373
+ export const createSynchronizedFieldsAfterChangeHook = ({ collection, fields })=>async ({ context, doc, operation, previousDoc, req })=>{
374
+ const synchronizedFields = getSynchronizedFields(fields);
375
+ if (hasSkipContext(context) || operation !== 'update' || synchronizedFields.length === 0) {
90
376
  return doc;
91
377
  }
92
378
  const nextDoc = asDocument(doc);
@@ -96,13 +382,12 @@ export const createSynchronizedFieldsAfterChangeHook = ({ collection })=>async (
96
382
  if (!group || !currentID) {
97
383
  return doc;
98
384
  }
99
- const synchronizedData = collection.synchronizedFields.reduce((acc, fieldName)=>{
100
- if (fieldName in nextDoc && !valuesAreEqual(nextDoc[fieldName], previous[fieldName])) {
101
- acc[fieldName] = nextDoc[fieldName];
102
- }
103
- return acc;
104
- }, {});
105
- if (Object.keys(synchronizedData).length === 0) {
385
+ const changedFields = synchronizedFields.filter((field)=>descriptorChanged({
386
+ field,
387
+ previous,
388
+ source: nextDoc
389
+ }));
390
+ if (changedFields.length === 0) {
106
391
  return doc;
107
392
  }
108
393
  const translations = await req.payload.find({
@@ -126,13 +411,32 @@ export const createSynchronizedFieldsAfterChangeHook = ({ collection })=>async (
126
411
  ]
127
412
  }
128
413
  });
129
- await Promise.all(translations.docs.map((translation)=>getID(asDocument(translation).id)).filter((id)=>id !== undefined).map((id)=>req.payload.update({
414
+ await Promise.all(translations.docs.map((translation)=>{
415
+ const translationDoc = asDocument(translation);
416
+ const id = getID(translationDoc.id);
417
+ if (id === undefined) {
418
+ return undefined;
419
+ }
420
+ const synchronizedData = getSynchronizedDataForTranslation({
421
+ fields: changedFields,
422
+ nextDoc,
423
+ previous,
424
+ translation: translationDoc
425
+ });
426
+ if (Object.keys(synchronizedData).length === 0) {
427
+ return undefined;
428
+ }
429
+ return {
430
+ id,
431
+ data: synchronizedData
432
+ };
433
+ }).filter((update)=>update !== undefined).map(({ id, data })=>req.payload.update({
130
434
  id,
131
435
  collection: collection.slug,
132
436
  context: {
133
437
  [MULTILANG_SKIP_HOOK]: true
134
438
  },
135
- data: synchronizedData,
439
+ data,
136
440
  overrideAccess: true,
137
441
  req
138
442
  })));
@@ -147,7 +451,7 @@ export const createHiddenFields = ({ collection })=>[
147
451
  hidden: true
148
452
  },
149
453
  index: true,
150
- label: 'Language'
454
+ label: LANGUAGE_LABEL
151
455
  },
152
456
  {
153
457
  name: collection.fieldNames.group,
@@ -157,7 +461,7 @@ export const createHiddenFields = ({ collection })=>[
157
461
  hidden: true
158
462
  },
159
463
  index: true,
160
- label: 'Translations'
464
+ label: TRANSLATIONS_LABEL
161
465
  },
162
466
  {
163
467
  name: collection.fieldNames.meta,
@@ -192,7 +496,7 @@ export const createLanguageColumnFields = ({ collection })=>{
192
496
  disableListColumn: false,
193
497
  width: '132px'
194
498
  },
195
- label: label || 'Translations'
499
+ label: label || TRANSLATIONS_LABEL
196
500
  }
197
501
  ];
198
502
  };
@@ -242,7 +546,7 @@ export const withTranslatedCollection = ({ collection, config })=>{
242
546
  Component: '@roxxel/payload-multilang/rsc#TranslationsTab',
243
547
  path: '/translations',
244
548
  tab: {
245
- label: 'Translations',
549
+ label: ({ t })=>t('payloadMultilang:translations'),
246
550
  order: 80
247
551
  }
248
552
  }
@@ -273,7 +577,8 @@ export const withTranslatedCollection = ({ collection, config })=>{
273
577
  ...config.hooks,
274
578
  afterChange: [
275
579
  createSynchronizedFieldsAfterChangeHook({
276
- collection
580
+ collection,
581
+ fields: config.fields
277
582
  }),
278
583
  ...config.hooks?.afterChange || []
279
584
  ],
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/hooks/translatedCollection.ts"],"sourcesContent":["import type {\n CollectionAfterChangeHook,\n CollectionBeforeChangeHook,\n CollectionConfig,\n Field,\n Where,\n} from 'payload'\n\nimport { randomUUID } from 'crypto'\n\nimport type { ResolvedMultilangCollection } from '../types.js'\n\nimport { MULTILANG_SKIP_HOOK } from '../constants.js'\nimport { asDocument, getID, getStringValue } from '../lib/data.js'\n\nconst hasSkipContext = (context?: Record<string, unknown>): boolean =>\n context?.[MULTILANG_SKIP_HOOK] === true\n\nconst valuesAreEqual = (left: unknown, right: unknown): boolean => {\n if (left === right) {\n return true\n }\n\n return JSON.stringify(left) === JSON.stringify(right)\n}\n\nconst isConfiguredLanguage = (\n collection: ResolvedMultilangCollection,\n language: string | undefined,\n): boolean =>\n Boolean(\n language &&\n collection.languages.some((configuredLanguage) => configuredLanguage.code === language),\n )\n\ntype CollectionEditViews = NonNullable<\n NonNullable<NonNullable<CollectionConfig['admin']>['components']>['views']\n>['edit']\n\nconst getDuplicateLanguageIDs = 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: Parameters<CollectionBeforeChangeHook>[0]['req']\n}): Promise<Array<number | string>> => {\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 result = await req.payload.find({\n collection: collection.slug,\n depth: 0,\n limit: 200,\n overrideAccess: true,\n req,\n where: {\n and,\n },\n })\n\n return result.docs\n .map((doc) => getID(asDocument(doc).id))\n .filter((id): id is number | string => id !== undefined)\n}\n\nexport const createTranslatedCollectionBeforeChangeHook =\n ({ collection }: { collection: ResolvedMultilangCollection }): CollectionBeforeChangeHook =>\n async ({ context, data, operation, originalDoc, req }) => {\n if (hasSkipContext(context)) {\n return data\n }\n\n const nextData = asDocument(data)\n const original = asDocument(originalDoc)\n const { group: groupField, language: languageField } = collection.fieldNames\n const originalLanguage = getStringValue(original[languageField])\n const originalLanguageIsConfigured = isConfiguredLanguage(collection, originalLanguage)\n const incomingLanguage = getStringValue(nextData[languageField])\n\n if (operation === 'update' && originalLanguage && !originalLanguageIsConfigured) {\n if (\n !incomingLanguage ||\n incomingLanguage === originalLanguage ||\n !isConfiguredLanguage(collection, incomingLanguage)\n ) {\n nextData[languageField] = ''\n }\n } else if (!incomingLanguage) {\n nextData[languageField] =\n operation === 'create'\n ? collection.defaultLanguage.code\n : originalLanguageIsConfigured\n ? originalLanguage\n : ''\n }\n\n const language = getStringValue(nextData[languageField])\n\n if (\n operation === 'update' &&\n originalLanguage &&\n originalLanguageIsConfigured &&\n language !== originalLanguage\n ) {\n throw new Error('Document language cannot be changed after creation.')\n }\n\n if (language && !isConfiguredLanguage(collection, language)) {\n throw new Error(`Language \"${language}\" is not configured for ${collection.slug}.`)\n }\n\n if (!getStringValue(nextData[groupField])) {\n const fallbackGroup = getStringValue(original[groupField])\n nextData[groupField] = fallbackGroup || randomUUID()\n }\n\n const group = getStringValue(nextData[groupField])\n\n if (language && group) {\n const currentID = operation === 'update' ? getID(original.id) : undefined\n const duplicateIDs = await getDuplicateLanguageIDs({\n collection,\n excludeID: currentID,\n group,\n language,\n req,\n })\n\n if (duplicateIDs.length > 0) {\n throw new Error(\n `A ${collection.slug} translation already exists for language \"${language}\" in this translation group.`,\n )\n }\n }\n\n return nextData\n }\n\nexport const createSynchronizedFieldsAfterChangeHook =\n ({ collection }: { collection: ResolvedMultilangCollection }): CollectionAfterChangeHook =>\n async ({ context, doc, operation, previousDoc, req }) => {\n if (\n hasSkipContext(context) ||\n operation !== 'update' ||\n collection.synchronizedFields.length === 0\n ) {\n return doc\n }\n\n const nextDoc = asDocument(doc)\n const previous = asDocument(previousDoc)\n const group = getStringValue(nextDoc[collection.fieldNames.group])\n const currentID = getID(nextDoc.id)\n\n if (!group || !currentID) {\n return doc\n }\n\n const synchronizedData = collection.synchronizedFields.reduce<Record<string, unknown>>(\n (acc, fieldName) => {\n if (fieldName in nextDoc && !valuesAreEqual(nextDoc[fieldName], previous[fieldName])) {\n acc[fieldName] = nextDoc[fieldName]\n }\n\n return acc\n },\n {},\n )\n\n if (Object.keys(synchronizedData).length === 0) {\n return doc\n }\n\n const translations = await req.payload.find({\n collection: collection.slug,\n depth: 0,\n limit: 200,\n overrideAccess: true,\n req,\n where: {\n and: [\n {\n [collection.fieldNames.group]: {\n equals: group,\n },\n },\n {\n id: {\n not_equals: currentID,\n },\n },\n ],\n },\n })\n\n await Promise.all(\n translations.docs\n .map((translation) => getID(asDocument(translation).id))\n .filter((id): id is number | string => id !== undefined)\n .map((id) =>\n req.payload.update({\n id,\n collection: collection.slug,\n context: {\n [MULTILANG_SKIP_HOOK]: true,\n },\n data: synchronizedData,\n overrideAccess: true,\n req,\n }),\n ),\n )\n\n return doc\n }\n\nexport const createHiddenFields = ({\n collection,\n}: {\n collection: ResolvedMultilangCollection\n}): Field[] => [\n {\n name: collection.fieldNames.language,\n type: 'text',\n admin: {\n disableListColumn: true,\n hidden: true,\n },\n index: true,\n label: 'Language',\n },\n {\n name: collection.fieldNames.group,\n type: 'text',\n admin: {\n disableListColumn: true,\n hidden: true,\n },\n index: true,\n label: 'Translations',\n },\n {\n name: collection.fieldNames.meta,\n type: 'json',\n admin: {\n disableListColumn: true,\n hidden: true,\n },\n },\n {\n name: 'multilangLanguageMetabox',\n type: 'ui',\n admin: {\n components: {\n Field: '@roxxel/payload-multilang/client#LanguageMetabox',\n },\n position: 'sidebar',\n },\n },\n]\n\nexport const getLanguageColumnName = (): string => 'payloadMultilangTranslations'\n\nexport const createLanguageColumnFields = ({\n collection,\n}: {\n collection: ResolvedMultilangCollection\n}): Field[] => {\n const label = collection.languages\n .map((language) => language.flagLabel || language.code.toUpperCase())\n .join(' ')\n\n return [\n {\n name: getLanguageColumnName(),\n type: 'ui',\n admin: {\n components: {\n Cell: '@roxxel/payload-multilang/rsc#TranslationColumnCell',\n },\n disableListColumn: false,\n width: '132px',\n },\n label: label || 'Translations',\n },\n ]\n}\n\nexport const translatedCollectionMeta = ({\n collection,\n}: {\n collection: ResolvedMultilangCollection\n}) => ({\n defaultLanguage: collection.defaultLanguage,\n fieldNames: collection.fieldNames,\n languages: collection.languages,\n})\n\nconst getDefaultColumns = ({\n collection,\n existingDefaultColumns,\n languageColumnName,\n}: {\n collection: ResolvedMultilangCollection\n existingDefaultColumns: string[]\n languageColumnName: string\n}): string[] => {\n const hiddenMultilangColumns = new Set([\n collection.fieldNames.group,\n collection.fieldNames.language,\n collection.fieldNames.meta,\n languageColumnName,\n ])\n const visibleExistingColumns = existingDefaultColumns.filter(\n (column) => !hiddenMultilangColumns.has(column),\n )\n\n return [...visibleExistingColumns, languageColumnName]\n}\n\nexport const withTranslatedCollection = ({\n collection,\n config,\n}: {\n collection: ResolvedMultilangCollection\n config: CollectionConfig\n}): CollectionConfig => {\n const existingDefaultColumns = config.admin?.defaultColumns || []\n const languageColumnName = getLanguageColumnName()\n const existingEditViews =\n (config.admin?.components?.views?.edit as Record<string, unknown> | undefined) || {}\n const defaultColumns = getDefaultColumns({\n collection,\n existingDefaultColumns,\n languageColumnName,\n })\n\n return {\n ...config,\n admin: {\n ...config.admin,\n components: {\n ...config.admin?.components,\n beforeList: [\n ...(config.admin?.components?.beforeList || []),\n '@roxxel/payload-multilang/client#LanguageListToolbar',\n ],\n beforeListTable: config.admin?.components?.beforeListTable,\n views: {\n ...config.admin?.components?.views,\n edit: {\n ...existingEditViews,\n translations: {\n Component: '@roxxel/payload-multilang/rsc#TranslationsTab',\n path: '/translations',\n tab: {\n label: 'Translations',\n order: 80,\n },\n },\n } as CollectionEditViews,\n },\n },\n custom: {\n ...config.admin?.custom,\n payloadMultilang: translatedCollectionMeta({\n collection,\n }),\n },\n defaultColumns,\n },\n endpoints: [...(config.endpoints || [])],\n fields: [\n ...(config.fields || []),\n ...createHiddenFields({ collection }),\n ...createLanguageColumnFields({ collection }),\n ],\n hooks: {\n ...config.hooks,\n afterChange: [\n createSynchronizedFieldsAfterChangeHook({\n collection,\n }),\n ...(config.hooks?.afterChange || []),\n ],\n beforeChange: [\n createTranslatedCollectionBeforeChangeHook({\n collection,\n }),\n ...(config.hooks?.beforeChange || []),\n ],\n },\n }\n}\n"],"names":["randomUUID","MULTILANG_SKIP_HOOK","asDocument","getID","getStringValue","hasSkipContext","context","valuesAreEqual","left","right","JSON","stringify","isConfiguredLanguage","collection","language","Boolean","languages","some","configuredLanguage","code","getDuplicateLanguageIDs","excludeID","group","req","and","fieldNames","equals","push","id","not_equals","result","payload","find","slug","depth","limit","overrideAccess","where","docs","map","doc","filter","undefined","createTranslatedCollectionBeforeChangeHook","data","operation","originalDoc","nextData","original","groupField","languageField","originalLanguage","originalLanguageIsConfigured","incomingLanguage","defaultLanguage","Error","fallbackGroup","currentID","duplicateIDs","length","createSynchronizedFieldsAfterChangeHook","previousDoc","synchronizedFields","nextDoc","previous","synchronizedData","reduce","acc","fieldName","Object","keys","translations","Promise","all","translation","update","createHiddenFields","name","type","admin","disableListColumn","hidden","index","label","meta","components","Field","position","getLanguageColumnName","createLanguageColumnFields","flagLabel","toUpperCase","join","Cell","width","translatedCollectionMeta","getDefaultColumns","existingDefaultColumns","languageColumnName","hiddenMultilangColumns","Set","visibleExistingColumns","column","has","withTranslatedCollection","config","defaultColumns","existingEditViews","views","edit","beforeList","beforeListTable","Component","path","tab","order","custom","payloadMultilang","endpoints","fields","hooks","afterChange","beforeChange"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/hooks/translatedCollection.ts"],"sourcesContent":["import type {\n CollectionAfterChangeHook,\n CollectionBeforeChangeHook,\n CollectionConfig,\n Field,\n Where,\n} from 'payload'\n\nimport { randomUUID } from 'crypto'\n\nimport type { ResolvedMultilangCollection } from '../types.js'\n\nimport { MULTILANG_SKIP_HOOK } from '../constants.js'\nimport { asDocument, getID, getStringValue } from '../lib/data.js'\n\nconst LANGUAGE_LABEL = {\n en: 'Language',\n uk: 'Мова',\n}\n\nconst TRANSLATIONS_LABEL = {\n en: 'Translations',\n uk: 'Переклади',\n}\n\ntype FieldAffectingData = { name: string } & Field\ntype SynchronizedBlock = {\n fields: SynchronizedField[]\n slug: string\n}\ntype SynchronizedField =\n | {\n blocks: SynchronizedBlock[]\n kind: 'blocks'\n name: string\n synchronizePosition: boolean\n }\n | {\n fields: SynchronizedField[]\n kind: 'array'\n name: string\n synchronizePosition: boolean\n }\n | {\n fields: SynchronizedField[]\n kind: 'group'\n name: string\n }\n | {\n kind: 'value'\n name: string\n }\ntype SynchronizedContainerField = Extract<SynchronizedField, { kind: 'array' | 'blocks' }>\n\nconst hasSkipContext = (context?: Record<string, unknown>): boolean =>\n context?.[MULTILANG_SKIP_HOOK] === true\n\nconst isObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\nconst toArray = (value: unknown): Record<string, unknown>[] =>\n Array.isArray(value) ? value.filter(isObject) : []\n\nconst valuesAreEqual = (left: unknown, right: unknown): boolean => {\n if (left === right) {\n return true\n }\n\n return JSON.stringify(left) === JSON.stringify(right)\n}\n\nconst fieldAffectsData = (field: Field): field is FieldAffectingData =>\n 'name' in field && typeof field.name === 'string' && field.name !== 'id' && field.type !== 'ui'\n\nconst fieldSynchronizes = (field: Field): boolean => {\n const custom = isObject(field.custom) ? field.custom : {}\n const multilang = isObject(custom.multilang) ? custom.multilang : {}\n\n return multilang.synchronize === true\n}\n\nconst fieldSynchronizesPosition = (field: Field): boolean => {\n const custom = isObject(field.custom) ? field.custom : {}\n const multilang = isObject(custom.multilang) ? custom.multilang : {}\n\n return multilang.synchronizePosition === true\n}\n\nconst hasName = (value: unknown): value is { name: string } =>\n isObject(value) && typeof value.name === 'string'\n\nconst getSynchronizedBlocks = (\n field: Field,\n inheritedSynchronize: boolean,\n): SynchronizedBlock[] => {\n if (field.type !== 'blocks') {\n return []\n }\n\n return field.blocks.flatMap((block) => {\n const fields = getSynchronizedFields(block.fields, inheritedSynchronize)\n\n if (fields.length === 0) {\n return []\n }\n\n return [\n {\n slug: block.slug,\n fields,\n },\n ]\n })\n}\n\nconst getSynchronizedTabFields = (\n field: Extract<Field, { type: 'tabs' }>,\n inheritedSynchronize: boolean,\n): SynchronizedField[] =>\n field.tabs.flatMap<SynchronizedField>((tab) => {\n const fields = getSynchronizedFields(tab.fields, inheritedSynchronize)\n\n if (fields.length === 0) {\n return []\n }\n\n if (hasName(tab)) {\n return [\n {\n name: tab.name,\n fields,\n kind: 'group',\n },\n ]\n }\n\n return fields\n })\n\nconst getSynchronizedFields = (\n fields: Field[] = [],\n inheritedSynchronize = false,\n): SynchronizedField[] =>\n fields.flatMap<SynchronizedField>((field) => {\n if (field.type === 'row' || field.type === 'collapsible') {\n return getSynchronizedFields(\n field.fields,\n inheritedSynchronize || fieldSynchronizes(field),\n )\n }\n\n if (field.type === 'tabs') {\n return getSynchronizedTabFields(field, inheritedSynchronize)\n }\n\n if (!fieldAffectsData(field)) {\n return []\n }\n\n const synchronize = inheritedSynchronize || fieldSynchronizes(field)\n\n if (field.type === 'array') {\n const fields = getSynchronizedFields(field.fields, synchronize)\n const synchronizePosition = synchronize || fieldSynchronizesPosition(field)\n\n if (!synchronizePosition && fields.length === 0) {\n return []\n }\n\n return [\n {\n name: field.name,\n fields,\n kind: 'array',\n synchronizePosition,\n },\n ]\n }\n\n if (field.type === 'blocks') {\n const blocks = getSynchronizedBlocks(field, synchronize)\n const synchronizePosition = synchronize || fieldSynchronizesPosition(field)\n\n if (!synchronizePosition && blocks.length === 0) {\n return []\n }\n\n return [\n {\n name: field.name,\n blocks,\n kind: 'blocks',\n synchronizePosition,\n },\n ]\n }\n\n if (field.type === 'group') {\n const fields = getSynchronizedFields(field.fields, synchronize)\n\n if (fields.length === 0) {\n return []\n }\n\n return [\n {\n name: field.name,\n fields,\n kind: 'group',\n },\n ]\n }\n\n if (!synchronize) {\n return []\n }\n\n return [\n {\n name: field.name,\n kind: 'value',\n },\n ]\n })\n\nconst getRowKey = (row: Record<string, unknown>, index: number): string => {\n const id = getID(row.id)\n\n return id === undefined ? `index:${index}` : `id:${String(id)}`\n}\n\nconst getShellSignature = (value: unknown, field: SynchronizedField): string =>\n JSON.stringify(\n toArray(value).map((row, index) => ({\n blockName: field.kind === 'blocks' ? getStringValue(row.blockName) : undefined,\n blockType: field.kind === 'blocks' ? getStringValue(row.blockType) : undefined,\n key: getRowKey(row, index),\n })),\n )\n\nconst positionChanged = (\n field: SynchronizedField,\n source: Record<string, unknown>,\n previous: Record<string, unknown>,\n): boolean => {\n if (\n (field.kind !== 'array' && field.kind !== 'blocks') ||\n !field.synchronizePosition ||\n !(field.name in source)\n ) {\n return false\n }\n\n return (\n getShellSignature(source[field.name], field) !==\n getShellSignature(previous[field.name], field)\n )\n}\n\nconst getBlockFieldsForRow = (\n field: SynchronizedField,\n sourceRow: Record<string, unknown>,\n): SynchronizedField[] => {\n if (field.kind !== 'blocks') {\n return []\n }\n\n const blockType = getStringValue(sourceRow.blockType)\n\n return field.blocks?.find((block) => block.slug === blockType)?.fields || []\n}\n\nconst createSynchronizedRow = (\n sourceRow: Record<string, unknown>,\n previousRow: Record<string, unknown> | undefined,\n targetRow: Record<string, unknown> | undefined,\n field: SynchronizedContainerField,\n): Record<string, unknown> => {\n const row = targetRow ? { ...targetRow } : {}\n\n if (field.kind === 'blocks') {\n row.id = randomUUID()\n }\n\n if (field.synchronizePosition && field.kind === 'blocks' && 'blockType' in sourceRow) {\n row.blockType = sourceRow.blockType\n }\n\n if (field.synchronizePosition && field.kind === 'blocks') {\n if ('blockName' in sourceRow) {\n row.blockName = sourceRow.blockName\n } else {\n delete row.blockName\n }\n }\n\n const fields =\n field.kind === 'array' ? field.fields : getBlockFieldsForRow(field, sourceRow)\n\n return synchronizeData({\n fields,\n previous: previousRow || {},\n source: sourceRow,\n target: row,\n })\n}\n\nconst synchronizeContainerValue = ({\n field,\n previousSourceValue,\n sourceValue,\n targetValue,\n}: {\n field: SynchronizedContainerField\n previousSourceValue: unknown\n sourceValue: unknown\n targetValue: unknown\n}): unknown[] => {\n const targetRows = toArray(targetValue)\n const previousSourceRows = toArray(previousSourceValue)\n const sourceRows = toArray(sourceValue)\n\n if (!field.synchronizePosition) {\n const sourceRowsByPreviousKey = new Map(\n sourceRows.map((row, index) => {\n const previousRow = previousSourceRows[index]\n const key = previousRow ? getRowKey(previousRow, index) : getRowKey(row, index)\n\n return [key, row] as const\n }),\n )\n\n return targetRows.map((targetRow, index) => {\n const previousRow = previousSourceRows[index]\n const sourceRow =\n (previousRow && sourceRowsByPreviousKey.get(getRowKey(previousRow, index))) ||\n sourceRows[index]\n\n return sourceRow\n ? createSynchronizedRow(sourceRow, previousRow, targetRow, field)\n : { ...targetRow }\n })\n }\n\n const previousSourceIndexesByKey = new Map(\n previousSourceRows.map((row, index) => [getRowKey(row, index), index] as const),\n )\n\n return sourceRows.map((sourceRow, index) => {\n const previousIndex = previousSourceIndexesByKey.get(getRowKey(sourceRow, index))\n\n return createSynchronizedRow(\n sourceRow,\n previousIndex === undefined ? undefined : previousSourceRows[previousIndex],\n previousIndex === undefined ? undefined : targetRows[previousIndex],\n field,\n )\n })\n}\n\nconst descriptorChanged = ({\n field,\n previous,\n source,\n}: {\n field: SynchronizedField\n previous: Record<string, unknown>\n source: Record<string, unknown>\n}): boolean => {\n if (field.kind === 'value') {\n return !valuesAreEqual(source[field.name], previous[field.name])\n }\n\n if (field.kind === 'group') {\n return descriptorsChanged({\n fields: field.fields,\n previous: asDocument(previous[field.name]),\n source: asDocument(source[field.name]),\n })\n }\n\n if (!(field.name in source)) {\n return false\n }\n\n if (positionChanged(field, source, previous)) {\n return true\n }\n\n const previousRows = toArray(previous[field.name])\n const previousRowsByKey = new Map(\n previousRows.map((row, index) => [getRowKey(row, index), row] as const),\n )\n\n return toArray(source[field.name]).some((sourceRow, index) => {\n const previousRow = previousRowsByKey.get(getRowKey(sourceRow, index)) || previousRows[index]\n const fields =\n field.kind === 'array' ? field.fields : getBlockFieldsForRow(field, sourceRow)\n\n return descriptorsChanged({\n fields,\n previous: previousRow || {},\n source: sourceRow,\n })\n })\n}\n\nconst descriptorsChanged = ({\n fields,\n previous,\n source,\n}: {\n fields: SynchronizedField[]\n previous: Record<string, unknown>\n source: Record<string, unknown>\n}): boolean =>\n fields.some((field) =>\n descriptorChanged({\n field,\n previous,\n source,\n }),\n )\n\nconst synchronizeData = ({\n fields,\n previous,\n source,\n target,\n}: {\n fields: SynchronizedField[]\n previous: Record<string, unknown>\n source: Record<string, unknown>\n target: Record<string, unknown>\n}): Record<string, unknown> =>\n fields.reduce<Record<string, unknown>>((acc, field) => {\n if (field.kind === 'value') {\n if (field.name in source) {\n acc[field.name] = source[field.name]\n } else {\n delete acc[field.name]\n }\n\n return acc\n }\n\n if (field.kind === 'group') {\n if (field.name in source) {\n const value = synchronizeData({\n fields: field.fields,\n previous: asDocument(previous[field.name]),\n source: asDocument(source[field.name]),\n target: asDocument(acc[field.name]),\n })\n\n if (!valuesAreEqual(value, acc[field.name])) {\n acc[field.name] = value\n }\n } else {\n delete acc[field.name]\n }\n\n return acc\n }\n\n const value = synchronizeContainerValue({\n field,\n previousSourceValue: previous[field.name],\n sourceValue: source[field.name],\n targetValue: target[field.name],\n })\n\n if (!valuesAreEqual(value, target[field.name])) {\n acc[field.name] = value\n }\n\n return acc\n }, { ...target })\n\nconst getSynchronizedDataForTranslation = ({\n fields,\n nextDoc,\n previous,\n translation,\n}: {\n fields: SynchronizedField[]\n nextDoc: Record<string, unknown>\n previous: Record<string, unknown>\n translation: Record<string, unknown>\n}): Record<string, unknown> => {\n const synchronized = synchronizeData({\n fields,\n previous,\n source: nextDoc,\n target: translation,\n })\n\n return fields.reduce<Record<string, unknown>>((acc, field) => {\n if (!valuesAreEqual(synchronized[field.name], translation[field.name])) {\n acc[field.name] = synchronized[field.name]\n }\n\n return acc\n }, {})\n}\n\nconst isConfiguredLanguage = (\n collection: ResolvedMultilangCollection,\n language: string | undefined,\n): boolean =>\n Boolean(\n language &&\n collection.languages.some((configuredLanguage) => configuredLanguage.code === language),\n )\n\ntype CollectionEditViews = NonNullable<\n NonNullable<NonNullable<CollectionConfig['admin']>['components']>['views']\n>['edit']\n\nconst getDuplicateLanguageIDs = 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: Parameters<CollectionBeforeChangeHook>[0]['req']\n}): Promise<Array<number | string>> => {\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 result = await req.payload.find({\n collection: collection.slug,\n depth: 0,\n limit: 200,\n overrideAccess: true,\n req,\n where: {\n and,\n },\n })\n\n return result.docs\n .map((doc) => getID(asDocument(doc).id))\n .filter((id): id is number | string => id !== undefined)\n}\n\nexport const createTranslatedCollectionBeforeChangeHook =\n ({ collection }: { collection: ResolvedMultilangCollection }): CollectionBeforeChangeHook =>\n async ({ context, data, operation, originalDoc, req }) => {\n if (hasSkipContext(context)) {\n return data\n }\n\n const nextData = asDocument(data)\n const original = asDocument(originalDoc)\n const { group: groupField, language: languageField } = collection.fieldNames\n const originalLanguage = getStringValue(original[languageField])\n const originalLanguageIsConfigured = isConfiguredLanguage(collection, originalLanguage)\n const incomingLanguage = getStringValue(nextData[languageField])\n\n if (operation === 'update' && originalLanguage && !originalLanguageIsConfigured) {\n if (\n !incomingLanguage ||\n incomingLanguage === originalLanguage ||\n !isConfiguredLanguage(collection, incomingLanguage)\n ) {\n nextData[languageField] = ''\n }\n } else if (!incomingLanguage) {\n nextData[languageField] =\n operation === 'create'\n ? collection.defaultLanguage.code\n : originalLanguageIsConfigured\n ? originalLanguage\n : ''\n }\n\n const language = getStringValue(nextData[languageField])\n\n if (\n operation === 'update' &&\n originalLanguage &&\n originalLanguageIsConfigured &&\n language !== originalLanguage\n ) {\n throw new Error('Document language cannot be changed after creation.')\n }\n\n if (language && !isConfiguredLanguage(collection, language)) {\n throw new Error(`Language \"${language}\" is not configured for ${collection.slug}.`)\n }\n\n if (!getStringValue(nextData[groupField])) {\n const fallbackGroup = getStringValue(original[groupField])\n nextData[groupField] = fallbackGroup || randomUUID()\n }\n\n const group = getStringValue(nextData[groupField])\n\n if (language && group) {\n const currentID = operation === 'update' ? getID(original.id) : undefined\n const duplicateIDs = await getDuplicateLanguageIDs({\n collection,\n excludeID: currentID,\n group,\n language,\n req,\n })\n\n if (duplicateIDs.length > 0) {\n throw new Error(\n `A ${collection.slug} translation already exists for language \"${language}\" in this translation group.`,\n )\n }\n }\n\n return nextData\n }\n\nexport const createSynchronizedFieldsAfterChangeHook =\n ({\n collection,\n fields,\n }: {\n collection: ResolvedMultilangCollection\n fields?: Field[]\n }): CollectionAfterChangeHook =>\n async ({ context, doc, operation, previousDoc, req }) => {\n const synchronizedFields = getSynchronizedFields(fields)\n\n if (hasSkipContext(context) || operation !== 'update' || synchronizedFields.length === 0) {\n return doc\n }\n\n const nextDoc = asDocument(doc)\n const previous = asDocument(previousDoc)\n const group = getStringValue(nextDoc[collection.fieldNames.group])\n const currentID = getID(nextDoc.id)\n\n if (!group || !currentID) {\n return doc\n }\n\n const changedFields = synchronizedFields.filter((field) =>\n descriptorChanged({\n field,\n previous,\n source: nextDoc,\n }),\n )\n\n if (changedFields.length === 0) {\n return doc\n }\n\n const translations = await req.payload.find({\n collection: collection.slug,\n depth: 0,\n limit: 200,\n overrideAccess: true,\n req,\n where: {\n and: [\n {\n [collection.fieldNames.group]: {\n equals: group,\n },\n },\n {\n id: {\n not_equals: currentID,\n },\n },\n ],\n },\n })\n\n await Promise.all(\n translations.docs\n .map((translation) => {\n const translationDoc = asDocument(translation)\n const id = getID(translationDoc.id)\n\n if (id === undefined) {\n return undefined\n }\n\n const synchronizedData = getSynchronizedDataForTranslation({\n fields: changedFields,\n nextDoc,\n previous,\n translation: translationDoc,\n })\n\n if (Object.keys(synchronizedData).length === 0) {\n return undefined\n }\n\n return {\n id,\n data: synchronizedData,\n }\n })\n .filter(\n (update): update is { data: Record<string, unknown>; id: number | string } =>\n update !== undefined,\n )\n .map(({ id, data }) =>\n req.payload.update({\n id,\n collection: collection.slug,\n context: {\n [MULTILANG_SKIP_HOOK]: true,\n },\n data,\n overrideAccess: true,\n req,\n }),\n ),\n )\n\n return doc\n }\n\nexport const createHiddenFields = ({\n collection,\n}: {\n collection: ResolvedMultilangCollection\n}): Field[] => [\n {\n name: collection.fieldNames.language,\n type: 'text',\n admin: {\n disableListColumn: true,\n hidden: true,\n },\n index: true,\n label: LANGUAGE_LABEL,\n },\n {\n name: collection.fieldNames.group,\n type: 'text',\n admin: {\n disableListColumn: true,\n hidden: true,\n },\n index: true,\n label: TRANSLATIONS_LABEL,\n },\n {\n name: collection.fieldNames.meta,\n type: 'json',\n admin: {\n disableListColumn: true,\n hidden: true,\n },\n },\n {\n name: 'multilangLanguageMetabox',\n type: 'ui',\n admin: {\n components: {\n Field: '@roxxel/payload-multilang/client#LanguageMetabox',\n },\n position: 'sidebar',\n },\n },\n]\n\nexport const getLanguageColumnName = (): string => 'payloadMultilangTranslations'\n\nexport const createLanguageColumnFields = ({\n collection,\n}: {\n collection: ResolvedMultilangCollection\n}): Field[] => {\n const label = collection.languages\n .map((language) => language.flagLabel || language.code.toUpperCase())\n .join(' ')\n\n return [\n {\n name: getLanguageColumnName(),\n type: 'ui',\n admin: {\n components: {\n Cell: '@roxxel/payload-multilang/rsc#TranslationColumnCell',\n },\n disableListColumn: false,\n width: '132px',\n },\n label: label || TRANSLATIONS_LABEL,\n },\n ]\n}\n\nexport const translatedCollectionMeta = ({\n collection,\n}: {\n collection: ResolvedMultilangCollection\n}) => ({\n defaultLanguage: collection.defaultLanguage,\n fieldNames: collection.fieldNames,\n languages: collection.languages,\n})\n\nconst getDefaultColumns = ({\n collection,\n existingDefaultColumns,\n languageColumnName,\n}: {\n collection: ResolvedMultilangCollection\n existingDefaultColumns: string[]\n languageColumnName: string\n}): string[] => {\n const hiddenMultilangColumns = new Set([\n collection.fieldNames.group,\n collection.fieldNames.language,\n collection.fieldNames.meta,\n languageColumnName,\n ])\n const visibleExistingColumns = existingDefaultColumns.filter(\n (column) => !hiddenMultilangColumns.has(column),\n )\n\n return [...visibleExistingColumns, languageColumnName]\n}\n\nexport const withTranslatedCollection = ({\n collection,\n config,\n}: {\n collection: ResolvedMultilangCollection\n config: CollectionConfig\n}): CollectionConfig => {\n const existingDefaultColumns = config.admin?.defaultColumns || []\n const languageColumnName = getLanguageColumnName()\n const existingEditViews =\n (config.admin?.components?.views?.edit as Record<string, unknown> | undefined) || {}\n const defaultColumns = getDefaultColumns({\n collection,\n existingDefaultColumns,\n languageColumnName,\n })\n\n return {\n ...config,\n admin: {\n ...config.admin,\n components: {\n ...config.admin?.components,\n beforeList: [\n ...(config.admin?.components?.beforeList || []),\n '@roxxel/payload-multilang/client#LanguageListToolbar',\n ],\n beforeListTable: config.admin?.components?.beforeListTable,\n views: {\n ...config.admin?.components?.views,\n edit: {\n ...existingEditViews,\n translations: {\n Component: '@roxxel/payload-multilang/rsc#TranslationsTab',\n path: '/translations',\n tab: {\n label: ({ t }) => t('payloadMultilang:translations'),\n order: 80,\n },\n },\n } as CollectionEditViews,\n },\n },\n custom: {\n ...config.admin?.custom,\n payloadMultilang: translatedCollectionMeta({\n collection,\n }),\n },\n defaultColumns,\n },\n endpoints: [...(config.endpoints || [])],\n fields: [\n ...(config.fields || []),\n ...createHiddenFields({ collection }),\n ...createLanguageColumnFields({ collection }),\n ],\n hooks: {\n ...config.hooks,\n afterChange: [\n createSynchronizedFieldsAfterChangeHook({\n collection,\n fields: config.fields,\n }),\n ...(config.hooks?.afterChange || []),\n ],\n beforeChange: [\n createTranslatedCollectionBeforeChangeHook({\n collection,\n }),\n ...(config.hooks?.beforeChange || []),\n ],\n },\n }\n}\n"],"names":["randomUUID","MULTILANG_SKIP_HOOK","asDocument","getID","getStringValue","LANGUAGE_LABEL","en","uk","TRANSLATIONS_LABEL","hasSkipContext","context","isObject","value","Array","isArray","toArray","filter","valuesAreEqual","left","right","JSON","stringify","fieldAffectsData","field","name","type","fieldSynchronizes","custom","multilang","synchronize","fieldSynchronizesPosition","synchronizePosition","hasName","getSynchronizedBlocks","inheritedSynchronize","blocks","flatMap","block","fields","getSynchronizedFields","length","slug","getSynchronizedTabFields","tabs","tab","kind","getRowKey","row","index","id","undefined","String","getShellSignature","map","blockName","blockType","key","positionChanged","source","previous","getBlockFieldsForRow","sourceRow","find","createSynchronizedRow","previousRow","targetRow","synchronizeData","target","synchronizeContainerValue","previousSourceValue","sourceValue","targetValue","targetRows","previousSourceRows","sourceRows","sourceRowsByPreviousKey","Map","get","previousSourceIndexesByKey","previousIndex","descriptorChanged","descriptorsChanged","previousRows","previousRowsByKey","some","reduce","acc","getSynchronizedDataForTranslation","nextDoc","translation","synchronized","isConfiguredLanguage","collection","language","Boolean","languages","configuredLanguage","code","getDuplicateLanguageIDs","excludeID","group","req","and","fieldNames","equals","push","not_equals","result","payload","depth","limit","overrideAccess","where","docs","doc","createTranslatedCollectionBeforeChangeHook","data","operation","originalDoc","nextData","original","groupField","languageField","originalLanguage","originalLanguageIsConfigured","incomingLanguage","defaultLanguage","Error","fallbackGroup","currentID","duplicateIDs","createSynchronizedFieldsAfterChangeHook","previousDoc","synchronizedFields","changedFields","translations","Promise","all","translationDoc","synchronizedData","Object","keys","update","createHiddenFields","admin","disableListColumn","hidden","label","meta","components","Field","position","getLanguageColumnName","createLanguageColumnFields","flagLabel","toUpperCase","join","Cell","width","translatedCollectionMeta","getDefaultColumns","existingDefaultColumns","languageColumnName","hiddenMultilangColumns","Set","visibleExistingColumns","column","has","withTranslatedCollection","config","defaultColumns","existingEditViews","views","edit","beforeList","beforeListTable","Component","path","t","order","payloadMultilang","endpoints","hooks","afterChange","beforeChange"],"mappings":"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"}
@@ -1,3 +1,7 @@
1
+ const LOCALIZED_CONTENT_LABEL = {
2
+ en: 'Localized content',
3
+ uk: 'Локалізований вміст'
4
+ };
1
5
  const withOptionalRequired = (field, required)=>{
2
6
  if (required || !('required' in field)) {
3
7
  return field;
@@ -42,7 +46,7 @@ export const translatedGlobalMeta = ({ global })=>({
42
46
  });
43
47
  export const createGlobalLanguageTabsField = ({ config, global })=>({
44
48
  type: 'tabs',
45
- label: global.label,
49
+ label: global.label === 'Localized content' ? LOCALIZED_CONTENT_LABEL : global.label,
46
50
  tabs: global.languages.map((language)=>({
47
51
  name: language.code,
48
52
  fields: config.fields.map((field)=>cloneField(field, language.code === global.defaultLanguage.code)),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/hooks/translatedGlobal.ts"],"sourcesContent":["import type { Field, GlobalConfig, Tab } from 'payload'\n\nimport type { ResolvedMultilangGlobal } from '../types.js'\n\nconst withOptionalRequired = (field: Field, required: boolean): Field => {\n if (required || !('required' in field)) {\n return field\n }\n\n return {\n ...field,\n required: false,\n } as Field\n}\n\nconst cloneField = (field: Field, required: boolean): Field => {\n if (field.type === 'tabs') {\n return withOptionalRequired(\n {\n ...field,\n tabs: field.tabs.map((tab) => ({\n ...tab,\n fields: tab.fields.map((tabField) => cloneField(tabField, required)),\n })),\n },\n required,\n )\n }\n\n if ('fields' in field && Array.isArray(field.fields)) {\n return withOptionalRequired(\n {\n ...field,\n fields: field.fields.map((childField) => cloneField(childField, required)),\n },\n required,\n )\n }\n\n if (field.type === 'blocks') {\n return withOptionalRequired(\n {\n ...field,\n blocks: field.blocks.map((block) => ({\n ...block,\n fields: block.fields.map((blockField) => cloneField(blockField, required)),\n })),\n },\n required,\n )\n }\n\n return withOptionalRequired(\n {\n ...field,\n },\n required,\n )\n}\n\nexport const translatedGlobalMeta = ({\n global,\n}: {\n global: ResolvedMultilangGlobal\n}) => ({\n defaultLanguage: global.defaultLanguage,\n languages: global.languages,\n})\n\nexport const createGlobalLanguageTabsField = ({\n config,\n global,\n}: {\n config: GlobalConfig\n global: ResolvedMultilangGlobal\n}): Field => ({\n type: 'tabs',\n label: global.label,\n tabs: global.languages.map(\n (language): Tab => ({\n name: language.code,\n fields: config.fields.map((field) =>\n cloneField(field, language.code === global.defaultLanguage.code),\n ),\n label: language.flagLabel ? `${language.flagLabel} ${language.name}` : language.name,\n }),\n ),\n})\n\nexport const withTranslatedGlobal = ({\n config,\n global,\n}: {\n config: GlobalConfig\n global: ResolvedMultilangGlobal\n}): GlobalConfig => ({\n ...config,\n admin: {\n ...config.admin,\n custom: {\n ...config.admin?.custom,\n payloadMultilang: translatedGlobalMeta({\n global,\n }),\n },\n },\n fields: [\n createGlobalLanguageTabsField({\n config,\n global,\n }),\n ],\n})\n"],"names":["withOptionalRequired","field","required","cloneField","type","tabs","map","tab","fields","tabField","Array","isArray","childField","blocks","block","blockField","translatedGlobalMeta","global","defaultLanguage","languages","createGlobalLanguageTabsField","config","label","language","name","code","flagLabel","withTranslatedGlobal","admin","custom","payloadMultilang"],"mappings":"AAIA,MAAMA,uBAAuB,CAACC,OAAcC;IAC1C,IAAIA,YAAY,CAAE,CAAA,cAAcD,KAAI,GAAI;QACtC,OAAOA;IACT;IAEA,OAAO;QACL,GAAGA,KAAK;QACRC,UAAU;IACZ;AACF;AAEA,MAAMC,aAAa,CAACF,OAAcC;IAChC,IAAID,MAAMG,IAAI,KAAK,QAAQ;QACzB,OAAOJ,qBACL;YACE,GAAGC,KAAK;YACRI,MAAMJ,MAAMI,IAAI,CAACC,GAAG,CAAC,CAACC,MAAS,CAAA;oBAC7B,GAAGA,GAAG;oBACNC,QAAQD,IAAIC,MAAM,CAACF,GAAG,CAAC,CAACG,WAAaN,WAAWM,UAAUP;gBAC5D,CAAA;QACF,GACAA;IAEJ;IAEA,IAAI,YAAYD,SAASS,MAAMC,OAAO,CAACV,MAAMO,MAAM,GAAG;QACpD,OAAOR,qBACL;YACE,GAAGC,KAAK;YACRO,QAAQP,MAAMO,MAAM,CAACF,GAAG,CAAC,CAACM,aAAeT,WAAWS,YAAYV;QAClE,GACAA;IAEJ;IAEA,IAAID,MAAMG,IAAI,KAAK,UAAU;QAC3B,OAAOJ,qBACL;YACE,GAAGC,KAAK;YACRY,QAAQZ,MAAMY,MAAM,CAACP,GAAG,CAAC,CAACQ,QAAW,CAAA;oBACnC,GAAGA,KAAK;oBACRN,QAAQM,MAAMN,MAAM,CAACF,GAAG,CAAC,CAACS,aAAeZ,WAAWY,YAAYb;gBAClE,CAAA;QACF,GACAA;IAEJ;IAEA,OAAOF,qBACL;QACE,GAAGC,KAAK;IACV,GACAC;AAEJ;AAEA,OAAO,MAAMc,uBAAuB,CAAC,EACnCC,MAAM,EAGP,GAAM,CAAA;QACLC,iBAAiBD,OAAOC,eAAe;QACvCC,WAAWF,OAAOE,SAAS;IAC7B,CAAA,EAAE;AAEF,OAAO,MAAMC,gCAAgC,CAAC,EAC5CC,MAAM,EACNJ,MAAM,EAIP,GAAa,CAAA;QACZb,MAAM;QACNkB,OAAOL,OAAOK,KAAK;QACnBjB,MAAMY,OAAOE,SAAS,CAACb,GAAG,CACxB,CAACiB,WAAmB,CAAA;gBAClBC,MAAMD,SAASE,IAAI;gBACnBjB,QAAQa,OAAOb,MAAM,CAACF,GAAG,CAAC,CAACL,QACzBE,WAAWF,OAAOsB,SAASE,IAAI,KAAKR,OAAOC,eAAe,CAACO,IAAI;gBAEjEH,OAAOC,SAASG,SAAS,GAAG,GAAGH,SAASG,SAAS,CAAC,CAAC,EAAEH,SAASC,IAAI,EAAE,GAAGD,SAASC,IAAI;YACtF,CAAA;IAEJ,CAAA,EAAE;AAEF,OAAO,MAAMG,uBAAuB,CAAC,EACnCN,MAAM,EACNJ,MAAM,EAIP,GAAoB,CAAA;QACnB,GAAGI,MAAM;QACTO,OAAO;YACL,GAAGP,OAAOO,KAAK;YACfC,QAAQ;gBACN,GAAGR,OAAOO,KAAK,EAAEC,MAAM;gBACvBC,kBAAkBd,qBAAqB;oBACrCC;gBACF;YACF;QACF;QACAT,QAAQ;YACNY,8BAA8B;gBAC5BC;gBACAJ;YACF;SACD;IACH,CAAA,EAAE"}
1
+ {"version":3,"sources":["../../src/hooks/translatedGlobal.ts"],"sourcesContent":["import type { Field, GlobalConfig, Tab } from 'payload'\n\nimport type { ResolvedMultilangGlobal } from '../types.js'\n\nconst LOCALIZED_CONTENT_LABEL = {\n en: 'Localized content',\n uk: 'Локалізований вміст',\n}\n\nconst withOptionalRequired = (field: Field, required: boolean): Field => {\n if (required || !('required' in field)) {\n return field\n }\n\n return {\n ...field,\n required: false,\n } as Field\n}\n\nconst cloneField = (field: Field, required: boolean): Field => {\n if (field.type === 'tabs') {\n return withOptionalRequired(\n {\n ...field,\n tabs: field.tabs.map((tab) => ({\n ...tab,\n fields: tab.fields.map((tabField) => cloneField(tabField, required)),\n })),\n },\n required,\n )\n }\n\n if ('fields' in field && Array.isArray(field.fields)) {\n return withOptionalRequired(\n {\n ...field,\n fields: field.fields.map((childField) => cloneField(childField, required)),\n },\n required,\n )\n }\n\n if (field.type === 'blocks') {\n return withOptionalRequired(\n {\n ...field,\n blocks: field.blocks.map((block) => ({\n ...block,\n fields: block.fields.map((blockField) => cloneField(blockField, required)),\n })),\n },\n required,\n )\n }\n\n return withOptionalRequired(\n {\n ...field,\n },\n required,\n )\n}\n\nexport const translatedGlobalMeta = ({\n global,\n}: {\n global: ResolvedMultilangGlobal\n}) => ({\n defaultLanguage: global.defaultLanguage,\n languages: global.languages,\n})\n\nexport const createGlobalLanguageTabsField = ({\n config,\n global,\n}: {\n config: GlobalConfig\n global: ResolvedMultilangGlobal\n}): Field => ({\n type: 'tabs',\n label:\n global.label === 'Localized content'\n ? LOCALIZED_CONTENT_LABEL\n : global.label,\n tabs: global.languages.map(\n (language): Tab => ({\n name: language.code,\n fields: config.fields.map((field) =>\n cloneField(field, language.code === global.defaultLanguage.code),\n ),\n label: language.flagLabel ? `${language.flagLabel} ${language.name}` : language.name,\n }),\n ),\n})\n\nexport const withTranslatedGlobal = ({\n config,\n global,\n}: {\n config: GlobalConfig\n global: ResolvedMultilangGlobal\n}): GlobalConfig => ({\n ...config,\n admin: {\n ...config.admin,\n custom: {\n ...config.admin?.custom,\n payloadMultilang: translatedGlobalMeta({\n global,\n }),\n },\n },\n fields: [\n createGlobalLanguageTabsField({\n config,\n global,\n }),\n ],\n})\n"],"names":["LOCALIZED_CONTENT_LABEL","en","uk","withOptionalRequired","field","required","cloneField","type","tabs","map","tab","fields","tabField","Array","isArray","childField","blocks","block","blockField","translatedGlobalMeta","global","defaultLanguage","languages","createGlobalLanguageTabsField","config","label","language","name","code","flagLabel","withTranslatedGlobal","admin","custom","payloadMultilang"],"mappings":"AAIA,MAAMA,0BAA0B;IAC9BC,IAAI;IACJC,IAAI;AACN;AAEA,MAAMC,uBAAuB,CAACC,OAAcC;IAC1C,IAAIA,YAAY,CAAE,CAAA,cAAcD,KAAI,GAAI;QACtC,OAAOA;IACT;IAEA,OAAO;QACL,GAAGA,KAAK;QACRC,UAAU;IACZ;AACF;AAEA,MAAMC,aAAa,CAACF,OAAcC;IAChC,IAAID,MAAMG,IAAI,KAAK,QAAQ;QACzB,OAAOJ,qBACL;YACE,GAAGC,KAAK;YACRI,MAAMJ,MAAMI,IAAI,CAACC,GAAG,CAAC,CAACC,MAAS,CAAA;oBAC7B,GAAGA,GAAG;oBACNC,QAAQD,IAAIC,MAAM,CAACF,GAAG,CAAC,CAACG,WAAaN,WAAWM,UAAUP;gBAC5D,CAAA;QACF,GACAA;IAEJ;IAEA,IAAI,YAAYD,SAASS,MAAMC,OAAO,CAACV,MAAMO,MAAM,GAAG;QACpD,OAAOR,qBACL;YACE,GAAGC,KAAK;YACRO,QAAQP,MAAMO,MAAM,CAACF,GAAG,CAAC,CAACM,aAAeT,WAAWS,YAAYV;QAClE,GACAA;IAEJ;IAEA,IAAID,MAAMG,IAAI,KAAK,UAAU;QAC3B,OAAOJ,qBACL;YACE,GAAGC,KAAK;YACRY,QAAQZ,MAAMY,MAAM,CAACP,GAAG,CAAC,CAACQ,QAAW,CAAA;oBACnC,GAAGA,KAAK;oBACRN,QAAQM,MAAMN,MAAM,CAACF,GAAG,CAAC,CAACS,aAAeZ,WAAWY,YAAYb;gBAClE,CAAA;QACF,GACAA;IAEJ;IAEA,OAAOF,qBACL;QACE,GAAGC,KAAK;IACV,GACAC;AAEJ;AAEA,OAAO,MAAMc,uBAAuB,CAAC,EACnCC,MAAM,EAGP,GAAM,CAAA;QACLC,iBAAiBD,OAAOC,eAAe;QACvCC,WAAWF,OAAOE,SAAS;IAC7B,CAAA,EAAE;AAEF,OAAO,MAAMC,gCAAgC,CAAC,EAC5CC,MAAM,EACNJ,MAAM,EAIP,GAAa,CAAA;QACZb,MAAM;QACNkB,OACEL,OAAOK,KAAK,KAAK,sBACbzB,0BACAoB,OAAOK,KAAK;QAClBjB,MAAMY,OAAOE,SAAS,CAACb,GAAG,CACxB,CAACiB,WAAmB,CAAA;gBAClBC,MAAMD,SAASE,IAAI;gBACnBjB,QAAQa,OAAOb,MAAM,CAACF,GAAG,CAAC,CAACL,QACzBE,WAAWF,OAAOsB,SAASE,IAAI,KAAKR,OAAOC,eAAe,CAACO,IAAI;gBAEjEH,OAAOC,SAASG,SAAS,GAAG,GAAGH,SAASG,SAAS,CAAC,CAAC,EAAEH,SAASC,IAAI,EAAE,GAAGD,SAASC,IAAI;YACtF,CAAA;IAEJ,CAAA,EAAE;AAEF,OAAO,MAAMG,uBAAuB,CAAC,EACnCN,MAAM,EACNJ,MAAM,EAIP,GAAoB,CAAA;QACnB,GAAGI,MAAM;QACTO,OAAO;YACL,GAAGP,OAAOO,KAAK;YACfC,QAAQ;gBACN,GAAGR,OAAOO,KAAK,EAAEC,MAAM;gBACvBC,kBAAkBd,qBAAqB;oBACrCC;gBACF;YACF;QACF;QACAT,QAAQ;YACNY,8BAA8B;gBAC5BC;gBACAJ;YACF;SACD;IACH,CAAA,EAAE"}
package/dist/index.d.ts CHANGED
@@ -3,5 +3,6 @@ import type { PayloadMultilangConfig } from './types.js';
3
3
  export { DEFAULT_FIELD_NAMES, MULTILANG_GROUP_FIELD, MULTILANG_LANGUAGE_FIELD, MULTILANG_META_FIELD, } from './constants.js';
4
4
  export { createMultilangHelpers, findGlobalByLanguageWithPayload, getDocumentTranslationsWithPayload, getDocumentTranslationWithPayload, getGroupTranslationsWithPayload, getMultilangDocumentLanguage, localizedSlugQuery, updateGlobalByLanguageWithPayload, withLanguage, } from './lib/data.js';
5
5
  export type { FindGlobalByLanguageArgs, GetDocumentTranslationArgs, GetDocumentTranslationsArgs, LocalizedSlugQuery, LocalizedSlugQueryArgs, LocalizedSlugQueryStatus, MultilangDocumentLanguageArgs, MultilangHelpers, PayloadFactory, UpdateGlobalByLanguageArgs, } from './lib/data.js';
6
+ export { type PayloadMultilangTranslationKey, payloadMultilangTranslations, type PayloadMultilangTranslations, } from './translations.js';
6
7
  export type { MultilangCollectionOptions, MultilangFieldNames, MultilangGlobalOptions, MultilangLanguage, PayloadMultilangConfig, ResolvedMultilangGlobal, TranslationMap, TranslationState, } from './types.js';
7
8
  export declare const payloadMultilang: (pluginOptions?: PayloadMultilangConfig) => Plugin;
package/dist/index.js CHANGED
@@ -2,8 +2,28 @@ import { createTranslationEndpoints } from './endpoints/translations.js';
2
2
  import { withTranslatedCollection } from './hooks/translatedCollection.js';
3
3
  import { withTranslatedGlobal } from './hooks/translatedGlobal.js';
4
4
  import { getCollectionConfig, getGlobalConfig, sanitizePluginConfig } from './lib/config.js';
5
+ import { payloadMultilangTranslations } from './translations.js';
5
6
  export { DEFAULT_FIELD_NAMES, MULTILANG_GROUP_FIELD, MULTILANG_LANGUAGE_FIELD, MULTILANG_META_FIELD } from './constants.js';
6
7
  export { createMultilangHelpers, findGlobalByLanguageWithPayload, getDocumentTranslationsWithPayload, getDocumentTranslationWithPayload, getGroupTranslationsWithPayload, getMultilangDocumentLanguage, localizedSlugQuery, updateGlobalByLanguageWithPayload, withLanguage } from './lib/data.js';
8
+ export { payloadMultilangTranslations } from './translations.js';
9
+ const isRecord = (value)=>Boolean(value) && typeof value === 'object' && !Array.isArray(value);
10
+ const mergeTranslations = (base, override)=>{
11
+ const output = {
12
+ ...base
13
+ };
14
+ Object.entries(override).forEach(([key, value])=>{
15
+ const existing = output[key];
16
+ output[key] = isRecord(existing) && isRecord(value) ? mergeTranslations(existing, value) : value;
17
+ });
18
+ return output;
19
+ };
20
+ const withPluginTranslations = (config)=>({
21
+ ...config,
22
+ i18n: {
23
+ ...config.i18n,
24
+ translations: mergeTranslations(payloadMultilangTranslations, config.i18n?.translations || {})
25
+ }
26
+ });
7
27
  export const payloadMultilang = (pluginOptions = {
8
28
  languages: []
9
29
  })=>(config)=>{
@@ -46,7 +66,7 @@ export const payloadMultilang = (pluginOptions = {
46
66
  global: translatedGlobal
47
67
  });
48
68
  });
49
- return {
69
+ return withPluginTranslations({
50
70
  ...config,
51
71
  admin: {
52
72
  ...config.admin,
@@ -61,7 +81,7 @@ export const payloadMultilang = (pluginOptions = {
61
81
  },
62
82
  collections,
63
83
  globals
64
- };
84
+ });
65
85
  };
66
86
 
67
87
  //# sourceMappingURL=index.js.map