From 6bfb698aa85cc5bb6157640337a3f19438da08a0 Mon Sep 17 00:00:00 2001 From: Konstantinos Bairaktaris Date: Thu, 25 Jul 2024 20:34:39 +0300 Subject: [PATCH] (Finally) fix the issue with how JSX handles newlines --- packages/cli/src/api/parsers/babel.js | 16 ++++++++++++++-- packages/cli/test/api/extract.hashedkeys.test.js | 8 ++++---- packages/cli/test/api/extract.sourcekeys.test.js | 8 ++++---- packages/react/README.md | 2 +- packages/react/src/components/T.jsx | 2 +- packages/react/src/utils/toStr.js | 3 +-- 6 files changed, 25 insertions(+), 14 deletions(-) diff --git a/packages/cli/src/api/parsers/babel.js b/packages/cli/src/api/parsers/babel.js index 3667c202..8060d995 100644 --- a/packages/cli/src/api/parsers/babel.js +++ b/packages/cli/src/api/parsers/babel.js @@ -73,7 +73,19 @@ function toStr(children, counter = 0) { } } else if (child.type === 'JSXText') { // Child is not a React element, append as-is - const chunk = child.value.trim().replace(/\s+/g, ' '); + let chunk = child.value; + + // Try to mimic how JSX is parsed in runtime React + const [startMatch] = /^[\s\n]*/.exec(child.value); + if (startMatch.includes('\n')) { + chunk = chunk.substring(startMatch.length); + } + + const [endMatch] = /[\s\n]*$/.exec(child.value); + if (endMatch.includes('\n')) { + chunk = chunk.substring(0, chunk.length - endMatch.length); + } + if (chunk) { result.push(chunk); } } else if ( child.type === 'JSXExpressionContainer' @@ -195,7 +207,7 @@ function babelExtractPhrases(HASHES, source, relativeFile, options) { if (!string && elem.name.name === 'T' && node.children && node.children.length) { const [result] = toStr(node.children); - string = result.join(' ').trim(); + string = result.join('').trim(); } if (!string) return; diff --git a/packages/cli/test/api/extract.hashedkeys.test.js b/packages/cli/test/api/extract.hashedkeys.test.js index 13bed81a..1be9dfa1 100644 --- a/packages/cli/test/api/extract.hashedkeys.test.js +++ b/packages/cli/test/api/extract.hashedkeys.test.js @@ -158,12 +158,12 @@ describe('extractPhrases with hashed keys', () => { string: 'Text 5', meta: { context: [], tags: [], occurrences: ['react.jsx'] }, }, - '404d0c0fef510bc89da7bc58ef160ccc': { - string: 'Text <1> 6 ', + '121b687b8625b4e58ba7f36dca77ad7f': { + string: 'Text <1>6', meta: { context: [], tags: [], occurrences: ['react.jsx'] }, }, - '4f5fe2d7356c474bd2f4c03176c6bc45': { - string: 'Text <1> <2> 7 ', + '3ed8a3c47f6a32ece9c9ae0c2a060d45': { + string: 'Text <1><2>7', meta: { context: [], tags: [], occurrences: ['react.jsx'] }, }, '1ecaf4c087b894bf86987fc2972ddba7': { diff --git a/packages/cli/test/api/extract.sourcekeys.test.js b/packages/cli/test/api/extract.sourcekeys.test.js index 05125385..ebf8f017 100644 --- a/packages/cli/test/api/extract.sourcekeys.test.js +++ b/packages/cli/test/api/extract.sourcekeys.test.js @@ -151,12 +151,12 @@ describe('extractPhrases with source keys', () => { string: 'Text 5', meta: { context: [], tags: [], occurrences: ['react.jsx'] }, }, - 'Text <1> 6 ': { - string: 'Text <1> 6 ', + 'Text <1>6': { + string: 'Text <1>6', meta: { context: [], tags: [], occurrences: ['react.jsx'] }, }, - 'Text <1> <2> 7 ': { - string: 'Text <1> <2> 7 ', + 'Text <1><2>7': { + string: 'Text <1><2>7', meta: { context: [], tags: [], occurrences: ['react.jsx'] }, }, 'Text 8::foo': { diff --git a/packages/react/README.md b/packages/react/README.md index b6f70020..c5a34d52 100644 --- a/packages/react/README.md +++ b/packages/react/README.md @@ -138,7 +138,7 @@ If you do this, the string that will be sent to Transifex for translation will look like this: ``` -A <1> button and a <2> bold walk into a bar +A <1>button and a <2>bold walk into a bar ``` As long as the translation respects the numbered tags, the T-component will diff --git a/packages/react/src/components/T.jsx b/packages/react/src/components/T.jsx index 140f8244..60b0ba01 100644 --- a/packages/react/src/components/T.jsx +++ b/packages/react/src/components/T.jsx @@ -49,7 +49,7 @@ export default function T({ _str, children, ...props }) { if (!children) { return t(_str, props); } const [templateArray, propsContainer] = toStr(children); - const templateString = templateArray.join(' ').trim(); + const templateString = templateArray.join(''); const translation = t(templateString, props); const result = toElement(translation, propsContainer); diff --git a/packages/react/src/utils/toStr.js b/packages/react/src/utils/toStr.js index 7da07fd9..861f827d 100644 --- a/packages/react/src/utils/toStr.js +++ b/packages/react/src/utils/toStr.js @@ -64,8 +64,7 @@ export function toStr(children, counter = 0) { // Child is not a React element, append as-is /* eslint-disable no-lonely-if */ if (typeof child === 'string' || child instanceof String) { - const chunk = child.trim().replace(/\s+/g, ' '); - if (chunk) { result.push(chunk); } + if (child) { result.push(child); } } else { result.push(child); }