@roxxel/payload-multilang 0.0.4 → 0.0.6
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/LICENSE.md +7 -0
- package/README.md +50 -14
- package/dist/components/LanguageListToolbar.js +4 -3
- package/dist/components/LanguageListToolbar.js.map +1 -1
- package/dist/components/LanguageMetabox.js +18 -17
- package/dist/components/LanguageMetabox.js.map +1 -1
- package/dist/components/TranslationActionsClient.js +16 -14
- package/dist/components/TranslationActionsClient.js.map +1 -1
- package/dist/components/TranslationColumnCell.js +4 -1
- package/dist/components/TranslationColumnCell.js.map +1 -1
- package/dist/components/TranslationColumnCellClient.js +16 -7
- package/dist/components/TranslationColumnCellClient.js.map +1 -1
- package/dist/components/TranslationsTab.js +9 -8
- package/dist/components/TranslationsTab.js.map +1 -1
- package/dist/constants.d.ts +4 -1
- package/dist/constants.js +4 -1
- package/dist/constants.js.map +1 -1
- package/dist/endpoints/translations.js +4 -6
- package/dist/endpoints/translations.js.map +1 -1
- package/dist/hooks/translatedCollection.d.ts +2 -1
- package/dist/hooks/translatedCollection.js +122 -15
- package/dist/hooks/translatedCollection.js.map +1 -1
- package/dist/hooks/translatedGlobal.js +5 -1
- package/dist/hooks/translatedGlobal.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +27 -3
- package/dist/index.js.map +1 -1
- package/dist/lib/config.js +2 -10
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/data.d.ts +72 -35
- package/dist/lib/data.js +118 -60
- package/dist/lib/data.js.map +1 -1
- package/dist/translations.d.ts +72 -0
- package/dist/translations.js +72 -0
- package/dist/translations.js.map +1 -0
- package/dist/types.d.ts +0 -19
- package/dist/types.js.map +1 -1
- package/docs/configuration.md +75 -10
- package/docs/helpers.md +95 -121
- package/docs/usage.md +115 -120
- package/package.json +1 -1
- package/dist/payload-config.d.js +0 -2
- package/dist/payload-config.d.js.map +0 -1
|
@@ -1,13 +1,99 @@
|
|
|
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.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 getSynchronizedFields = (fields = [])=>fields.flatMap((field)=>{
|
|
28
|
+
if (!fieldAffectsData(field) || !fieldSynchronizes(field)) {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
return [
|
|
32
|
+
{
|
|
33
|
+
name: field.name,
|
|
34
|
+
type: field.type,
|
|
35
|
+
strategy: field.type === 'array' || field.type === 'blocks' ? 'shell' : 'value'
|
|
36
|
+
}
|
|
37
|
+
];
|
|
38
|
+
});
|
|
39
|
+
const getRowKey = (row, index)=>{
|
|
40
|
+
const id = getID(row.id);
|
|
41
|
+
return id === undefined ? `index:${index}` : `id:${String(id)}`;
|
|
42
|
+
};
|
|
43
|
+
const getShellSignature = (value, field)=>JSON.stringify(toArray(value).map((row, index)=>({
|
|
44
|
+
blockName: field.type === 'blocks' ? getStringValue(row.blockName) : undefined,
|
|
45
|
+
blockType: field.type === 'blocks' ? getStringValue(row.blockType) : undefined,
|
|
46
|
+
key: getRowKey(row, index)
|
|
47
|
+
})));
|
|
48
|
+
const shellChanged = (field, nextDoc, previous)=>{
|
|
49
|
+
if (!(field.name in nextDoc)) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return getShellSignature(nextDoc[field.name], field) !== getShellSignature(previous[field.name], field);
|
|
53
|
+
};
|
|
54
|
+
const createShellRow = (sourceRow, targetRow, field)=>{
|
|
55
|
+
const row = targetRow ? {
|
|
56
|
+
...targetRow
|
|
57
|
+
} : {};
|
|
58
|
+
if (field.type === 'blocks' && 'blockType' in sourceRow) {
|
|
59
|
+
row.blockType = sourceRow.blockType;
|
|
60
|
+
}
|
|
61
|
+
if (field.type === 'blocks') {
|
|
62
|
+
if ('blockName' in sourceRow) {
|
|
63
|
+
row.blockName = sourceRow.blockName;
|
|
64
|
+
} else {
|
|
65
|
+
delete row.blockName;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return row;
|
|
69
|
+
};
|
|
70
|
+
const synchronizeShellValue = ({ field, previousSourceValue, sourceValue, targetValue })=>{
|
|
71
|
+
const targetRows = toArray(targetValue);
|
|
72
|
+
const previousSourceIndexesByKey = new Map(toArray(previousSourceValue).map((row, index)=>[
|
|
73
|
+
getRowKey(row, index),
|
|
74
|
+
index
|
|
75
|
+
]));
|
|
76
|
+
return toArray(sourceValue).map((sourceRow, index)=>{
|
|
77
|
+
const previousIndex = previousSourceIndexesByKey.get(getRowKey(sourceRow, index));
|
|
78
|
+
return createShellRow(sourceRow, previousIndex === undefined ? undefined : targetRows[previousIndex], field);
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
const getSynchronizedDataForTranslation = ({ fields, nextDoc, previous, translation })=>fields.reduce((acc, field)=>{
|
|
82
|
+
if (field.strategy === 'value') {
|
|
83
|
+
acc[field.name] = nextDoc[field.name];
|
|
84
|
+
return acc;
|
|
85
|
+
}
|
|
86
|
+
const value = synchronizeShellValue({
|
|
87
|
+
field,
|
|
88
|
+
previousSourceValue: previous[field.name],
|
|
89
|
+
sourceValue: nextDoc[field.name],
|
|
90
|
+
targetValue: translation[field.name]
|
|
91
|
+
});
|
|
92
|
+
if (!valuesAreEqual(value, translation[field.name])) {
|
|
93
|
+
acc[field.name] = value;
|
|
94
|
+
}
|
|
95
|
+
return acc;
|
|
96
|
+
}, {});
|
|
11
97
|
const isConfiguredLanguage = (collection, language)=>Boolean(language && collection.languages.some((configuredLanguage)=>configuredLanguage.code === language));
|
|
12
98
|
const getDuplicateLanguageIDs = async ({ collection, excludeID, group, language, req })=>{
|
|
13
99
|
const and = [
|
|
@@ -85,8 +171,9 @@ export const createTranslatedCollectionBeforeChangeHook = ({ collection })=>asyn
|
|
|
85
171
|
}
|
|
86
172
|
return nextData;
|
|
87
173
|
};
|
|
88
|
-
export const createSynchronizedFieldsAfterChangeHook = ({ collection })=>async ({ context, doc, operation, previousDoc, req })=>{
|
|
89
|
-
|
|
174
|
+
export const createSynchronizedFieldsAfterChangeHook = ({ collection, fields })=>async ({ context, doc, operation, previousDoc, req })=>{
|
|
175
|
+
const synchronizedFields = getSynchronizedFields(fields);
|
|
176
|
+
if (hasSkipContext(context) || operation !== 'update' || synchronizedFields.length === 0) {
|
|
90
177
|
return doc;
|
|
91
178
|
}
|
|
92
179
|
const nextDoc = asDocument(doc);
|
|
@@ -96,13 +183,13 @@ export const createSynchronizedFieldsAfterChangeHook = ({ collection })=>async (
|
|
|
96
183
|
if (!group || !currentID) {
|
|
97
184
|
return doc;
|
|
98
185
|
}
|
|
99
|
-
const
|
|
100
|
-
if (
|
|
101
|
-
|
|
186
|
+
const changedFields = synchronizedFields.filter((field)=>{
|
|
187
|
+
if (field.strategy === 'shell') {
|
|
188
|
+
return shellChanged(field, nextDoc, previous);
|
|
102
189
|
}
|
|
103
|
-
return
|
|
104
|
-
}
|
|
105
|
-
if (
|
|
190
|
+
return field.name in nextDoc && !valuesAreEqual(nextDoc[field.name], previous[field.name]);
|
|
191
|
+
});
|
|
192
|
+
if (changedFields.length === 0) {
|
|
106
193
|
return doc;
|
|
107
194
|
}
|
|
108
195
|
const translations = await req.payload.find({
|
|
@@ -126,13 +213,32 @@ export const createSynchronizedFieldsAfterChangeHook = ({ collection })=>async (
|
|
|
126
213
|
]
|
|
127
214
|
}
|
|
128
215
|
});
|
|
129
|
-
await Promise.all(translations.docs.map((translation)=>
|
|
216
|
+
await Promise.all(translations.docs.map((translation)=>{
|
|
217
|
+
const translationDoc = asDocument(translation);
|
|
218
|
+
const id = getID(translationDoc.id);
|
|
219
|
+
if (id === undefined) {
|
|
220
|
+
return undefined;
|
|
221
|
+
}
|
|
222
|
+
const synchronizedData = getSynchronizedDataForTranslation({
|
|
223
|
+
fields: changedFields,
|
|
224
|
+
nextDoc,
|
|
225
|
+
previous,
|
|
226
|
+
translation: translationDoc
|
|
227
|
+
});
|
|
228
|
+
if (Object.keys(synchronizedData).length === 0) {
|
|
229
|
+
return undefined;
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
id,
|
|
233
|
+
data: synchronizedData
|
|
234
|
+
};
|
|
235
|
+
}).filter((update)=>update !== undefined).map(({ id, data })=>req.payload.update({
|
|
130
236
|
id,
|
|
131
237
|
collection: collection.slug,
|
|
132
238
|
context: {
|
|
133
239
|
[MULTILANG_SKIP_HOOK]: true
|
|
134
240
|
},
|
|
135
|
-
data
|
|
241
|
+
data,
|
|
136
242
|
overrideAccess: true,
|
|
137
243
|
req
|
|
138
244
|
})));
|
|
@@ -147,7 +253,7 @@ export const createHiddenFields = ({ collection })=>[
|
|
|
147
253
|
hidden: true
|
|
148
254
|
},
|
|
149
255
|
index: true,
|
|
150
|
-
label:
|
|
256
|
+
label: LANGUAGE_LABEL
|
|
151
257
|
},
|
|
152
258
|
{
|
|
153
259
|
name: collection.fieldNames.group,
|
|
@@ -157,7 +263,7 @@ export const createHiddenFields = ({ collection })=>[
|
|
|
157
263
|
hidden: true
|
|
158
264
|
},
|
|
159
265
|
index: true,
|
|
160
|
-
label:
|
|
266
|
+
label: TRANSLATIONS_LABEL
|
|
161
267
|
},
|
|
162
268
|
{
|
|
163
269
|
name: collection.fieldNames.meta,
|
|
@@ -192,7 +298,7 @@ export const createLanguageColumnFields = ({ collection })=>{
|
|
|
192
298
|
disableListColumn: false,
|
|
193
299
|
width: '132px'
|
|
194
300
|
},
|
|
195
|
-
label: label ||
|
|
301
|
+
label: label || TRANSLATIONS_LABEL
|
|
196
302
|
}
|
|
197
303
|
];
|
|
198
304
|
};
|
|
@@ -242,7 +348,7 @@ export const withTranslatedCollection = ({ collection, config })=>{
|
|
|
242
348
|
Component: '@roxxel/payload-multilang/rsc#TranslationsTab',
|
|
243
349
|
path: '/translations',
|
|
244
350
|
tab: {
|
|
245
|
-
label: '
|
|
351
|
+
label: ({ t })=>t('payloadMultilang:translations'),
|
|
246
352
|
order: 80
|
|
247
353
|
}
|
|
248
354
|
}
|
|
@@ -273,7 +379,8 @@ export const withTranslatedCollection = ({ collection, config })=>{
|
|
|
273
379
|
...config.hooks,
|
|
274
380
|
afterChange: [
|
|
275
381
|
createSynchronizedFieldsAfterChangeHook({
|
|
276
|
-
collection
|
|
382
|
+
collection,
|
|
383
|
+
fields: config.fields
|
|
277
384
|
}),
|
|
278
385
|
...config.hooks?.afterChange || []
|
|
279
386
|
],
|
|
@@ -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 SynchronizedField = {\n name: string\n strategy: 'shell' | 'value'\n type: 'array' | 'blocks' | Field['type']\n}\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.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 getSynchronizedFields = (fields: Field[] = []): SynchronizedField[] =>\n fields.flatMap((field) => {\n if (!fieldAffectsData(field) || !fieldSynchronizes(field)) {\n return []\n }\n\n return [\n {\n name: field.name,\n type: field.type,\n strategy: field.type === 'array' || field.type === 'blocks' ? 'shell' : '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.type === 'blocks' ? getStringValue(row.blockName) : undefined,\n blockType: field.type === 'blocks' ? getStringValue(row.blockType) : undefined,\n key: getRowKey(row, index),\n })),\n )\n\nconst shellChanged = (\n field: SynchronizedField,\n nextDoc: Record<string, unknown>,\n previous: Record<string, unknown>,\n): boolean => {\n if (!(field.name in nextDoc)) {\n return false\n }\n\n return (\n getShellSignature(nextDoc[field.name], field) !==\n getShellSignature(previous[field.name], field)\n )\n}\n\nconst createShellRow = (\n sourceRow: Record<string, unknown>,\n targetRow: Record<string, unknown> | undefined,\n field: SynchronizedField,\n): Record<string, unknown> => {\n const row = targetRow ? { ...targetRow } : {}\n\n if (field.type === 'blocks' && 'blockType' in sourceRow) {\n row.blockType = sourceRow.blockType\n }\n\n if (field.type === 'blocks') {\n if ('blockName' in sourceRow) {\n row.blockName = sourceRow.blockName\n } else {\n delete row.blockName\n }\n }\n\n return row\n}\n\nconst synchronizeShellValue = ({\n field,\n previousSourceValue,\n sourceValue,\n targetValue,\n}: {\n field: SynchronizedField\n previousSourceValue: unknown\n sourceValue: unknown\n targetValue: unknown\n}): unknown[] => {\n const targetRows = toArray(targetValue)\n const previousSourceIndexesByKey = new Map(\n toArray(previousSourceValue).map((row, index) => [getRowKey(row, index), index] as const),\n )\n\n return toArray(sourceValue).map((sourceRow, index) => {\n const previousIndex = previousSourceIndexesByKey.get(getRowKey(sourceRow, index))\n\n return createShellRow(\n sourceRow,\n previousIndex === undefined ? undefined : targetRows[previousIndex],\n field,\n )\n })\n}\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 fields.reduce<Record<string, unknown>>((acc, field) => {\n if (field.strategy === 'value') {\n acc[field.name] = nextDoc[field.name]\n\n return acc\n }\n\n const value = synchronizeShellValue({\n field,\n previousSourceValue: previous[field.name],\n sourceValue: nextDoc[field.name],\n targetValue: translation[field.name],\n })\n\n if (!valuesAreEqual(value, translation[field.name])) {\n acc[field.name] = value\n }\n\n return acc\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 if (field.strategy === 'shell') {\n return shellChanged(field, nextDoc, previous)\n }\n\n return field.name in nextDoc && !valuesAreEqual(nextDoc[field.name], previous[field.name])\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","getSynchronizedFields","fields","flatMap","strategy","getRowKey","row","index","id","undefined","String","getShellSignature","map","blockName","blockType","key","shellChanged","nextDoc","previous","createShellRow","sourceRow","targetRow","synchronizeShellValue","previousSourceValue","sourceValue","targetValue","targetRows","previousSourceIndexesByKey","Map","previousIndex","get","getSynchronizedDataForTranslation","translation","reduce","acc","isConfiguredLanguage","collection","language","Boolean","languages","some","configuredLanguage","code","getDuplicateLanguageIDs","excludeID","group","req","and","fieldNames","equals","push","not_equals","result","payload","find","slug","depth","limit","overrideAccess","where","docs","doc","createTranslatedCollectionBeforeChangeHook","data","operation","originalDoc","nextData","original","groupField","languageField","originalLanguage","originalLanguageIsConfigured","incomingLanguage","defaultLanguage","Error","fallbackGroup","currentID","duplicateIDs","length","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","tab","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,
|
|
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
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { Plugin } from 'payload';
|
|
2
2
|
import type { PayloadMultilangConfig } from './types.js';
|
|
3
|
-
export {
|
|
3
|
+
export { DEFAULT_FIELD_NAMES, MULTILANG_GROUP_FIELD, MULTILANG_LANGUAGE_FIELD, MULTILANG_META_FIELD, } from './constants.js';
|
|
4
|
+
export { createMultilangHelpers, findGlobalByLanguageWithPayload, getDocumentTranslationsWithPayload, getDocumentTranslationWithPayload, getGroupTranslationsWithPayload, getMultilangDocumentLanguage, localizedSlugQuery, updateGlobalByLanguageWithPayload, withLanguage, } from './lib/data.js';
|
|
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';
|
|
4
7
|
export type { MultilangCollectionOptions, MultilangFieldNames, MultilangGlobalOptions, MultilangLanguage, PayloadMultilangConfig, ResolvedMultilangGlobal, TranslationMap, TranslationState, } from './types.js';
|
|
5
8
|
export declare const payloadMultilang: (pluginOptions?: PayloadMultilangConfig) => Plugin;
|
package/dist/index.js
CHANGED
|
@@ -2,13 +2,37 @@ 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
|
-
|
|
5
|
+
import { payloadMultilangTranslations } from './translations.js';
|
|
6
|
+
export { DEFAULT_FIELD_NAMES, MULTILANG_GROUP_FIELD, MULTILANG_LANGUAGE_FIELD, MULTILANG_META_FIELD } from './constants.js';
|
|
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
|
+
});
|
|
6
27
|
export const payloadMultilang = (pluginOptions = {
|
|
7
28
|
languages: []
|
|
8
29
|
})=>(config)=>{
|
|
9
30
|
if (pluginOptions.disabled) {
|
|
10
31
|
return config;
|
|
11
32
|
}
|
|
33
|
+
if (config.localization) {
|
|
34
|
+
throw new Error('payload-multilang uses per-language documents and cannot run with Payload built-in localization enabled. Set localization: false and do not use localized: true fields for this plugin model.');
|
|
35
|
+
}
|
|
12
36
|
const pluginConfig = sanitizePluginConfig(pluginOptions);
|
|
13
37
|
const incomingCollections = config.collections || [];
|
|
14
38
|
const collections = incomingCollections.map((collection)=>{
|
|
@@ -42,7 +66,7 @@ export const payloadMultilang = (pluginOptions = {
|
|
|
42
66
|
global: translatedGlobal
|
|
43
67
|
});
|
|
44
68
|
});
|
|
45
|
-
return {
|
|
69
|
+
return withPluginTranslations({
|
|
46
70
|
...config,
|
|
47
71
|
admin: {
|
|
48
72
|
...config.admin,
|
|
@@ -57,7 +81,7 @@ export const payloadMultilang = (pluginOptions = {
|
|
|
57
81
|
},
|
|
58
82
|
collections,
|
|
59
83
|
globals
|
|
60
|
-
};
|
|
84
|
+
});
|
|
61
85
|
};
|
|
62
86
|
|
|
63
87
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { CollectionConfig, Config, GlobalConfig, Plugin } from 'payload'\n\nimport type { PayloadMultilangConfig } from './types.js'\n\nimport { createTranslationEndpoints } from './endpoints/translations.js'\nimport { withTranslatedCollection } from './hooks/translatedCollection.js'\nimport { withTranslatedGlobal } from './hooks/translatedGlobal.js'\nimport {\n getCollectionConfig,\n getGlobalConfig,\n sanitizePluginConfig,\n} from './lib/config.js'\n\nexport {\n
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { CollectionConfig, Config, GlobalConfig, Plugin } from 'payload'\n\nimport type { PayloadMultilangConfig } from './types.js'\n\nimport { createTranslationEndpoints } from './endpoints/translations.js'\nimport { withTranslatedCollection } from './hooks/translatedCollection.js'\nimport { withTranslatedGlobal } from './hooks/translatedGlobal.js'\nimport {\n getCollectionConfig,\n getGlobalConfig,\n sanitizePluginConfig,\n} from './lib/config.js'\nimport { payloadMultilangTranslations } from './translations.js'\n\nexport {\n DEFAULT_FIELD_NAMES,\n MULTILANG_GROUP_FIELD,\n MULTILANG_LANGUAGE_FIELD,\n MULTILANG_META_FIELD,\n} from './constants.js'\n\nexport {\n createMultilangHelpers,\n findGlobalByLanguageWithPayload,\n getDocumentTranslationsWithPayload,\n getDocumentTranslationWithPayload,\n getGroupTranslationsWithPayload,\n getMultilangDocumentLanguage,\n localizedSlugQuery,\n updateGlobalByLanguageWithPayload,\n withLanguage,\n} from './lib/data.js'\n\nexport type {\n FindGlobalByLanguageArgs,\n GetDocumentTranslationArgs,\n GetDocumentTranslationsArgs,\n LocalizedSlugQuery,\n LocalizedSlugQueryArgs,\n LocalizedSlugQueryStatus,\n MultilangDocumentLanguageArgs,\n MultilangHelpers,\n PayloadFactory,\n UpdateGlobalByLanguageArgs,\n} from './lib/data.js'\n\nexport {\n type PayloadMultilangTranslationKey,\n payloadMultilangTranslations,\n type PayloadMultilangTranslations,\n} from './translations.js'\n\nexport type {\n MultilangCollectionOptions,\n MultilangFieldNames,\n MultilangGlobalOptions,\n MultilangLanguage,\n PayloadMultilangConfig,\n ResolvedMultilangGlobal,\n TranslationMap,\n TranslationState,\n} from './types.js'\n\ntype TranslationRecord = Record<string, unknown>\n\nconst isRecord = (value: unknown): value is TranslationRecord =>\n Boolean(value) && typeof value === 'object' && !Array.isArray(value)\n\nconst mergeTranslations = (\n base: TranslationRecord,\n override: TranslationRecord,\n): TranslationRecord => {\n const output: TranslationRecord = { ...base }\n\n Object.entries(override).forEach(([key, value]) => {\n const existing = output[key]\n\n output[key] =\n isRecord(existing) && isRecord(value)\n ? mergeTranslations(existing, value)\n : value\n })\n\n return output\n}\n\nconst withPluginTranslations = (config: Config): Config => ({\n ...config,\n i18n: {\n ...config.i18n,\n translations: mergeTranslations(\n payloadMultilangTranslations,\n (config.i18n?.translations || {}) as TranslationRecord,\n ) as NonNullable<Config['i18n']>['translations'],\n },\n})\n\nexport const payloadMultilang =\n (pluginOptions: PayloadMultilangConfig = { languages: [] }): Plugin =>\n (config: Config): Config => {\n if (pluginOptions.disabled) {\n return config\n }\n\n if (config.localization) {\n throw new Error(\n 'payload-multilang uses per-language documents and cannot run with Payload built-in localization enabled. Set localization: false and do not use localized: true fields for this plugin model.',\n )\n }\n\n const pluginConfig = sanitizePluginConfig(pluginOptions)\n\n const incomingCollections = config.collections || []\n const collections = incomingCollections.map(\n (collection): CollectionConfig => {\n const translatedCollection = getCollectionConfig(\n pluginConfig.collections,\n collection.slug,\n )\n\n if (!translatedCollection) {\n return collection\n }\n\n const translated = withTranslatedCollection({\n collection: translatedCollection,\n config: collection,\n })\n\n return {\n ...translated,\n endpoints: [\n ...(translated.endpoints || []),\n ...createTranslationEndpoints({\n collection: translatedCollection,\n collectionConfig: collection,\n }),\n ],\n }\n },\n )\n\n const incomingGlobals = config.globals || []\n const globals = incomingGlobals.map((globalConfig): GlobalConfig => {\n const translatedGlobal = getGlobalConfig(\n pluginConfig.globals,\n globalConfig.slug,\n )\n\n if (!translatedGlobal) {\n return globalConfig\n }\n\n return withTranslatedGlobal({\n config: globalConfig,\n global: translatedGlobal,\n })\n })\n\n return withPluginTranslations({\n ...config,\n admin: {\n ...config.admin,\n custom: {\n ...config.admin?.custom,\n payloadMultilang: {\n ...((config.admin?.custom as\n | { payloadMultilang?: Record<string, unknown> }\n | undefined)?.payloadMultilang || {}),\n defaultLanguage: pluginConfig.defaultLanguage,\n languages: pluginConfig.languages,\n },\n },\n },\n collections,\n globals,\n })\n }\n"],"names":["createTranslationEndpoints","withTranslatedCollection","withTranslatedGlobal","getCollectionConfig","getGlobalConfig","sanitizePluginConfig","payloadMultilangTranslations","DEFAULT_FIELD_NAMES","MULTILANG_GROUP_FIELD","MULTILANG_LANGUAGE_FIELD","MULTILANG_META_FIELD","createMultilangHelpers","findGlobalByLanguageWithPayload","getDocumentTranslationsWithPayload","getDocumentTranslationWithPayload","getGroupTranslationsWithPayload","getMultilangDocumentLanguage","localizedSlugQuery","updateGlobalByLanguageWithPayload","withLanguage","isRecord","value","Boolean","Array","isArray","mergeTranslations","base","override","output","Object","entries","forEach","key","existing","withPluginTranslations","config","i18n","translations","payloadMultilang","pluginOptions","languages","disabled","localization","Error","pluginConfig","incomingCollections","collections","map","collection","translatedCollection","slug","translated","endpoints","collectionConfig","incomingGlobals","globals","globalConfig","translatedGlobal","global","admin","custom","defaultLanguage"],"mappings":"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"}
|
package/dist/lib/config.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DEFAULT_FIELD_NAMES, DEFAULT_GLOBAL_TABS_LABEL } from '../constants.js';
|
|
2
2
|
export const resolveFieldNames = (pluginFieldNames, collectionFieldNames)=>({
|
|
3
3
|
...DEFAULT_FIELD_NAMES,
|
|
4
4
|
...pluginFieldNames || {},
|
|
5
5
|
...collectionFieldNames || {}
|
|
6
6
|
});
|
|
7
|
-
const uniqueStrings = (...values)=>Array.from(new Set(values.flatMap((value)=>(value || []).map((field)=>field.trim()).filter(Boolean))));
|
|
8
7
|
export const sanitizePluginConfig = (pluginOptions)=>{
|
|
9
8
|
const languages = normalizeConfiguredLanguages(pluginOptions.languages || []);
|
|
10
9
|
const defaultLanguage = languages.find((language)=>language.isDefault);
|
|
@@ -20,15 +19,8 @@ export const sanitizePluginConfig = (pluginOptions)=>{
|
|
|
20
19
|
{
|
|
21
20
|
slug,
|
|
22
21
|
defaultLanguage,
|
|
23
|
-
duplicate: collectionOptions.duplicate ?? true,
|
|
24
|
-
duplicateExcludeFields: [
|
|
25
|
-
...DEFAULT_DUPLICATE_EXCLUDE_FIELDS,
|
|
26
|
-
...pluginOptions.duplicateExcludeFields || [],
|
|
27
|
-
...collectionOptions.duplicateExcludeFields || []
|
|
28
|
-
],
|
|
29
22
|
fieldNames: resolveFieldNames(pluginOptions.fieldNames, collectionOptions.fields),
|
|
30
|
-
languages
|
|
31
|
-
synchronizedFields: uniqueStrings(collectionOptions.synchronizedFields)
|
|
23
|
+
languages
|
|
32
24
|
}
|
|
33
25
|
];
|
|
34
26
|
});
|
package/dist/lib/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/lib/config.ts"],"sourcesContent":["import type {\n MultilangCollectionOptions,\n MultilangFieldNames,\n MultilangGlobalOptions,\n MultilangLanguage,\n PayloadMultilangConfig,\n ResolvedMultilangCollection,\n ResolvedMultilangGlobal,\n} from '../types.js'\n\nimport {
|
|
1
|
+
{"version":3,"sources":["../../src/lib/config.ts"],"sourcesContent":["import type {\n MultilangCollectionOptions,\n MultilangFieldNames,\n MultilangGlobalOptions,\n MultilangLanguage,\n PayloadMultilangConfig,\n ResolvedMultilangCollection,\n ResolvedMultilangGlobal,\n} from '../types.js'\n\nimport { DEFAULT_FIELD_NAMES, DEFAULT_GLOBAL_TABS_LABEL } from '../constants.js'\n\nexport type SanitizedPluginConfig = {\n collections: ResolvedMultilangCollection[]\n defaultLanguage: MultilangLanguage\n disabled: boolean\n globals: ResolvedMultilangGlobal[]\n languages: MultilangLanguage[]\n raw: PayloadMultilangConfig\n}\n\nexport const resolveFieldNames = (\n pluginFieldNames?: Partial<MultilangFieldNames>,\n collectionFieldNames?: Partial<MultilangFieldNames>,\n): MultilangFieldNames => ({\n ...DEFAULT_FIELD_NAMES,\n ...(pluginFieldNames || {}),\n ...(collectionFieldNames || {}),\n})\n\nexport const sanitizePluginConfig = (\n pluginOptions: PayloadMultilangConfig,\n): SanitizedPluginConfig => {\n const languages = normalizeConfiguredLanguages(pluginOptions.languages || [])\n const defaultLanguage = languages.find((language) => language.isDefault)\n\n if (!defaultLanguage) {\n throw new Error('payload-multilang requires one active default language.')\n }\n\n const collections = Object.entries(pluginOptions.collections || {}).flatMap(\n ([slug, value]) => {\n if (!value) {\n return []\n }\n\n const collectionOptions: MultilangCollectionOptions =\n value === true ? {} : value\n\n return [\n {\n slug,\n defaultLanguage,\n fieldNames: resolveFieldNames(\n pluginOptions.fieldNames,\n collectionOptions.fields,\n ),\n languages,\n },\n ]\n },\n )\n\n const globals = Object.entries(pluginOptions.globals || {}).flatMap(\n ([slug, value]) => {\n if (!value) {\n return []\n }\n\n const globalOptions: MultilangGlobalOptions = value === true ? {} : value\n\n return [\n {\n slug,\n defaultLanguage,\n label: globalOptions.label?.trim() || DEFAULT_GLOBAL_TABS_LABEL,\n languages,\n },\n ]\n },\n )\n\n return {\n collections,\n defaultLanguage,\n disabled: Boolean(pluginOptions.disabled),\n globals,\n languages,\n raw: pluginOptions,\n }\n}\n\nexport const getCollectionConfig = (\n collections: ResolvedMultilangCollection[],\n slug: string,\n): ResolvedMultilangCollection | undefined =>\n collections.find((collection) => collection.slug === slug)\n\nexport const getGlobalConfig = (\n globals: ResolvedMultilangGlobal[],\n slug: string,\n): ResolvedMultilangGlobal | undefined =>\n globals.find((global) => global.slug === slug)\n\nexport const normalizeConfiguredLanguages = (\n languages: MultilangLanguage[],\n): MultilangLanguage[] => {\n const seen = new Set<string>()\n const normalized = languages.map((language, index): MultilangLanguage => {\n const code = language.code.trim().toLowerCase()\n\n if (!code) {\n throw new Error(`payload-multilang language at index ${index} is missing a code.`)\n }\n\n if (seen.has(code)) {\n throw new Error(`payload-multilang language code \"${code}\" is duplicated.`)\n }\n\n seen.add(code)\n\n return {\n id: language.id,\n name: language.name?.trim() || code,\n active: language.active !== false,\n code,\n direction: language.direction === 'rtl' ? 'rtl' : 'ltr',\n flagLabel: language.flagLabel?.trim(),\n isDefault: language.isDefault === true,\n locale: language.locale?.trim(),\n order: typeof language.order === 'number' ? language.order : 0,\n }\n })\n\n const activeDefaults = normalized.filter(\n (language) => language.active !== false && language.isDefault,\n )\n\n if (activeDefaults.length !== 1) {\n throw new Error('payload-multilang requires exactly one active default language.')\n }\n\n return normalized.sort((a, b) => {\n const order = (a.order || 0) - (b.order || 0)\n\n if (order !== 0) {\n return order\n }\n\n return a.code.localeCompare(b.code)\n })\n}\n"],"names":["DEFAULT_FIELD_NAMES","DEFAULT_GLOBAL_TABS_LABEL","resolveFieldNames","pluginFieldNames","collectionFieldNames","sanitizePluginConfig","pluginOptions","languages","normalizeConfiguredLanguages","defaultLanguage","find","language","isDefault","Error","collections","Object","entries","flatMap","slug","value","collectionOptions","fieldNames","fields","globals","globalOptions","label","trim","disabled","Boolean","raw","getCollectionConfig","collection","getGlobalConfig","global","seen","Set","normalized","map","index","code","toLowerCase","has","add","id","name","active","direction","flagLabel","locale","order","activeDefaults","filter","length","sort","a","b","localeCompare"],"mappings":"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"}
|