mirror of
https://wget.la/https://github.com/leookun/cursor-byok
synced 2026-10-06 13:44:21 +08:00
feat(docs): enhance documentation site with internationalization and layout updates
- Updated the demo API to support locale-based content rendering. - Refactored the main layout to accommodate language-specific metadata and routing. - Introduced new components for blog and documentation pages, ensuring proper language handling. - Added styles for improved UI consistency across different sections. - Removed deprecated layout files and streamlined the structure for better maintainability.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { loader } from 'fumadocs-core/source';
|
||||
import { pageSchema } from 'fumadocs-core/source/schema';
|
||||
import { defineCollections } from 'fumadocs-mdx/macro';
|
||||
import { htmlLang, i18n, type Language } from './i18n';
|
||||
|
||||
const blog = defineCollections({
|
||||
type: 'doc',
|
||||
@@ -11,6 +12,7 @@ const blog = defineCollections({
|
||||
export const blogSource = loader({
|
||||
baseUrl: '/blog',
|
||||
source: blog.toFumadocsSource(),
|
||||
i18n,
|
||||
});
|
||||
|
||||
export function getBlogDate(path: string): Date {
|
||||
@@ -19,8 +21,8 @@ export function getBlogDate(path: string): Date {
|
||||
return match ? new Date(`${match[1]}T00:00:00Z`) : new Date(0);
|
||||
}
|
||||
|
||||
export function formatBlogDate(path: string): string {
|
||||
return new Intl.DateTimeFormat('zh-CN', {
|
||||
export function formatBlogDate(path: string, lang: Language = i18n.defaultLanguage): string {
|
||||
return new Intl.DateTimeFormat(htmlLang[lang], {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import { gitConfig } from './shared';
|
||||
|
||||
export type RepoStats = {
|
||||
stars: number | null;
|
||||
version: string | null;
|
||||
};
|
||||
|
||||
async function fetchJson(url: string): Promise<Record<string, unknown> | null> {
|
||||
try {
|
||||
const res = await fetch(url, {
|
||||
headers: { Accept: 'application/vnd.github+json' },
|
||||
next: { revalidate: 3600 },
|
||||
});
|
||||
if (!res.ok) return null;
|
||||
return (await res.json()) as Record<string, unknown>;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getRepoStats(): Promise<RepoStats> {
|
||||
const base = `https://api.github.com/repos/${gitConfig.user}/${gitConfig.repo}`;
|
||||
const [repo, release] = await Promise.all([
|
||||
fetchJson(base),
|
||||
fetchJson(`${base}/releases/latest`),
|
||||
]);
|
||||
|
||||
return {
|
||||
stars: typeof repo?.stargazers_count === 'number' ? repo.stargazers_count : null,
|
||||
version: typeof release?.tag_name === 'string' ? release.tag_name : null,
|
||||
};
|
||||
}
|
||||
|
||||
export function formatStars(stars: number): string {
|
||||
return new Intl.NumberFormat('en', {
|
||||
notation: 'compact',
|
||||
maximumFractionDigits: 1,
|
||||
}).format(stars);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { defineI18n } from 'fumadocs-core/i18n';
|
||||
|
||||
export const i18n = defineI18n({
|
||||
defaultLanguage: 'zh',
|
||||
languages: ['zh', 'en'],
|
||||
hideLocale: 'default-locale',
|
||||
});
|
||||
|
||||
export type Language = (typeof i18n)['languages'][number];
|
||||
|
||||
export function isLanguage(value: string): value is Language {
|
||||
return i18n.languages.some((lang) => lang === value);
|
||||
}
|
||||
|
||||
/** HTML `lang` attribute values. */
|
||||
export const htmlLang: Record<Language, string> = {
|
||||
zh: 'zh-CN',
|
||||
en: 'en',
|
||||
};
|
||||
|
||||
/** Locale ids understood by the desktop app / demo. */
|
||||
export const demoLocale: Record<Language, string> = {
|
||||
zh: 'zh-CN',
|
||||
en: 'en-US',
|
||||
};
|
||||
@@ -1,27 +1,35 @@
|
||||
import { zhCN } from '@fumadocs/language/zh-cn';
|
||||
import { defineTranslations } from 'fumadocs-core/i18n';
|
||||
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
|
||||
import { uiTranslations } from 'fumadocs-ui/i18n';
|
||||
import { i18n, type Language } from './i18n';
|
||||
import { appName, gitConfig, releaseUrl } from './shared';
|
||||
|
||||
export const translations = defineTranslations().extend(uiTranslations()).preset(zhCN());
|
||||
export const translations = i18n.translations().extend(uiTranslations()).preset('zh', zhCN());
|
||||
|
||||
const navLabels: Record<Language, { docs: string; blog: string; download: string }> = {
|
||||
zh: { docs: '文档', blog: '开发者博客', download: '下载' },
|
||||
en: { docs: 'Docs', blog: 'Blog', download: 'Download' },
|
||||
};
|
||||
|
||||
export function baseOptions(lang: Language): BaseLayoutProps {
|
||||
const labels = navLabels[lang];
|
||||
const prefix = lang === i18n.defaultLanguage ? '' : `/${lang}`;
|
||||
|
||||
export function baseOptions(): BaseLayoutProps {
|
||||
return {
|
||||
nav: {
|
||||
title: appName,
|
||||
},
|
||||
links: [
|
||||
{
|
||||
text: '文档',
|
||||
url: '/docs',
|
||||
text: labels.docs,
|
||||
url: `${prefix}/docs`,
|
||||
},
|
||||
{
|
||||
text: '开发者博客',
|
||||
url: '/blog',
|
||||
text: labels.blog,
|
||||
url: `${prefix}/blog`,
|
||||
},
|
||||
{
|
||||
text: '下载',
|
||||
text: labels.download,
|
||||
url: releaseUrl,
|
||||
external: true,
|
||||
},
|
||||
|
||||
+13
-2
@@ -3,6 +3,7 @@ import { lucideIconsPlugin } from 'fumadocs-core/source/lucide-icons';
|
||||
import { docsContentRoute, docsImageRoute, docsRoute } from './shared';
|
||||
import { defineDocs } from 'fumadocs-mdx/macro';
|
||||
import { metaSchema, pageSchema } from 'fumadocs-core/source/schema';
|
||||
import { i18n } from './i18n';
|
||||
|
||||
const docs = defineDocs({
|
||||
dir: 'content/docs',
|
||||
@@ -22,14 +23,22 @@ export const source = loader({
|
||||
baseUrl: docsRoute,
|
||||
source: docs.toFumadocsSource(),
|
||||
plugins: [lucideIconsPlugin()],
|
||||
i18n,
|
||||
});
|
||||
|
||||
/** Locale URL prefix segment; empty for the default (hidden) locale. */
|
||||
function localeSegment(page: (typeof source)['$inferPage']): string | undefined {
|
||||
return page.locale === i18n.defaultLanguage ? undefined : page.locale;
|
||||
}
|
||||
|
||||
export function getPageImageUrl(page: (typeof source)['$inferPage']) {
|
||||
const segments = [...page.slugs, 'image.png'];
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: '/' + [page.locale, ...docsImageRoute.split('/'), ...segments].filter(Boolean).join('/'),
|
||||
url:
|
||||
'/' +
|
||||
[localeSegment(page), ...docsImageRoute.split('/'), ...segments].filter(Boolean).join('/'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -38,7 +47,9 @@ export function getPageMarkdownUrl(page: (typeof source)['$inferPage']) {
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: '/' + [page.locale, ...docsContentRoute.split('/'), ...segments].filter(Boolean).join('/'),
|
||||
url:
|
||||
'/' +
|
||||
[localeSegment(page), ...docsContentRoute.split('/'), ...segments].filter(Boolean).join('/'),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user