DEV Community

Jose Carlos Correa
Jose Carlos Correa

Posted on

i18n Slides for tech speakers with mdx-deck

i18n Slides for tech speakers

If you are a developer who likes to share what you learn probbaly, you like to do it in local meetups or in big conferences either way your slides are a great asset to share content, but it's only accesible to people who know the language your slides are writen.

Another topic that will make you would like to keep reading is that speaking about code it implies share code, that in the most slide's making software is kinda weak.

If we are developers let's code our slides internationalized.

Tools

We are going to use:

  • mdx-deck
  • i18next
  • react-i18next

Process

Let's create a new folder an a new npm project on it

kmdir slides && cd slides

and

npm init -y

Installing dependencies

npm i mdx-deck i18next react-i18next 

Create project structure

Now we need to create a /src folder with our code base

mkdir src src/translations src/components src/docs

and our main file:

touch src/deck.mdx

Running our project

Now that we have our dependencies and de entire project structure let's configure our scripts in order to execute our project:

{
  "name": "slides",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "dependencies": {
    "i18next": "^19.4.4",
    "mdx-deck": "^4.1.1",
    "mdx-deck-code-surfer": "^0.5.5",
    "react-i18next": "^11.4.0"
  },
  "devDependencies": {},
  "scripts": {
    "dev": "mdx-deck ./src/deck.mdx",
    "build": "mdx-deck build ./src/deck.mdx",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

Now everything is settup add the next content to your deck.mdx file:

# Welcome to mdx-deck

---

## this is a mix of markdown and React

Since we are using mdx you can write on this file md tags or react components imported on our deck.mdx file like the next one:

// ./src/components/Counter.js
import React, { useState } from "react";

function Counter() {
  const [counter, setCounter] = useState(0);
  return (
    <div>
      <button onClick={() => setCounter(counter - 1)}>-</button>
      <h3>{counter}</h3>
      <button onClick={() => setCounter(counter + 1)}>+</button>
    </div>
  );
}

export default Counter;

Once you create your Counter component, update your deck.mdx file for it to use it:

import Counter from "./components/Counter";


# Welcome to mdx-deck

<Counter />

---

## this is a mix of markdown and React

With the Counter component render we should be something like this in our first slide:

Layout

Once last thing for you to know about mdx-deck it's their new layout system with their own built in components: Head, Header and Footer.

If you want to add metadata and SEO related stuff to your project you can use the Head component directly in your :

<Head>
  <title>My Presentation</title>
  <meta name="twitter:card" content="summary_large_image" />
  <meta name="twitter:site" content="@jossdz" />
  <meta name="twitter:title" content="My Presentation" />
  <meta name="twitter:description" content="A really great presentation" />
  <meta name="twitter:image" content="https://example.com/card.png" />
</Head>

If what you want is display persistent content across slides what you want to use is the Header and Footer components, as Head you can import both from mdx-deck- package and use it directly in your deck.mdx file:


import Counter from "./components/Counter";
import { Head, Header, Footer } from "mdx-deck";

<Head>
  <title>My Presentation | @jossdz</title>
  <meta name="twitter:card" content="summary_large_image" />
  <meta name="twitter:site" content="@jxnblk" />
  <meta name="twitter:title" content="My Presentation" />
  <meta name="twitter:description" content="A really great presentation" />
  <meta name="twitter:image" content="https://example.com/card.png" />
</Head>

<Header>

## i18n slides

</Header>

<Footer>

@jossdz

</Footer>

# Welcome to mdx-deck

<Counter />

---

## this is a mix of markdown and React


Adding i18n

Let's add i18next, for this part you'll need a i18n file and as muchs json files as you want for each language(for me will be enough with en and es):

touch src/i18n.js src/translations/es.json src/translations/en.json

in our i18n file we need to place the i18next config:

import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import en from "./translations/en.json";
import es from "./translations/es.json";

i18n.use(initReactI18next).init({
  lng: "en",
  resources: {
    en,
    es,
  },
});

export default i18n;

for the [lang].json files the structure shuold be like follows:

{
  "translation": {
    "key": "hello world",
    "list": ["item1", "item2", "item3", "item4"]
  }
}

If we want to make this configuration aplied to our site we neet to import our file i18n in our deck, this import can be placed at the top of the file:

import "./i18n"
import Counter from "./components/Counter";
import { Head, Header, Footer } from "mdx-deck";

Now we can create the components to show out content based on the default language and to change our language:

touch src/components/IntlContent.js src/components/ChangeLang.js
  • src/components/IntlContent.js
// import the useTranslation hook from react-i18next
import { useTranslation } from "react-i18next";
import React from "react";

const IntlContent = ({ label, htmlElement }) => {
// then extract the 't' funtion to get the translation based on a key in our translation json
  const { t } = useTranslation();
  switch (htmlElement) {
    case "h1":
      return <h1>{t(label)}</h1>;
    case "h2":
      return <h2>{t(label)}</h2>;
    case "h3":
      return <h3>{t(label)}</h3>;
    case "h4":
      return <h4>{t(label)}</h4>;
    case "ul":
      const elements = t("list", { returnObjects: true });
      return (
        <ul>
          {elements.map((el, i) => (
            <li key={i}>{el}</li>
          ))}
        </ul>
      );
    default:
      return <p>{t(label)}</p>;
  }
};

export default IntlContent;
  • src/components/IntlContent.js
import React from "react";
import { useTranslation } from "react-i18next";

function ChangeLang() {
// now we will get the i18n object which let us change the language
  const { i18n } = useTranslation();

  const changeLanguage = (lng) => {
    i18n.changeLanguage(lng);
  };

  return (
    <div>
// you shuold change the languajes based on the ones you are using
      <button onClick={() => changeLanguage("es")}>es</button>
      <button onClick={() => changeLanguage("en")}>en</button>
    </div>
  );
}

export default ChangeLang;

Least let's use our new powerful couple of components in our deck, at the end your deck should looks like this:

import "./i18n";
import Counter from "./components/Counter";
import { Head, Header, Footer } from "mdx-deck";
import ChangeLang from "./components/ChangeLang";
import IntlContent from "./components/IntlContent";

<Head>
  <title>My Presentation | @jossdz</title>
  <meta name="twitter:card" content="summary_large_image" />
  <meta name="twitter:site" content="@jxnblk" />
  <meta name="twitter:title" content="My Presentation" />
  <meta name="twitter:description" content="A really great presentation" />
  <meta name="twitter:image" content="https://example.com/card.png" />
</Head>

<Header>

## i18n slides

</Header>

<Footer>

@jossdz


</Footer>

<ChangeLang/>

<IntlContent htmlElement="h1" label="key"/>


<Counter />

---

## this is a mix of markdown and React

Thanks for read, here you can find the slides deployed and
the repo.

If you want to add themes or code demos check the next items on mdx-deck docs:

Top comments (0)