DEV Community

Cover image for Google Translate customization#2 under NextJS.
Vyacheslav Chub for Valor Labs

Posted on

Google Translate customization#2 under NextJS.

You know, sometimes our first feelings let us down because the world around us is not so simple. I was going to stop with Google Translate customization under NextJS. This article reveals an approach to translate any content freely without pain via Google Translate on a NextJS-based project. But a couple of occasions became a game changer.

First, I found one essential improvement regarding language configuration. Second, my colleague Bruno Silva provided me with valuable code improvements. I decided to make a new series by analyzing the factors above. I recommend reading the previous series if you want to understand my following thoughts.

Let's get started.

The central part of the previous solution is public/assets/scripts/lang-config.js contains custom languages settings

window.__GOOGLE_TRANSLATION_CONFIG__ = {
  languages: [
    { title: "English", name: "en" },
    { title: "Deutsch", name: "de" },
    { title: "Español", name: "es" },
    { title: "Français", name: "fr" },
  ],
  defaultLanguage: "en",
};
Enter fullscreen mode Exit fullscreen mode

The solution above is legal, but it doesn't look like a NextJS-pure. I found a more elegant way to define global data through NextJS config. Let's add env section to next.config.js and remove public/assets/scripts/lang-config.js

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  env: {
    GOOGLE_TRANSLATION_CONFIG: JSON.stringify({
      languages: [
        { title: "English", name: "en" },
        { title: "Deutsch", name: "de" },
        { title: "Español", name: "es" },
        { title: "Français", name: "fr" },
      ],
      defaultLanguage: "en",
    }),
  },
};

module.exports = nextConfig;
Enter fullscreen mode Exit fullscreen mode

I also changed public/assets/scripts/translation.js the following way because pageLanguage parameter is not mandatory.

function TranslateInit() {
  new google.translate.TranslateElement();
}
Enter fullscreen mode Exit fullscreen mode

According to Bruno's proposal, I encapsulated most of the logic into a custom hook, src/hooks/useLanguageSwitcher.ts.

import { useEffect, useState } from "react";
import { parseCookies, setCookie } from "nookies";
import { NextPageContext } from "next";

export const COOKIE_NAME = "googtrans";

export interface LanguageDescriptor {
  name: string;
  title: string;
}

export interface LanguageConfig {
  languages: LanguageDescriptor[];
  defaultLanguage: string;
}

export type UseLanguageSwitcherResult = {
  currentLanguage: string;
  switchLanguage: (lang: string) => () => void;
  languageConfig: LanguageConfig | undefined;
};

export type UseLanguageSwitcherOptions = {
  context?: NextPageContext;
};

export const getLanguageConfig = (): LanguageConfig | undefined => {
  let cfg: LanguageConfig | undefined;

  if (process.env.GOOGLE_TRANSLATION_CONFIG) {
    try {
      cfg = JSON.parse(process.env.GOOGLE_TRANSLATION_CONFIG ?? "{}");
    } catch (e) {}
  }

  return cfg;
};

export const useLanguageSwitcher = ({
  context,
}: UseLanguageSwitcherOptions = {}): UseLanguageSwitcherResult => {
  const [currentLanguage, setCurrentLanguage] = useState<string>("");

  useEffect(() => {
    const cfg = getLanguageConfig();
    const cookies = parseCookies(context);
    const existingLanguageCookieValue = cookies[COOKIE_NAME];

    let languageValue = "";
    if (existingLanguageCookieValue) {
      const sp = existingLanguageCookieValue.split("/");
      if (sp.length > 2) {
        languageValue = sp[2];
      }
    }
    if (cfg && !languageValue) {
      languageValue = cfg.defaultLanguage;
    }
    setCurrentLanguage(languageValue);
  }, []);

  const switchLanguage = (lang: string) => () => {
    setCookie(context, COOKIE_NAME, "/auto/" + lang);
    window.location.reload();
  };

  return {
    currentLanguage,
    switchLanguage,
    languageConfig: getLanguageConfig(),
  };
};

export default useLanguageSwitcher;
Enter fullscreen mode Exit fullscreen mode

Important note. process.env.GOOGLE_TRANSLATION_CONFIG allows us to get GOOGLE_TRANSLATION_CONFIG variable from the above mentioned NextJS config.

A couple of final stitches.

src/components/lang-switcher.tsx

import { NextPageContext } from "next";
import useLanguageSwitcher, {
  LanguageDescriptor,
} from "@/hooks/useLanguageSwitcher";
import React from "react";

export type LanguageSwitcherProps = {
  context?: NextPageContext;
};

export const LanguageSwitcher = ({ context }: LanguageSwitcherProps = {}) => {
  const { currentLanguage, switchLanguage, languageConfig } =
    useLanguageSwitcher({ context });

  if (!languageConfig) {
    return null;
  }

  return (
    <div className="text-center notranslate">
      {languageConfig.languages.map((ld: LanguageDescriptor, i: number) => (
        <React.Fragment key={`l_s_${ld}`}>
          {currentLanguage === ld.name ||
          (currentLanguage === "auto" &&
            languageConfig.defaultLanguage === ld.name) ? (
            <span className="mx-3 text-orange-300">{ld.title}</span>
          ) : (
            <a
              onClick={switchLanguage(ld.name)}
              className="mx-3 text-blue-300 cursor-pointer hover:underline"
            >
              {ld.title}
            </a>
          )}
        </React.Fragment>
      ))}
    </div>
  );
};

export default LanguageSwitcher;
Enter fullscreen mode Exit fullscreen mode

useLanguageSwitcher looks elegant :)

src/pages/_document.tsx

import { Html, Head, Main, NextScript } from "next/document";
import Script from "next/script";

export default function Document() {
  return (
    <Html>
      <Head>
        <Script
          src="/assets/scripts/translation.js"
          strategy="beforeInteractive"
        />
        {process.env.GOOGLE_TRANSLATION_CONFIG && (
          <Script
            src="//translate.google.com/translate_a/element.js?cb=TranslateInit"
            strategy="afterInteractive"
          />
        )}
      </Head>
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}
Enter fullscreen mode Exit fullscreen mode

We don't even physically include the translation engine if the config is missing.

You can find the final solution here.

May the Google Translate, NextJS, and Force be with you!

Top comments (2)

Collapse
 
kawtharklayshe profile image
kawthar klayshe • Edited

how to add arabic lang? with ar not work correct

Collapse
 
buchslava profile image
Vyacheslav Chub • Edited

@kawtharklayshe This is a question of RTL support. Please, look at github.com/buchslava/nextjs-gtrans...

The solution above is not 100% perfect, but it illustrates how it works.