Files
wehub-resource-sync d68f003000
CI / Change detection (push) Has been cancelled
CI / Version drift (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Workflow RLM cache (push) Has been cancelled
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / npm wrapper smoke (push) Has been cancelled
CI / Mobile runtime smoke (push) Has been cancelled
CI / Workflow lint (push) Has been cancelled
CI / Documentation (push) Has been cancelled
Web Frontend / Lint & Type Check (push) Failing after 1s
Auto-close harvested PRs / close (push) Failing after 1s
cargo-deny / cargo-deny (bans licenses sources) (push) Failing after 1s
Security audit / cargo-audit (push) Failing after 1s
Sync to CNB / sync (push) Failing after 1s
cargo-deny / cargo-deny (advisories) (push) Failing after 3s
Web Frontend / Deploy to Cloudflare (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:08:23 +08:00

91 lines
2.6 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Metadata } from "next";
/** Canonical origin for the production site (no trailing slash). */
export const SITE_URL = "https://codewhale.net";
export const SITE_NAME = "CodeWhale";
/** The one-line product identity, used as the default OG image alt text. */
export const IDENTITY_PHRASE =
"The calm, provider-honest terminal coding agent for any model — open models first.";
/** Shared OG card rendered by app/opengraph-image.tsx (1200×630 PNG). */
const OG_IMAGE = {
url: `${SITE_URL}/opengraph-image`,
width: 1200,
height: 630,
alt: `${SITE_NAME}${IDENTITY_PHRASE}`,
};
/**
* buildPageMetadata — per-page SEO metadata for the bilingual (en/zh) site.
*
* Produces a canonical URL for the rendered locale, hreflang alternates for
* both locales (plus `x-default` pointing at the English page), and matching
* Open Graph / Twitter card fields wired to the shared OG image.
*
* @param path Route path WITHOUT the locale prefix, with a leading
* slash: "/" for the homepage, "/install", "/docs", …
* @param locale Locale of the page being rendered: "en" | "zh".
* @param title Localized page <title> (full string; no template is applied).
* @param description Localized meta description, same locale as `title`.
*
* Usage in a page or layout:
* ```ts
* export async function generateMetadata({ params }) {
* const { locale } = await params;
* const isZh = locale === "zh";
* return buildPageMetadata({
* path: "/install",
* locale,
* title: isZh ? "安装 · CodeWhale" : "Install · CodeWhale",
* description: isZh ? "…" : "…",
* });
* }
* ```
*/
export function buildPageMetadata({
path,
locale,
title,
description,
}: {
path: string;
locale: string;
title: string;
description: string;
}): Metadata {
// "/" → "" so the homepage canonical is /en, not /en/.
const suffix = path === "/" ? "" : path.replace(/\/+$/, "");
const canonical = `${SITE_URL}/${locale}${suffix}`;
return {
metadataBase: new URL(SITE_URL),
title,
description,
alternates: {
canonical,
languages: {
en: `${SITE_URL}/en${suffix}`,
zh: `${SITE_URL}/zh${suffix}`,
"x-default": `${SITE_URL}/en${suffix}`,
},
},
openGraph: {
title,
description,
url: canonical,
siteName: SITE_NAME,
type: "website",
locale: locale === "zh" ? "zh_CN" : "en_US",
images: [OG_IMAGE],
},
twitter: {
card: "summary_large_image",
title,
description,
images: [OG_IMAGE.url],
},
};
}