next-translate icon indicating copy to clipboard operation
next-translate copied to clipboard

Locales are not loaded if Shallow Routing

Open ettalhaoui opened this issue 5 years ago • 7 comments

Hi,

In order to change the location and maintain the app state and the current path (Change language while staying on the same route). I ran into this issue: next Router changes the local, but next-translate remains in the current language.

<Link href={router.asPath} locale={lng} key={lng} shallow={true}>...</Link>

ettalhaoui avatar Feb 22 '21 21:02 ettalhaoui

I would like to understand in which cases you need shallow=true? Shallow routing allows you to change the URL without running data fetching methods again, which includes getServerSideProps, getStaticProps, and getInitialProps. But next-translate needs these methods to download the page translations.

aralroca avatar Feb 23 '21 09:02 aralroca

I am currently working on migrating from react-app to nextJs, we are using redux for state management. So if I change the app language without shallow=true, as you say NextJs run data fetching methods again. That cause the redux store to initialize, so we lost any data fetching that was happening on the client side.

ettalhaoui avatar Feb 23 '21 10:02 ettalhaoui

@Aeet Maybe you can use a hook using the getT function to download in client-side the necessary translations without doing it in the fetching methods.

I guess it would be something like this:

import React, { Fragment, useEffect, useState } from 'react'
import Link from 'next/link'
import { useRouter } from 'next/router'
import useTranslation from 'next-translate/useTranslation'
import getT from 'next-translate/getT'

export default function useShallowTranslation(ns) {
  const { locale } = useRouter()
  const { lang, t } = useTranslation(ns)
  const [_t, setT] = useState(() => t)
  const [_lang, setLang] = useState(lang)

  useEffect(() => {
    if (lang === locale) return
    getT(locale, ns).then(newT => {
      setT(() => newT)
      setLang(locale)
    })
  }, [lang, locale])

  return { t: _t, lang: _lang }
}

Then you should use it similar to useTranslation but always passing the namespace it needs:

export default function MyComponent() {
  const { t, lang } = useShallowTranslation('common')
  const title = t('title')

  return <h1>{title}</h1>
}

I think it should work. It's not a very elegant solution, but I hope it can work for you in this particular case (since it's not very usual to use shallow=true to switch languages).

aralroca avatar Feb 23 '21 12:02 aralroca

I don't know if there is a way to know if the current navigation comes from shallow routing, if so, maybe we could force the I18nProvider to download the necessary page translations inside a useEffect, so you don't have to use the above workaround...

aralroca avatar Feb 23 '21 12:02 aralroca

Was there any updates in here? I'm facing the same issue.

Thing is We have some requests to be made in our _app.js's getInitialProps and they get re-executed upon lang change :(

fedealu avatar Jan 25 '22 20:01 fedealu

@Aeet Maybe you can use a hook using the getT function to download in client-side the necessary translations without doing it in the fetching methods.

export default function useShallowTranslation(ns) { const { locale } = useRouter() const { lang, t } = useTranslation(ns) const [_t, setT] = useState(() => t) const [_lang, setLang] = useState(lang)

useEffect(() => { if (lang === locale) return getT(locale, ns).then(newT => { setT(() => newT) setLang(locale) }) }, [lang, locale])

If you switch the initial language and then u go back to it, it won't be reflected in the UI because lang from useTranslation doesn't change on shallow routing. Instead of the lang===locale check I added a mounted Ref check

import { useEffect, useRef, useState } from 'react'
import { useRouter } from 'next/router'
import _useTranslation from 'next-translate/useTranslation'
import getT from 'next-translate/getT'

export default function useTranslation(ns: string) {
  const mounted = useRef(false)
  const { locale } = useRouter()
  const { lang, t } = _useTranslation(ns)
  const [_t, setT] = useState(() => t)
  const [_lang, setLang] = useState(lang)
  useEffect(() => {
    if (!mounted.current) {
      mounted.current = true
      return
    }
    getT(locale, ns).then((newT) => {
      setT(() => newT)
      setLang(locale)
    })
  }, [lang, locale])

  return { t: _t, lang: _lang }
}

andrewdoro avatar Sep 17 '22 18:09 andrewdoro

I don't know if there is a way to know if the current navigation comes from shallow routing, if so, maybe we could force the I18nProvider to download the necessary page translations inside a useEffect, so you don't have to use the above workaround...

also Next can now identify if the route change is using shallow Update: I tried implementing shallow support in the provider, but for some reason the router object is not being updated #917

https://github.com/vercel/next.js/pull/19802 https://nextjs.org/docs/api-reference/next/router#routerevents

andrewdoro avatar Sep 18 '22 10:09 andrewdoro