mirror of
https://github.com/multica-ai/multica.git
synced 2026-06-17 11:48:42 +02:00
* 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>
77 lines
2.7 KiB
TypeScript
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 };
|
|
}
|