Merge pull request #15 from MkSaaSHQ/docs
feat: docs support theme and i18n
This commit is contained in:
commit
02af1c7512
@ -24,7 +24,8 @@ import {
|
||||
|
||||
/**
|
||||
* Fumadocs documentation
|
||||
* 1. https://fumadocs.com/docs/configuration
|
||||
*
|
||||
* https://fumadocs.vercel.app/docs/headless/content-collections
|
||||
*/
|
||||
const docs = defineCollection({
|
||||
name: 'docs',
|
||||
|
||||
@ -3,11 +3,15 @@ title: Hello World
|
||||
description: Your first document
|
||||
---
|
||||
|
||||
Welcome to the docs! You can start writing documents in `/content/docs`.
|
||||
Hey there!
|
||||
|
||||
## What is Next?
|
||||
## Heading
|
||||
|
||||
<Cards>
|
||||
<Card title="Learn more about Next.js" href="https://nextjs.org/docs" />
|
||||
<Card title="Learn more about Fumadocs" href="https://fumadocs.vercel.app" />
|
||||
</Cards>
|
||||
|
||||
### Heading
|
||||
|
||||
#### Heading
|
||||
|
||||
8
content/docs/index.zh.mdx
Normal file
8
content/docs/index.zh.mdx
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
title: 中文
|
||||
description: 您的第一個文檔
|
||||
---
|
||||
|
||||
## Hi 中文
|
||||
|
||||
Fumadocs 對 i18n 有良好的支持
|
||||
@ -1,17 +1,21 @@
|
||||
---
|
||||
title: Components
|
||||
description: Components
|
||||
title: Test Document
|
||||
description: Your first document
|
||||
---
|
||||
|
||||
## Code Block
|
||||
Hey there!
|
||||
|
||||
```js
|
||||
console.log('Hello World');
|
||||
```
|
||||
|
||||
## Cards
|
||||
## Heading
|
||||
|
||||
<Cards>
|
||||
<Card title="Learn more about Next.js" href="https://nextjs.org/docs" />
|
||||
<Card title="Learn more about Fumadocs" href="https://fumadocs.vercel.app" />
|
||||
</Cards>
|
||||
|
||||
### Heading
|
||||
|
||||
```js
|
||||
console.log('Hello World');
|
||||
```
|
||||
|
||||
#### Heading
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { source } from '@/lib/source';
|
||||
import { source } from '@/lib/docs/source';
|
||||
import { MDXContent } from '@content-collections/mdx/react';
|
||||
import defaultMdxComponents, { createRelativeLink } from 'fumadocs-ui/mdx';
|
||||
import {
|
||||
@ -9,12 +9,14 @@ import {
|
||||
} from 'fumadocs-ui/page';
|
||||
import type { Metadata } from 'next';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { Locale } from 'next-intl';
|
||||
|
||||
export default async function Page(props: {
|
||||
params: Promise<{ slug?: string[] }>;
|
||||
export default async function Page({
|
||||
params,
|
||||
}: {
|
||||
params: { slug?: string[]; locale: Locale };
|
||||
}) {
|
||||
const params = await props.params;
|
||||
const page = source.getPage(params.slug);
|
||||
const page = source.getPage(params.slug, params.locale);
|
||||
if (!page) notFound();
|
||||
|
||||
return (
|
||||
@ -40,11 +42,12 @@ export function generateStaticParams() {
|
||||
return source.generateParams();
|
||||
}
|
||||
|
||||
export async function generateMetadata(props: {
|
||||
params: Promise<{ slug?: string[] }>;
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
params: { slug?: string[]; locale: Locale };
|
||||
}) {
|
||||
const params = await props.params;
|
||||
const page = source.getPage(params.slug);
|
||||
const page = source.getPage(params.slug, params.locale);
|
||||
if (!page) notFound();
|
||||
|
||||
return {
|
||||
36
src/app/[locale]/docs/layout.config.tsx
Normal file
36
src/app/[locale]/docs/layout.config.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import { ThemeSwitcher } from '@/components/layout/theme-switcher';
|
||||
import { Logo } from '@/components/logo';
|
||||
import { websiteConfig } from '@/config';
|
||||
import { defaultMessages } from '@/i18n/messages';
|
||||
import { docsI18nConfig } from '@/lib/docs/i18n';
|
||||
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
|
||||
|
||||
/**
|
||||
* Docs layout configurations
|
||||
*
|
||||
* https://fumadocs.vercel.app/docs/ui/layouts/docs
|
||||
*/
|
||||
export const baseOptions: BaseLayoutProps = {
|
||||
i18n: docsI18nConfig,
|
||||
githubUrl: websiteConfig.social.github ?? undefined,
|
||||
nav: {
|
||||
title: (
|
||||
<>
|
||||
<Logo className="size-6" />
|
||||
{defaultMessages.Metadata.name}
|
||||
</>
|
||||
),
|
||||
},
|
||||
links: [
|
||||
{
|
||||
text: 'Homepage',
|
||||
url: '/',
|
||||
active: 'nested-url',
|
||||
}
|
||||
],
|
||||
themeSwitch: {
|
||||
enabled: true,
|
||||
mode: 'light-dark-system',
|
||||
component: <ThemeSwitcher />
|
||||
},
|
||||
};
|
||||
72
src/app/[locale]/docs/layout.tsx
Normal file
72
src/app/[locale]/docs/layout.tsx
Normal file
@ -0,0 +1,72 @@
|
||||
import { baseOptions } from '@/app/[locale]/docs/layout.config';
|
||||
import { source } from '@/lib/docs/source';
|
||||
import { Translations } from 'fumadocs-ui/i18n';
|
||||
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
|
||||
import { Locale } from 'next-intl';
|
||||
import type { ReactNode } from 'react';
|
||||
import { DocsProviders } from './providers-docs';
|
||||
import { I18nProvider } from 'fumadocs-ui/i18n';
|
||||
|
||||
import '@/styles/docs.css';
|
||||
|
||||
const zhTranslations: Partial<Translations> = {
|
||||
toc: '目录',
|
||||
search: '搜索文档',
|
||||
lastUpdate: '最后更新于',
|
||||
searchNoResult: '没有结果',
|
||||
previousPage: '上一页',
|
||||
nextPage: '下一页',
|
||||
chooseLanguage: '选择语言',
|
||||
};
|
||||
|
||||
const enTranslations: Partial<Translations> = {
|
||||
toc: 'Table of Contents',
|
||||
search: 'Search docs',
|
||||
lastUpdate: 'Last updated on',
|
||||
searchNoResult: 'No results',
|
||||
previousPage: 'Previous',
|
||||
nextPage: 'Next',
|
||||
chooseLanguage: 'Select language',
|
||||
};
|
||||
|
||||
// Map of locale to translations
|
||||
const translations: Record<string, Partial<Translations>> = {
|
||||
zh: zhTranslations,
|
||||
en: enTranslations,
|
||||
};
|
||||
|
||||
// available languages that will be displayed on UI
|
||||
// make sure `locale` is consistent with your i18n config
|
||||
const locales = [
|
||||
{
|
||||
name: 'English',
|
||||
locale: 'en',
|
||||
},
|
||||
{
|
||||
name: 'Chinese',
|
||||
locale: 'zh',
|
||||
},
|
||||
];
|
||||
|
||||
interface DocsLayoutProps {
|
||||
children: ReactNode;
|
||||
params: Promise<{ locale: Locale }>;
|
||||
}
|
||||
|
||||
export default async function DocsRootLayout({ children, params }: DocsLayoutProps) {
|
||||
const { locale } = await params;
|
||||
|
||||
return (
|
||||
<DocsProviders>
|
||||
<I18nProvider
|
||||
locales={locales}
|
||||
locale={locale}
|
||||
translations={translations[locale] || enTranslations}
|
||||
>
|
||||
<DocsLayout tree={source.pageTree[locale]} {...baseOptions}>
|
||||
{children}
|
||||
</DocsLayout>
|
||||
</I18nProvider>
|
||||
</DocsProviders>
|
||||
);
|
||||
}
|
||||
24
src/app/[locale]/docs/providers-docs.tsx
Normal file
24
src/app/[locale]/docs/providers-docs.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
'use client';
|
||||
|
||||
import { RootProvider } from 'fumadocs-ui/provider';
|
||||
import { useTheme } from 'next-themes';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
interface DocsProvidersProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
/***
|
||||
* Docs Configuration
|
||||
*
|
||||
* https://fumadocs.vercel.app/docs/ui/theme#lightdark-modes
|
||||
*/
|
||||
export function DocsProviders({ children }: DocsProvidersProps) {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<RootProvider theme={theme}>
|
||||
{children}
|
||||
</RootProvider>
|
||||
);
|
||||
}
|
||||
@ -34,7 +34,7 @@ export default async function LocaleLayout({
|
||||
}
|
||||
|
||||
return (
|
||||
<html lang={locale} suppressHydrationWarning>
|
||||
<html suppressHydrationWarning lang={locale}>
|
||||
<body
|
||||
suppressHydrationWarning
|
||||
className={cn(
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { source } from '@/lib/source';
|
||||
import { source } from '@/lib/docs/source';
|
||||
import { createFromSource } from 'fumadocs-core/search/server';
|
||||
|
||||
export const { GET } = createFromSource(source);
|
||||
|
||||
@ -1,34 +0,0 @@
|
||||
import { defaultMessages } from '@/i18n/messages';
|
||||
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
|
||||
|
||||
/**
|
||||
* Shared layout configurations
|
||||
*
|
||||
* you can customise layouts individually from:
|
||||
* Home Layout: app/(home)/layout.tsx
|
||||
* Docs Layout: app/docs/layout.tsx
|
||||
*/
|
||||
export const baseOptions: BaseLayoutProps = {
|
||||
nav: {
|
||||
title: (
|
||||
<>
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-label="Logo"
|
||||
>
|
||||
<circle cx={12} cy={12} r={12} fill="currentColor" />
|
||||
</svg>
|
||||
{defaultMessages.Metadata.name}
|
||||
</>
|
||||
),
|
||||
},
|
||||
links: [
|
||||
{
|
||||
text: 'Documentation',
|
||||
url: '/docs',
|
||||
active: 'nested-url',
|
||||
},
|
||||
],
|
||||
};
|
||||
@ -1,22 +0,0 @@
|
||||
import { baseOptions } from '@/app/docs/layout.config';
|
||||
import { source } from '@/lib/source';
|
||||
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
|
||||
import { RootProvider } from 'fumadocs-ui/provider';
|
||||
import type { ReactNode } from 'react';
|
||||
import { fontDMSans } from '@/assets/fonts';
|
||||
|
||||
import '@/styles/docs.css';
|
||||
|
||||
export default function Layout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<html lang="en" className={fontDMSans.className} suppressHydrationWarning>
|
||||
<body className="flex flex-col min-h-screen">
|
||||
<RootProvider>
|
||||
<DocsLayout tree={source.pageTree} {...baseOptions}>
|
||||
{children}
|
||||
</DocsLayout>
|
||||
</RootProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
12
src/lib/docs/i18n.ts
Normal file
12
src/lib/docs/i18n.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { DEFAULT_LOCALE, LOCALES } from '@/i18n/routing';
|
||||
import type { I18nConfig } from 'fumadocs-core/i18n';
|
||||
|
||||
/**
|
||||
* Internationalization configuration for FumaDocs
|
||||
*
|
||||
* https://fumadocs.vercel.app/docs/ui/internationalization
|
||||
*/
|
||||
export const docsI18nConfig: I18nConfig = {
|
||||
defaultLanguage: DEFAULT_LOCALE,
|
||||
languages: LOCALES,
|
||||
};
|
||||
@ -1,8 +1,10 @@
|
||||
import { createMDXSource } from '@fumadocs/content-collections';
|
||||
import { allDocs, allMetas } from 'content-collections';
|
||||
import { loader } from 'fumadocs-core/source';
|
||||
import { createMDXSource } from '@fumadocs/content-collections';
|
||||
import { docsI18nConfig } from './i18n';
|
||||
|
||||
export const source = loader({
|
||||
baseUrl: '/docs',
|
||||
i18n: docsI18nConfig,
|
||||
source: createMDXSource(allDocs, allMetas),
|
||||
});
|
||||
@ -23,6 +23,6 @@ export const config = {
|
||||
// (e.g. `/pathnames` -> `/zh/pathnames`)
|
||||
// Exclude API routes and other Next.js internal routes
|
||||
// if not exclude api routes, auth routes will not work
|
||||
'/((?!api|_next|_vercel|docs|.*\\..*).*)',
|
||||
'/((?!api|_next|_vercel|.*\\..*).*)',
|
||||
],
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user