Files
multica/apps/docs/lib/site.ts
김보경/DAXTF 5aa4fb7487 MUL-2760: feat(i18n): add Korean locale support (#3369)
* feat: add korean locale support

* feat(i18n): localize Korean landing page

* fix(i18n): refine Korean landing copy

* fix(i18n): refine Korean translations

* fix(i18n): translate Korean landing subpages

* fix(i18n): route Korean landing docs links

* fix(i18n): add Korean use case content

* fix(i18n): polish Korean locale copy

* fix(i18n): improve Korean landing copy

* fix(onboarding): persist Korean helper artifacts

Co-authored-by: multica-agent <github@multica.ai>

* fix(web): add use case locale fallback

Co-authored-by: multica-agent <github@multica.ai>

* Align Korean pull requests wording

Co-authored-by: multica-agent <github@multica.ai>

* fix(i18n): dedupe docs href helper

Co-authored-by: multica-agent <github@multica.ai>

* fix(i18n): localize changelog dates

Co-authored-by: multica-agent <github@multica.ai>

* fix(docs): prerender Korean fallback pages

Co-authored-by: multica-agent <github@multica.ai>

* fix(docs): align fallback hreflang metadata

Co-authored-by: multica-agent <github@multica.ai>

* fix(i18n): preserve Chinese CJK font fallback order

Co-authored-by: multica-agent <github@multica.ai>

* chore(onboarding): update localized comment wording

Co-authored-by: multica-agent <github@multica.ai>

* test(i18n): harden CJK font fallback assertions

Co-authored-by: multica-agent <github@multica.ai>

* fix(docs): keep Chinese font fallbacks first

Co-authored-by: multica-agent <github@multica.ai>

* test(i18n): harden locale fallback coverage

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: multica-agent <github@multica.ai>
2026-05-29 15:16:22 +08:00

77 lines
2.7 KiB
TypeScript

import { source } from "@/lib/source";
import { i18n } from "@/lib/i18n";
import { existsSync } from "node:fs";
import { join } from "node:path";
// Canonical production origin and basePath for the docs app. Used by the
// sitemap and per-page hreflang metadata — anywhere we need to construct
// absolute URLs for search engines.
export const SITE_ORIGIN = "https://www.multica.ai";
export const DOCS_BASE_PATH = "/docs";
/**
* Build an absolute URL for a docs page from its Fumadocs-relative url
* (e.g. "/agents" or "/zh/agents"). The home page comes through as "/",
* which would naively serialize to ".../docs/" with a trailing slash —
* Next serves the home at ".../docs" (no trailing), so we strip the lone
* slash to keep the sitemap entry and the page's own canonical link byte-
* identical. Otherwise Search Console flags a canonical mismatch.
*/
export function absoluteDocsUrl(relative: string): string {
const path = relative === "/" ? "" : relative;
return `${SITE_ORIGIN}${DOCS_BASE_PATH}${path}`;
}
function docsContentRoots(): string[] {
return [
join(process.cwd(), "content", "docs"),
join(process.cwd(), "apps", "docs", "content", "docs"),
];
}
function pageSourceStem(slugs: string[]): string {
return slugs.length === 0 ? "index" : slugs.join("/");
}
function hasLocalizedMdx(slugs: string[], lang: string): boolean {
const stem = pageSourceStem(slugs);
const candidates =
lang === i18n.defaultLanguage
? [`${stem}.mdx`, `${stem}/index.mdx`]
: [`${stem}.${lang}.mdx`, `${stem}/index.${lang}.mdx`];
return docsContentRoots().some((root) =>
candidates.some((candidate) => existsSync(join(root, candidate))),
);
}
/**
* Build Next.js `metadata.alternates` for a docs page:
* - `canonical` points at the default-language version (Google consolidates
* ranking signals onto it)
* - `languages` lists every available locale under its hreflang code,
* plus an `x-default` fallback pointing at the canonical URL
*
* Slugs that only exist in one language still get a valid alternates block;
* Google will only serve what's declared.
*/
export function docsAlternates(slugs: string[]): {
canonical: string;
languages: Record<string, string>;
} {
const languages: Record<string, string> = {};
for (const lang of i18n.languages) {
if (!hasLocalizedMdx(slugs, lang)) continue;
const page = source.getPage(slugs, lang);
if (!page) continue;
languages[lang] = absoluteDocsUrl(page.url);
}
const canonical =
languages[i18n.defaultLanguage] ?? Object.values(languages)[0];
if (canonical) languages["x-default"] = canonical;
return { canonical: canonical ?? absoluteDocsUrl("/"), languages };
}