next-i18next
Version:
The easiest way to translate your NextJs apps.
1 lines • 9.7 kB
Source Map (JSON)
{"version":3,"file":"client.cjs","names":["initReactI18next","I18nextProvider"],"sources":["../../src/appRouter/client.tsx"],"sourcesContent":["'use client'\n\nimport React, { useState, useEffect, useCallback } from 'react'\nimport { createInstance } from 'i18next'\nimport type { i18n as I18NextClient, Resource, FlatNamespace, KeyPrefix, Module } from 'i18next'\nimport {\n I18nextProvider,\n useTranslation,\n type UseTranslationOptions,\n type UseTranslationResponse,\n type FallbackNs,\n} from 'react-i18next'\nimport { initReactI18next } from 'react-i18next/initReactI18next'\nimport resourcesToBackend from 'i18next-resources-to-backend'\nimport { useParams, useRouter } from 'next/navigation'\n\ntype $Tuple<T> = readonly [T?, ...T[]]\n\n// ---------------------------------------------------------------------------\n// I18nProvider\n// ---------------------------------------------------------------------------\n\nexport interface I18nProviderProps {\n children: React.ReactNode\n /** Current language (detected on the server, passed from layout) */\n language: string\n /** Server-loaded resources to hydrate the client instance */\n resources?: Resource\n /** All supported languages */\n supportedLngs?: string[]\n /** Default namespace */\n defaultNS?: string\n /** Fallback language */\n fallbackLng?: string | string[] | Record<string, string[]>\n /** Path to locale files (for lazy-loading additional namespaces on the client) */\n localePath?: string\n /** Locale file structure pattern */\n localeStructure?: string\n /** Locale file extension */\n localeExtension?: string\n /** Extra i18next plugins (e.g., i18next-http-backend, i18next-locize-backend) */\n use?: any[]\n /** Additional i18next init options */\n i18nextOptions?: Record<string, any>\n}\n\n/**\n * Client-side i18next provider for App Router.\n * Creates an i18next instance hydrated with server-loaded resources,\n * with fallback dynamic loading for additional namespaces.\n *\n * Supports custom backends via the `use` prop — pass i18next-http-backend,\n * i18next-locize-backend, or i18next-chained-backend to load translations\n * from external sources.\n *\n * @example\n * ```tsx\n * // In app/[lng]/layout.tsx (Server Component)\n * import { I18nProvider } from 'next-i18next/client'\n * import { getT, getResources } from 'next-i18next/server'\n *\n * export default async function Layout({ children, params }) {\n * const { lng } = await params\n * const { i18n } = await getT()\n * const resources = getResources(i18n, ['common'])\n * return (\n * <I18nProvider language={lng} resources={resources}>\n * {children}\n * </I18nProvider>\n * )\n * }\n * ```\n */\nexport function I18nProvider({\n children,\n language,\n resources,\n supportedLngs,\n defaultNS = 'common',\n fallbackLng,\n localePath = '/locales',\n localeStructure = '{{lng}}/{{ns}}',\n localeExtension = 'json',\n use = [],\n i18nextOptions = {},\n}: I18nProviderProps) {\n const [instance] = useState<I18NextClient>(() => {\n const inst = createInstance()\n inst.use(initReactI18next)\n\n const userHasBackend = use.some((b: Module) => b.type === 'backend')\n\n // Track which namespaces are bundled in the server-provided resources\n // so the default fetch backend can skip them\n const bundledNsSet = resources\n ? new Set(Object.values(resources).flatMap(r => Object.keys(r as Record<string, unknown>)))\n : new Set<string>()\n const bundledNs = bundledNsSet.size > 0 ? [...bundledNsSet] : [defaultNS]\n\n // Only add the default fetch-based backend if user hasn't provided one.\n // This allows using i18next-http-backend, i18next-locize-backend,\n // i18next-chained-backend, etc.\n if (!userHasBackend) {\n inst.use(resourcesToBackend((lng: string, ns: string) => {\n // Skip fetching for namespaces already provided via server resources\n if (bundledNsSet.has(ns)) return {}\n const path = `${localePath}/${localeStructure\n .replace('{{lng}}', lng)\n .replace('{{ns}}', ns)}.${localeExtension}`\n return fetch(path).then(r => r.ok ? r.json() : {})\n }))\n }\n\n // Apply user-provided plugins\n use.forEach((plugin: any) => inst.use(plugin))\n\n const hasAnyBackend = userHasBackend || !resources\n\n inst.init({\n lng: language,\n resources,\n ns: bundledNs,\n partialBundledLanguages: hasAnyBackend,\n defaultNS,\n fallbackLng: fallbackLng ?? language,\n supportedLngs: supportedLngs ?? (resources ? Object.keys(resources) : [language]),\n fallbackNS: defaultNS,\n interpolation: { escapeValue: false },\n react: { useSuspense: false },\n ...i18nextOptions,\n })\n\n return inst\n })\n\n // Sync language when the prop changes (e.g., after navigation)\n useEffect(() => {\n if (instance.language !== language) {\n instance.changeLanguage(language)\n }\n }, [instance, language])\n\n return (\n <I18nextProvider i18n={instance}>\n {children}\n </I18nextProvider>\n )\n}\n\n// ---------------------------------------------------------------------------\n// useT — translation hook for Client Components\n// ---------------------------------------------------------------------------\n\n/**\n * Translation hook for Client Components in App Router.\n * Works in both locale-in-path and no-locale-path modes:\n * - Locale-in-path: reads language from URL params (`[lng]` or `[locale]`) and syncs\n * - No-locale-path: uses the language set by I18nProvider (from server detection)\n *\n * @example\n * ```tsx\n * 'use client'\n * import { useT } from 'next-i18next/client'\n *\n * export default function Counter() {\n * const { t } = useT('home')\n * return <p>{t('greeting')}</p>\n * }\n * ```\n */\nexport function useT<\n Ns extends FlatNamespace | $Tuple<FlatNamespace> | undefined = undefined,\n KPrefix extends KeyPrefix<FallbackNs<Ns>> = undefined,\n>(\n ns?: Ns,\n options?: UseTranslationOptions<KPrefix>,\n): UseTranslationResponse<FallbackNs<Ns>, KPrefix> {\n const params = useParams()\n // Support both [lng] and [locale] param names\n const lngFromParams = typeof params?.lng === 'string'\n ? params.lng\n : typeof params?.locale === 'string'\n ? params.locale\n : undefined\n const ret = useTranslation(ns, options)\n\n // Sync language from URL params (locale-in-path mode)\n useEffect(() => {\n if (lngFromParams && ret.i18n.resolvedLanguage !== lngFromParams) {\n ret.i18n.changeLanguage(lngFromParams)\n }\n }, [lngFromParams, ret.i18n])\n\n return ret\n}\n\n// ---------------------------------------------------------------------------\n// useChangeLanguage — for no-locale-path mode\n// ---------------------------------------------------------------------------\n\n/**\n * Hook for changing the language without URL navigation (no-locale-path mode).\n * Updates cookie + i18next instance + triggers server re-render via router.refresh().\n *\n * @example\n * ```tsx\n * 'use client'\n * import { useChangeLanguage } from 'next-i18next/client'\n *\n * export default function LanguageSwitcher() {\n * const changeLanguage = useChangeLanguage()\n * return <button onClick={() => changeLanguage('de')}>Deutsch</button>\n * }\n * ```\n */\nexport function useChangeLanguage(cookieName = 'i18next') {\n const { i18n } = useTranslation()\n const router = useRouter()\n\n return useCallback(async (newLng: string) => {\n document.cookie = `${cookieName}=${newLng};path=/;max-age=${365 * 24 * 60 * 60};SameSite=Lax`\n await i18n.changeLanguage(newLng)\n router.refresh()\n }, [i18n, router, cookieName])\n}\n\n// Re-export useful react-i18next utilities for convenience\nexport { Trans } from 'react-i18next'\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyEA,SAAgB,aAAa,EAC3B,UACA,UACA,WACA,eACA,YAAY,UACZ,aACA,aAAa,YACb,kBAAkB,kBAClB,kBAAkB,QAClB,MAAM,EAAE,EACR,iBAAiB,EAAE,IACC;CACpB,MAAM,CAAC,aAAA,GAAA,MAAA,gBAA0C;EAC/C,MAAM,QAAA,GAAA,QAAA,iBAAuB;AAC7B,OAAK,IAAIA,+BAAAA,iBAAiB;EAE1B,MAAM,iBAAiB,IAAI,MAAM,MAAc,EAAE,SAAS,UAAU;EAIpE,MAAM,eAAe,YACjB,IAAI,IAAI,OAAO,OAAO,UAAU,CAAC,SAAQ,MAAK,OAAO,KAAK,EAA6B,CAAC,CAAC,mBACzF,IAAI,KAAa;EACrB,MAAM,YAAY,aAAa,OAAO,IAAI,CAAC,GAAG,aAAa,GAAG,CAAC,UAAU;AAKzE,MAAI,CAAC,eACH,MAAK,KAAA,GAAA,6BAAA,UAAwB,KAAa,OAAe;AAEvD,OAAI,aAAa,IAAI,GAAG,CAAE,QAAO,EAAE;GACnC,MAAM,OAAO,GAAG,WAAW,GAAG,gBAC3B,QAAQ,WAAW,IAAI,CACvB,QAAQ,UAAU,GAAG,CAAC,GAAG;AAC5B,UAAO,MAAM,KAAK,CAAC,MAAK,MAAK,EAAE,KAAK,EAAE,MAAM,GAAG,EAAE,CAAC;IAClD,CAAC;AAIL,MAAI,SAAS,WAAgB,KAAK,IAAI,OAAO,CAAC;EAE9C,MAAM,gBAAgB,kBAAkB,CAAC;AAEzC,OAAK,KAAK;GACR,KAAK;GACL;GACA,IAAI;GACJ,yBAAyB;GACzB;GACA,aAAa,eAAe;GAC5B,eAAe,kBAAkB,YAAY,OAAO,KAAK,UAAU,GAAG,CAAC,SAAS;GAChF,YAAY;GACZ,eAAe,EAAE,aAAa,OAAO;GACrC,OAAO,EAAE,aAAa,OAAO;GAC7B,GAAG;GACJ,CAAC;AAEF,SAAO;GACP;AAGF,EAAA,GAAA,MAAA,iBAAgB;AACd,MAAI,SAAS,aAAa,SACxB,UAAS,eAAe,SAAS;IAElC,CAAC,UAAU,SAAS,CAAC;AAExB,QACE,iBAAA,GAAA,kBAAA,KAACC,cAAAA,iBAAD;EAAiB,MAAM;EACpB;EACe,CAAA;;;;;;;;;;;;;;;;;;;AAyBtB,SAAgB,KAId,IACA,SACiD;CACjD,MAAM,UAAA,GAAA,gBAAA,YAAoB;CAE1B,MAAM,gBAAgB,OAAO,QAAQ,QAAQ,WACzC,OAAO,MACP,OAAO,QAAQ,WAAW,WACxB,OAAO,SACP,KAAA;CACN,MAAM,OAAA,GAAA,cAAA,gBAAqB,IAAI,QAAQ;AAGvC,EAAA,GAAA,MAAA,iBAAgB;AACd,MAAI,iBAAiB,IAAI,KAAK,qBAAqB,cACjD,KAAI,KAAK,eAAe,cAAc;IAEvC,CAAC,eAAe,IAAI,KAAK,CAAC;AAE7B,QAAO;;;;;;;;;;;;;;;;;AAsBT,SAAgB,kBAAkB,aAAa,WAAW;CACxD,MAAM,EAAE,UAAA,GAAA,cAAA,iBAAyB;CACjC,MAAM,UAAA,GAAA,gBAAA,YAAoB;AAE1B,SAAA,GAAA,MAAA,aAAmB,OAAO,WAAmB;AAC3C,WAAS,SAAS,GAAG,WAAW,GAAG,OAAO,kBAAkB,MAAM,KAAK,KAAK,GAAG;AAC/E,QAAM,KAAK,eAAe,OAAO;AACjC,SAAO,SAAS;IACf;EAAC;EAAM;EAAQ;EAAW,CAAC"}