import { cookies, headers } from "next/headers"; import { createInstance, i18n, LanguageDetectorAsyncModule } from "i18next"; import resourcesToBackend from "i18next-resources-to-backend"; import { getServerSession } from "next-auth"; import { authOptions } from "@/config/authConfig"; import I18nClientProvider from "./I18nClientProvider"; import universalLanguageDetect from "@unly/universal-language-detector"; const FALLBACK_LANG = "zh"; const SUPPORTED_LANGUAGES = ["zh"]; export const detectLanguage = async (): Promise => { // Logic to get language preference from cookies/headers/session const cookiesList = cookies(); const cookiesObj = cookiesList .getAll() .reduce<{ [name: string]: string }>( (acc, cookie) => ({ ...acc, [cookie.name]: cookie.value }), {}, ); const headersList = headers(); console.time("[i18n] detectLanguage total"); console.time("[i18n] getServerSession"); const session = await getServerSession(authOptions); console.timeEnd("[i18n] getServerSession"); console.time("[i18n] universalLanguageDetect"); const lang = universalLanguageDetect({ supportedLanguages: SUPPORTED_LANGUAGES, fallbackLanguage: FALLBACK_LANG, acceptLanguageHeader: headersList.get("accept-language") || undefined, serverCookies: cookiesObj, }); console.timeEnd("[i18n] universalLanguageDetect"); console.timeEnd("[i18n] detectLanguage total"); return lang; }; const languageDetector: LanguageDetectorAsyncModule = { type: "languageDetector", detect: detectLanguage, async: true, }; const initI18next = async (namespaces: string[]): Promise => { const label = `[i18n] initI18next ns=${namespaces.join(",")}`; console.time(label); const i18nInstance = createInstance(); await i18nInstance .use(languageDetector) .use( resourcesToBackend((language: string, namespace: string) => { return import(`./${language}/${namespace}.json`); }), ) .init({ fallbackLng: "en", interpolation: { escapeValue: false, }, ns: namespaces, }); return i18nInstance as i18n; }; export const getServerI18n = async (...namespaces: string[]) => { return initI18next(namespaces); }; interface Props { children: React.ReactNode; namespaces: string[]; } // Provides the resources for the client export const I18nProvider: React.FC = async ({ children, namespaces, }) => { const i18n = await getServerI18n(...namespaces); const language = i18n.language; const resources = namespaces.reduce<{ [ns: string]: any }>( (acc, ns) => ({ ...acc, [ns]: i18n.getResourceBundle(language, ns), }), {}, ); return ( {children} ); };