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"
}
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...
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:
Which is then used like in the documentation example:
I guess this issue can then be closed. Finally :D