@roxxel/payload-multilang 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +165 -0
  2. package/dist/components/LanguageListToolbar.d.ts +6 -0
  3. package/dist/components/LanguageListToolbar.js +69 -0
  4. package/dist/components/LanguageListToolbar.js.map +1 -0
  5. package/dist/components/LanguageMetabox.d.ts +2 -0
  6. package/dist/components/LanguageMetabox.js +275 -0
  7. package/dist/components/LanguageMetabox.js.map +1 -0
  8. package/dist/components/TranslationActionsClient.d.ts +10 -0
  9. package/dist/components/TranslationActionsClient.js +166 -0
  10. package/dist/components/TranslationActionsClient.js.map +1 -0
  11. package/dist/components/TranslationColumnCell.d.ts +2 -0
  12. package/dist/components/TranslationColumnCell.js +69 -0
  13. package/dist/components/TranslationColumnCell.js.map +1 -0
  14. package/dist/components/TranslationColumnCellClient.d.ts +12 -0
  15. package/dist/components/TranslationColumnCellClient.js +107 -0
  16. package/dist/components/TranslationColumnCellClient.js.map +1 -0
  17. package/dist/components/TranslationsTab.d.ts +2 -0
  18. package/dist/components/TranslationsTab.js +118 -0
  19. package/dist/components/TranslationsTab.js.map +1 -0
  20. package/dist/components/config.d.ts +40 -0
  21. package/dist/components/config.js +31 -0
  22. package/dist/components/config.js.map +1 -0
  23. package/dist/constants.d.ts +5 -0
  24. package/dist/constants.js +24 -0
  25. package/dist/constants.js.map +1 -0
  26. package/dist/endpoints/translations.d.ts +19 -0
  27. package/dist/endpoints/translations.js +301 -0
  28. package/dist/endpoints/translations.js.map +1 -0
  29. package/dist/exports/client.d.ts +4 -0
  30. package/dist/exports/client.js +6 -0
  31. package/dist/exports/client.js.map +1 -0
  32. package/dist/exports/rsc.d.ts +2 -0
  33. package/dist/exports/rsc.js +4 -0
  34. package/dist/exports/rsc.js.map +1 -0
  35. package/dist/hooks/translatedCollection.d.ts +26 -0
  36. package/dist/hooks/translatedCollection.js +290 -0
  37. package/dist/hooks/translatedCollection.js.map +1 -0
  38. package/dist/hooks/translatedGlobal.d.ts +16 -0
  39. package/dist/hooks/translatedGlobal.js +71 -0
  40. package/dist/hooks/translatedGlobal.js.map +1 -0
  41. package/dist/index.d.ts +5 -0
  42. package/dist/index.js +63 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/lib/config.d.ts +14 -0
  45. package/dist/lib/config.js +96 -0
  46. package/dist/lib/config.js.map +1 -0
  47. package/dist/lib/data.d.ts +107 -0
  48. package/dist/lib/data.js +307 -0
  49. package/dist/lib/data.js.map +1 -0
  50. package/dist/payload-config.d.js +2 -0
  51. package/dist/payload-config.d.js.map +1 -0
  52. package/dist/styles/admin.css +316 -0
  53. package/dist/types.d.ts +96 -0
  54. package/dist/types.js +3 -0
  55. package/dist/types.js.map +1 -0
  56. package/docs/assets/admin-ui/collection-list-language-shortcuts.png +0 -0
  57. package/docs/assets/admin-ui/english-post-translations.png +0 -0
  58. package/docs/assets/admin-ui/ukrainian-post-translations.png +0 -0
  59. package/docs/configuration.md +192 -0
  60. package/docs/helpers.md +231 -0
  61. package/docs/usage.md +269 -0
  62. package/package.json +95 -0
@@ -0,0 +1,290 @@
1
+ import { randomUUID } from 'crypto';
2
+ import { MULTILANG_SKIP_HOOK } from '../constants.js';
3
+ import { asDocument, getID, getStringValue } from '../lib/data.js';
4
+ const hasSkipContext = (context)=>context?.[MULTILANG_SKIP_HOOK] === true;
5
+ const valuesAreEqual = (left, right)=>{
6
+ if (left === right) {
7
+ return true;
8
+ }
9
+ return JSON.stringify(left) === JSON.stringify(right);
10
+ };
11
+ const isConfiguredLanguage = (collection, language)=>Boolean(language && collection.languages.some((configuredLanguage)=>configuredLanguage.code === language));
12
+ const getDuplicateLanguageIDs = async ({ collection, excludeID, group, language, req })=>{
13
+ const and = [
14
+ {
15
+ [collection.fieldNames.group]: {
16
+ equals: group
17
+ }
18
+ },
19
+ {
20
+ [collection.fieldNames.language]: {
21
+ equals: language
22
+ }
23
+ }
24
+ ];
25
+ if (excludeID) {
26
+ and.push({
27
+ id: {
28
+ not_equals: excludeID
29
+ }
30
+ });
31
+ }
32
+ const result = await req.payload.find({
33
+ collection: collection.slug,
34
+ depth: 0,
35
+ limit: 200,
36
+ overrideAccess: true,
37
+ req,
38
+ where: {
39
+ and
40
+ }
41
+ });
42
+ return result.docs.map((doc)=>getID(asDocument(doc).id)).filter((id)=>id !== undefined);
43
+ };
44
+ export const createTranslatedCollectionBeforeChangeHook = ({ collection })=>async ({ context, data, operation, originalDoc, req })=>{
45
+ if (hasSkipContext(context)) {
46
+ return data;
47
+ }
48
+ const nextData = asDocument(data);
49
+ const original = asDocument(originalDoc);
50
+ const { group: groupField, language: languageField } = collection.fieldNames;
51
+ const originalLanguage = getStringValue(original[languageField]);
52
+ const originalLanguageIsConfigured = isConfiguredLanguage(collection, originalLanguage);
53
+ const incomingLanguage = getStringValue(nextData[languageField]);
54
+ if (operation === 'update' && originalLanguage && !originalLanguageIsConfigured) {
55
+ if (!incomingLanguage || incomingLanguage === originalLanguage || !isConfiguredLanguage(collection, incomingLanguage)) {
56
+ nextData[languageField] = '';
57
+ }
58
+ } else if (!incomingLanguage) {
59
+ nextData[languageField] = operation === 'create' ? collection.defaultLanguage.code : originalLanguageIsConfigured ? originalLanguage : '';
60
+ }
61
+ const language = getStringValue(nextData[languageField]);
62
+ if (operation === 'update' && originalLanguage && originalLanguageIsConfigured && language !== originalLanguage) {
63
+ throw new Error('Document language cannot be changed after creation.');
64
+ }
65
+ if (language && !isConfiguredLanguage(collection, language)) {
66
+ throw new Error(`Language "${language}" is not configured for ${collection.slug}.`);
67
+ }
68
+ if (!getStringValue(nextData[groupField])) {
69
+ const fallbackGroup = getStringValue(original[groupField]);
70
+ nextData[groupField] = fallbackGroup || randomUUID();
71
+ }
72
+ const group = getStringValue(nextData[groupField]);
73
+ if (language && group) {
74
+ const currentID = operation === 'update' ? getID(original.id) : undefined;
75
+ const duplicateIDs = await getDuplicateLanguageIDs({
76
+ collection,
77
+ excludeID: currentID,
78
+ group,
79
+ language,
80
+ req
81
+ });
82
+ if (duplicateIDs.length > 0) {
83
+ throw new Error(`A ${collection.slug} translation already exists for language "${language}" in this translation group.`);
84
+ }
85
+ }
86
+ return nextData;
87
+ };
88
+ export const createSynchronizedFieldsAfterChangeHook = ({ collection })=>async ({ context, doc, operation, previousDoc, req })=>{
89
+ if (hasSkipContext(context) || operation !== 'update' || collection.synchronizedFields.length === 0) {
90
+ return doc;
91
+ }
92
+ const nextDoc = asDocument(doc);
93
+ const previous = asDocument(previousDoc);
94
+ const group = getStringValue(nextDoc[collection.fieldNames.group]);
95
+ const currentID = getID(nextDoc.id);
96
+ if (!group || !currentID) {
97
+ return doc;
98
+ }
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) {
106
+ return doc;
107
+ }
108
+ const translations = await req.payload.find({
109
+ collection: collection.slug,
110
+ depth: 0,
111
+ limit: 200,
112
+ overrideAccess: true,
113
+ req,
114
+ where: {
115
+ and: [
116
+ {
117
+ [collection.fieldNames.group]: {
118
+ equals: group
119
+ }
120
+ },
121
+ {
122
+ id: {
123
+ not_equals: currentID
124
+ }
125
+ }
126
+ ]
127
+ }
128
+ });
129
+ await Promise.all(translations.docs.map((translation)=>getID(asDocument(translation).id)).filter((id)=>id !== undefined).map((id)=>req.payload.update({
130
+ id,
131
+ collection: collection.slug,
132
+ context: {
133
+ [MULTILANG_SKIP_HOOK]: true
134
+ },
135
+ data: synchronizedData,
136
+ overrideAccess: true,
137
+ req
138
+ })));
139
+ return doc;
140
+ };
141
+ export const createHiddenFields = ({ collection })=>[
142
+ {
143
+ name: collection.fieldNames.language,
144
+ type: 'text',
145
+ admin: {
146
+ disableListColumn: true,
147
+ hidden: true
148
+ },
149
+ index: true,
150
+ label: 'Language'
151
+ },
152
+ {
153
+ name: collection.fieldNames.group,
154
+ type: 'text',
155
+ admin: {
156
+ disableListColumn: true,
157
+ hidden: true
158
+ },
159
+ index: true,
160
+ label: 'Translations'
161
+ },
162
+ {
163
+ name: collection.fieldNames.meta,
164
+ type: 'json',
165
+ admin: {
166
+ disableListColumn: true,
167
+ hidden: true
168
+ }
169
+ },
170
+ {
171
+ name: 'multilangLanguageMetabox',
172
+ type: 'ui',
173
+ admin: {
174
+ components: {
175
+ Field: '@roxxel/payload-multilang/client#LanguageMetabox'
176
+ },
177
+ position: 'sidebar'
178
+ }
179
+ }
180
+ ];
181
+ export const getLanguageColumnName = ()=>'payloadMultilangTranslations';
182
+ export const createLanguageColumnFields = ({ collection })=>{
183
+ const label = collection.languages.map((language)=>language.flagLabel || language.code.toUpperCase()).join(' ');
184
+ return [
185
+ {
186
+ name: getLanguageColumnName(),
187
+ type: 'ui',
188
+ admin: {
189
+ components: {
190
+ Cell: '@roxxel/payload-multilang/rsc#TranslationColumnCell'
191
+ },
192
+ disableListColumn: false,
193
+ width: '132px'
194
+ },
195
+ label: label || 'Translations'
196
+ }
197
+ ];
198
+ };
199
+ export const translatedCollectionMeta = ({ collection })=>({
200
+ defaultLanguage: collection.defaultLanguage,
201
+ fieldNames: collection.fieldNames,
202
+ languages: collection.languages
203
+ });
204
+ const getDefaultColumns = ({ collection, existingDefaultColumns, languageColumnName })=>{
205
+ const hiddenMultilangColumns = new Set([
206
+ collection.fieldNames.group,
207
+ collection.fieldNames.language,
208
+ collection.fieldNames.meta,
209
+ languageColumnName
210
+ ]);
211
+ const visibleExistingColumns = existingDefaultColumns.filter((column)=>!hiddenMultilangColumns.has(column));
212
+ return [
213
+ ...visibleExistingColumns,
214
+ languageColumnName
215
+ ];
216
+ };
217
+ export const withTranslatedCollection = ({ collection, config })=>{
218
+ const existingDefaultColumns = config.admin?.defaultColumns || [];
219
+ const languageColumnName = getLanguageColumnName();
220
+ const existingEditViews = config.admin?.components?.views?.edit || {};
221
+ const defaultColumns = getDefaultColumns({
222
+ collection,
223
+ existingDefaultColumns,
224
+ languageColumnName
225
+ });
226
+ return {
227
+ ...config,
228
+ admin: {
229
+ ...config.admin,
230
+ components: {
231
+ ...config.admin?.components,
232
+ beforeList: [
233
+ ...config.admin?.components?.beforeList || [],
234
+ '@roxxel/payload-multilang/client#LanguageListToolbar'
235
+ ],
236
+ beforeListTable: config.admin?.components?.beforeListTable,
237
+ views: {
238
+ ...config.admin?.components?.views,
239
+ edit: {
240
+ ...existingEditViews,
241
+ translations: {
242
+ Component: '@roxxel/payload-multilang/rsc#TranslationsTab',
243
+ path: '/translations',
244
+ tab: {
245
+ label: 'Translations',
246
+ order: 80
247
+ }
248
+ }
249
+ }
250
+ }
251
+ },
252
+ custom: {
253
+ ...config.admin?.custom,
254
+ payloadMultilang: translatedCollectionMeta({
255
+ collection
256
+ })
257
+ },
258
+ defaultColumns
259
+ },
260
+ endpoints: [
261
+ ...config.endpoints || []
262
+ ],
263
+ fields: [
264
+ ...config.fields || [],
265
+ ...createHiddenFields({
266
+ collection
267
+ }),
268
+ ...createLanguageColumnFields({
269
+ collection
270
+ })
271
+ ],
272
+ hooks: {
273
+ ...config.hooks,
274
+ afterChange: [
275
+ createSynchronizedFieldsAfterChangeHook({
276
+ collection
277
+ }),
278
+ ...config.hooks?.afterChange || []
279
+ ],
280
+ beforeChange: [
281
+ createTranslatedCollectionBeforeChangeHook({
282
+ collection
283
+ }),
284
+ ...config.hooks?.beforeChange || []
285
+ ]
286
+ }
287
+ };
288
+ };
289
+
290
+ //# sourceMappingURL=translatedCollection.js.map
@@ -0,0 +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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"}
@@ -0,0 +1,16 @@
1
+ import type { Field, GlobalConfig } from 'payload';
2
+ import type { ResolvedMultilangGlobal } from '../types.js';
3
+ export declare const translatedGlobalMeta: ({ global, }: {
4
+ global: ResolvedMultilangGlobal;
5
+ }) => {
6
+ defaultLanguage: import("../types.js").MultilangLanguage;
7
+ languages: import("../types.js").MultilangLanguage[];
8
+ };
9
+ export declare const createGlobalLanguageTabsField: ({ config, global, }: {
10
+ config: GlobalConfig;
11
+ global: ResolvedMultilangGlobal;
12
+ }) => Field;
13
+ export declare const withTranslatedGlobal: ({ config, global, }: {
14
+ config: GlobalConfig;
15
+ global: ResolvedMultilangGlobal;
16
+ }) => GlobalConfig;
@@ -0,0 +1,71 @@
1
+ const withOptionalRequired = (field, required)=>{
2
+ if (required || !('required' in field)) {
3
+ return field;
4
+ }
5
+ return {
6
+ ...field,
7
+ required: false
8
+ };
9
+ };
10
+ const cloneField = (field, required)=>{
11
+ if (field.type === 'tabs') {
12
+ return withOptionalRequired({
13
+ ...field,
14
+ tabs: field.tabs.map((tab)=>({
15
+ ...tab,
16
+ fields: tab.fields.map((tabField)=>cloneField(tabField, required))
17
+ }))
18
+ }, required);
19
+ }
20
+ if ('fields' in field && Array.isArray(field.fields)) {
21
+ return withOptionalRequired({
22
+ ...field,
23
+ fields: field.fields.map((childField)=>cloneField(childField, required))
24
+ }, required);
25
+ }
26
+ if (field.type === 'blocks') {
27
+ return withOptionalRequired({
28
+ ...field,
29
+ blocks: field.blocks.map((block)=>({
30
+ ...block,
31
+ fields: block.fields.map((blockField)=>cloneField(blockField, required))
32
+ }))
33
+ }, required);
34
+ }
35
+ return withOptionalRequired({
36
+ ...field
37
+ }, required);
38
+ };
39
+ export const translatedGlobalMeta = ({ global })=>({
40
+ defaultLanguage: global.defaultLanguage,
41
+ languages: global.languages
42
+ });
43
+ export const createGlobalLanguageTabsField = ({ config, global })=>({
44
+ type: 'tabs',
45
+ label: global.label,
46
+ tabs: global.languages.map((language)=>({
47
+ name: language.code,
48
+ fields: config.fields.map((field)=>cloneField(field, language.code === global.defaultLanguage.code)),
49
+ label: language.flagLabel ? `${language.flagLabel} ${language.name}` : language.name
50
+ }))
51
+ });
52
+ export const withTranslatedGlobal = ({ config, global })=>({
53
+ ...config,
54
+ admin: {
55
+ ...config.admin,
56
+ custom: {
57
+ ...config.admin?.custom,
58
+ payloadMultilang: translatedGlobalMeta({
59
+ global
60
+ })
61
+ }
62
+ },
63
+ fields: [
64
+ createGlobalLanguageTabsField({
65
+ config,
66
+ global
67
+ })
68
+ ]
69
+ });
70
+
71
+ //# sourceMappingURL=translatedGlobal.js.map
@@ -0,0 +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"}
@@ -0,0 +1,5 @@
1
+ import type { Plugin } from 'payload';
2
+ import type { PayloadMultilangConfig } from './types.js';
3
+ export { findGlobalByLanguage, findGlobalByLanguageWithPayload, getDocumentTranslation, getDocumentTranslations, getLanguages, updateGlobalByLanguage, updateGlobalByLanguageWithPayload, withLanguage, } from './lib/data.js';
4
+ export type { MultilangCollectionOptions, MultilangFieldNames, MultilangGlobalOptions, MultilangLanguage, PayloadMultilangConfig, ResolvedMultilangGlobal, TranslationMap, TranslationState, } from './types.js';
5
+ export declare const payloadMultilang: (pluginOptions?: PayloadMultilangConfig) => Plugin;
package/dist/index.js ADDED
@@ -0,0 +1,63 @@
1
+ import { createTranslationEndpoints } from './endpoints/translations.js';
2
+ import { withTranslatedCollection } from './hooks/translatedCollection.js';
3
+ import { withTranslatedGlobal } from './hooks/translatedGlobal.js';
4
+ import { getCollectionConfig, getGlobalConfig, sanitizePluginConfig } from './lib/config.js';
5
+ export { findGlobalByLanguage, findGlobalByLanguageWithPayload, getDocumentTranslation, getDocumentTranslations, getLanguages, updateGlobalByLanguage, updateGlobalByLanguageWithPayload, withLanguage } from './lib/data.js';
6
+ export const payloadMultilang = (pluginOptions = {
7
+ languages: []
8
+ })=>(config)=>{
9
+ if (pluginOptions.disabled) {
10
+ return config;
11
+ }
12
+ const pluginConfig = sanitizePluginConfig(pluginOptions);
13
+ const incomingCollections = config.collections || [];
14
+ const collections = incomingCollections.map((collection)=>{
15
+ const translatedCollection = getCollectionConfig(pluginConfig.collections, collection.slug);
16
+ if (!translatedCollection) {
17
+ return collection;
18
+ }
19
+ const translated = withTranslatedCollection({
20
+ collection: translatedCollection,
21
+ config: collection
22
+ });
23
+ return {
24
+ ...translated,
25
+ endpoints: [
26
+ ...translated.endpoints || [],
27
+ ...createTranslationEndpoints({
28
+ collection: translatedCollection,
29
+ collectionConfig: collection
30
+ })
31
+ ]
32
+ };
33
+ });
34
+ const incomingGlobals = config.globals || [];
35
+ const globals = incomingGlobals.map((globalConfig)=>{
36
+ const translatedGlobal = getGlobalConfig(pluginConfig.globals, globalConfig.slug);
37
+ if (!translatedGlobal) {
38
+ return globalConfig;
39
+ }
40
+ return withTranslatedGlobal({
41
+ config: globalConfig,
42
+ global: translatedGlobal
43
+ });
44
+ });
45
+ return {
46
+ ...config,
47
+ admin: {
48
+ ...config.admin,
49
+ custom: {
50
+ ...config.admin?.custom,
51
+ payloadMultilang: {
52
+ ...config.admin?.custom?.payloadMultilang || {},
53
+ defaultLanguage: pluginConfig.defaultLanguage,
54
+ languages: pluginConfig.languages
55
+ }
56
+ }
57
+ },
58
+ collections,
59
+ globals
60
+ };
61
+ };
62
+
63
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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 findGlobalByLanguage,\n findGlobalByLanguageWithPayload,\n getDocumentTranslation,\n getDocumentTranslations,\n getLanguages,\n updateGlobalByLanguage,\n updateGlobalByLanguageWithPayload,\n withLanguage,\n} from './lib/data.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\nexport const payloadMultilang =\n (pluginOptions: PayloadMultilangConfig = { languages: [] }): Plugin =>\n (config: Config): Config => {\n if (pluginOptions.disabled) {\n return config\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 {\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","findGlobalByLanguage","findGlobalByLanguageWithPayload","getDocumentTranslation","getDocumentTranslations","getLanguages","updateGlobalByLanguage","updateGlobalByLanguageWithPayload","withLanguage","payloadMultilang","pluginOptions","languages","config","disabled","pluginConfig","incomingCollections","collections","map","collection","translatedCollection","slug","translated","endpoints","collectionConfig","incomingGlobals","globals","globalConfig","translatedGlobal","global","admin","custom","defaultLanguage"],"mappings":"AAIA,SAASA,0BAA0B,QAAQ,8BAA6B;AACxE,SAASC,wBAAwB,QAAQ,kCAAiC;AAC1E,SAASC,oBAAoB,QAAQ,8BAA6B;AAClE,SACEC,mBAAmB,EACnBC,eAAe,EACfC,oBAAoB,QACf,kBAAiB;AAExB,SACEC,oBAAoB,EACpBC,+BAA+B,EAC/BC,sBAAsB,EACtBC,uBAAuB,EACvBC,YAAY,EACZC,sBAAsB,EACtBC,iCAAiC,EACjCC,YAAY,QACP,gBAAe;AAatB,OAAO,MAAMC,mBACX,CAACC,gBAAwC;IAAEC,WAAW,EAAE;AAAC,CAAC,GAC1D,CAACC;QACC,IAAIF,cAAcG,QAAQ,EAAE;YAC1B,OAAOD;QACT;QAEA,MAAME,eAAed,qBAAqBU;QAE1C,MAAMK,sBAAsBH,OAAOI,WAAW,IAAI,EAAE;QACpD,MAAMA,cAAcD,oBAAoBE,GAAG,CACzC,CAACC;YACC,MAAMC,uBAAuBrB,oBAC3BgB,aAAaE,WAAW,EACxBE,WAAWE,IAAI;YAGjB,IAAI,CAACD,sBAAsB;gBACzB,OAAOD;YACT;YAEA,MAAMG,aAAazB,yBAAyB;gBAC1CsB,YAAYC;gBACZP,QAAQM;YACV;YAEA,OAAO;gBACL,GAAGG,UAAU;gBACbC,WAAW;uBACLD,WAAWC,SAAS,IAAI,EAAE;uBAC3B3B,2BAA2B;wBAC5BuB,YAAYC;wBACZI,kBAAkBL;oBACpB;iBACD;YACH;QACF;QAGF,MAAMM,kBAAkBZ,OAAOa,OAAO,IAAI,EAAE;QAC5C,MAAMA,UAAUD,gBAAgBP,GAAG,CAAC,CAACS;YACnC,MAAMC,mBAAmB5B,gBACvBe,aAAaW,OAAO,EACpBC,aAAaN,IAAI;YAGnB,IAAI,CAACO,kBAAkB;gBACrB,OAAOD;YACT;YAEA,OAAO7B,qBAAqB;gBAC1Be,QAAQc;gBACRE,QAAQD;YACV;QACF;QAEA,OAAO;YACL,GAAGf,MAAM;YACTiB,OAAO;gBACL,GAAGjB,OAAOiB,KAAK;gBACfC,QAAQ;oBACN,GAAGlB,OAAOiB,KAAK,EAAEC,MAAM;oBACvBrB,kBAAkB;wBAChB,GAAI,AAACG,OAAOiB,KAAK,EAAEC,QAEHrB,oBAAoB,CAAC,CAAC;wBACtCsB,iBAAiBjB,aAAaiB,eAAe;wBAC7CpB,WAAWG,aAAaH,SAAS;oBACnC;gBACF;YACF;YACAK;YACAS;QACF;IACF,EAAC"}
@@ -0,0 +1,14 @@
1
+ import type { MultilangFieldNames, MultilangLanguage, PayloadMultilangConfig, ResolvedMultilangCollection, ResolvedMultilangGlobal } from '../types.js';
2
+ export type SanitizedPluginConfig = {
3
+ collections: ResolvedMultilangCollection[];
4
+ defaultLanguage: MultilangLanguage;
5
+ disabled: boolean;
6
+ globals: ResolvedMultilangGlobal[];
7
+ languages: MultilangLanguage[];
8
+ raw: PayloadMultilangConfig;
9
+ };
10
+ export declare const resolveFieldNames: (pluginFieldNames?: Partial<MultilangFieldNames>, collectionFieldNames?: Partial<MultilangFieldNames>) => MultilangFieldNames;
11
+ export declare const sanitizePluginConfig: (pluginOptions: PayloadMultilangConfig) => SanitizedPluginConfig;
12
+ export declare const getCollectionConfig: (collections: ResolvedMultilangCollection[], slug: string) => ResolvedMultilangCollection | undefined;
13
+ export declare const getGlobalConfig: (globals: ResolvedMultilangGlobal[], slug: string) => ResolvedMultilangGlobal | undefined;
14
+ export declare const normalizeConfiguredLanguages: (languages: MultilangLanguage[]) => MultilangLanguage[];
@@ -0,0 +1,96 @@
1
+ import { DEFAULT_DUPLICATE_EXCLUDE_FIELDS, DEFAULT_FIELD_NAMES, DEFAULT_GLOBAL_TABS_LABEL } from '../constants.js';
2
+ export const resolveFieldNames = (pluginFieldNames, collectionFieldNames)=>({
3
+ ...DEFAULT_FIELD_NAMES,
4
+ ...pluginFieldNames || {},
5
+ ...collectionFieldNames || {}
6
+ });
7
+ const uniqueStrings = (...values)=>Array.from(new Set(values.flatMap((value)=>(value || []).map((field)=>field.trim()).filter(Boolean))));
8
+ export const sanitizePluginConfig = (pluginOptions)=>{
9
+ const languages = normalizeConfiguredLanguages(pluginOptions.languages || []);
10
+ const defaultLanguage = languages.find((language)=>language.isDefault);
11
+ if (!defaultLanguage) {
12
+ throw new Error('payload-multilang requires one active default language.');
13
+ }
14
+ const collections = Object.entries(pluginOptions.collections || {}).flatMap(([slug, value])=>{
15
+ if (!value) {
16
+ return [];
17
+ }
18
+ const collectionOptions = value === true ? {} : value;
19
+ return [
20
+ {
21
+ slug,
22
+ defaultLanguage,
23
+ duplicate: collectionOptions.duplicate ?? true,
24
+ duplicateExcludeFields: [
25
+ ...DEFAULT_DUPLICATE_EXCLUDE_FIELDS,
26
+ ...pluginOptions.duplicateExcludeFields || [],
27
+ ...collectionOptions.duplicateExcludeFields || []
28
+ ],
29
+ fieldNames: resolveFieldNames(pluginOptions.fieldNames, collectionOptions.fields),
30
+ languages,
31
+ synchronizedFields: uniqueStrings(collectionOptions.synchronizedFields)
32
+ }
33
+ ];
34
+ });
35
+ const globals = Object.entries(pluginOptions.globals || {}).flatMap(([slug, value])=>{
36
+ if (!value) {
37
+ return [];
38
+ }
39
+ const globalOptions = value === true ? {} : value;
40
+ return [
41
+ {
42
+ slug,
43
+ defaultLanguage,
44
+ label: globalOptions.label?.trim() || DEFAULT_GLOBAL_TABS_LABEL,
45
+ languages
46
+ }
47
+ ];
48
+ });
49
+ return {
50
+ collections,
51
+ defaultLanguage,
52
+ disabled: Boolean(pluginOptions.disabled),
53
+ globals,
54
+ languages,
55
+ raw: pluginOptions
56
+ };
57
+ };
58
+ export const getCollectionConfig = (collections, slug)=>collections.find((collection)=>collection.slug === slug);
59
+ export const getGlobalConfig = (globals, slug)=>globals.find((global)=>global.slug === slug);
60
+ export const normalizeConfiguredLanguages = (languages)=>{
61
+ const seen = new Set();
62
+ const normalized = languages.map((language, index)=>{
63
+ const code = language.code.trim().toLowerCase();
64
+ if (!code) {
65
+ throw new Error(`payload-multilang language at index ${index} is missing a code.`);
66
+ }
67
+ if (seen.has(code)) {
68
+ throw new Error(`payload-multilang language code "${code}" is duplicated.`);
69
+ }
70
+ seen.add(code);
71
+ return {
72
+ id: language.id,
73
+ name: language.name?.trim() || code,
74
+ active: language.active !== false,
75
+ code,
76
+ direction: language.direction === 'rtl' ? 'rtl' : 'ltr',
77
+ flagLabel: language.flagLabel?.trim(),
78
+ isDefault: language.isDefault === true,
79
+ locale: language.locale?.trim(),
80
+ order: typeof language.order === 'number' ? language.order : 0
81
+ };
82
+ });
83
+ const activeDefaults = normalized.filter((language)=>language.active !== false && language.isDefault);
84
+ if (activeDefaults.length !== 1) {
85
+ throw new Error('payload-multilang requires exactly one active default language.');
86
+ }
87
+ return normalized.sort((a, b)=>{
88
+ const order = (a.order || 0) - (b.order || 0);
89
+ if (order !== 0) {
90
+ return order;
91
+ }
92
+ return a.code.localeCompare(b.code);
93
+ });
94
+ };
95
+
96
+ //# sourceMappingURL=config.js.map
@@ -0,0 +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 {\n DEFAULT_DUPLICATE_EXCLUDE_FIELDS,\n DEFAULT_FIELD_NAMES,\n DEFAULT_GLOBAL_TABS_LABEL,\n} 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\nconst uniqueStrings = (...values: Array<string[] | undefined>): string[] =>\n Array.from(\n new Set(\n values.flatMap((value) =>\n (value || []).map((field) => field.trim()).filter(Boolean),\n ),\n ),\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 duplicate: collectionOptions.duplicate ?? true,\n duplicateExcludeFields: [\n ...DEFAULT_DUPLICATE_EXCLUDE_FIELDS,\n ...(pluginOptions.duplicateExcludeFields || []),\n ...(collectionOptions.duplicateExcludeFields || []),\n ],\n fieldNames: resolveFieldNames(\n pluginOptions.fieldNames,\n collectionOptions.fields,\n ),\n languages,\n synchronizedFields: uniqueStrings(collectionOptions.synchronizedFields),\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_DUPLICATE_EXCLUDE_FIELDS","DEFAULT_FIELD_NAMES","DEFAULT_GLOBAL_TABS_LABEL","resolveFieldNames","pluginFieldNames","collectionFieldNames","uniqueStrings","values","Array","from","Set","flatMap","value","map","field","trim","filter","Boolean","sanitizePluginConfig","pluginOptions","languages","normalizeConfiguredLanguages","defaultLanguage","find","language","isDefault","Error","collections","Object","entries","slug","collectionOptions","duplicate","duplicateExcludeFields","fieldNames","fields","synchronizedFields","globals","globalOptions","label","disabled","raw","getCollectionConfig","collection","getGlobalConfig","global","seen","normalized","index","code","toLowerCase","has","add","id","name","active","direction","flagLabel","locale","order","activeDefaults","length","sort","a","b","localeCompare"],"mappings":"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"}