11'use client' ;
22
33import { ThemeProvider } from 'next-themes' ;
4- import { type ComponentPropsWithoutRef , lazy , type ReactNode } from 'react' ;
4+ import {
5+ type ComponentPropsWithoutRef ,
6+ lazy ,
7+ type ReactNode ,
8+ useMemo ,
9+ } from 'react' ;
510import { DirectionProvider } from '@radix-ui/react-direction' ;
611import type { DefaultSearchDialogProps } from '@/components/dialog/search-default' ;
712import { SidebarProvider } from '@/contexts/sidebar' ;
813import { SearchProvider , type SearchProviderProps } from '@/contexts/search' ;
14+ import { useEffectEvent } from 'fumadocs-core/utils/use-effect-event' ;
15+ import {
16+ defaultTranslations ,
17+ I18nContext ,
18+ type LocaleItem ,
19+ type Translations ,
20+ } from '@/contexts/i18n' ;
21+ import { usePathname , useRouter } from 'fumadocs-core/framework' ;
922
1023interface SearchOptions
1124 extends Omit < SearchProviderProps , 'options' | 'children' > {
@@ -41,6 +54,32 @@ export interface RootProviderProps {
4154 enabled ?: boolean ;
4255 } ;
4356
57+ i18n ?: Omit < I18nProviderProps , 'children' > ;
58+
59+ children ?: ReactNode ;
60+ }
61+
62+ export interface I18nProviderProps {
63+ /**
64+ * Current locale
65+ */
66+ locale : string ;
67+
68+ /**
69+ * Handle changes to the locale, redirect user when not specified.
70+ */
71+ onLocaleChange ?: ( v : string ) => void ;
72+
73+ /**
74+ * Translations of current locale
75+ */
76+ translations ?: Partial < Translations > ;
77+
78+ /**
79+ * Available languages
80+ */
81+ locales ?: LocaleItem [ ] ;
82+
4483 children ?: ReactNode ;
4584}
4685
@@ -53,9 +92,14 @@ export function RootProvider({
5392 dir = 'ltr' ,
5493 theme = { } ,
5594 search,
95+ i18n,
5696} : RootProviderProps ) {
5797 let body = children ;
5898
99+ if ( i18n ) {
100+ body = < I18nProvider { ...i18n } > { body } </ I18nProvider > ;
101+ }
102+
59103 if ( search ?. enabled !== false )
60104 body = (
61105 < SearchProvider SearchDialog = { DefaultSearchDialog } { ...search } >
@@ -82,3 +126,48 @@ export function RootProvider({
82126 </ DirectionProvider >
83127 ) ;
84128}
129+
130+ function I18nProvider ( {
131+ locales = [ ] ,
132+ locale,
133+ onLocaleChange,
134+ ...props
135+ } : I18nProviderProps ) {
136+ const router = useRouter ( ) ;
137+ const pathname = usePathname ( ) ;
138+ const onChange = useEffectEvent ( ( value : string ) => {
139+ if ( onLocaleChange ) {
140+ return onLocaleChange ( value ) ;
141+ }
142+ const segments = pathname . split ( '/' ) . filter ( ( v ) => v . length > 0 ) ;
143+
144+ // If locale prefix hidden
145+ if ( segments [ 0 ] !== locale ) {
146+ segments . unshift ( value ) ;
147+ } else {
148+ segments [ 0 ] = value ;
149+ }
150+
151+ router . push ( `/${ segments . join ( '/' ) } ` ) ;
152+ router . refresh ( ) ;
153+ } ) ;
154+
155+ return (
156+ < I18nContext . Provider
157+ value = { useMemo (
158+ ( ) => ( {
159+ locale,
160+ locales,
161+ text : {
162+ ...defaultTranslations ,
163+ ...props . translations ,
164+ } ,
165+ onChange,
166+ } ) ,
167+ [ locale , locales , onChange , props . translations ] ,
168+ ) }
169+ >
170+ { props . children }
171+ </ I18nContext . Provider >
172+ ) ;
173+ }
0 commit comments