- PublicLayout: 删除桌面导航/下拉菜单/页脚三处入口及空页脚,移除 about 导航类型与判断 - App: 移除 /about 路由及 AboutPage 引入 - 删除 pages/About 页面 - DocumentMeta: 移除 /about 的 meta 处理与 about 描述文案(中/英) - i18n: 移除 aboutTitle / aboutIntro / footerAbout(中/英) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
185 lines
5.3 KiB
TypeScript
185 lines
5.3 KiB
TypeScript
import { useEffect, useMemo } from "react";
|
|
import { useLocation } from "react-router-dom";
|
|
import { useI18n, type Lang } from "../i18n";
|
|
|
|
type PageMeta = {
|
|
title: string;
|
|
description: string;
|
|
};
|
|
|
|
const descriptions: Record<Lang, Record<string, string>> = {
|
|
"zh-CN": {
|
|
home: "ARK 官方数据库集中整理官方教材、公告、视频、图片与常用文件,帮助社区快速找到可信资料。",
|
|
browse: "浏览 ARK 资料库中的官方资料、教程、公告、图片、视频与可下载文件。",
|
|
categories:
|
|
"按分类探索 ARK 官方资料,快速定位所需教材、公告、视频、图片与文件。",
|
|
official: "查看 ARK 官方推荐资料,获取优先整理的重点内容与可信资源。",
|
|
favorites: "收藏功能开发中,未来可在这里集中管理常用 ARK 资料。",
|
|
search: "在 ARK 资料库中搜索标题、分类、标签、简介、文件类型与正文内容。",
|
|
},
|
|
en: {
|
|
home: "ARK Library organizes official decks, announcements, videos, images, and files so the community can find trusted resources quickly.",
|
|
browse:
|
|
"Browse official ARK resources, guides, announcements, images, videos, and downloadable files.",
|
|
categories:
|
|
"Explore ARK resources by category and quickly find guides, announcements, videos, images, and files.",
|
|
official:
|
|
"View official ARK recommendations and prioritized trusted resources.",
|
|
favorites:
|
|
"Favorites are in development and will help you manage commonly used ARK resources.",
|
|
search:
|
|
"Search ARK Library titles, categories, tags, summaries, file types, and body text.",
|
|
},
|
|
ja: {},
|
|
ko: {},
|
|
vi: {},
|
|
id: {},
|
|
ms: {},
|
|
};
|
|
|
|
const localeMap: Record<Lang, string> = {
|
|
"zh-CN": "zh_CN",
|
|
en: "en_US",
|
|
ja: "ja_JP",
|
|
ko: "ko_KR",
|
|
vi: "vi_VN",
|
|
id: "id_ID",
|
|
ms: "ms_MY",
|
|
};
|
|
|
|
function metaDescription(lang: Lang, key: string) {
|
|
return (
|
|
descriptions[lang][key] || descriptions.en[key] || descriptions.en.home
|
|
);
|
|
}
|
|
|
|
function setMeta(selector: string, attr: "name" | "property", value: string) {
|
|
let meta = document.head.querySelector<HTMLMetaElement>(selector);
|
|
if (!meta) {
|
|
meta = document.createElement("meta");
|
|
meta.setAttribute(attr, value);
|
|
document.head.appendChild(meta);
|
|
}
|
|
return meta;
|
|
}
|
|
|
|
function setLink(rel: string, href: string) {
|
|
let link = document.head.querySelector<HTMLLinkElement>(`link[rel="${rel}"]`);
|
|
if (!link) {
|
|
link = document.createElement("link");
|
|
link.rel = rel;
|
|
document.head.appendChild(link);
|
|
}
|
|
link.href = href;
|
|
}
|
|
|
|
function routeMeta(
|
|
pathname: string,
|
|
search: string,
|
|
lang: Lang,
|
|
t: (key: string) => string,
|
|
): PageMeta {
|
|
const params = new URLSearchParams(search);
|
|
const q = params.get("q")?.trim();
|
|
const sort = params.get("sort");
|
|
|
|
if (pathname === "/") {
|
|
return {
|
|
title: t("heroTitle"),
|
|
description: metaDescription(lang, "home"),
|
|
};
|
|
}
|
|
|
|
if (pathname === "/browse") {
|
|
if (q) {
|
|
return {
|
|
title: `${t("search")}: ${q}`,
|
|
description: metaDescription(lang, "search"),
|
|
};
|
|
}
|
|
if (sort === "latest") {
|
|
return {
|
|
title: t("latest"),
|
|
description: metaDescription(lang, "browse"),
|
|
};
|
|
}
|
|
if (sort === "popular") {
|
|
return {
|
|
title: t("popular"),
|
|
description: metaDescription(lang, "browse"),
|
|
};
|
|
}
|
|
return { title: t("all"), description: metaDescription(lang, "browse") };
|
|
}
|
|
|
|
if (pathname === "/categories") {
|
|
return {
|
|
title: t("categories"),
|
|
description: metaDescription(lang, "categories"),
|
|
};
|
|
}
|
|
|
|
if (pathname === "/official-recommendations") {
|
|
return {
|
|
title: t("official"),
|
|
description: metaDescription(lang, "official"),
|
|
};
|
|
}
|
|
|
|
if (pathname === "/favorites") {
|
|
return {
|
|
title: t("favorites"),
|
|
description: metaDescription(lang, "favorites"),
|
|
};
|
|
}
|
|
|
|
return { title: t("brand"), description: metaDescription(lang, "home") };
|
|
}
|
|
|
|
export function DocumentMeta() {
|
|
const { pathname, search } = useLocation();
|
|
const { lang, t } = useI18n();
|
|
const meta = useMemo(
|
|
() => routeMeta(pathname, search, lang, t),
|
|
[lang, pathname, search, t],
|
|
);
|
|
|
|
useEffect(() => {
|
|
const brand = t("brand");
|
|
const title = pathname === "/" ? meta.title : `${meta.title} | ${brand}`;
|
|
const canonical = `${window.location.origin}${pathname}${search}`;
|
|
|
|
document.documentElement.lang = lang;
|
|
document.title = title;
|
|
|
|
setMeta('meta[name="description"]', "name", "description").content =
|
|
meta.description;
|
|
setMeta(
|
|
'meta[name="application-name"]',
|
|
"name",
|
|
"application-name",
|
|
).content = brand;
|
|
setMeta('meta[property="og:title"]', "property", "og:title").content =
|
|
title;
|
|
setMeta(
|
|
'meta[property="og:description"]',
|
|
"property",
|
|
"og:description",
|
|
).content = meta.description;
|
|
setMeta('meta[property="og:url"]', "property", "og:url").content =
|
|
canonical;
|
|
setMeta('meta[property="og:locale"]', "property", "og:locale").content =
|
|
localeMap[lang];
|
|
setMeta('meta[name="twitter:title"]', "name", "twitter:title").content =
|
|
title;
|
|
setMeta(
|
|
'meta[name="twitter:description"]',
|
|
"name",
|
|
"twitter:description",
|
|
).content = meta.description;
|
|
setLink("canonical", canonical);
|
|
}, [lang, meta.description, meta.title, pathname, search, t]);
|
|
|
|
return null;
|
|
}
|