React-i18next: Trans-Component does not work with next/link`s Link component.

Created on 17 Apr 2020  路  4Comments  路  Source: i18next/react-i18next

Describe the bug
I'm trying to use the native next/link component inside the Trans component of react-i18next to render a text containing a link for different languages. This causes an Error: React.Children.only expected to receive a single React element child. on the link component. It seems like the Trans component adds something to the actual content of the link which causes this error.

Funnily enough: I also tried it with the link component of react-router-dom and even a standard <a> tag and with these two options everything works as expected.
Since my project relies heavily on the next/link switching is not really an option.

Occurs in react-i18next version
node: 12.12.0

"next": "^9.3.0",
"react-i18next": "^11.0.0",

To Reproduce
I tried the example from the Trans documentation:

 <Trans i18nKey="userMessagesUnread" count={2}>
     Hello <strong title={t('nameTitle')}>{{ name: 'Hans' }}</strong>, you have {{ count: 2 }} unread message. <Link href="/msgs">Go to messages</>
</Trans>

with the standard json coming with it:

{
  "userMessagesUnread": "Hello <1><0>{{name}}</0></1>, you have <3>{{count}}</3> unread message. <5>Go to messages</5>",
  "nameTitle": "Whatever title should be on the strong element"
}

Most helpful comment

Thanks for the gentle nudge in the right direction. It seems like the next/link requires a non-string descendant. So everything can be fed to the Link component except for pure strings, since it expects any kind of component or component like structure like an <a>-tag.

I solved it with a intermediate component:

export const LinkText = ({ href, children, ...props }) => {
  return (
     <Link href={href || ''} >
        <a>{children}</a>
    </Link>
  );
};

Which is then used like in the documentation example:

<Trans i18nKey="userMessagesUnread" count={2}>
    Hello <strong title={t('nameTitle')}>{{ name: 'Hans' }}</strong>, you have {{ count: 2 }} unread message. <LinkText href="/msgs">Go to messages</LinkText>
</Trans>

I guess this issue can then be closed. Finally :D

All 4 comments

Not sure...but there is no real magic happening...we just clone the element and replace children with the content from translations: https://github.com/i18next/react-i18next/blob/master/src/Trans.js#L147

Guess it's more the next link that does eventually not support string as children but need a single react element...?!?

Thanks for the gentle nudge in the right direction. It seems like the next/link requires a non-string descendant. So everything can be fed to the Link component except for pure strings, since it expects any kind of component or component like structure like an <a>-tag.

I solved it with a intermediate component:

export const LinkText = ({ href, children, ...props }) => {
  return (
     <Link href={href || ''} >
        <a>{children}</a>
    </Link>
  );
};

Which is then used like in the documentation example:

<Trans i18nKey="userMessagesUnread" count={2}>
    Hello <strong title={t('nameTitle')}>{{ name: 'Hans' }}</strong>, you have {{ count: 2 }} unread message. <LinkText href="/msgs">Go to messages</LinkText>
</Trans>

I guess this issue can then be closed. Finally :D

Another solution is to intercept the array and pull out the single child: https://github.com/vercel/next.js/issues/1605#issuecomment-301561842

Thanks for the gentle nudge in the right direction. It seems like the next/link requires a non-string descendant. So everything can be fed to the Link component except for pure strings, since it expects any kind of component or component like structure like an <a>-tag.

I solved it with a intermediate component:

export const LinkText = ({ href, children, ...props }) => {
  return (
     <Link href={href || ''} >
        <a>{children}</a>
    </Link>
  );
};

Which is then used like in the documentation example:

<Trans i18nKey="userMessagesUnread" count={2}>
    Hello <strong title={t('nameTitle')}>{{ name: 'Hans' }}</strong>, you have {{ count: 2 }} unread message. <LinkText href="/msgs">Go to messages</LinkText>
</Trans>

I guess this issue can then be closed. Finally :D

@ClemensGrunewald , what does your translation value for userMessagesUnread look like?

userMessagesUnread: "Hello <1>{{ name }}</1>, you have {{ count}} unread message. <3>Go to messages</3>"

I ask because I am trying this, but something about the NextJS Link component & Trans is not rendering the link as a link.

<Trans t={t} i18nKey="confirm">
    some plain text{' '}
        <span>
              to upload your documentation into{' '}
                <LinkText href="/account">your account</LinkText>{' '}
             before this is confirmed.
        </span>
</Trans>

(note: {' '} counts as a child)

and then:

    "confirm": "some plain text <2>to upload your documentation into <4>your account</4> before this is confirmed </2>",

however the your account string is not an anchor element...

Was this page helpful?
0 / 5 - 0 ratings