UNPKG

next-i18next

Version:

The easiest way to translate your NextJs apps.

132 lines (131 loc) 4.7 kB
'use client'; "use client"; import { useCallback, useEffect, useState } from "react"; import { createInstance } from "i18next"; import { I18nextProvider, Trans, useTranslation } from "react-i18next"; import { initReactI18next } from "react-i18next/initReactI18next"; import resourcesToBackend from "i18next-resources-to-backend"; import { useParams, useRouter } from "next/navigation"; import { jsx } from "react/jsx-runtime"; //#region src/appRouter/client.tsx /** * Client-side i18next provider for App Router. * Creates an i18next instance hydrated with server-loaded resources, * with fallback dynamic loading for additional namespaces. * * Supports custom backends via the `use` prop — pass i18next-http-backend, * i18next-locize-backend, or i18next-chained-backend to load translations * from external sources. * * @example * ```tsx * // In app/[lng]/layout.tsx (Server Component) * import { I18nProvider } from 'next-i18next/client' * import { getT, getResources } from 'next-i18next/server' * * export default async function Layout({ children, params }) { * const { lng } = await params * const { i18n } = await getT() * const resources = getResources(i18n, ['common']) * return ( * <I18nProvider language={lng} resources={resources}> * {children} * </I18nProvider> * ) * } * ``` */ function I18nProvider({ children, language, resources, supportedLngs, defaultNS = "common", fallbackLng, localePath = "/locales", localeStructure = "{{lng}}/{{ns}}", localeExtension = "json", use = [], i18nextOptions = {} }) { const [instance] = useState(() => { const inst = createInstance(); inst.use(initReactI18next); const userHasBackend = use.some((b) => b.type === "backend"); const bundledNsSet = resources ? new Set(Object.values(resources).flatMap((r) => Object.keys(r))) : /* @__PURE__ */ new Set(); const bundledNs = bundledNsSet.size > 0 ? [...bundledNsSet] : [defaultNS]; if (!userHasBackend) inst.use(resourcesToBackend((lng, ns) => { if (bundledNsSet.has(ns)) return {}; const path = `${localePath}/${localeStructure.replace("{{lng}}", lng).replace("{{ns}}", ns)}.${localeExtension}`; return fetch(path).then((r) => r.ok ? r.json() : {}); })); use.forEach((plugin) => inst.use(plugin)); const hasAnyBackend = userHasBackend || !resources; inst.init({ lng: language, resources, ns: bundledNs, partialBundledLanguages: hasAnyBackend, defaultNS, fallbackLng: fallbackLng ?? language, supportedLngs: supportedLngs ?? (resources ? Object.keys(resources) : [language]), fallbackNS: defaultNS, interpolation: { escapeValue: false }, react: { useSuspense: false }, ...i18nextOptions }); return inst; }); useEffect(() => { if (instance.language !== language) instance.changeLanguage(language); }, [instance, language]); return /* @__PURE__ */ jsx(I18nextProvider, { i18n: instance, children }); } /** * Translation hook for Client Components in App Router. * Works in both locale-in-path and no-locale-path modes: * - Locale-in-path: reads language from URL params (`[lng]` or `[locale]`) and syncs * - No-locale-path: uses the language set by I18nProvider (from server detection) * * @example * ```tsx * 'use client' * import { useT } from 'next-i18next/client' * * export default function Counter() { * const { t } = useT('home') * return <p>{t('greeting')}</p> * } * ``` */ function useT(ns, options) { const params = useParams(); const lngFromParams = typeof params?.lng === "string" ? params.lng : typeof params?.locale === "string" ? params.locale : void 0; const ret = useTranslation(ns, options); useEffect(() => { if (lngFromParams && ret.i18n.resolvedLanguage !== lngFromParams) ret.i18n.changeLanguage(lngFromParams); }, [lngFromParams, ret.i18n]); return ret; } /** * Hook for changing the language without URL navigation (no-locale-path mode). * Updates cookie + i18next instance + triggers server re-render via router.refresh(). * * @example * ```tsx * 'use client' * import { useChangeLanguage } from 'next-i18next/client' * * export default function LanguageSwitcher() { * const changeLanguage = useChangeLanguage() * return <button onClick={() => changeLanguage('de')}>Deutsch</button> * } * ``` */ function useChangeLanguage(cookieName = "i18next") { const { i18n } = useTranslation(); const router = useRouter(); return useCallback(async (newLng) => { document.cookie = `${cookieName}=${newLng};path=/;max-age=${365 * 24 * 60 * 60};SameSite=Lax`; await i18n.changeLanguage(newLng); router.refresh(); }, [ i18n, router, cookieName ]); } //#endregion export { I18nProvider, Trans, useChangeLanguage, useT }; //# sourceMappingURL=client.mjs.map