diff --git a/apps/docs/app/[lang]/[...slug]/page.tsx b/apps/docs/app/[lang]/[...slug]/page.tsx index 7ddfeaa578..0f0b42e540 100644 --- a/apps/docs/app/[lang]/[...slug]/page.tsx +++ b/apps/docs/app/[lang]/[...slug]/page.tsx @@ -9,7 +9,7 @@ import { notFound } from "next/navigation"; import defaultMdxComponents from "fumadocs-ui/mdx"; import type { Metadata } from "next"; import { docsAlternates } from "@/lib/site"; -import { docsContentLang, i18n, type Lang } from "@/lib/i18n"; +import { i18n, type Lang } from "@/lib/i18n"; import { DocsLocaleProvider, LocaleLink } from "@/components/locale-link"; import { docsSlugStaticParams } from "@/lib/static-params"; @@ -24,7 +24,7 @@ export default async function Page(props: { }) { const params = await props.params; const lang = asLang(params.lang); - const page = source.getPage(params.slug, docsContentLang(lang)); + const page = source.getPage(params.slug, lang); if (!page) notFound(); const MDX = page.data.body; @@ -51,7 +51,7 @@ export async function generateMetadata(props: { }): Promise { const params = await props.params; const lang = asLang(params.lang); - const page = source.getPage(params.slug, docsContentLang(lang)); + const page = source.getPage(params.slug, lang); if (!page) notFound(); return { diff --git a/apps/docs/app/[lang]/layout.tsx b/apps/docs/app/[lang]/layout.tsx index 08e31d1098..a34b120c99 100644 --- a/apps/docs/app/[lang]/layout.tsx +++ b/apps/docs/app/[lang]/layout.tsx @@ -7,7 +7,7 @@ import type { Metadata } from "next"; import { cn } from "@multica/ui/lib/utils"; import { baseOptions } from "@/app/layout.config"; import { source } from "@/lib/source"; -import { docsContentLang, i18n, type Lang } from "@/lib/i18n"; +import { i18n, type Lang } from "@/lib/i18n"; import { uiTranslations, localeLabels } from "@/lib/translations"; import { DocsSettings } from "@/components/docs-settings"; @@ -76,7 +76,6 @@ export default async function Layout({ const lang = (i18n.languages as readonly string[]).includes(rawLang) ? (rawLang as Lang) : (i18n.defaultLanguage as Lang); - const contentLang = docsContentLang(lang); const locales = i18n.languages.map((l) => ({ locale: l, name: localeLabels[l], @@ -84,7 +83,7 @@ export default async function Layout({ return ( is mounted as // the sidebar footer instead — two labelled buttons, not three diff --git a/apps/docs/app/[lang]/page.tsx b/apps/docs/app/[lang]/page.tsx index 832a0c01a3..e6a05f4b7c 100644 --- a/apps/docs/app/[lang]/page.tsx +++ b/apps/docs/app/[lang]/page.tsx @@ -5,7 +5,7 @@ import defaultMdxComponents from "fumadocs-ui/mdx"; import type { Metadata } from "next"; import { DocsHero } from "@/components/hero"; import { Byline, NumberedCards, NumberedCard, NumberedSteps, Step } from "@/components/editorial"; -import { docsContentLang, i18n, type Lang } from "@/lib/i18n"; +import { i18n, type Lang } from "@/lib/i18n"; import { homeCopy } from "@/lib/translations"; import { docsAlternates } from "@/lib/site"; import { DocsLocaleProvider, LocaleLink } from "@/components/locale-link"; @@ -31,7 +31,7 @@ export default async function Page({ }) { const { lang: rawLang } = await params; const lang = asLang(rawLang); - const page = source.getPage([], docsContentLang(lang)); + const page = source.getPage([], lang); if (!page) notFound(); const MDX = page.data.body; @@ -77,7 +77,7 @@ export async function generateMetadata({ }): Promise { const { lang: rawLang } = await params; const lang = asLang(rawLang); - const page = source.getPage([], docsContentLang(lang)); + const page = source.getPage([], lang); if (!page) notFound(); return { diff --git a/apps/docs/lib/i18n.ts b/apps/docs/lib/i18n.ts index 45ebb1f4af..d332615459 100644 --- a/apps/docs/lib/i18n.ts +++ b/apps/docs/lib/i18n.ts @@ -1,9 +1,9 @@ import { defineI18n } from "fumadocs-core/i18n"; -// English is the default; Chinese is available under /zh/. +// English is the default; Chinese (/zh/) and Korean (/ko/) are available. // hideLocale: 'default-locale' keeps English URLs prefix-free // (`/docs/`) while translated locales live under `/docs//...`. -// parser: 'dot' picks up `page.zh.mdx` and `meta.zh.json`. +// parser: 'dot' picks up `page.zh.mdx` / `page.ko.mdx` and `meta..json`. export const i18n = defineI18n({ languages: ["en", "zh", "ko"], defaultLanguage: "en", @@ -12,10 +12,3 @@ export const i18n = defineI18n({ }); export type Lang = (typeof i18n.languages)[number]; - -// Korean docs routes are enabled before the full MDX corpus is translated. -// Until `*.ko.mdx` files exist, render the English source with Korean docs -// chrome so `/docs/ko/...` remains a stable locale URL instead of 404ing. -export function docsContentLang(lang: Lang): Lang { - return lang === "ko" ? "en" : lang; -} diff --git a/apps/docs/lib/static-params.test.ts b/apps/docs/lib/static-params.test.ts index b69b0506f1..6e3c2063bf 100644 --- a/apps/docs/lib/static-params.test.ts +++ b/apps/docs/lib/static-params.test.ts @@ -1,13 +1,24 @@ import { describe, expect, it } from "vitest"; -import { docsSlugStaticParams, type DocsStaticParam } from "./static-params"; +import { docsSlugStaticParams } from "./static-params"; + +// `source.generateParams()` hands back loosely-typed params (`lang: string`), +// so the inputs here mirror that shape — the `lang` strings are validated and +// narrowed by `docsSlugStaticParams` itself. +type RawParam = { lang: string; slug: string[] }; describe("docsSlugStaticParams", () => { - it("adds Korean fallback params for every English slug page", () => { - const params: DocsStaticParam[] = [ + it("returns every localized slug page and drops the home param", () => { + // Each locale's pages come straight from `source.generateParams()` now + // that `*.ko.mdx` files exist — Korean is a first-class locale, not an + // English fallback. The only transform is dropping the empty-slug home + // param (rendered by `[lang]/page.tsx`, not the catch-all route). + const params: RawParam[] = [ { lang: "en", slug: [] }, { lang: "en", slug: ["agents"] }, { lang: "en", slug: ["cli", "reference"] }, { lang: "zh", slug: ["agents"] }, + { lang: "ko", slug: ["agents"] }, + { lang: "ko", slug: ["cli", "reference"] }, ]; expect(docsSlugStaticParams(params)).toEqual([ @@ -19,17 +30,15 @@ describe("docsSlugStaticParams", () => { ]); }); - it("keeps existing localized params and does not duplicate Korean pages", () => { - const params: DocsStaticParam[] = [ - { lang: "en", slug: ["agents"] }, + it("drops unknown languages and de-duplicates repeated params", () => { + const params: RawParam[] = [ { lang: "ko", slug: ["agents"] }, - { lang: "zh", slug: ["guides", "quickstart"] }, + { lang: "ko", slug: ["agents"] }, + { lang: "fr", slug: ["agents"] }, ]; expect(docsSlugStaticParams(params)).toEqual([ - { lang: "en", slug: ["agents"] }, { lang: "ko", slug: ["agents"] }, - { lang: "zh", slug: ["guides", "quickstart"] }, ]); }); }); diff --git a/apps/docs/lib/static-params.ts b/apps/docs/lib/static-params.ts index 19e1d60efa..126374d26e 100644 --- a/apps/docs/lib/static-params.ts +++ b/apps/docs/lib/static-params.ts @@ -1,4 +1,4 @@ -import { docsContentLang, i18n, type Lang } from "@/lib/i18n"; +import { i18n, type Lang } from "@/lib/i18n"; export type DocsStaticParam = { lang: Lang; @@ -37,15 +37,5 @@ export function docsSlugStaticParams( for (const param of slugParams) addParam(param); - for (const lang of i18n.languages) { - const contentLang = docsContentLang(lang); - if (contentLang === lang) continue; - - for (const param of slugParams) { - if (param.lang !== contentLang) continue; - addParam({ lang, slug: param.slug }); - } - } - return output; } diff --git a/apps/docs/lib/translations.ts b/apps/docs/lib/translations.ts index 8af4584646..ccd2f94bc3 100644 --- a/apps/docs/lib/translations.ts +++ b/apps/docs/lib/translations.ts @@ -54,7 +54,7 @@ export const homeCopy = { }, ko: { eyebrow: "Multica 문서", - titleLead: "사람과 agent,", + titleLead: "사람과 에이전트,", titleAccent: "한곳에서.", byline: ["시작하기", "2026년 4월 업데이트", "약 6분 읽기"], },