@roxxel/payload-multilang 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +165 -0
- package/dist/components/LanguageListToolbar.d.ts +6 -0
- package/dist/components/LanguageListToolbar.js +69 -0
- package/dist/components/LanguageListToolbar.js.map +1 -0
- package/dist/components/LanguageMetabox.d.ts +2 -0
- package/dist/components/LanguageMetabox.js +275 -0
- package/dist/components/LanguageMetabox.js.map +1 -0
- package/dist/components/TranslationActionsClient.d.ts +10 -0
- package/dist/components/TranslationActionsClient.js +166 -0
- package/dist/components/TranslationActionsClient.js.map +1 -0
- package/dist/components/TranslationColumnCell.d.ts +2 -0
- package/dist/components/TranslationColumnCell.js +69 -0
- package/dist/components/TranslationColumnCell.js.map +1 -0
- package/dist/components/TranslationColumnCellClient.d.ts +12 -0
- package/dist/components/TranslationColumnCellClient.js +107 -0
- package/dist/components/TranslationColumnCellClient.js.map +1 -0
- package/dist/components/TranslationsTab.d.ts +2 -0
- package/dist/components/TranslationsTab.js +118 -0
- package/dist/components/TranslationsTab.js.map +1 -0
- package/dist/components/config.d.ts +40 -0
- package/dist/components/config.js +31 -0
- package/dist/components/config.js.map +1 -0
- package/dist/constants.d.ts +5 -0
- package/dist/constants.js +24 -0
- package/dist/constants.js.map +1 -0
- package/dist/endpoints/translations.d.ts +19 -0
- package/dist/endpoints/translations.js +301 -0
- package/dist/endpoints/translations.js.map +1 -0
- package/dist/exports/client.d.ts +4 -0
- package/dist/exports/client.js +6 -0
- package/dist/exports/client.js.map +1 -0
- package/dist/exports/rsc.d.ts +2 -0
- package/dist/exports/rsc.js +4 -0
- package/dist/exports/rsc.js.map +1 -0
- package/dist/hooks/translatedCollection.d.ts +26 -0
- package/dist/hooks/translatedCollection.js +290 -0
- package/dist/hooks/translatedCollection.js.map +1 -0
- package/dist/hooks/translatedGlobal.d.ts +16 -0
- package/dist/hooks/translatedGlobal.js +71 -0
- package/dist/hooks/translatedGlobal.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +63 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/config.d.ts +14 -0
- package/dist/lib/config.js +96 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/data.d.ts +107 -0
- package/dist/lib/data.js +307 -0
- package/dist/lib/data.js.map +1 -0
- package/dist/payload-config.d.js +2 -0
- package/dist/payload-config.d.js.map +1 -0
- package/dist/styles/admin.css +316 -0
- package/dist/types.d.ts +96 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/docs/assets/admin-ui/collection-list-language-shortcuts.png +0 -0
- package/docs/assets/admin-ui/english-post-translations.png +0 -0
- package/docs/assets/admin-ui/ukrainian-post-translations.png +0 -0
- package/docs/configuration.md +192 -0
- package/docs/helpers.md +231 -0
- package/docs/usage.md +269 -0
- package/package.json +95 -0
|
@@ -0,0 +1,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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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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"}
|