Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

<Trans> breaks React JSX semantics with regards to booleans #1904

Closed
1 of 3 tasks
gaearon opened this issue Apr 4, 2024 · 3 comments · Fixed by #1906
Closed
1 of 3 tasks

<Trans> breaks React JSX semantics with regards to booleans #1904

gaearon opened this issue Apr 4, 2024 · 3 comments · Fixed by #1906
Labels

Comments

@gaearon
Copy link

gaearon commented Apr 4, 2024

Describe the bug

From:

https://lingui.dev/tutorials/react#jsx-to-messageformat-transformations

Obviously, you can also shoot yourself in the foot. Some expressions are valid and won't throw any error, yet it doesn't make any sense to write:

// Oh, seriously?
<Trans>{isOpen && <Modal />}</Trans>

If in doubt, imagine how the final message should look like.

Setting aside the unnecessarily mocking tone of the docs, this behavior seems quite problematic because it changes the React JSX semantics. Booleans are supposed to be ignored in the output, but Lingui just turns them to strings.

Even if you "know" that's what it does, it's still super easy to miss <Trans> outside of some JSX, and to change that JSX according to React rules. It's then a significant pitfall that it doesn't work. Especially considering Lingui reconstructs the entire tree below so it breaks not just one level deep, but all the way down. This is highly non-obvious.

To Reproduce
Steps to reproduce the behavior, possibly with minimal code sample, e.g:

import { Trans } from "@lingui/react"

export default function App() {
   return (
      <Text>
        <Trans>
          foo
          <span>
            {false && 'lol'}
            {true && 'wut'}
          </span>
          bar
        </Trans>
      </Text>
   )
}

Expected behavior

<Text>foowutbar</Text>

Actual behavior

<Text>foofalsewutbar</Text>
  • jsLingui version lingui --version 4.5.0
  • Babel version npm list @babel/core 7.22.10
  • Macro support:
  • I'm using SWC with @lingui/swc-plugin
  • I'm using Babel with babel-macro-plugin
  • I'm not using macro
  • Your Babel config (e.g. .babelrc) or framework you use (Create React App, NextJs, Vite)
    • Expo
@timofei-iatsenko
Copy link
Collaborator

Thanks for report. I Actually have no idea how proper React code after transform should looks like. Currently your example would be transformed into:

<Text>
  <_Trans
    id={"ULdFKt"}
    message={"foo<0>{0}{1}</0>bar"}
    values={{
      0: false && "lol",
      1: true && "wut",
    }}
    components={{
      0: <span />,
    }}
  />
</Text>;

And these differences in semantics came from

values={{
      0: false && "lol",
      1: true && "wut",
    }}

Which are simply interpolated into the string like so:

`${false && "lol"}` // -≥ will print false

@timofei-iatsenko
Copy link
Collaborator

I probably found a solution:

If every value would be wrapped into JSX Fragment, than lingui/react's formatElements will treat them as JSX components and inline as children, thus JSX semantics would be preserved

values={{
-      0: false && "lol",
+      0: <>{false && "lol"}</>,
    }}

@gaearon
Copy link
Author

gaearon commented Apr 4, 2024

I think maybe it would be enough to do this for values that aren’t strings and numbers?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants