6
6
createElement ,
7
7
isValidElement ,
8
8
cloneElement ,
9
+ ReactNode ,
9
10
} from "react" ;
10
11
import { CachedSyncIterable } from "cached-iterable" ;
11
12
import { createParseMarkup , MarkupParser } from "./markup.js" ;
@@ -101,6 +102,7 @@ export class ReactLocalization {
101
102
vars ?: Record < string , FluentVariable > ;
102
103
elems ?: Record < string , ReactElement > ;
103
104
attrs ?: Record < string , boolean > ;
105
+ nestedElems ?: boolean ;
104
106
} = { }
105
107
) : ReactElement {
106
108
const bundle = this . getBundle ( id ) ;
@@ -186,9 +188,8 @@ export class ReactLocalization {
186
188
return cloneElement ( sourceElement , localizedProps , messageValue ) ;
187
189
}
188
190
189
- let elemsLower : Map < string , ReactElement > ;
191
+ const elemsLower : Map < string , ReactElement > = new Map ( ) ;
190
192
if ( args . elems ) {
191
- elemsLower = new Map ( ) ;
192
193
for ( let [ name , elem ] of Object . entries ( args . elems ) ) {
193
194
// Ignore elems which are not valid React elements.
194
195
if ( ! isValidElement ( elem ) ) {
@@ -201,39 +202,55 @@ export class ReactLocalization {
201
202
// If the message contains markup, parse it and try to match the children
202
203
// found in the translation with the args passed to this function.
203
204
const translationNodes = this . parseMarkup ( messageValue ) ;
204
- const translatedChildren = translationNodes . map (
205
- ( { nodeName , textContent } ) => {
206
- if ( nodeName === "#text" ) {
207
- return textContent ;
208
- }
205
+ const translatedChildren = translateChildren (
206
+ translationNodes ,
207
+ elemsLower ,
208
+ args . nestedElems
209
+ ) ;
209
210
210
- const childName = nodeName . toLowerCase ( ) ;
211
- const sourceChild = elemsLower ?. get ( childName ) ;
211
+ return cloneElement ( sourceElement , localizedProps , ...translatedChildren ) ;
212
+ }
213
+ }
212
214
213
- // If the child is not expected just take its textContent.
214
- if ( ! sourceChild ) {
215
- return textContent ;
216
- }
215
+ function translateChildren (
216
+ translationNodes : Node [ ] ,
217
+ elemsLower : Map < string , ReactElement > ,
218
+ recursive : boolean | undefined
219
+ ) : ReactNode [ ] {
220
+ return translationNodes . map ( ( { nodeName, textContent, childNodes } ) => {
221
+ if ( nodeName === "#text" ) {
222
+ return textContent ;
223
+ }
217
224
218
- // If the element passed in the elems prop is a known void element,
219
- // explicitly dismiss any textContent which might have accidentally been
220
- // defined in the translation to prevent the "void element tags must not
221
- // have children" error.
222
- if (
223
- typeof sourceChild . type === "string" &&
224
- sourceChild . type in voidElementTags
225
- ) {
226
- return sourceChild ;
227
- }
225
+ const childName = nodeName . toLowerCase ( ) ;
226
+ const sourceChild = elemsLower ?. get ( childName ) ;
228
227
229
- // TODO Protect contents of elements wrapped in <Localized>
230
- // https://github.com/projectfluent/fluent.js/issues/184
231
- // TODO Control localizable attributes on elements passed as props
232
- // https://github.com/projectfluent/fluent.js/issues/185
233
- return cloneElement ( sourceChild , undefined , textContent ) ;
234
- }
235
- ) ;
228
+ let translatedChildren = recursive
229
+ ? translateChildren ( [ ...childNodes ] , elemsLower , true )
230
+ : [ textContent ] ;
236
231
237
- return cloneElement ( sourceElement , localizedProps , ...translatedChildren ) ;
238
- }
232
+ // If the child is not expected just take its content.
233
+ if ( ! sourceChild ) {
234
+ return recursive
235
+ ? createElement ( Fragment , null , ...translatedChildren )
236
+ : textContent ;
237
+ }
238
+
239
+ // If the element passed in the elems prop is a known void element,
240
+ // explicitly dismiss any textContent which might have accidentally been
241
+ // defined in the translation to prevent the "void element tags must not
242
+ // have children" error.
243
+ if (
244
+ typeof sourceChild . type === "string" &&
245
+ sourceChild . type in voidElementTags
246
+ ) {
247
+ return sourceChild ;
248
+ }
249
+
250
+ // TODO Protect contents of elements wrapped in <Localized>
251
+ // https://github.com/projectfluent/fluent.js/issues/184
252
+ // TODO Control localizable attributes on elements passed as props
253
+ // https://github.com/projectfluent/fluent.js/issues/185
254
+ return cloneElement ( sourceChild , undefined , ...translatedChildren ) ;
255
+ } ) ;
239
256
}
0 commit comments