Skip to content

Commit 969da26

Browse files
committed
Improve i18n examples
1 parent 96b400e commit 969da26

File tree

21 files changed

+283
-231
lines changed

21 files changed

+283
-231
lines changed

.changeset/rare-garlics-hammer.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'fumadocs-core': patch
3+
'@fumadocs/cli': patch
4+
'fumadocs-ui': patch
5+
---
6+
7+
Improve i18n api

apps/docs/content/docs/ui/internationalization.mdx

+81-56
Original file line numberDiff line numberDiff line change
@@ -49,24 +49,6 @@ export const source = loader({
4949
});
5050
```
5151

52-
Update the usages to your source to include a locale code:
53-
54-
```ts
55-
import { source } from '@/lib/source';
56-
57-
// get page tree
58-
source.pageTree[params.lang];
59-
60-
// get page
61-
source.getPage(params.slug, params.lang);
62-
63-
// get pages
64-
source.getPages(params.lang);
65-
```
66-
67-
Note that without providing a locale code, it uses your default locale instead.
68-
You can see [Source API](/docs/headless/source-api) for other usages.
69-
7052
### Middleware
7153

7254
Create a middleware that redirects users to appropriate locale.
@@ -83,28 +65,57 @@ Create a middleware that redirects users to appropriate locale.
8365

8466
See [Middleware](/docs/headless/internationalization#middleware) for customisable options.
8567

86-
### Root Layout
68+
### Routing
8769

8870
Create a dynamic route `/app/[lang]`, and move all special files from `/app` to
8971
the folder.
9072

91-
A `I18nProvider` is needed for localization. Wrap the root provider inside your I18n provider.
73+
A `I18nProvider` is needed for localization.
74+
Wrap the root provider inside your I18n provider, and provide available languages & translations to it.
75+
76+
Note that only English translations are provided by default.
9277

9378
```tsx
9479
import { RootProvider } from 'fumadocs-ui/provider';
95-
import { I18nProvider } from 'fumadocs-ui/i18n';
80+
import { I18nProvider, type Translations } from 'fumadocs-ui/i18n';
81+
82+
const cn: Translations = {
83+
search: 'Translated Content',
84+
// other props
85+
};
9686

97-
export default function RootLayout({
87+
// available languages that will be displayed on UI
88+
// make sure `locale` is consistent with your i18n config
89+
const locales = [
90+
{
91+
name: 'English',
92+
locale: 'en',
93+
},
94+
{
95+
name: 'Chinese',
96+
locale: 'cn',
97+
},
98+
];
99+
100+
export default async function RootLayout({
98101
params,
99102
children,
100103
}: {
101-
params: { lang: string };
104+
params: Promise<{ lang: string }>;
102105
children: React.ReactNode;
103106
}) {
104107
return (
105-
<html lang={params.lang}>
108+
<html lang={(await params).lang}>
106109
<body>
107-
<I18nProvider locale={params.lang}>
110+
<I18nProvider
111+
locale={(await params).lang}
112+
locales={locales}
113+
translations={
114+
{
115+
cn,
116+
}[(await params).lang]
117+
}
118+
>
108119
<RootProvider>{children}</RootProvider>
109120
</I18nProvider>
110121
</body>
@@ -113,47 +124,61 @@ export default function RootLayout({
113124
}
114125
```
115126

116-
### Writing Documents
127+
### Source
117128

118-
see [Page Conventions](/docs/ui/page-conventions#internationalization) to learn how to organize your documents.
129+
Update the usages to your source to include a locale code:
119130

120-
### Search
131+
```ts
132+
import { source } from '@/lib/source';
121133

122-
Configure i18n on your search solution.
134+
// get page tree
135+
source.pageTree[params.lang];
123136

124-
You don't need further changes if you're using the `createFromSource` shortcut.
137+
// get page
138+
source.getPage(params.slug, params.lang);
125139

126-
For the built-in Orama search, see [Search I18n](/docs/headless/search/orama#internationalization).
140+
// get pages
141+
source.getPages(params.lang);
142+
```
127143

128-
### Adding Translations
144+
like:
129145

130-
We only provide English translation by default, you have to pass your translations to the provider.
146+
```tsx title="app/[lang]/layout.tsx"
147+
import { source } from '@/lib/source';
148+
import { DocsLayout } from 'fumadocs-ui/docs';
149+
import type { ReactNode } from 'react';
131150

132-
```tsx
133-
import { I18nProvider } from 'fumadocs-ui/i18n';
134-
135-
<I18nProvider
136-
locales={[
137-
{
138-
name: 'English',
139-
locale: 'en',
140-
},
141-
{
142-
name: 'Chinese',
143-
locale: 'cn',
144-
},
145-
]}
146-
translations={
147-
{
148-
cn: {
149-
search: 'Translated Content',
150-
},
151-
}[locale]
152-
}
153-
// other props
154-
/>;
151+
export default async function Layout({
152+
params,
153+
children,
154+
}: {
155+
params: Promise<{ lang: string }>;
156+
children: ReactNode;
157+
}) {
158+
const pageTree = source.pageTree[(await params).lang];
159+
160+
return <DocsLayout pageTree={pageTree}>{children}</DocsLayout>;
161+
}
155162
```
156163

164+
Note that without providing a locale code, it uses your default locale instead.
165+
You can see [Source API](/docs/headless/source-api) for other usages.
166+
167+
### Writing Documents
168+
169+
see [Page Conventions](/docs/ui/page-conventions#internationalization) to learn how to organize your documents.
170+
171+
### Search
172+
173+
Configure i18n on your search solution.
174+
175+
- Built-in Search (Orama):
176+
- For `createFromSource` and most languages, no further changes are needed.
177+
- For special languages like Chinese & Japanese, they require additional config.
178+
See [Orama I18n](/docs/headless/search/orama#internationalization) guide.
179+
- Cloud Solutions (e.g. Algolia):
180+
- They usually have official support for multilingual.
181+
157182
### Add Language Switch
158183

159184
To allow users changing their language, enable `i18n` on your layouts.

examples/i18n/app/api/search/route.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { createFromSource } from 'fumadocs-core/search/server';
44
import { createTokenizer } from '@orama/tokenizers/mandarin';
55
import { stopwords } from '@orama/stopwords/mandarin';
66

7-
export const { GET, search } = createFromSource(source, undefined, {
7+
export const { GET } = createFromSource(source, undefined, {
88
localeMap: {
99
// the prop name should be its locale code in your i18n config, (e.g. `cn`)
1010
cn: {

packages/cli/src/plugins/i18n.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ export const i18nPlugin: Plugin = {
2727
type: 'code',
2828
title: 'page.tsx',
2929
code: `
30-
export default function Page({
30+
export default async function Page({
3131
params,
3232
}: {
33-
${picocolors.underline(picocolors.bold('params: { lang: string; slug?: string[] };'))}
33+
${picocolors.underline(picocolors.bold('params: Promise<{ lang: string; slug?: string[] }>'))}
3434
})
3535
`.trim(),
3636
},

packages/core/src/search/client/static.ts

+10-5
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ import {
66
type RawData,
77
} from '@orama/orama';
88
import { type SortedResult } from '@/server';
9-
import { searchSimple } from '@/search/search/simple';
10-
import { searchAdvanced } from '@/search/search/advanced';
11-
import { type advancedSchema } from '@/search/create-db';
12-
import { type schema } from '@/search/create-db-simple';
9+
import { searchSimple } from '@/search/orama/search/simple';
10+
import { searchAdvanced } from '@/search/orama/search/advanced';
11+
import {
12+
type advancedSchema,
13+
type simpleSchema,
14+
} from '@/search/orama/create-db';
1315

1416
export interface StaticOptions {
1517
/**
@@ -86,7 +88,10 @@ export function createStaticClient({
8688

8789
if (!cached) return [];
8890
if (cached.type === 'simple')
89-
return searchSimple(cached as unknown as Orama<typeof schema>, query);
91+
return searchSimple(
92+
cached as unknown as Orama<typeof simpleSchema>,
93+
query,
94+
);
9095

9196
return searchAdvanced(
9297
cached.db as Orama<typeof advancedSchema>,

packages/core/src/search/create-db-simple.ts

-44
This file was deleted.

packages/core/src/search/create-db.ts renamed to packages/core/src/search/orama/create-db.ts

+38-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
type PartialSchemaDeep,
66
type TypedDocument,
77
} from '@orama/orama';
8-
import { type AdvancedOptions } from '@/search/server';
8+
import { type AdvancedOptions, type SimpleOptions } from '@/search/server';
99

1010
export type AdvancedDocument = TypedDocument<Orama<typeof advancedSchema>>;
1111
export const advancedSchema = {
@@ -85,3 +85,40 @@ export async function createDB({
8585
await insertMultiple(db, mapTo);
8686
return db;
8787
}
88+
89+
export type SimpleDocument = TypedDocument<Orama<typeof simpleSchema>>;
90+
export const simpleSchema = {
91+
url: 'string',
92+
title: 'string',
93+
description: 'string',
94+
content: 'string',
95+
keywords: 'string',
96+
} as const;
97+
98+
export async function createDBSimple({
99+
indexes,
100+
tokenizer,
101+
...rest
102+
}: SimpleOptions): Promise<Orama<typeof simpleSchema>> {
103+
const items = typeof indexes === 'function' ? await indexes() : indexes;
104+
const db = (await create({
105+
schema: simpleSchema,
106+
components: {
107+
tokenizer,
108+
},
109+
...rest,
110+
})) as unknown as Orama<typeof simpleSchema>;
111+
112+
await insertMultiple(
113+
db,
114+
items.map((page) => ({
115+
title: page.title,
116+
description: page.description,
117+
url: page.url,
118+
content: page.content,
119+
keywords: page.keywords,
120+
})),
121+
);
122+
123+
return db;
124+
}

packages/core/src/search/create-from-source.ts renamed to packages/core/src/search/orama/create-from-source.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
type Page,
1313
} from '@/source';
1414
import { type StructuredData } from '@/mdx-plugins';
15-
import { type LocaleMap } from '@/search/i18n-api';
15+
import { type LocaleMap } from '@/search/orama/create-i18n';
1616

1717
function pageToIndex(page: Page): AdvancedIndex {
1818
if (!('structuredData' in page.data)) {

packages/core/src/search/i18n-api.ts renamed to packages/core/src/search/orama/create-i18n.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ import {
1010
type SearchServer,
1111
type SimpleOptions,
1212
} from '@/search/server';
13-
import { createEndpoint } from '@/search/create-endpoint';
13+
import { createEndpoint } from '@/search/orama/create-endpoint';
1414
import { type I18nConfig } from '@/i18n';
15-
import { STEMMERS } from '@/search/_stemmers';
15+
import { STEMMERS } from '@/search/orama/_stemmers';
1616

1717
export type LocaleMap<O> = Record<string, Language | O>;
1818

packages/core/src/search/search/advanced.ts renamed to packages/core/src/search/orama/search/advanced.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import { getByID, type Orama, search, type SearchParams } from '@orama/orama';
2-
import { type AdvancedDocument, type advancedSchema } from '@/search/create-db';
2+
import {
3+
type AdvancedDocument,
4+
type advancedSchema,
5+
} from '@/search/orama/create-db';
36
import { removeUndefined } from '@/utils/remove-undefined';
47
import type { SortedResult } from '@/server';
58

packages/core/src/search/search/simple.ts renamed to packages/core/src/search/orama/search/simple.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
import { type Orama, search, type SearchParams } from '@orama/orama';
22
import type { SortedResult } from '@/server';
3-
import { type schema, type SimpleDocument } from '@/search/create-db-simple';
3+
import {
4+
type simpleSchema,
5+
type SimpleDocument,
6+
} from '@/search/orama/create-db';
47

58
export async function searchSimple(
6-
db: Orama<typeof schema>,
9+
db: Orama<typeof simpleSchema>,
710
query: string,
8-
params: Partial<SearchParams<Orama<typeof schema>, SimpleDocument>> = {},
11+
params: Partial<
12+
SearchParams<Orama<typeof simpleSchema>, SimpleDocument>
13+
> = {},
914
): Promise<SortedResult[]> {
1015
const result = await search(db, {
1116
term: query,

0 commit comments

Comments
 (0)