Multi-language Landing page with Next.js
Tags: nextjs
Published: 2023-07-14 15:20:09

Woovi master plan is to learn a lot about instant payments at Brazil, and be the best platform of instant payments in the world.

For this to happen everything that we build needs to support multi-languages.

One place that was missing multi-language support was in our landing page that uses Next.js.

## Static website with Next.js

Even though Next.js is know because of their server side capabilities, our landing page is just a static website.
We use `next export` to build our landing page and deploy to CloudFlare Pages and CloudFront.

Next has support to i18n out of the box, but not yet when using `next export`. So we need to figure it out the solution ourselves.

## Our landing page architecture

We build our landing page using two approaches: tsx and MDX.

When a page has many different styles and customization, we built it using React/tsx. Example: [https://woovi.com/shopper/](https://woovi.com/shopper/)

```tsx
<Typography>           
  {t('Create your free account now')}
</Typography>
```

For _tsx_ we mainly use `t()` to translate string literals.

When a page is more about content, like an article, it is written in MDX. MDX is Markdown with support to React components. Example: [https://woovi.com/articles/a-woovi-e-a-primeira-plataforma-pix-first-do-mundo/](https://woovi.com/articles/a-woovi-e-a-primeira-plataforma-pix-first-do-mundo/)

```jsx
import { ArticleLayout } from '../../templates/ArticleLayout';

export const meta = {
  title: "Pix Débito Automático - Cobrança Pix com Débito Automático",
}

## Módulo para cobranças automáticas usando Pix

O módulo Woovi

export default ({ children }) => <ArticleLayout meta={meta}>{children}</ArticleLayout>;
```

For mdx we don't translate using `t()` because this is a full text, and translation of word by word does not provide a good translation.

When adding multi-language support we need to take this architecture in consideration.


## Our approach to Multi-language using Next.js

To make our landing page multi-language we have the following structure:

```jsx
pages/
pages/en
pages/<another-language>
```

pages/ is our default language, Portuguese.

We duplicate all the pages inside `pages/` to all the languages we are supporting, right now only `en`, English.

For `tsx` files it will be translate using `t()`.
For `mdx` files we duplicate and translate the whole content at once.

We check the pathname of the router to determine what language the page is:

```tsx
const defaultLang = 'ptBR';

export const getLangFromPathname = (pathname: string) => {
  const langs = ['en', 'ptBR'];

  for (const lang of langs) {
    if (pathname.startsWith(`/${lang}`)) {
      return lang;
    }
  }

  return defaultLang;
}
```

Inside `_app.tsx` we have this code to make provide the language from the pathname to a React context

```tsx
const MyApp = ({ Component, pageProps, router }: NextAppProps<AppProps>) => {
   const lang = getLangFromPathname(router.pathname);

   const { t } = useTranslation(lang);

   return (
     <I18Context.Provider value={{ lang }}>
        {getLayout(<Component {...pageProps} />, { t })}
     </I18Context.Provider>
   )
}
```

Our custom `useTranslation` hook reads the language from the context and return the correct `t()`.

```tsx
export const useI18n = () => {
  return useContext(I18Context) || {};
};

export const useTranslation = (defaultLang = 'ptBR') => {
  const { lang = defaultLang } = useI18n();

  const t: TFunction = (key) => {
    const langs = {
      ptBR,
      en,
    };

    const trans = langs[lang][key];

    if (!trans) {
      if (process.env.NODE_ENV === 'development') {
        // eslint-disable-next-line no-console
        console.log('Missing translation: ', key);
      }
    }

    return trans;
  };

  return { t };
};
```

Link is another thing that needs to be fixed in a multi-language website.

We created the hook `useHrefLang` to return the correct href based on current language

```tsx
export const useHrefLang = (href: string) => {
  const { lang = defaultLang } = useI18n();

  if (href?.startsWith('https://')) {
    return href;
  }

  if (href?.startsWith('http://')) {
    return href;
  }

  if (lang === defaultLang) {
    return href;
  }

  if (href?.startsWith(`/${lang}`)) {
    return href;
  }

  return `/${lang}${href}`;
};
```

## In Conclusion
There are much more engineering in a landing page than it looks like.
A multi-language landing page takes more time to maintain, so make sure is the right moment for you to have one.

Check the final result here: [https://woovi.com/en](https://woovi.com/en)

---
Woovi
[Woovi](https://www.woovi.com) is a Startup that enables shoppers to pay as they like. To make this possible, Woovi provides instant payment solutions for merchants to accept orders.

If you want to work with us, we are [hiring](https://woovi.com/jobs/)!

---
Photo by <a href="https://unsplash.com/pt-br/@kalenemsley?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Kalen Emsley</a> on <a href="https://unsplash.com/images/nature/mountain?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>