4

We use the MDXJS package to write content in Markdown and use React components in it.

Is there a way of using the i18next / react-i18next package inside the MDX / Markdown files?

Danziger
  • 19,628
  • 4
  • 53
  • 83
jakecahill
  • 53
  • 3
  • Do you mean something like this? https://www.npmjs.com/package/i18next-markdown-jsx-plugin – adrai Nov 06 '18 at 21:14

1 Answers1

7

Using i18next inside MDX:

When you import an MDX file, you just use it as any other React component:

import { default as SomeContent } from './some-content.mdx';

...

<SomeContent />

Therefore, you can also pass down some props, in this case the t function, and use it inside in some specific ways:

import { default as SomeContent } from './some-content.mdx';

export const SomeComponent: React.FC = React.memo((props) => {
  const { t } = useTranslation();

  return (
    <SomeContent t={ t } someProp="Some value" />
  );
});

If you want to check if this is working or see which props are accessible from within your MDX file, add this inside it:

<pre>{ JSON.stringify(props, null, '  ') }</pre>
<pre>{ typeof props.t }</pre>

For the example above, it will display:

{"someProp":"Some value"}
function

Note that you can't use these props inside "raw" MD elements, even if you add a wrapper around them:

### Doesn't work: { props.t('some.translation') }

Doesn't work: { props.t('some.translation') }.

Doesn't work: <>{ props.t('some.translation') }</>.

Doesn't work: <Fragment>{ props.t('some.translation') }</Fragment>.

Doesn't work: <span>{ props.t('some.translation') }</span>.

So you would have to write HTML tags instead:

<h3>Works: { props.t('some.translation') }</h3>

<p>Works: { props.t('some.translation') }.</p>

<p>Works: <>{ props.t('some.translation') }</>.</p>

<p>Works: <Fragment>{ props.t('some.translation') }</Fragment>.</p>

<p>Works: <span>{ props.t('some.translation') }</span>.</p>

Using MDX in i18next:

If you set returnObjects: true in your i18next config, you can also add MDX components inside your translation files:

import { default as ContentEN } from './content.en.mdx';
import { default as ContentES } from './content.es.mdx';

i18next.use(initReactI18next).init({
  resources: {
    en: {
      translation: {
        content: ContentEN,
      },
    },
    es: {
      translation: {
        content: ContentES,
      },
    },
  },

  returnObjects: true,
}));

And then you would use it like this in any of your components (and yes, you can also pass down t or any other prop, just as before:

export const SomeComponent: React.FC = React.memo((props) => {
  const { t } = useTranslation();
  const Content = t('content');

  return (
    <Content t={ t } someProp="Some value" />
  );
});

Using i18next inside @mdx-js/runtime:

If you are using @mdx-js/runtime, then you would pass down your props as scope:

import { default as SomeContent } from './some-content.mdx';

export const SomeComponent: React.FC = React.memo((props) => {
  const { t } = useTranslation();

  return (
    <MDX components={ ... } scope={ { t, someProp: 'Some value' } }>{ props.mdx }</MDX>
  );
});
Danziger
  • 19,628
  • 4
  • 53
  • 83