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",
};
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;
I also changed public/assets/scripts/translation.js the following way because pageLanguage
parameter is not mandatory.
function TranslateInit() {
new google.translate.TranslateElement();
}
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;
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;
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>
);
}
We don't even physically include the translation engine if the config is missing.
You can find the final solution here.
Top comments (2)
how to add arabic lang? with ar not work correct
@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.