Skip to content

Commit 6070d8d

Browse files
fix(db-mongodb): localization transforms on nested localized fields (#14600)
Fixes an issue where nested `localized: true` fields were not adhering to the correct data shape if a parent was already localized. This change works with the `compatibility.allowLocalizedWithinLocalized` flag set and not set. Before it would only work if you had the compatibility flag set to true, which is not the default. The issue surfaces when querying with `locale: "all"`. Example: ```ts fields: [{ name: 'localizedBlocks', type: 'blocks', blocks: [ { slug: 'nestedBlock', fields: [ { name: 'nestedArray', type: 'array', fields: [ { name: 'item', type: 'text', }, ], localized: true, // <-- this }, ], }, ], localized: true, // <-- and this }], ``` When transforming the outgoing data it would not include everything because it would incorrectly attempt to access data by locale on nested data instead of just the field name. #### Before (incorrect) ```ts localizedBlocks: { en: [ { blockType: 'nestedBlock', nestedArray: [] // <-- missing } ] } ``` #### After (correct) ```ts localizedBlocks: { en: [ { blockType: 'nestedBlock', nestedArray: [ // <-- included { id: '69162b9cecdd3484a55e196b', item: 'EN Nested', }, ], }, ] } ```
1 parent e1f07ff commit 6070d8d

File tree

6 files changed

+586
-5
lines changed

6 files changed

+586
-5
lines changed

packages/db-mongodb/src/utilities/transform.ts

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -305,11 +305,13 @@ const stripFields = ({
305305
config,
306306
data,
307307
fields,
308+
parentIsLocalized = false,
308309
reservedKeys = [],
309310
}: {
310311
config: SanitizedConfig
311312
data: any
312313
fields: FlattenedField[]
314+
parentIsLocalized?: boolean
313315
reservedKeys?: string[]
314316
}) => {
315317
for (const k in data) {
@@ -325,12 +327,14 @@ const stripFields = ({
325327
continue
326328
}
327329

330+
const shouldLocalizeField = fieldShouldBeLocalized({ field, parentIsLocalized })
331+
328332
if (field.type === 'blocks') {
329333
reservedKeys.push('blockType')
330334
}
331335

332336
if ('flattenedFields' in field || 'blocks' in field) {
333-
if (field.localized && config.localization) {
337+
if (shouldLocalizeField && config.localization) {
334338
for (const localeKey in fieldData) {
335339
if (!config.localization.localeCodes.some((code) => code === localeKey)) {
336340
delete fieldData[localeKey]
@@ -389,7 +393,13 @@ const stripFields = ({
389393
continue
390394
}
391395

392-
stripFields({ config, data, fields, reservedKeys })
396+
stripFields({
397+
config,
398+
data,
399+
fields,
400+
parentIsLocalized: parentIsLocalized || field.localized,
401+
reservedKeys,
402+
})
393403
}
394404

395405
if (hasNull) {
@@ -398,7 +408,13 @@ const stripFields = ({
398408

399409
continue
400410
} else {
401-
stripFields({ config, data: localeData, fields: field.flattenedFields, reservedKeys })
411+
stripFields({
412+
config,
413+
data: localeData,
414+
fields: field.flattenedFields,
415+
parentIsLocalized: parentIsLocalized || field.localized,
416+
reservedKeys,
417+
})
402418
}
403419
}
404420
continue
@@ -451,7 +467,13 @@ const stripFields = ({
451467
continue
452468
}
453469

454-
stripFields({ config, data, fields, reservedKeys })
470+
stripFields({
471+
config,
472+
data,
473+
fields,
474+
parentIsLocalized: parentIsLocalized || field.localized,
475+
reservedKeys,
476+
})
455477
}
456478

457479
if (hasNull) {
@@ -460,7 +482,13 @@ const stripFields = ({
460482

461483
continue
462484
} else {
463-
stripFields({ config, data: fieldData, fields: field.flattenedFields, reservedKeys })
485+
stripFields({
486+
config,
487+
data: fieldData,
488+
fields: field.flattenedFields,
489+
parentIsLocalized: parentIsLocalized || field.localized,
490+
reservedKeys,
491+
})
464492
}
465493
}
466494
}
@@ -524,6 +552,7 @@ export const transform = ({
524552
config,
525553
data,
526554
fields: flattenAllFields({ cache: true, fields }),
555+
parentIsLocalized: false,
527556
reservedKeys: ['id', 'globalType'],
528557
})
529558
}
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
import type { CollectionConfig } from 'payload'
2+
3+
import { allFieldsLocalizedSlug } from '../../shared.js'
4+
5+
export const AllFieldsLocalized: CollectionConfig = {
6+
slug: allFieldsLocalizedSlug,
7+
fields: [
8+
// Simple localized fields
9+
{
10+
name: 'text',
11+
type: 'text',
12+
localized: true,
13+
},
14+
{
15+
name: 'textarea',
16+
type: 'textarea',
17+
localized: true,
18+
},
19+
{
20+
name: 'number',
21+
type: 'number',
22+
localized: true,
23+
},
24+
{
25+
name: 'email',
26+
type: 'email',
27+
localized: true,
28+
},
29+
{
30+
name: 'code',
31+
type: 'code',
32+
localized: true,
33+
},
34+
{
35+
name: 'json',
36+
type: 'json',
37+
localized: true,
38+
},
39+
{
40+
name: 'select',
41+
type: 'select',
42+
localized: true,
43+
options: ['option1', 'option2', 'option3'],
44+
},
45+
{
46+
name: 'radio',
47+
type: 'radio',
48+
localized: true,
49+
options: ['radio1', 'radio2'],
50+
},
51+
{
52+
name: 'checkbox',
53+
type: 'checkbox',
54+
localized: true,
55+
},
56+
{
57+
name: 'date',
58+
type: 'date',
59+
localized: true,
60+
},
61+
62+
// Localized group with localized children
63+
{
64+
name: 'localizedGroup',
65+
type: 'group',
66+
fields: [
67+
{
68+
name: 'title',
69+
type: 'text',
70+
localized: true,
71+
},
72+
{
73+
name: 'description',
74+
type: 'textarea',
75+
localized: true,
76+
},
77+
],
78+
localized: true,
79+
},
80+
81+
// Non-localized group with localized children
82+
{
83+
name: 'nonLocalizedGroup',
84+
type: 'group',
85+
fields: [
86+
{
87+
name: 'localizedText',
88+
type: 'text',
89+
localized: true,
90+
},
91+
{
92+
name: 'nonLocalizedText',
93+
type: 'text',
94+
},
95+
],
96+
},
97+
98+
// Localized array with localized children
99+
{
100+
name: 'localizedArray',
101+
type: 'array',
102+
fields: [
103+
{
104+
name: 'item',
105+
type: 'text',
106+
localized: true,
107+
},
108+
],
109+
localized: true,
110+
},
111+
112+
// Non-localized array with localized children
113+
{
114+
name: 'nonLocalizedArray',
115+
type: 'array',
116+
fields: [
117+
{
118+
name: 'localizedItem',
119+
type: 'text',
120+
localized: true,
121+
},
122+
],
123+
},
124+
125+
// Localized blocks with nested localized fields
126+
{
127+
name: 'localizedBlocks',
128+
type: 'blocks',
129+
blocks: [
130+
{
131+
slug: 'localizedTextBlock',
132+
fields: [
133+
{
134+
name: 'text',
135+
type: 'text',
136+
localized: true,
137+
},
138+
],
139+
},
140+
{
141+
slug: 'nestedBlock',
142+
fields: [
143+
{
144+
name: 'nestedArray',
145+
type: 'array',
146+
fields: [
147+
{
148+
name: 'item',
149+
type: 'text',
150+
},
151+
],
152+
localized: true,
153+
},
154+
],
155+
},
156+
],
157+
localized: true,
158+
},
159+
160+
// Named tabs with localized tab
161+
{
162+
type: 'tabs',
163+
tabs: [
164+
{
165+
name: 'localizedTab',
166+
fields: [
167+
{
168+
name: 'tabText',
169+
type: 'text',
170+
localized: true,
171+
},
172+
],
173+
label: 'Localized Tab',
174+
localized: true,
175+
},
176+
{
177+
name: 'nonLocalizedTab',
178+
fields: [
179+
{
180+
name: 'localizedInNonLocalizedTab',
181+
type: 'text',
182+
localized: true,
183+
},
184+
],
185+
label: 'Non-Localized Tab',
186+
},
187+
],
188+
},
189+
190+
// Unnamed tab (passes through)
191+
{
192+
type: 'tabs',
193+
tabs: [
194+
{
195+
fields: [
196+
{
197+
name: 'unnamedTabLocalizedText',
198+
type: 'text',
199+
localized: true,
200+
},
201+
],
202+
label: 'Unnamed Tab',
203+
},
204+
],
205+
},
206+
207+
// Deeply nested: localized group > non-localized group > localized array
208+
{
209+
name: 'g1',
210+
type: 'group',
211+
label: 'Deeply Nested Group',
212+
fields: [
213+
{
214+
name: 'g2',
215+
type: 'group',
216+
fields: [
217+
{
218+
name: 'g2a1',
219+
type: 'array',
220+
fields: [
221+
{
222+
name: 'text',
223+
type: 'text',
224+
localized: true,
225+
},
226+
],
227+
localized: true,
228+
},
229+
],
230+
},
231+
],
232+
localized: true,
233+
},
234+
],
235+
versions: {
236+
drafts: true,
237+
},
238+
}

test/localization/config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type { LocalizedPost } from './payload-types.js'
88

99
import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
1010
import { devUser } from '../credentials.js'
11+
import { AllFieldsLocalized } from './collections/AllFields/index.js'
1112
import { ArrayCollection } from './collections/Array/index.js'
1213
import { ArrayWithFallbackCollection } from './collections/ArrayWithFallback/index.js'
1314
import { BlocksCollection } from './collections/Blocks/index.js'
@@ -68,6 +69,7 @@ export default buildConfigWithDefaults({
6869
NestedFields,
6970
LocalizedDrafts,
7071
LocalizedDateFields,
72+
AllFieldsLocalized,
7173
{
7274
admin: {
7375
listSearchableFields: 'name',

0 commit comments

Comments
 (0)