From 1286a8c0ea4a0a6b2fed0ad58ca3029448d69bf5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 19 Jan 2026 12:24:45 +0000 Subject: [PATCH 1/2] Initial plan From 444c8b1a8265cc3a709e2301bc06dc98f77cf6ec Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 19 Jan 2026 12:36:34 +0000 Subject: [PATCH 2/2] Add i18n configuration and restructure app for multilingual support Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com> --- .../{ => [lang]}/docs/[[...slug]]/page.tsx | 8 +- apps/docs/app/[lang]/docs/layout.tsx | 21 ++ apps/docs/app/[lang]/layout.tsx | 25 +++ apps/docs/app/[lang]/page.tsx | 191 +++++++++++++++++ apps/docs/app/docs/layout.tsx | 23 --- apps/docs/app/layout.tsx | 15 +- apps/docs/app/page.tsx | 194 +----------------- apps/docs/app/source.ts | 2 + apps/docs/lib/i18n.ts | 15 ++ apps/docs/middleware.ts | 21 ++ pnpm-workspace.yaml | 10 +- 11 files changed, 297 insertions(+), 228 deletions(-) rename apps/docs/app/{ => [lang]}/docs/[[...slug]]/page.tsx (85%) create mode 100644 apps/docs/app/[lang]/docs/layout.tsx create mode 100644 apps/docs/app/[lang]/layout.tsx create mode 100644 apps/docs/app/[lang]/page.tsx delete mode 100644 apps/docs/app/docs/layout.tsx create mode 100644 apps/docs/lib/i18n.ts create mode 100644 apps/docs/middleware.ts diff --git a/apps/docs/app/docs/[[...slug]]/page.tsx b/apps/docs/app/[lang]/docs/[[...slug]]/page.tsx similarity index 85% rename from apps/docs/app/docs/[[...slug]]/page.tsx rename to apps/docs/app/[lang]/docs/[[...slug]]/page.tsx index 7d6ef5cb3a..e684766516 100644 --- a/apps/docs/app/docs/[[...slug]]/page.tsx +++ b/apps/docs/app/[lang]/docs/[[...slug]]/page.tsx @@ -17,10 +17,10 @@ const components = { }; export default async function Page(props: { - params: Promise<{ slug?: string[] }>; + params: Promise<{ lang: string; slug?: string[] }>; }) { const params = await props.params; - const page = await source.getPage(params.slug ?? []); + const page = source.getPage(params.slug ?? [], params.lang); if (!page) notFound(); const data = page.data as any; @@ -46,10 +46,10 @@ export async function generateStaticParams() { } export async function generateMetadata(props: { - params: Promise<{ slug?: string[] }>; + params: Promise<{ lang: string; slug?: string[] }>; }): Promise { const params = await props.params; - const page = await source.getPage(params.slug ?? []); + const page = source.getPage(params.slug ?? [], params.lang); if (!page) notFound(); return { diff --git a/apps/docs/app/[lang]/docs/layout.tsx b/apps/docs/app/[lang]/docs/layout.tsx new file mode 100644 index 0000000000..0d2d7810dc --- /dev/null +++ b/apps/docs/app/[lang]/docs/layout.tsx @@ -0,0 +1,21 @@ +import { source } from '@/app/source'; +import type { Metadata } from 'next'; +import { DocsLayout } from 'fumadocs-ui/layouts/docs'; +import type { ReactNode } from 'react'; +import { baseOptions } from '@/app/layout.config'; + +export default async function Layout({ + params, + children, +}: { + params: Promise<{ lang: string }>; + children: ReactNode; +}) { + const { lang } = await params; + + return ( + + {children} + + ); +} diff --git a/apps/docs/app/[lang]/layout.tsx b/apps/docs/app/[lang]/layout.tsx new file mode 100644 index 0000000000..96dfd091f3 --- /dev/null +++ b/apps/docs/app/[lang]/layout.tsx @@ -0,0 +1,25 @@ +import type { ReactNode } from 'react'; +import { RootProvider } from 'fumadocs-ui/provider/next'; +import { i18n } from '@/lib/i18n'; + +export default async function LanguageLayout({ + params, + children, +}: { + params: Promise<{ lang: string }>; + children: ReactNode; +}) { + const { lang } = await params; + + return ( + + + {children} + + + ); +} + +export async function generateStaticParams() { + return i18n.languages.map((lang) => ({ lang })); +} diff --git a/apps/docs/app/[lang]/page.tsx b/apps/docs/app/[lang]/page.tsx new file mode 100644 index 0000000000..bf40b3582e --- /dev/null +++ b/apps/docs/app/[lang]/page.tsx @@ -0,0 +1,191 @@ +import Link from 'next/link'; +import { Database, FileJson, Layers, ShieldCheck, Zap, Globe, Cpu, LayoutTemplate, Bot } from 'lucide-react'; +import { HomeLayout } from 'fumadocs-ui/layouts/home'; +import { baseOptions } from '@/app/layout.config'; + +export default function HomePage() { + return ( + +
+ + {/* Hero Section */} +
+
+ + Protocol Specification v1.0 +
+ +

+ The ObjectStack
Protocol +

+ +

+ The Open Standard for Metadata-Driven Enterprise Software. +
+ Validatable. Database-Agnostic. AI-Native. +

+ +
+ + Start Building + + + Read Specification + +
+ + {/* Code Preview Decorator */} +
+
+
+
+
+
+
+ contract.zod.ts +
+
+
+
+                  import {'{'} ObjectProtocol {'}'} from '@objectstack/spec';

+ export const Issue = ObjectProtocol.define({'{'}
+   code: 'issue_tracker',
+   fields: {'{'}
+     summary: Field.text({'{'} required: true {'}'}),
+     priority: Field.select(['P0', 'P1', 'P2']),
+     assignee: Field.lookup('users')
+   {'}'},
+   policy: {'{'} audit: true, api_access: 'public' {'}'}
+ {'}'}); +
+
+
+ {/* Glow Effect behind Code */} +
+
+
+ + {/* Grid Pattern Background */} +
+ + {/* Feature Grid */} +
+ } + title="ObjectQL Data Layer" + href="/docs/specifications/data/architecture" + description="Strict JSON schemas for entities, fields, and relationships. It is the SQL you can send over the wire." + /> + } + title="ObjectUI View Layer" + href="/docs/specifications/ui/sdui-protocol" + description="Server-Driven UI protocol defining forms, grids, and dashboards. Decouples logic from the frontend implementation." + /> + } + title="ObjectOS Kernel" + href="/docs/specifications/server/kernel-architecture" + description="The runtime contract for permissions, workflows, and automation. Stateless business logic execution." + /> + } + title="Zero-Trust Security" + href="/docs/specifications/server/permission-governance" + description="Policy-as-Code. ACLs and Field Level Security are compiled into the database query engine." + /> + } + title="Zod-First Definition" + href="/docs/specifications/data/schema-definition" + description="The entire protocol is defined in Zod. Runtime validation and static type inference come for free." + /> + } + title="Universal Backend" + href="/docs/concepts/architecture" + description="Protocol adapters for Postgres, MongoDB, REST and GraphQL. Write once, run on any infrastructure." + /> +
+ + {/* Personas Section */} +
+

+ Built for Builders +

+
+ } + title="Platform Architects" + description="Design scalable Internal Developer Platforms (IDP) that unify your data silos." + href="/docs/concepts/enterprise-patterns" + action="Explore Patterns" + /> + } + title="AI Engineers" + description="Feed LLMs with perfectly structured, deterministic JSON schemas they can actually understand." + href="/docs/concepts/ai-codex" + action="View Codex" + /> + } + title="Framework Builders" + description="Implement the protocol in your language. Write drivers for React, Vue, Flutter, or Go." + href="/docs/specifications/data/architecture" + action="Read Spec" + /> +
+
+ +
+
+ ); +} + +function FeatureCard({ icon, title, description, href }: { icon: React.ReactNode; title: string; description: string; href?: string }) { + const CardContent = ( +
+
+ {icon} +
+

+ {title} +

+

+ {description} +

+
+ ); + + if (href) { + return ( + + {CardContent} + + ); + } + + return CardContent; +} + +function PersonaCard({ icon, title, description, href, action }: { icon: React.ReactNode; title: string; description: string; href: string; action: string }) { + return ( + + {icon} +

{title}

+

+ {description} +

+
+ {action} β†’ +
+ + ) +} diff --git a/apps/docs/app/docs/layout.tsx b/apps/docs/app/docs/layout.tsx deleted file mode 100644 index 2f612c52b9..0000000000 --- a/apps/docs/app/docs/layout.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { source } from '@/app/source'; -import type { Metadata } from 'next'; -import { DocsLayout } from 'fumadocs-ui/layouts/docs'; -import type { ReactNode } from 'react'; -import { baseOptions } from '@/app/layout.config'; - -export default function Layout({ children }: { children: ReactNode }) { - return ( - - {children} - - ); -} - -export function generateMetadata(): Metadata { - return { - title: { - default: 'ObjectStack Protocol', - template: '%s | ObjectStack Protocol', - }, - description: 'The Metadata-Driven Documentation Engine for the Low-Code Era.', - }; -} diff --git a/apps/docs/app/layout.tsx b/apps/docs/app/layout.tsx index e513542299..7070de3c7e 100644 --- a/apps/docs/app/layout.tsx +++ b/apps/docs/app/layout.tsx @@ -1,7 +1,8 @@ import './global.css'; -import { RootProvider } from 'fumadocs-ui/provider/next'; +import { redirect } from 'next/navigation'; import type { ReactNode } from 'react'; import type { Metadata } from 'next'; +import { i18n } from '@/lib/i18n'; export const metadata: Metadata = { title: { @@ -14,12 +15,8 @@ export const metadata: Metadata = { }, }; -export default function Layout({ children }: { children: ReactNode }) { - return ( - - - {children} - - - ); +export default function RootLayout({ children }: { children: ReactNode }) { + // Root layout is only used for redirects with middleware + // The actual layout is in [lang]/layout.tsx + return children; } diff --git a/apps/docs/app/page.tsx b/apps/docs/app/page.tsx index bf40b3582e..a00faffed1 100644 --- a/apps/docs/app/page.tsx +++ b/apps/docs/app/page.tsx @@ -1,191 +1,7 @@ -import Link from 'next/link'; -import { Database, FileJson, Layers, ShieldCheck, Zap, Globe, Cpu, LayoutTemplate, Bot } from 'lucide-react'; -import { HomeLayout } from 'fumadocs-ui/layouts/home'; -import { baseOptions } from '@/app/layout.config'; +import { redirect } from 'next/navigation'; +import { i18n } from '@/lib/i18n'; -export default function HomePage() { - return ( - -
- - {/* Hero Section */} -
-
- - Protocol Specification v1.0 -
- -

- The ObjectStack
Protocol -

- -

- The Open Standard for Metadata-Driven Enterprise Software. -
- Validatable. Database-Agnostic. AI-Native. -

- -
- - Start Building - - - Read Specification - -
- - {/* Code Preview Decorator */} -
-
-
-
-
-
-
- contract.zod.ts -
-
-
-
-                  import {'{'} ObjectProtocol {'}'} from '@objectstack/spec';

- export const Issue = ObjectProtocol.define({'{'}
-   code: 'issue_tracker',
-   fields: {'{'}
-     summary: Field.text({'{'} required: true {'}'}),
-     priority: Field.select(['P0', 'P1', 'P2']),
-     assignee: Field.lookup('users')
-   {'}'},
-   policy: {'{'} audit: true, api_access: 'public' {'}'}
- {'}'}); -
-
-
- {/* Glow Effect behind Code */} -
-
-
- - {/* Grid Pattern Background */} -
- - {/* Feature Grid */} -
- } - title="ObjectQL Data Layer" - href="/docs/specifications/data/architecture" - description="Strict JSON schemas for entities, fields, and relationships. It is the SQL you can send over the wire." - /> - } - title="ObjectUI View Layer" - href="/docs/specifications/ui/sdui-protocol" - description="Server-Driven UI protocol defining forms, grids, and dashboards. Decouples logic from the frontend implementation." - /> - } - title="ObjectOS Kernel" - href="/docs/specifications/server/kernel-architecture" - description="The runtime contract for permissions, workflows, and automation. Stateless business logic execution." - /> - } - title="Zero-Trust Security" - href="/docs/specifications/server/permission-governance" - description="Policy-as-Code. ACLs and Field Level Security are compiled into the database query engine." - /> - } - title="Zod-First Definition" - href="/docs/specifications/data/schema-definition" - description="The entire protocol is defined in Zod. Runtime validation and static type inference come for free." - /> - } - title="Universal Backend" - href="/docs/concepts/architecture" - description="Protocol adapters for Postgres, MongoDB, REST and GraphQL. Write once, run on any infrastructure." - /> -
- - {/* Personas Section */} -
-

- Built for Builders -

-
- } - title="Platform Architects" - description="Design scalable Internal Developer Platforms (IDP) that unify your data silos." - href="/docs/concepts/enterprise-patterns" - action="Explore Patterns" - /> - } - title="AI Engineers" - description="Feed LLMs with perfectly structured, deterministic JSON schemas they can actually understand." - href="/docs/concepts/ai-codex" - action="View Codex" - /> - } - title="Framework Builders" - description="Implement the protocol in your language. Write drivers for React, Vue, Flutter, or Go." - href="/docs/specifications/data/architecture" - action="Read Spec" - /> -
-
- -
-
- ); -} - -function FeatureCard({ icon, title, description, href }: { icon: React.ReactNode; title: string; description: string; href?: string }) { - const CardContent = ( -
-
- {icon} -
-

- {title} -

-

- {description} -

-
- ); - - if (href) { - return ( - - {CardContent} - - ); - } - - return CardContent; -} - -function PersonaCard({ icon, title, description, href, action }: { icon: React.ReactNode; title: string; description: string; href: string; action: string }) { - return ( - - {icon} -

{title}

-

- {description} -

-
- {action} β†’ -
- - ) +export default function RootPage() { + // Middleware should handle this, but as a fallback + redirect(`/${i18n.defaultLanguage}`); } diff --git a/apps/docs/app/source.ts b/apps/docs/app/source.ts index b53e60f585..82838edb5b 100644 --- a/apps/docs/app/source.ts +++ b/apps/docs/app/source.ts @@ -1,7 +1,9 @@ import { docs } from 'fumadocs-mdx:collections/server'; import { loader } from 'fumadocs-core/source'; +import { i18n } from '@/lib/i18n'; export const source = loader({ baseUrl: '/docs', + i18n, source: (docs as any).toFumadocsSource(), }); diff --git a/apps/docs/lib/i18n.ts b/apps/docs/lib/i18n.ts new file mode 100644 index 0000000000..9657059893 --- /dev/null +++ b/apps/docs/lib/i18n.ts @@ -0,0 +1,15 @@ +import { defineI18n } from 'fumadocs-core/i18n'; + +/** + * i18n Configuration for ObjectStack Documentation + * + * Supported Languages: + * - en: English (Default) + * - cn: Chinese (δΈ­ζ–‡) + */ +export const i18n = defineI18n({ + defaultLanguage: 'en', + languages: ['en', 'cn'], + // Hide locale prefix for default language (e.g., /docs instead of /en/docs) + hideLocale: 'default-locale', +}); diff --git a/apps/docs/middleware.ts b/apps/docs/middleware.ts new file mode 100644 index 0000000000..6c9d3d9d65 --- /dev/null +++ b/apps/docs/middleware.ts @@ -0,0 +1,21 @@ +import { createI18nMiddleware } from 'fumadocs-core/i18n/middleware'; +import { i18n } from '@/lib/i18n'; + +/** + * Middleware for automatic language detection and redirection + * + * This middleware: + * - Detects the user's preferred language from browser settings or cookies + * - Redirects users to the appropriate localized version + * - Stores language preference as a cookie + */ +export default createI18nMiddleware(i18n); + +export const config = { + // Match all routes except: + // - API routes (/api/*) + // - Next.js static files (/_next/static/*) + // - Next.js image optimization (/_next/image/*) + // - Favicon and other static assets + matcher: ['/((?!api|_next/static|_next/image|favicon.ico|.*\\..*).*)'], +}; diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 6ec400e42e..f7b1a786ad 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,4 +1,8 @@ packages: - - 'packages/*' - - 'apps/*' - - 'examples/*' + - packages/* + - apps/* + - examples/* + +onlyBuiltDependencies: + - esbuild + - sharp