From 1b5cd0f416bef3e1dfd42f218cc324b08e790efa Mon Sep 17 00:00:00 2001 From: KimiSwitch Dev Date: Sat, 1 Aug 2026 19:56:54 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat(website):=20SEO=20=E5=8D=87=E7=BA=A7?= =?UTF-8?q?=20=E2=80=94=20BrowserRouter=20+=20=E6=AF=8F=E9=A1=B5=E7=8B=AC?= =?UTF-8?q?=E7=AB=8B=20meta?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - HashRouter → BrowserRouter(basename /KimiSwitch),GitHub Pages 用 404.html SPA 回退,/features /download /changelog 变为可收录的独立 URL - 每页独立 title / description / OG / canonical(RouteMeta 随语言切换) - og:image 改为绝对地址;sitemap 更新为干净路径 --- website/index.html | 4 ++-- website/package-lock.json | 18 +++++++++++++++ website/package.json | 1 + website/public/sitemap.xml | 16 +++---------- website/src/App.tsx | 46 ++++++++++++++++++++++++++++++++++++++ website/src/i18n.tsx | 44 ++++++++++++++++++++++++++++++++++++ website/src/main.tsx | 6 ++--- website/vite.config.ts | 25 ++++++++++++++++++--- 8 files changed, 139 insertions(+), 21 deletions(-) diff --git a/website/index.html b/website/index.html index 3780887..5311bea 100644 --- a/website/index.html +++ b/website/index.html @@ -24,7 +24,7 @@ content="一键切换 Kimi / 智谱 GLM / MiniMax / DeepSeek 等供应商,用量与账单一目了然,开源 MIT。" /> - + @@ -33,7 +33,7 @@ name="twitter:description" content="一键切换 Kimi / 智谱 GLM / MiniMax / DeepSeek 等供应商,用量与账单一目了然,开源 MIT。" /> - + diff --git a/website/package-lock.json b/website/package-lock.json index 06e9f0a..7a20a1f 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -16,6 +16,7 @@ "react-router-dom": "^7.18.2" }, "devDependencies": { + "@types/node": "^26.1.2", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "@vitejs/plugin-react": "^4.3.1", @@ -1299,6 +1300,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/node": { + "version": "26.1.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz", + "integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.3.0" + } + }, "node_modules/@types/prop-types": { "version": "15.7.15", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", @@ -2723,6 +2734,13 @@ "node": ">=14.17" } }, + "node_modules/undici-types": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", + "dev": true, + "license": "MIT" + }, "node_modules/update-browserslist-db": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", diff --git a/website/package.json b/website/package.json index 92569c2..308d390 100644 --- a/website/package.json +++ b/website/package.json @@ -17,6 +17,7 @@ "react-router-dom": "^7.18.2" }, "devDependencies": { + "@types/node": "^26.1.2", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "@vitejs/plugin-react": "^4.3.1", diff --git a/website/public/sitemap.xml b/website/public/sitemap.xml index 28e0e8d..813d306 100644 --- a/website/public/sitemap.xml +++ b/website/public/sitemap.xml @@ -6,27 +6,17 @@ 1.0 - https://billowliu2.github.io/KimiSwitch/#/features + https://billowliu2.github.io/KimiSwitch/features monthly 0.8 - https://billowliu2.github.io/KimiSwitch/#/showcase - monthly - 0.8 - - - https://billowliu2.github.io/KimiSwitch/#/performance - monthly - 0.7 - - - https://billowliu2.github.io/KimiSwitch/#/changelog + https://billowliu2.github.io/KimiSwitch/changelog weekly 0.7 - https://billowliu2.github.io/KimiSwitch/#/download + https://billowliu2.github.io/KimiSwitch/download weekly 0.9 diff --git a/website/src/App.tsx b/website/src/App.tsx index 52b988f..703eb36 100644 --- a/website/src/App.tsx +++ b/website/src/App.tsx @@ -8,6 +8,10 @@ import Download from "./sections/Download"; import Privacy from "./sections/Privacy"; import CTA from "./sections/CTA"; import Footer from "./components/Footer"; +import { useLang, type Dict } from "./i18n"; + +const SITE_URL = "https://billowliu2.github.io/KimiSwitch"; +type SeoKey = keyof Dict["seo"]; function ScrollToTop() { const { pathname } = useLocation(); @@ -17,6 +21,47 @@ function ScrollToTop() { return null; } +/** Update per-page title / description / OG / canonical for SEO. */ +function RouteMeta() { + const { pathname } = useLocation(); + const { t } = useLang(); + + useEffect(() => { + const page = pathname.replace(/\/+$/, ""); + const seoKey: SeoKey = + page === "/features" || page === "/changelog" || page === "/download" + ? (page.slice(1) as SeoKey) + : "home"; + const m = t.seo[seoKey]; + const url = `${SITE_URL}${page === "" ? "/" : page}`; + + document.title = m.title; + setMeta("name", "description", m.description); + setMeta("property", "og:title", m.title); + setMeta("property", "og:description", m.description); + setMeta("property", "og:url", url); + setMeta("name", "twitter:title", m.title); + setMeta("name", "twitter:description", m.description); + + const canon = document.querySelector('link[rel="canonical"]'); + if (canon) canon.setAttribute("href", url); + }, [pathname, t]); + + return null; +} + +function setMeta(attr: "name" | "property", key: string, content: string) { + let el = document.head.querySelector( + `meta[${attr}="${key}"]`, + ); + if (!el) { + el = document.createElement("meta"); + el.setAttribute(attr, key); + document.head.appendChild(el); + } + el.setAttribute("content", content); +} + const homePage = ( <> @@ -30,6 +75,7 @@ export default function App() {