@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,316 @@
1
+ .payload-multilang-page {
2
+ padding: 32px;
3
+ }
4
+
5
+ .payload-multilang-page__header {
6
+ align-items: flex-start;
7
+ display: flex;
8
+ gap: 16px;
9
+ justify-content: space-between;
10
+ margin-bottom: 24px;
11
+ }
12
+
13
+ .payload-multilang-page__header h1,
14
+ .payload-multilang-page__header h2,
15
+ .payload-multilang-panel h2 {
16
+ margin: 0 0 8px;
17
+ }
18
+
19
+ .payload-multilang-page__header p {
20
+ margin: 0;
21
+ opacity: 0.75;
22
+ }
23
+
24
+ .payload-multilang-layout {
25
+ align-items: flex-start;
26
+ display: grid;
27
+ gap: 24px;
28
+ grid-template-columns: minmax(280px, 360px) minmax(0, 1fr);
29
+ }
30
+
31
+
32
+
33
+
34
+ .payload-multilang-panel--wide {
35
+ min-width: 0;
36
+ }
37
+
38
+ .payload-multilang-form,
39
+ .payload-multilang-field {
40
+ display: grid;
41
+ gap: 12px;
42
+ }
43
+
44
+ .payload-multilang-form label,
45
+ .payload-multilang-field {
46
+ display: grid;
47
+ font-size: 13px;
48
+ gap: 6px;
49
+ }
50
+
51
+ .payload-multilang-form input,
52
+ .payload-multilang-form select,
53
+ .payload-multilang-field select,
54
+ .payload-multilang-inline-form input,
55
+ .payload-multilang-inline-form select,
56
+ .payload-multilang-toolbar__select select {
57
+ background: var(--theme-input-bg);
58
+ border: 1px solid var(--theme-elevation-150);
59
+ border-radius: 4px;
60
+ color: var(--theme-text);
61
+ min-height: 36px;
62
+ padding: 6px 10px;
63
+ }
64
+
65
+ .payload-multilang-checkbox {
66
+ align-items: center;
67
+ display: flex !important;
68
+ flex-direction: row;
69
+ }
70
+
71
+ .payload-multilang-actions,
72
+ .payload-multilang-inline-form {
73
+ align-items: center;
74
+ display: flex;
75
+ flex-wrap: wrap;
76
+ gap: 10px;
77
+ }
78
+
79
+ .payload-multilang-inline-form {
80
+ margin-bottom: 12px;
81
+ }
82
+
83
+ .payload-multilang-table {
84
+ border-collapse: collapse;
85
+ width: 100%;
86
+ }
87
+
88
+ .payload-multilang-table th,
89
+ .payload-multilang-table td {
90
+ border-bottom: 1px solid var(--theme-elevation-100);
91
+ padding: 10px 8px;
92
+ text-align: left;
93
+ vertical-align: top;
94
+ }
95
+
96
+ .payload-multilang-table--compact th,
97
+ .payload-multilang-table--compact td {
98
+ padding: 6px 8px;
99
+ }
100
+
101
+ .payload-multilang-table__actions {
102
+ display: flex;
103
+ gap: 8px;
104
+ }
105
+
106
+ .payload-multilang-table__actions button {
107
+ background: transparent;
108
+ border: 0;
109
+ color: var(--theme-success-500);
110
+ cursor: pointer;
111
+ padding: 0;
112
+ }
113
+
114
+ .payload-multilang-toolbar {
115
+ align-items: center;
116
+ display: flex;
117
+ gap: 8px;
118
+ margin-bottom: 0;
119
+ position: fixed;
120
+ right: 96px;
121
+ top: 14px;
122
+ z-index: 30;
123
+ }
124
+
125
+ .payload-multilang-toolbar__select {
126
+ align-items: center;
127
+ display: flex;
128
+ }
129
+
130
+ .payload-multilang-toolbar__select select {
131
+ min-width: 150px;
132
+ }
133
+
134
+ .payload-multilang-metabox__header {
135
+ align-items: center;
136
+ display: flex;
137
+ justify-content: space-between;
138
+ margin-bottom: 12px;
139
+ }
140
+
141
+ .payload-multilang-metabox__header h3 {
142
+ margin: 0;
143
+ }
144
+
145
+ .payload-multilang-language-select {
146
+ align-items: center;
147
+ display: grid;
148
+ gap: 8px;
149
+ grid-template-columns: 32px minmax(0, 1fr);
150
+ }
151
+
152
+ .payload-multilang-language-select__flag {
153
+ align-items: center;
154
+ border: 1px solid var(--theme-elevation-150);
155
+ border-radius: 4px;
156
+ display: inline-flex;
157
+ font-size: 13px;
158
+ height: 36px;
159
+ justify-content: center;
160
+ }
161
+
162
+ .payload-multilang-translations {
163
+ display: grid;
164
+ gap: 8px;
165
+ margin-top: 16px;
166
+ }
167
+
168
+ .payload-multilang-translations h4 {
169
+ font-size: 13px;
170
+ margin: 0;
171
+ }
172
+
173
+ .payload-multilang-translation-row {
174
+ align-items: center;
175
+ border-top: 1px solid var(--theme-elevation-100);
176
+ display: grid;
177
+ gap: 8px;
178
+ grid-template-columns: 32px minmax(0, 1fr) auto;
179
+ min-height: 36px;
180
+ padding-top: 8px;
181
+ }
182
+
183
+ .payload-multilang-translation-row__flag,
184
+ .payload-multilang-flag-cell {
185
+ align-items: center;
186
+ display: inline-flex;
187
+ justify-content: center;
188
+ white-space: nowrap;
189
+ }
190
+
191
+ .payload-multilang-flag-cell--current {
192
+ font-weight: 600;
193
+ }
194
+
195
+ .payload-multilang-flag-cell--unassigned {
196
+ color: var(--theme-elevation-500);
197
+ font-size: 13px;
198
+ }
199
+
200
+ .payload-multilang-column-stack {
201
+ align-items: center;
202
+ display: flex;
203
+ flex-direction: row;
204
+ gap: 16px;
205
+ justify-content: flex-start;
206
+ white-space: nowrap;
207
+ }
208
+
209
+ .payload-multilang-column-stack>span {
210
+ width: 34px;
211
+ }
212
+
213
+ .cell-payloadMultilangTranslations {
214
+ padding-left: 0px;
215
+ }
216
+
217
+ #heading-payloadMultilangTranslations {
218
+ padding-left: calc(var(--base) * 0.3);
219
+ }
220
+
221
+ #heading-payloadMultilangTranslations .field-label {
222
+ font-size: 18px;
223
+ word-spacing: 28px;
224
+
225
+ }
226
+
227
+ .payload-multilang-flag-cell {
228
+ font-size: 18px;
229
+ min-height: 34px;
230
+ }
231
+
232
+ .payload-multilang-translation-row__name {
233
+ min-width: 0;
234
+ overflow: hidden;
235
+ text-overflow: ellipsis;
236
+ white-space: nowrap;
237
+ }
238
+
239
+ .payload-multilang-translation-row__current {
240
+ font-size: 12px;
241
+ opacity: 0.75;
242
+ }
243
+
244
+ .payload-multilang-link-button,
245
+ .payload-multilang-cell-action {
246
+ align-items: center;
247
+ background: transparent;
248
+ border: 0;
249
+ color: var(--theme-success-500);
250
+ cursor: pointer;
251
+ display: inline-flex;
252
+ font: inherit;
253
+ height: 34px;
254
+ justify-content: center;
255
+ padding: 0;
256
+ text-decoration: none;
257
+ width: 34px;
258
+ }
259
+
260
+ .payload-multilang-icon-button {
261
+ align-items: center;
262
+ background: var(--theme-elevation-50);
263
+ border: 1px solid var(--theme-elevation-150);
264
+ border-radius: 4px;
265
+ color: var(--theme-text);
266
+ cursor: pointer;
267
+ display: inline-flex;
268
+ font: inherit;
269
+ font-size: 18px;
270
+ height: 34px;
271
+ justify-content: center;
272
+ line-height: 1;
273
+ padding: 0;
274
+ width: 34px;
275
+ }
276
+
277
+ .payload-multilang-icon-button:disabled,
278
+ .payload-multilang-link-button:disabled {
279
+ cursor: not-allowed;
280
+ opacity: 0.5;
281
+ }
282
+
283
+ .payload-multilang-pen-icon {
284
+ display: block;
285
+ height: 20px;
286
+ width: 20px;
287
+ }
288
+
289
+ .payload-multilang-muted,
290
+ .payload-multilang-status {
291
+ font-size: 12px;
292
+ opacity: 0.75;
293
+ }
294
+
295
+ .payload-multilang-status--linked {
296
+ opacity: 1;
297
+ }
298
+
299
+ .payload-multilang-cell {
300
+ white-space: nowrap;
301
+ }
302
+
303
+ .payload-multilang-cell--muted {
304
+ opacity: 0.65;
305
+ }
306
+
307
+ @media (max-width: 900px) {
308
+ .payload-multilang-toolbar {
309
+ margin-bottom: 16px;
310
+ position: static;
311
+ }
312
+
313
+ .payload-multilang-layout {
314
+ grid-template-columns: 1fr;
315
+ }
316
+ }
@@ -0,0 +1,96 @@
1
+ import type { CollectionSlug, GlobalSlug, Where } from 'payload';
2
+ export type MultilangDirection = 'ltr' | 'rtl';
3
+ export type MultilangFieldNames = {
4
+ group: string;
5
+ language: string;
6
+ meta: string;
7
+ };
8
+ export type MultilangLanguage = {
9
+ active?: boolean;
10
+ code: string;
11
+ direction?: MultilangDirection;
12
+ flagLabel?: string;
13
+ id?: number | string;
14
+ isDefault?: boolean;
15
+ locale?: string;
16
+ name: string;
17
+ order?: number;
18
+ };
19
+ export type MultilangCollectionOptions = {
20
+ /**
21
+ * Set to false to create empty translation documents by default.
22
+ */
23
+ duplicate?: boolean;
24
+ /**
25
+ * Additional top-level document field names that should not be copied when creating a translation.
26
+ */
27
+ duplicateExcludeFields?: string[];
28
+ /**
29
+ * Override hidden metadata field names for this collection.
30
+ */
31
+ fields?: Partial<MultilangFieldNames>;
32
+ /**
33
+ * Top-level document field names that should share one value across every language in a translation group.
34
+ */
35
+ synchronizedFields?: string[];
36
+ };
37
+ export type MultilangGlobalOptions = {
38
+ /**
39
+ * Override the top-level tabs field label used to group localized global fields.
40
+ */
41
+ label?: string;
42
+ };
43
+ export type PayloadMultilangConfig = {
44
+ /**
45
+ * Payload collections that should use Polylang-style document translations.
46
+ */
47
+ collections?: Partial<Record<CollectionSlug, MultilangCollectionOptions | true>>;
48
+ /**
49
+ * Disable runtime UI/endpoints/hooks while keeping schema fields in place.
50
+ */
51
+ disabled?: boolean;
52
+ /**
53
+ * Global duplicate exclusions applied to all enabled collections.
54
+ */
55
+ duplicateExcludeFields?: string[];
56
+ /**
57
+ * Global hidden metadata field name overrides.
58
+ */
59
+ fieldNames?: Partial<MultilangFieldNames>;
60
+ /**
61
+ * Payload globals that should store language-specific values in one singleton document.
62
+ */
63
+ globals?: Partial<Record<GlobalSlug, MultilangGlobalOptions | true>>;
64
+ /**
65
+ * Languages available to translated collections. Codes are normalized to
66
+ * lowercase during plugin setup.
67
+ */
68
+ languages: MultilangLanguage[];
69
+ };
70
+ export type ResolvedMultilangCollection = {
71
+ defaultLanguage: MultilangLanguage;
72
+ duplicate: boolean;
73
+ duplicateExcludeFields: string[];
74
+ fieldNames: MultilangFieldNames;
75
+ languages: MultilangLanguage[];
76
+ slug: string;
77
+ synchronizedFields: string[];
78
+ };
79
+ export type ResolvedMultilangGlobal = {
80
+ defaultLanguage: MultilangLanguage;
81
+ label: string;
82
+ languages: MultilangLanguage[];
83
+ slug: string;
84
+ };
85
+ export type TranslationMap<TDoc = Record<string, unknown>> = Record<string, TDoc>;
86
+ export type TranslationState<TDoc = Record<string, unknown>> = {
87
+ group?: string;
88
+ language?: string;
89
+ source?: TDoc;
90
+ translations: TranslationMap<TDoc>;
91
+ };
92
+ export type WithLanguageArgs = {
93
+ fieldName?: string;
94
+ language: string;
95
+ where?: Where;
96
+ };
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ export { };
2
+
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { CollectionSlug, GlobalSlug, Where } from 'payload'\n\nexport type MultilangDirection = 'ltr' | 'rtl'\n\nexport type MultilangFieldNames = {\n group: string\n language: string\n meta: string\n}\n\nexport type MultilangLanguage = {\n active?: boolean\n code: string\n direction?: MultilangDirection\n flagLabel?: string\n id?: number | string\n isDefault?: boolean\n locale?: string\n name: string\n order?: number\n}\n\nexport type MultilangCollectionOptions = {\n /**\n * Set to false to create empty translation documents by default.\n */\n duplicate?: boolean\n /**\n * Additional top-level document field names that should not be copied when creating a translation.\n */\n duplicateExcludeFields?: string[]\n /**\n * Override hidden metadata field names for this collection.\n */\n fields?: Partial<MultilangFieldNames>\n /**\n * Top-level document field names that should share one value across every language in a translation group.\n */\n synchronizedFields?: string[]\n}\n\nexport type MultilangGlobalOptions = {\n /**\n * Override the top-level tabs field label used to group localized global fields.\n */\n label?: string\n}\n\nexport type PayloadMultilangConfig = {\n /**\n * Payload collections that should use Polylang-style document translations.\n */\n collections?: Partial<Record<CollectionSlug, MultilangCollectionOptions | true>>\n /**\n * Disable runtime UI/endpoints/hooks while keeping schema fields in place.\n */\n disabled?: boolean\n /**\n * Global duplicate exclusions applied to all enabled collections.\n */\n duplicateExcludeFields?: string[]\n /**\n * Global hidden metadata field name overrides.\n */\n fieldNames?: Partial<MultilangFieldNames>\n /**\n * Payload globals that should store language-specific values in one singleton document.\n */\n globals?: Partial<Record<GlobalSlug, MultilangGlobalOptions | true>>\n /**\n * Languages available to translated collections. Codes are normalized to\n * lowercase during plugin setup.\n */\n languages: MultilangLanguage[]\n}\n\nexport type ResolvedMultilangCollection = {\n defaultLanguage: MultilangLanguage\n duplicate: boolean\n duplicateExcludeFields: string[]\n fieldNames: MultilangFieldNames\n languages: MultilangLanguage[]\n slug: string\n synchronizedFields: string[]\n}\n\nexport type ResolvedMultilangGlobal = {\n defaultLanguage: MultilangLanguage\n label: string\n languages: MultilangLanguage[]\n slug: string\n}\n\nexport type TranslationMap<TDoc = Record<string, unknown>> = Record<string, TDoc>\n\nexport type TranslationState<TDoc = Record<string, unknown>> = {\n group?: string\n language?: string\n source?: TDoc\n translations: TranslationMap<TDoc>\n}\n\nexport type WithLanguageArgs = {\n fieldName?: string\n language: string\n where?: Where\n}\n"],"names":[],"mappings":"AAsGA,WAIC"}
@@ -0,0 +1,192 @@
1
+ # Configuration
2
+
3
+ Use `payloadMultilang()` in `payload.config.ts`.
4
+
5
+ ```ts
6
+ import { buildConfig } from 'payload'
7
+ import { payloadMultilang } from '@roxxel/payload-multilang'
8
+
9
+ export default buildConfig({
10
+ localization: false,
11
+ plugins: [
12
+ payloadMultilang({
13
+ collections: {
14
+ posts: true,
15
+ },
16
+ globals: {
17
+ 'site-settings': true,
18
+ },
19
+ languages: [
20
+ {
21
+ code: 'en',
22
+ isDefault: true,
23
+ name: 'English',
24
+ },
25
+ {
26
+ code: 'uk',
27
+ name: 'Ukrainian',
28
+ },
29
+ ],
30
+ }),
31
+ ],
32
+ })
33
+ ```
34
+
35
+ ## Plugin Options
36
+
37
+ | Option | Type | Description |
38
+ | ------------------------ | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
39
+ | `languages` | `MultilangLanguage[]` | Required. Languages available in the admin UI and helper APIs. Exactly one active language must set `isDefault: true`. |
40
+ | `collections` | `Record<string, true \| MultilangCollectionOptions>` | Collection slugs to localize. |
41
+ | `globals` | `Record<string, true \| MultilangGlobalOptions>` | Global slugs to localize. |
42
+ | `fieldNames` | `Partial<MultilangFieldNames>` | Optional custom field names used by localized collections. |
43
+ | `duplicateExcludeFields` | `string[]` | Extra top-level fields to skip when a new translation duplicates source document data. |
44
+ | `disabled` | `boolean` | Returns the Payload config unchanged when true. Useful for temporary opt-out in specific environments. |
45
+
46
+ ## Languages
47
+
48
+ ```ts
49
+ type MultilangLanguage = {
50
+ active?: boolean
51
+ code: string
52
+ direction?: 'ltr' | 'rtl'
53
+ flagLabel?: string
54
+ id?: number | string
55
+ isDefault?: boolean
56
+ locale?: string
57
+ name: string
58
+ order?: number
59
+ }
60
+ ```
61
+
62
+ Language codes are trimmed and normalized to lowercase. Duplicate codes are rejected.
63
+
64
+ ```ts
65
+ payloadMultilang({
66
+ languages: [
67
+ {
68
+ code: 'en',
69
+ flagLabel: 'EN',
70
+ isDefault: true,
71
+ locale: 'en_US',
72
+ name: 'English',
73
+ order: 0,
74
+ },
75
+ {
76
+ code: 'uk',
77
+ flagLabel: 'UK',
78
+ locale: 'uk_UA',
79
+ name: 'Ukrainian',
80
+ order: 1,
81
+ },
82
+ ],
83
+ })
84
+ ```
85
+
86
+ ## Collections
87
+
88
+ Set a collection slug to `true` for default behavior.
89
+
90
+ ```ts
91
+ payloadMultilang({
92
+ collections: {
93
+ posts: true,
94
+ },
95
+ languages,
96
+ })
97
+ ```
98
+
99
+ Use collection options when you need custom translation behavior.
100
+
101
+ ```ts
102
+ payloadMultilang({
103
+ collections: {
104
+ posts: {
105
+ duplicate: true,
106
+ duplicateExcludeFields: ['legacyId'],
107
+ synchronizedFields: ['featuredImage', 'author'],
108
+ },
109
+ },
110
+ languages,
111
+ })
112
+ ```
113
+
114
+ | Option | Type | Description |
115
+ | ------------------------ | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
116
+ | `duplicate` | `boolean` | Defaults to `true`. When creating a translation, copy source document fields into the new document. Set to `false` to create empty translation documents by default. |
117
+ | `duplicateExcludeFields` | `string[]` | Additional top-level fields to skip during duplication for this collection. |
118
+ | `fields` | `Partial<MultilangFieldNames>` | Override metadata field names for this collection. |
119
+ | `synchronizedFields` | `string[]` | Top-level fields that should stay the same across all translations in a group. |
120
+
121
+ ## Globals
122
+
123
+ Set a global slug to `true` for default behavior.
124
+
125
+ ```ts
126
+ payloadMultilang({
127
+ globals: {
128
+ 'site-settings': true,
129
+ },
130
+ languages,
131
+ })
132
+ ```
133
+
134
+ Use `label` to customize the admin tabs wrapper label.
135
+
136
+ ```ts
137
+ payloadMultilang({
138
+ globals: {
139
+ 'site-settings': {
140
+ label: 'Settings translations',
141
+ },
142
+ },
143
+ languages,
144
+ })
145
+ ```
146
+
147
+ Read and update localized global values with `findGlobalByLanguage` and `updateGlobalByLanguage`.
148
+
149
+ ## Custom Field Names
150
+
151
+ The default collection metadata field names are:
152
+
153
+ ```ts
154
+ {
155
+ language: '_multilangLanguage',
156
+ group: '_multilangGroup',
157
+ meta: '_multilangMeta',
158
+ }
159
+ ```
160
+
161
+ Override them globally:
162
+
163
+ ```ts
164
+ payloadMultilang({
165
+ fieldNames: {
166
+ language: 'languageCode',
167
+ group: 'translationGroup',
168
+ meta: 'translationMeta',
169
+ },
170
+ collections: {
171
+ posts: true,
172
+ },
173
+ languages,
174
+ })
175
+ ```
176
+
177
+ Or override them for one collection:
178
+
179
+ ```ts
180
+ payloadMultilang({
181
+ collections: {
182
+ posts: {
183
+ fields: {
184
+ language: 'postLanguage',
185
+ },
186
+ },
187
+ },
188
+ languages,
189
+ })
190
+ ```
191
+
192
+ When using custom field names, pass the same names to helpers that accept `fieldNames` or `fieldName`.