From 3b73ae74a359b545509d8c515547525f82210cc5 Mon Sep 17 00:00:00 2001 From: Sven Slootweg Date: Sun, 13 Dec 2020 20:26:49 +0100 Subject: [PATCH 1/2] (@fluent-react) Fix ignore-nested-children test --- fluent-react/test/localized_overlay.test.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fluent-react/test/localized_overlay.test.js b/fluent-react/test/localized_overlay.test.js index 67fa15a43..52e3af1ec 100644 --- a/fluent-react/test/localized_overlay.test.js +++ b/fluent-react/test/localized_overlay.test.js @@ -270,7 +270,10 @@ foo = Click ! const renderer = TestRenderer.create( - }}> + , + em: + }}>
From 83640229458c816a9c65c1346945fe96dd738362 Mon Sep 17 00:00:00 2001 From: Sven Slootweg Date: Sun, 13 Dec 2020 20:29:59 +0100 Subject: [PATCH 2/2] (@fluent/react) Process nested children --- fluent-react/src/localized.ts | 86 +++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 33 deletions(-) diff --git a/fluent-react/src/localized.ts b/fluent-react/src/localized.ts index 33dfda189..76817fceb 100644 --- a/fluent-react/src/localized.ts +++ b/fluent-react/src/localized.ts @@ -143,46 +143,66 @@ export function Localized(props: LocalizedProps): ReactElement { } } + function transformTranslatedNodes(nodes: Node[]): ReactNode[] { + return nodes.map(childNode => { + if (childNode.nodeName === "#text") { + return childNode.textContent; + } - // If the message contains markup, parse it and try to match the children - // found in the translation with the props passed to this Localized. - const translationNodes = l10n.parseMarkup(messageValue); - const translatedChildren = translationNodes.map(childNode => { - if (childNode.nodeName === "#text") { - return childNode.textContent; - } + const childName = childNode.nodeName.toLowerCase(); - const childName = childNode.nodeName.toLowerCase(); + // If the child is not expected just take its textContent. + if ( + !elemsLower || + !Object.prototype.hasOwnProperty.call(elemsLower, childName) + ) { + return childNode.textContent; + } - // If the child is not expected just take its textContent. - if ( - !elemsLower || - !Object.prototype.hasOwnProperty.call(elemsLower, childName) - ) { - return childNode.textContent; - } + const sourceChild = elemsLower[childName]; - const sourceChild = elemsLower[childName]; + // Ignore elems which are not valid React elements. + if (!isValidElement(sourceChild)) { + return childNode.textContent; + } - // Ignore elems which are not valid React elements. - if (!isValidElement(sourceChild)) { - return childNode.textContent; - } + // If the element passed in the elems prop is a known void element, + // explicitly dismiss any textContent which might have accidentally been + // defined in the translation to prevent the "void element tags must not + // have children" error. + if (sourceChild.type in voidElementTags) { + return sourceChild; + } - // If the element passed in the elems prop is a known void element, - // explicitly dismiss any textContent which might have accidentally been - // defined in the translation to prevent the "void element tags must not - // have children" error. - if (sourceChild.type in voidElementTags) { - return sourceChild; - } + // If the element defines childNodes, then ignore any textContents that + // may have been accidentally defined, and instead traverse the children + // and recursively give them the same treatment. This is needed to handle + // a nested tree of markup tags. + if ( + childNode.childNodes !== undefined && + childNode.childNodes.length > 0 + ) { + const childNodesArray = Array.from(childNode.childNodes); + + if (childNodesArray.some(node => node.nodeName !== "#text")) { + const transformedChildren = transformTranslatedNodes(childNodesArray); + + return cloneElement(sourceChild, undefined, ... transformedChildren); + } + } + + // TODO Protect contents of elements wrapped in + // https://github.com/projectfluent/fluent.js/issues/184 + // TODO Control localizable attributes on elements passed as props + // https://github.com/projectfluent/fluent.js/issues/185 + return cloneElement(sourceChild, undefined, childNode.textContent); + }); + } - // TODO Protect contents of elements wrapped in - // https://github.com/projectfluent/fluent.js/issues/184 - // TODO Control localizable attributes on elements passed as props - // https://github.com/projectfluent/fluent.js/issues/185 - return cloneElement(sourceChild, undefined, childNode.textContent); - }); + // If the message contains markup, parse it and try to match the children + // found in the translation with the props passed to this Localized. + const translationNodes = l10n.parseMarkup(messageValue); + const translatedChildren = transformTranslatedNodes(translationNodes); return cloneElement(child, localizedProps, ...translatedChildren); }