import { cookies, headers } from 'next/headers' import Negotiator from 'negotiator' import { match } from '@formatjs/intl-localematcher' import { createInstance } from 'i18next' import resourcesToBackend from 'i18next-resources-to-backend' import { initReactI18next } from 'react-i18next/initReactI18next' import { i18n } from '.' import type { Locale } from '.' // https://locize.com/blog/next-13-app-dir-i18n/ const initI18next = async (lng: Locale, ns: string) => { const i18nInstance = createInstance() await i18nInstance .use(initReactI18next) .use(resourcesToBackend((language: string, namespace: string) => import(`./${language}/${namespace}.ts`))) .init({ lng: lng === 'zh-Hans' ? 'zh-Hans' : lng, ns, fallbackLng: 'en-US', }) return i18nInstance } export async function useTranslation(lng: Locale, ns = '', options: Record = {}) { const i18nextInstance = await initI18next(lng, ns) return { t: i18nextInstance.getFixedT(lng, ns, options.keyPrefix), i18n: i18nextInstance, } } export const getLocaleOnServer = (): Locale => { const locales: string[] = i18n.locales let languages: string[] | undefined // get locale from cookie const localeCookie = cookies().get('locale') languages = localeCookie?.value ? [localeCookie.value] : [] if (!languages.length) { // Negotiator expects plain object so we need to transform headers const negotiatorHeaders: Record = {} headers().forEach((value, key) => (negotiatorHeaders[key] = value)) // Use negotiator and intl-localematcher to get best locale languages = new Negotiator({ headers: negotiatorHeaders }).languages() } // Validate languages if (!Array.isArray(languages) || languages.length === 0 || !languages.every(lang => typeof lang === 'string' && /^[\w-]+$/.test(lang))) languages = [i18n.defaultLocale] // match locale const matchedLocale = match(languages, locales, i18n.defaultLocale) as Locale return matchedLocale }