NextJS Router compatible Weglot Language Switcher

I’ve started using an automatic translation service called Weglot on SecureMailMerge (which by the way offers advanced mail merge functionality for Outlook 365, if you’re interested).

The site runs on NextJS and is statically generated wherever possible. Weglot on the other hand acts as a proxy, so you create your subdomain language, e.g. de.securemailmerge.com and the proxy translates the text.

The first issue you run into is that if you navigate to a new part of your site, the new content is fetched and displayed using the NextJS router (client-side) and Weglot doesn’t get a chance to translate this.

Their support had a very simply quick solution to this. You just add the whole body as a dynamic element:

Next up was the language switcher. The original JS widget doesn’t play nicely with the NextJS router either.

The problem being: On page load the url is captured and set as the target in the language switcher. So let’s say you visit the /help page but then you click through to the getting started guide /help/getting-started and then decide to switch to German. Well, you won’t land on the German version of the Getting Started guide, but rather on the German version of the /help page.

Not a big issue, but still not ideal user experience.

Thankfully if you are using subdomain based translations the fix is a very simple custom language switcher. Feel free to use mine and adapt as you want:

import { useRouter } from 'next/router'
import React, { useEffect, useState } from 'react'

export type WeglotLanguageSwitcherProps = {
  domain: string
  langs: { [key: string]: string }
}

/* global window */

/**
 * NextJS compatible Weglot Language Switcher (uses TailwindCSS)
 *
 * Usage:
 * <WeglotLanguageSwitcher
 *     domain="yourdomain.com"
 *     langs={{ www: 'EN', de: 'DE', fr: 'FR', es: 'ES' }} />
 * where key is the subdomain, and
 *     value is the name of the language you want to display
 */
export const WeglotLanguageSwitcher = ({
  domain,
  langs,
}: WeglotLanguageSwitcherProps) => {
  const [hostname, setHostname] = useState('')
  const [pathAndQuery, setPathAndQuery] = useState('')
  const router = useRouter()

  useEffect(() => {
    router.events.on('routeChangeComplete', setPathAndQuery)
    router.events.on('hashChangeComplete', setPathAndQuery)
    return () => {
      router.events.off('routeChangeComplete', setPathAndQuery)
      router.events.off('hashChangeComplete', setPathAndQuery)
    }
  }, [router, router.events])

  useEffect(() => {
    setHostname(window.location.hostname.toLowerCase())
    setPathAndQuery(window.location.pathname + window.location.hash)
  }, [])

  // In weglot add Excluded Block for ".custom-wg-languages"
  // In your own css hide original language switcher
  // via #weglot-switcher-1 { display: none; }
  return (
    <div
      className={
        'custom-wg-languages fixed left-0 bottom-0 z-50 ' +
        'flex flex-row gap-2 rounded-tr bg-white px-2'
      }
    >
      {Object.keys(langs).map((lang) => (
        <a
          key={lang}
          href={
            hostname.startsWith(lang)
              ? '#'
              : `https://${lang}.${domain}` + pathAndQuery
          }
          className={
            'block border-b-4  p-2 font-bold text-gray-600' +
            'hover:border-red-600 hover:text-black ' +
            (hostname.startsWith(lang)
              ? 'border-red-600'
              : 'border-transparent')
          }
        >
          {langs[lang]}
        </a>
      ))}
    </div>
  )
}

Import the file and add the JSX to your _app.jsx and replace the domain and configure which domains/translations you have:

<WeglotLanguageSwitcher 
    domain="yourdomain.com" 
    langs={{ www: 'EN', de: 'DE', fr: 'FR', es: 'ES' }} />

Remember to hide the original Weglot switcher in your CSS and you probably want to add an exclusion for “.custom-wp-languages” to Weglot, so the language names don’t get translated in the switcher.

Voila, have fun.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.