--- import '@/styles/global.css' import LoadingOverlay from '@/components/LoadingOverlay.astro' import AuthModal from '@/components/AuthModal.astro' import { href, ROUTES } from '@/lib/routes' import { getI18n, LOCALE_NAMES, setApiLocale, DEFAULT_LOCALE, STORED_LOCALE_KEY } from '@/lib/i18n' import { config } from '@/lib/config' import { i18n } from 'astro:config/client'; import { toCodes } from 'astro:i18n'; interface Props { title?: string description?: string keywords?: string } const { title, description, keywords } = Astro.props const { locale, t, getRelativeLocaleUrl } = getI18n(Astro) // 语言偏好跳转用的 localStorage key 与默认语言:注入到 的 data-*,供内联脚本读取 const storedLocaleKey = STORED_LOCALE_KEY const defaultLocaleVal = DEFAULT_LOCALE const SITE_NAME = config.siteName ?? "Xisoa" const siteTitle = title ? `${title} — ${SITE_NAME}` : SITE_NAME // 默认 TDK(按语言);各页面可经 Layout 的 description/keywords 覆盖。 const defaultDescription = locale === 'cn' ? `${SITE_NAME} 时尚档案收录全球时装周走秀造型与街头抓拍,可按品牌、季节、城市检索。` : `${SITE_NAME} is a fashion archive of runway looks and street style snaps — browse by brand, season and city.` const defaultKeywords = locale === 'cn' ? "时尚档案,走秀,街拍,时装周,品牌,季节" : "fashion archive,runway looks,street style,fashion week,brand,season" const metaDescription = description ?? defaultDescription const metaKeywords = keywords ?? defaultKeywords // 把当前语言注入 API 请求层:所有服务端(SSG/SSR)取数统一走 X-Locale 头, // 后端据此选 *_en / *_cn 列。客户端 fetch 则自行按 URL path 推断,无需此处。 setApiLocale(locale) // 经 data-* 属性把已翻译字符串传入打包型 script 标签;define:vars 会让脚本变内联 classic,导致 import 报错,故改用 data 属性 // (账号区现改为 Alpine 组件,译文直接在模板内用 t() 输出,无需 data 属性中转) const pathName = Astro.url.pathname; const locales = toCodes(i18n!.locales); const pathWithoutLocale = locales.includes(pathName.split('/')[1]) ? pathName.replace(`/${locale}`, '') : pathName; // 顶部导航数据模型(替代原 Alpine x-data 的 menu/state)。 // 下拉菜单与语言列表均由此驱动渲染,逻辑用 Astro 原生 script 控制开合。 const headers = { // 走秀下拉菜单项:方案 A —— 全部 + 4 个真实 collection_type 分类(与列表页 COLLECTION 筛选一致), // 深链带 ?collection= 参数,由 RunwayLooks 的 init() 读取并自动选中筛选。 runways: [ { label: t('all runway looks'), href: href(locale, ROUTES.runwayLooks) }, { label: t('Ready-to-Wear'), href: href(locale, ROUTES.runwayLooks) + '?collection=rtw' }, { label: t('Menswear'), href: href(locale, ROUTES.runwayLooks) + '?collection=menswear' }, { label: t('Couture'), href: href(locale, ROUTES.runwayLooks) + '?collection=couture' }, { label: t('Resort'), href: href(locale, ROUTES.runwayLooks) + '?collection=resort' }, ], // 街拍下拉菜单项:按地区分类(值即后端 street_snap.city 列存储的规范英文名), // 深链带 ?city= 参数,由 StreetSnaps 的 init() 读取并自动选中筛选。 // 仅保留 4 项(全部 + 三座核心时尚之都),完整城市列表在街拍页侧栏 City 筛选里。 streets: [ { label: t('All Street Snaps'), href: href(locale, ROUTES.streetSnaps) }, { label: t('Paris'), href: href(locale, ROUTES.streetSnaps) + '?city=Paris' }, { label: t('New York'), href: href(locale, ROUTES.streetSnaps) + '?city=New York' }, { label: t('Milan'), href: href(locale, ROUTES.streetSnaps) + '?city=Milan' }, ], // 语言列表(code + 本地名称) languages: locales.map((l) => ({ code: l, label: LOCALE_NAMES[l].nativeName })), } --- {siteTitle}
{/* 全局登录门禁层(login-required 属性 + 统一登录浮层) */} {/* 语言建议弹窗:未选过语言且浏览器语言与当前首页不符时,提示是否切换到该语言 */}