mirror of
https://github.com/KivotosRailway/website.git
synced 2026-11-03 11:06:32 +09:00
修改页面标题路由
This commit is contained in:
+4
-1
@@ -4,7 +4,10 @@ import { SiteFooter } from "@/components/layout/site-footer";
|
|||||||
import { SiteHeader } from "@/components/layout/site-header";
|
import { SiteHeader } from "@/components/layout/site-header";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Kivotos Railway",
|
title: {
|
||||||
|
default: "基沃托斯铁道公团 KivotosRailway",
|
||||||
|
template: "%s | 基沃托斯铁道公团 KivotosRailway",
|
||||||
|
},
|
||||||
description: "Kivotos Railway website",
|
description: "Kivotos Railway website",
|
||||||
icons: {
|
icons: {
|
||||||
icon: "/icon/icon.svg",
|
icon: "/icon/icon.svg",
|
||||||
|
|||||||
@@ -1,8 +1,22 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { getNewsBySlug, markdownToHtml } from "@/data/news";
|
import { getNewsBySlug, markdownToHtml } from "@/data/news";
|
||||||
import { PageBreadcrumb } from "@/components/layout/page-breadcrumb";
|
import { PageBreadcrumb } from "@/components/layout/page-breadcrumb";
|
||||||
import { NewsShare } from "@/components/news/news-share";
|
import { NewsShare } from "@/components/news/news-share";
|
||||||
|
|
||||||
|
export async function generateMetadata({
|
||||||
|
params,
|
||||||
|
}: {
|
||||||
|
params: Promise<{ slug: string }>;
|
||||||
|
}): Promise<Metadata> {
|
||||||
|
const { slug } = await params;
|
||||||
|
const item = await getNewsBySlug(slug);
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: item?.title ?? "新闻",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export default async function NewsDetailPage({
|
export default async function NewsDetailPage({
|
||||||
params,
|
params,
|
||||||
}: {
|
}: {
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
import Link from "next/link";
|
import type { Metadata } from "next";
|
||||||
import { getNewsList } from "@/data/news";
|
import { getNewsList } from "@/data/news";
|
||||||
import { PageBreadcrumb } from "@/components/layout/page-breadcrumb";
|
import { PageBreadcrumb } from "@/components/layout/page-breadcrumb";
|
||||||
import { NewsList } from "@/components/news/news-list";
|
import { NewsList } from "@/components/news/news-list";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "新闻",
|
||||||
|
};
|
||||||
|
|
||||||
export default async function NewsPage() {
|
export default async function NewsPage() {
|
||||||
const news = await getNewsList();
|
const news = await getNewsList();
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { getLocaleMessages } from "@/lib/i18n";
|
||||||
|
|
||||||
export function PageBreadcrumb({ items }: { items: Array<{ label: string; href?: string }> }) {
|
export function PageBreadcrumb({ items }: { items: Array<{ label: string; href?: string }> }) {
|
||||||
return (
|
return (
|
||||||
<nav className="page-breadcrumb" aria-label="页面位置">
|
<nav className="page-breadcrumb" aria-label={getLocaleMessages("zh-CN").common.pagePosition}>
|
||||||
{items.map((item, index) => (
|
{items.map((item, index) => (
|
||||||
<span className="page-breadcrumb-item" key={`${item.label}-${index}`}>
|
<span className="page-breadcrumb-item" key={`${item.label}-${index}`}>
|
||||||
{index > 0 && <span className="page-breadcrumb-separator" aria-hidden="true">›</span>}
|
{index > 0 && <span className="page-breadcrumb-separator" aria-hidden="true">›</span>}
|
||||||
|
|||||||
@@ -10,7 +10,23 @@ export function SiteHeader() {
|
|||||||
const [menuOpen, setMenuOpen] = useState(false);
|
const [menuOpen, setMenuOpen] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const syncLocale = (nextLocale: Locale) => setLocale(nextLocale);
|
const updateDocumentTitle = (nextLocale: Locale) => {
|
||||||
|
const messages = getLocaleMessages(nextLocale);
|
||||||
|
const pathname = window.location.pathname;
|
||||||
|
const currentTitle = document.title.split(" | ")[0];
|
||||||
|
const pageLabel = pathname === "/"
|
||||||
|
? ""
|
||||||
|
: pathname === "/news"
|
||||||
|
? messages.common.news
|
||||||
|
: currentTitle;
|
||||||
|
|
||||||
|
document.title = pageLabel ? `${pageLabel} | ${messages.common.siteTitle}` : messages.common.siteTitle;
|
||||||
|
};
|
||||||
|
|
||||||
|
const syncLocale = (nextLocale: Locale) => {
|
||||||
|
setLocale(nextLocale);
|
||||||
|
updateDocumentTitle(nextLocale);
|
||||||
|
};
|
||||||
|
|
||||||
const handleLocaleChange = (event: Event) => {
|
const handleLocaleChange = (event: Event) => {
|
||||||
const nextLocale = (event as CustomEvent<{ locale?: Locale }>).detail?.locale ?? getInitialLocale();
|
const nextLocale = (event as CustomEvent<{ locale?: Locale }>).detail?.locale ?? getInitialLocale();
|
||||||
|
|||||||
@@ -1,11 +1,25 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import type { NewsItem } from "@/data/news";
|
import type { NewsItem } from "@/data/news";
|
||||||
|
import { getInitialLocale, getLocaleMessages, type Locale } from "@/lib/i18n";
|
||||||
|
|
||||||
export function NewsList({ news }: { news: NewsItem[] }) {
|
export function NewsList({ news }: { news: NewsItem[] }) {
|
||||||
const [activeCategory, setActiveCategory] = useState<string | null>(null);
|
const [activeCategory, setActiveCategory] = useState<string | null>(null);
|
||||||
|
const [locale, setLocale] = useState<Locale>("zh-CN");
|
||||||
|
const messages = getLocaleMessages(locale);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const syncLocale = () => setLocale(getInitialLocale());
|
||||||
|
syncLocale();
|
||||||
|
window.addEventListener("kr-locale-change", syncLocale);
|
||||||
|
window.addEventListener("storage", syncLocale);
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("kr-locale-change", syncLocale);
|
||||||
|
window.removeEventListener("storage", syncLocale);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
const categories = Array.from(new Set(news.map((item) => item.category).filter(Boolean)));
|
const categories = Array.from(new Set(news.map((item) => item.category).filter(Boolean)));
|
||||||
const visibleNews = activeCategory
|
const visibleNews = activeCategory
|
||||||
? news.filter((item) => item.category === activeCategory)
|
? news.filter((item) => item.category === activeCategory)
|
||||||
@@ -20,7 +34,7 @@ export function NewsList({ news }: { news: NewsItem[] }) {
|
|||||||
className={`news-filter-button${activeCategory === null ? " active" : ""}`}
|
className={`news-filter-button${activeCategory === null ? " active" : ""}`}
|
||||||
onClick={() => setActiveCategory(null)}
|
onClick={() => setActiveCategory(null)}
|
||||||
>
|
>
|
||||||
全部
|
{messages.common.all}
|
||||||
</button>
|
</button>
|
||||||
{categories.map((category) => (
|
{categories.map((category) => (
|
||||||
<button
|
<button
|
||||||
@@ -34,7 +48,7 @@ export function NewsList({ news }: { news: NewsItem[] }) {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="news-toolbar-right">
|
<div className="news-toolbar-right">
|
||||||
<button type="button" className="news-view-toggle" aria-label="列表视图">☰</button>
|
<button type="button" className="news-view-toggle" aria-label={messages.common.listView}>☰</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,23 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import { getInitialLocale, getLocaleMessages, type Locale } from "@/lib/i18n";
|
||||||
|
|
||||||
export function NewsShare({ title }: { title: string }) {
|
export function NewsShare({ title }: { title: string }) {
|
||||||
const [copied, setCopied] = useState(false);
|
const [copied, setCopied] = useState(false);
|
||||||
|
const [locale, setLocale] = useState<Locale>("zh-CN");
|
||||||
|
const messages = getLocaleMessages(locale);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const syncLocale = () => setLocale(getInitialLocale());
|
||||||
|
syncLocale();
|
||||||
|
window.addEventListener("kr-locale-change", syncLocale);
|
||||||
|
window.addEventListener("storage", syncLocale);
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("kr-locale-change", syncLocale);
|
||||||
|
window.removeEventListener("storage", syncLocale);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
const getPageUrl = () => window.location.href;
|
const getPageUrl = () => window.location.href;
|
||||||
|
|
||||||
@@ -27,13 +41,13 @@ export function NewsShare({ title }: { title: string }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="news-share" aria-labelledby="news-share-title">
|
<section className="news-share" aria-labelledby="news-share-title">
|
||||||
<h2 id="news-share-title">分享</h2>
|
<h2 id="news-share-title">{messages.common.share}</h2>
|
||||||
<div className="news-share-actions">
|
<div className="news-share-actions">
|
||||||
<button type="button" className="news-share-button" onClick={copyLink} aria-label="复制链接">
|
<button type="button" className="news-share-button" onClick={copyLink} aria-label={messages.common.copyLink}>
|
||||||
{copied ? "已复制" : "复制链接"}
|
{copied ? messages.common.copied : messages.common.copyLink}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" className="news-share-button news-share-x" onClick={shareOnX} aria-label="分享到 X">X</button>
|
<button type="button" className="news-share-button news-share-x" onClick={shareOnX} aria-label={messages.common.shareX}>X</button>
|
||||||
<button type="button" className="news-share-button news-share-qq" onClick={shareOnQq} aria-label="分享到 QQ">QQ</button>
|
<button type="button" className="news-share-button news-share-qq" onClick={shareOnQq} aria-label={messages.common.shareQq}>QQ</button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|||||||
+15
-1
@@ -5,12 +5,26 @@
|
|||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"kicker": "KIVOTOS RAILWAY / MINECRAFT SERVER",
|
"kicker": "KIVOTOS RAILWAY / MINECRAFT SERVER",
|
||||||
"title": ["Connecting every journey", "by rail"],
|
"title": ["Railways", "connect us"],
|
||||||
"subtitle": "A Minecraft railway world built and expanded by players together.",
|
"subtitle": "A Minecraft railway world built and expanded by players together.",
|
||||||
"cta": "Explore KivotosRailway",
|
"cta": "Explore KivotosRailway",
|
||||||
"latest": "Latest News",
|
"latest": "Latest News",
|
||||||
"more": "View more"
|
"more": "View more"
|
||||||
},
|
},
|
||||||
|
"common": {
|
||||||
|
"siteTitle": "KivotosRailway",
|
||||||
|
"home": "Home",
|
||||||
|
"news": "News",
|
||||||
|
"share": "Share",
|
||||||
|
"copyLink": "Copy link",
|
||||||
|
"copied": "Copied",
|
||||||
|
"shareX": "Share on X",
|
||||||
|
"shareQq": "Share on QQ",
|
||||||
|
"all": "All",
|
||||||
|
"listView": "List view",
|
||||||
|
"pagePosition": "Page position",
|
||||||
|
"backToNews": "Back to news list"
|
||||||
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"follow": "Follow us",
|
"follow": "Follow us",
|
||||||
"social": ["Weibo", "WeChat", "X", "LinkedIn", "Video"],
|
"social": ["Weibo", "WeChat", "X", "LinkedIn", "Video"],
|
||||||
|
|||||||
@@ -11,6 +11,20 @@
|
|||||||
"latest": "最新ニュース",
|
"latest": "最新ニュース",
|
||||||
"more": "もっと見る"
|
"more": "もっと見る"
|
||||||
},
|
},
|
||||||
|
"common": {
|
||||||
|
"siteTitle": "KivotosRailway",
|
||||||
|
"home": "ホーム",
|
||||||
|
"news": "ニュース",
|
||||||
|
"share": "共有",
|
||||||
|
"copyLink": "リンクをコピー",
|
||||||
|
"copied": "コピーしました",
|
||||||
|
"shareX": "Xで共有",
|
||||||
|
"shareQq": "QQで共有",
|
||||||
|
"all": "すべて",
|
||||||
|
"listView": "リスト表示",
|
||||||
|
"pagePosition": "ページの位置",
|
||||||
|
"backToNews": "ニュース一覧へ戻る"
|
||||||
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"follow": "フォローする",
|
"follow": "フォローする",
|
||||||
"social": ["Weibo", "WeChat", "X", "LinkedIn", "動画"],
|
"social": ["Weibo", "WeChat", "X", "LinkedIn", "動画"],
|
||||||
|
|||||||
+15
-1
@@ -11,6 +11,20 @@
|
|||||||
"latest": "最新资讯",
|
"latest": "最新资讯",
|
||||||
"more": "浏览更多"
|
"more": "浏览更多"
|
||||||
},
|
},
|
||||||
|
"common": {
|
||||||
|
"siteTitle": "基沃托斯铁道公团 KivotosRailway",
|
||||||
|
"home": "首页",
|
||||||
|
"news": "新闻",
|
||||||
|
"share": "分享",
|
||||||
|
"copyLink": "复制链接",
|
||||||
|
"copied": "已复制",
|
||||||
|
"shareX": "分享到 X",
|
||||||
|
"shareQq": "分享到 QQ",
|
||||||
|
"all": "全部",
|
||||||
|
"listView": "列表视图",
|
||||||
|
"pagePosition": "页面位置",
|
||||||
|
"backToNews": "返回新闻列表"
|
||||||
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"follow": "关注我们",
|
"follow": "关注我们",
|
||||||
"social": ["微博", "微信", "X", "LinkedIn", "视频"],
|
"social": ["微博", "微信", "X", "LinkedIn", "视频"],
|
||||||
@@ -22,6 +36,6 @@
|
|||||||
"legal": ["KR Wiki", "状态监控", "友情链接"],
|
"legal": ["KR Wiki", "状态监控", "友情链接"],
|
||||||
"description": "Minecraft 是 Mojang Studios 的注册商标,基沃托斯铁道公团与 Mojang Studios 及微软公司、网易公司没有从属、授权或合作关系。站内提及的城市、地区、组织、品牌及相关设定,除特别说明外,均为虚构内容,不指向任何现实机构或商业实体。",
|
"description": "Minecraft 是 Mojang Studios 的注册商标,基沃托斯铁道公团与 Mojang Studios 及微软公司、网易公司没有从属、授权或合作关系。站内提及的城市、地区、组织、品牌及相关设定,除特别说明外,均为虚构内容,不指向任何现实机构或商业实体。",
|
||||||
"copyright": "© 2024 - 2026 基沃托斯铁道公团 KivotosRailway",
|
"copyright": "© 2024 - 2026 基沃托斯铁道公团 KivotosRailway",
|
||||||
"icp": "桂ICP备2025054269号-5"
|
"icp": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,20 @@
|
|||||||
"latest": "最新資訊",
|
"latest": "最新資訊",
|
||||||
"more": "瀏覽更多"
|
"more": "瀏覽更多"
|
||||||
},
|
},
|
||||||
|
"common": {
|
||||||
|
"siteTitle": "基沃托斯鐵道公團 KivotosRailway",
|
||||||
|
"home": "首頁",
|
||||||
|
"news": "新聞",
|
||||||
|
"share": "分享",
|
||||||
|
"copyLink": "複製連結",
|
||||||
|
"copied": "已複製",
|
||||||
|
"shareX": "分享到 X",
|
||||||
|
"shareQq": "分享到 QQ",
|
||||||
|
"all": "全部",
|
||||||
|
"listView": "列表視圖",
|
||||||
|
"pagePosition": "頁面位置",
|
||||||
|
"backToNews": "返回新聞列表"
|
||||||
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"follow": "關注我們",
|
"follow": "關注我們",
|
||||||
"social": ["微博", "微信", "X", "LinkedIn", "影片"],
|
"social": ["微博", "微信", "X", "LinkedIn", "影片"],
|
||||||
|
|||||||
Reference in New Issue
Block a user