mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-25 04:20:02 +00:00
UI: Deprecate I18nProvider in favour of <RootProvider /> i18n prop
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'fumadocs-ui': patch
|
||||
---
|
||||
|
||||
Deprecate I18nProvider in favour of `<RootProvider />` `i18n` prop
|
||||
@@ -2,7 +2,6 @@ import 'fumadocs-ui/style.css';
|
||||
import { RootProvider } from 'fumadocs-ui/provider';
|
||||
import { Inter } from 'next/font/google';
|
||||
import type { ReactNode } from 'react';
|
||||
import { I18nProvider } from 'fumadocs-ui/i18n';
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ['latin'],
|
||||
@@ -25,20 +24,20 @@ export default async function Layout({
|
||||
minHeight: '100vh',
|
||||
}}
|
||||
>
|
||||
<I18nProvider
|
||||
locale={lang}
|
||||
locales={[
|
||||
{
|
||||
name: 'English',
|
||||
locale: 'en',
|
||||
},
|
||||
{
|
||||
name: 'Chinese',
|
||||
locale: 'cn',
|
||||
},
|
||||
]}
|
||||
translations={
|
||||
{
|
||||
<RootProvider
|
||||
i18n={{
|
||||
locale: lang,
|
||||
locales: [
|
||||
{
|
||||
name: 'English',
|
||||
locale: 'en',
|
||||
},
|
||||
{
|
||||
name: 'Chinese',
|
||||
locale: 'cn',
|
||||
},
|
||||
],
|
||||
translations: {
|
||||
cn: {
|
||||
toc: '目錄',
|
||||
search: '搜尋文檔',
|
||||
@@ -48,11 +47,11 @@ export default async function Layout({
|
||||
nextPage: '下一頁',
|
||||
chooseLanguage: '選擇語言',
|
||||
},
|
||||
}[lang]
|
||||
}
|
||||
}[lang],
|
||||
}}
|
||||
>
|
||||
<RootProvider>{children}</RootProvider>
|
||||
</I18nProvider>
|
||||
{children}
|
||||
</RootProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
'use client';
|
||||
import { useEffectEvent } from 'fumadocs-core/utils/use-effect-event';
|
||||
import { createContext, type ReactNode, useContext, useMemo } from 'react';
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
import { createContext, useContext } from 'react';
|
||||
|
||||
export interface Translations {
|
||||
search: string;
|
||||
@@ -56,79 +54,3 @@ export function I18nLabel(props: { label: keyof Translations }): string {
|
||||
export function useI18n(): I18nContextType {
|
||||
return useContext(I18nContext);
|
||||
}
|
||||
|
||||
export interface I18nProviderProps {
|
||||
/**
|
||||
* Current locale
|
||||
*/
|
||||
locale: string;
|
||||
|
||||
/**
|
||||
* Handle changes to the locale, redirect user when not specified.
|
||||
*/
|
||||
onLocaleChange?: (v: string) => void;
|
||||
|
||||
/**
|
||||
* Translations of current locale
|
||||
*/
|
||||
translations?: Partial<Translations>;
|
||||
|
||||
/**
|
||||
* Available languages
|
||||
*/
|
||||
locales?: LocaleItem[];
|
||||
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
export function I18nProvider({
|
||||
locales = [],
|
||||
locale,
|
||||
onChange: _onChange,
|
||||
onLocaleChange = _onChange,
|
||||
...props
|
||||
}: I18nProviderProps & {
|
||||
// TODO: remove next major
|
||||
/**
|
||||
* @deprecated use `onLocaleChange` instead
|
||||
*/
|
||||
onChange?: I18nProviderProps['onLocaleChange'];
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const onChange = useEffectEvent((value: string) => {
|
||||
if (onLocaleChange) {
|
||||
return onLocaleChange(value);
|
||||
}
|
||||
const segments = pathname.split('/').filter((v) => v.length > 0);
|
||||
|
||||
// If locale prefix hidden
|
||||
if (segments[0] !== locale) {
|
||||
segments.unshift(value);
|
||||
} else {
|
||||
segments[0] = value;
|
||||
}
|
||||
|
||||
router.push(`/${segments.join('/')}`);
|
||||
router.refresh();
|
||||
});
|
||||
|
||||
return (
|
||||
<I18nContext.Provider
|
||||
value={useMemo(
|
||||
() => ({
|
||||
locale,
|
||||
locales,
|
||||
text: {
|
||||
...defaultTranslations,
|
||||
...props.translations,
|
||||
},
|
||||
onChange,
|
||||
}),
|
||||
[locale, locales, onChange, props.translations],
|
||||
)}
|
||||
>
|
||||
{props.children}
|
||||
</I18nContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,64 @@
|
||||
export {
|
||||
defaultTranslations,
|
||||
type Translations,
|
||||
I18nProvider,
|
||||
type I18nProviderProps,
|
||||
} from './contexts/i18n';
|
||||
'use client';
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
import { useEffectEvent } from 'fumadocs-core/utils/use-effect-event';
|
||||
import { useMemo } from 'react';
|
||||
import { defaultTranslations, I18nContext } from '@/contexts/i18n';
|
||||
import type { I18nProviderProps } from './provider/base';
|
||||
|
||||
export type { I18nProviderProps };
|
||||
export { defaultTranslations, type Translations } from './contexts/i18n';
|
||||
|
||||
// TODO: remove next major
|
||||
/**
|
||||
* @deprecated legacy I18n Provider, use `<RootProvider i18n={...} />` instead
|
||||
*/
|
||||
export function I18nProvider({
|
||||
locales = [],
|
||||
locale,
|
||||
onChange: _onChange,
|
||||
onLocaleChange = _onChange,
|
||||
...props
|
||||
}: I18nProviderProps & {
|
||||
/**
|
||||
* @deprecated use `onLocaleChange` instead
|
||||
*/
|
||||
onChange?: I18nProviderProps['onLocaleChange'];
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const onChange = useEffectEvent((value: string) => {
|
||||
if (onLocaleChange) {
|
||||
return onLocaleChange(value);
|
||||
}
|
||||
const segments = pathname.split('/').filter((v) => v.length > 0);
|
||||
|
||||
// If locale prefix hidden
|
||||
if (segments[0] !== locale) {
|
||||
segments.unshift(value);
|
||||
} else {
|
||||
segments[0] = value;
|
||||
}
|
||||
|
||||
router.push(`/${segments.join('/')}`);
|
||||
router.refresh();
|
||||
});
|
||||
|
||||
return (
|
||||
<I18nContext.Provider
|
||||
value={useMemo(
|
||||
() => ({
|
||||
locale,
|
||||
locales,
|
||||
text: {
|
||||
...defaultTranslations,
|
||||
...props.translations,
|
||||
},
|
||||
onChange,
|
||||
}),
|
||||
[locale, locales, onChange, props.translations],
|
||||
)}
|
||||
>
|
||||
{props.children}
|
||||
</I18nContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,24 @@
|
||||
'use client';
|
||||
|
||||
import { ThemeProvider } from 'next-themes';
|
||||
import { type ComponentPropsWithoutRef, lazy, type ReactNode } from 'react';
|
||||
import {
|
||||
type ComponentPropsWithoutRef,
|
||||
lazy,
|
||||
type ReactNode,
|
||||
useMemo,
|
||||
} from 'react';
|
||||
import { DirectionProvider } from '@radix-ui/react-direction';
|
||||
import type { DefaultSearchDialogProps } from '@/components/dialog/search-default';
|
||||
import { SidebarProvider } from '@/contexts/sidebar';
|
||||
import { SearchProvider, type SearchProviderProps } from '@/contexts/search';
|
||||
import { useEffectEvent } from 'fumadocs-core/utils/use-effect-event';
|
||||
import {
|
||||
defaultTranslations,
|
||||
I18nContext,
|
||||
type LocaleItem,
|
||||
type Translations,
|
||||
} from '@/contexts/i18n';
|
||||
import { usePathname, useRouter } from 'fumadocs-core/framework';
|
||||
|
||||
interface SearchOptions
|
||||
extends Omit<SearchProviderProps, 'options' | 'children'> {
|
||||
@@ -41,6 +54,32 @@ export interface RootProviderProps {
|
||||
enabled?: boolean;
|
||||
};
|
||||
|
||||
i18n?: Omit<I18nProviderProps, 'children'>;
|
||||
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
export interface I18nProviderProps {
|
||||
/**
|
||||
* Current locale
|
||||
*/
|
||||
locale: string;
|
||||
|
||||
/**
|
||||
* Handle changes to the locale, redirect user when not specified.
|
||||
*/
|
||||
onLocaleChange?: (v: string) => void;
|
||||
|
||||
/**
|
||||
* Translations of current locale
|
||||
*/
|
||||
translations?: Partial<Translations>;
|
||||
|
||||
/**
|
||||
* Available languages
|
||||
*/
|
||||
locales?: LocaleItem[];
|
||||
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
@@ -53,9 +92,14 @@ export function RootProvider({
|
||||
dir = 'ltr',
|
||||
theme = {},
|
||||
search,
|
||||
i18n,
|
||||
}: RootProviderProps) {
|
||||
let body = children;
|
||||
|
||||
if (i18n) {
|
||||
body = <I18nProvider {...i18n}>{body}</I18nProvider>;
|
||||
}
|
||||
|
||||
if (search?.enabled !== false)
|
||||
body = (
|
||||
<SearchProvider SearchDialog={DefaultSearchDialog} {...search}>
|
||||
@@ -82,3 +126,48 @@ export function RootProvider({
|
||||
</DirectionProvider>
|
||||
);
|
||||
}
|
||||
|
||||
function I18nProvider({
|
||||
locales = [],
|
||||
locale,
|
||||
onLocaleChange,
|
||||
...props
|
||||
}: I18nProviderProps) {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const onChange = useEffectEvent((value: string) => {
|
||||
if (onLocaleChange) {
|
||||
return onLocaleChange(value);
|
||||
}
|
||||
const segments = pathname.split('/').filter((v) => v.length > 0);
|
||||
|
||||
// If locale prefix hidden
|
||||
if (segments[0] !== locale) {
|
||||
segments.unshift(value);
|
||||
} else {
|
||||
segments[0] = value;
|
||||
}
|
||||
|
||||
router.push(`/${segments.join('/')}`);
|
||||
router.refresh();
|
||||
});
|
||||
|
||||
return (
|
||||
<I18nContext.Provider
|
||||
value={useMemo(
|
||||
() => ({
|
||||
locale,
|
||||
locales,
|
||||
text: {
|
||||
...defaultTranslations,
|
||||
...props.translations,
|
||||
},
|
||||
onChange,
|
||||
}),
|
||||
[locale, locales, onChange, props.translations],
|
||||
)}
|
||||
>
|
||||
{props.children}
|
||||
</I18nContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user