Skip to content

Instantly share code, notes, and snippets.

@kachar
Last active February 1, 2023 06:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kachar/ee23a13dffa493179e835f3482f3b470 to your computer and use it in GitHub Desktop.
Save kachar/ee23a13dffa493179e835f3482f3b470 to your computer and use it in GitHub Desktop.
i18next + next.js 10 + Material UI
import React, { useEffect } from 'react'
import Head from 'next/head'
import { AppProps } from 'next/app'
import { ThemeProvider } from '@material-ui/core/styles'
import CssBaseline from '@material-ui/core/CssBaseline'
import theme from 'common/theme'
import useNextLocale from 'common/useNextLocale'
export default function CustomApp(props: AppProps) {
const { Component, pageProps } = props
useNextLocale({ i18nResources: pageProps.i18nResources })
useEffect(() => {
// Remove the server-side injected CSS.
const jssStyles = document.querySelector('#jss-server-side')
if (jssStyles && jssStyles.parentElement) {
jssStyles.parentElement.removeChild(jssStyles)
}
}, [])
return (
<React.Fragment>
<Head>
<title>i18next + next.js</title>
<meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width" />
</Head>
<ThemeProvider theme={theme}>
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
<CssBaseline />
<Component {...pageProps} />
</ThemeProvider>
</React.Fragment>
)
}
import { useCallback } from 'react'
import { useRouter } from 'next/router'
import { useTranslation } from 'react-i18next'
import { Box, Button, ButtonGroup } from '@material-ui/core'
export default function LocaleSwitcher() {
const { t } = useTranslation()
const router = useRouter()
const changeLang = useCallback(
// Same route different language
(locale: string) => () => router.push(router.route, undefined, { locale }),
[],
)
return (
<Box textAlign="center">
<ButtonGroup
disableRipple
variant="outlined"
color="primary"
aria-label="text primary button group">
<Button onClick={changeLang('bg')}>{t('BG')}</Button>
<Button onClick={changeLang('en')}>{t('EN')}</Button>
</ButtonGroup>
</Box>
)
}
import i18next from 'i18next'
import { useEffect } from 'react'
import { useRouter } from 'next/router'
import { initReactI18next } from 'react-i18next'
interface Translation {
[key: string]: string
}
interface Translations {
[namespace: string]: Translation
}
interface I18nResources {
translations: Translations
namespaces: string[]
}
type LocaleSetupProps = {
i18nResources: I18nResources
fallback?: string
}
export default function useNextLocale({
i18nResources = { translations: {}, namespaces: ['common'] },
}: LocaleSetupProps) {
const router = useRouter()
const { translations, namespaces } = i18nResources
const locale = router.locale || router.defaultLocale
if (!i18next.isInitialized) {
i18next.use(initReactI18next).init({
lng: locale,
preload: locale ? [locale] : [],
ns: namespaces,
supportedLngs: router.locales,
fallbackLng: router.defaultLocale,
react: { useSuspense: false },
})
}
i18next.setDefaultNamespace(namespaces[0])
if (locale) {
// Initialize language
if (!i18next.language) {
i18next.changeLanguage(locale)
}
for (const ns of namespaces) {
if (!i18next.hasResourceBundle(locale, ns)) {
i18next.addResourceBundle(locale, ns, translations[ns])
}
}
}
useEffect(() => {
// if (!i18next?.language) return
if (!router.locale) return
// Handle router locale updates
if (i18next.language !== router.locale) {
i18next.changeLanguage(router.locale)
}
}, [i18next, router.locale])
return { locale }
}
export async function getTranslations(locale: string | undefined, namespaces: string[]) {
const translations: Translations = {}
for (const ns of namespaces) {
const { default: data = {} }: { default: Translation } = await import(
`../../public/locales/${locale}/${ns}.json` // Adjust the correct path to public
)
translations[ns] = data
}
return { translations, namespaces }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment