@ -1,5 +1,7 @@
|
||||
---
|
||||
import '../styles/global.css'
|
||||
import { getLocaleFromUrl, useTranslations, useTranslatedPath } from '../i18n/utils'
|
||||
import { LOCALES, LOCALE_NAMES, isLocaleSegment } from '../i18n/config'
|
||||
|
||||
const {
|
||||
title = 'Portfolio Studio',
|
||||
@ -8,6 +10,17 @@ const {
|
||||
activeThemePalette = null,
|
||||
} = Astro.props;
|
||||
|
||||
const locale = getLocaleFromUrl(Astro.url)
|
||||
const { t } = useTranslations(locale)
|
||||
const translatePath = useTranslatedPath(locale)
|
||||
|
||||
// 当前路径去掉语言前缀,用于语言切换器拼出「同页面、另一语言」的链接
|
||||
const _segs = Astro.url.pathname.split('/').filter(Boolean);
|
||||
const _stripped = _segs.length > 0 && isLocaleSegment(_segs[0])
|
||||
? '/' + _segs.slice(1).join('/')
|
||||
: Astro.url.pathname;
|
||||
const currentPath = _stripped === '' ? '/' : _stripped;
|
||||
|
||||
const categories: { label: string; value: PortfolioCategory | 'Trending Colors' }[] = [
|
||||
{ label: 'x', value: 'x' },
|
||||
{ label: 'Backstage', value: 'Backstage' },
|
||||
@ -17,7 +30,7 @@ const categories: { label: string; value: PortfolioCategory | 'Trending Colors'
|
||||
];
|
||||
---
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<html lang={locale === 'cn' ? 'zh-CN' : 'en'}>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
@ -25,25 +38,55 @@ const categories: { label: string; value: PortfolioCategory | 'Trending Colors'
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<title>{title}</title>
|
||||
<!-- 语言自动检测:无前缀且浏览器为中文环境时,跳转到 /cn 前缀 -->
|
||||
<script is:inline>
|
||||
(function () {
|
||||
try {
|
||||
var SUPPORTED = ['en', 'cn'];
|
||||
var path = location.pathname;
|
||||
var seg = path.split('/').filter(Boolean)[0];
|
||||
if (SUPPORTED.indexOf(seg) !== -1) return; // 已有语言前缀
|
||||
var lang = (navigator.language || 'en').toLowerCase();
|
||||
var target = lang.indexOf('zh') === 0 ? 'cn' : 'en';
|
||||
if (target === 'en') return; // 默认英文无需跳转
|
||||
if (sessionStorage.getItem('lang-redirect')) return;
|
||||
sessionStorage.setItem('lang-redirect', '1');
|
||||
location.pathname = '/' + target + path;
|
||||
} catch (e) {}
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<body class="bg-white text-black min-h-screen flex flex-col p-[30px_50px] font-sans">
|
||||
|
||||
<!-- 顶部主导航栏 -->
|
||||
<nav class="flex justify-between items-center text-[13px] font-bold tracking-[0.5px] uppercase">
|
||||
<div class="flex flex-wrap items-center gap-6 md:gap-8">
|
||||
<a href="/portfolio" class="relative pb-[2px] cursor-pointer transition-opacity duration-200 flex items-center gap-1.5 hover:opacity-60 opacity-80 border-b-2 border-transparent">Portfolio<span class="text-[10px] bg-black text-white px-1.5 py-0.5 rounded-full font-mono tracking-tight leading-none">
|
||||
<a href={translatePath('/portfolio')} class="relative pb-[2px] cursor-pointer transition-opacity duration-200 flex items-center gap-1.5 hover:opacity-60 opacity-80 border-b-2 border-transparent">{t('Portfolio')}<span class="text-[10px] bg-black text-white px-1.5 py-0.5 rounded-full font-mono tracking-tight leading-none">
|
||||
2026
|
||||
</span></a>
|
||||
<a href="/shows" class="relative pb-[2px] cursor-pointer hover:opacity-60 transition-opacity duration-200">All Shows</a>
|
||||
<a href="/latest-projects" class="relative pb-[2px] cursor-pointer hover:opacity-60 transition-opacity duration-200">Latest Projects</a>
|
||||
<a href="/about" class="relative pb-[2px] cursor-pointer hover:opacity-60 transition-opacity duration-200">About</a>
|
||||
<a href="/contact" class="relative pb-[2px] cursor-pointer hover:opacity-60 transition-opacity duration-200">Contact Us</a>
|
||||
<a href={translatePath('/shows')} class="relative pb-[2px] cursor-pointer hover:opacity-60 transition-opacity duration-200">{t('All Shows')}</a>
|
||||
<a href={translatePath('/latest-projects')} class="relative pb-[2px] cursor-pointer hover:opacity-60 transition-opacity duration-200">{t('Latest Projects')}</a>
|
||||
<a href={translatePath('/about')} class="relative pb-[2px] cursor-pointer hover:opacity-60 transition-opacity duration-200">{t('About')}</a>
|
||||
<a href={translatePath('/contact')} class="relative pb-[2px] cursor-pointer hover:opacity-60 transition-opacity duration-200">{t('Contact Us')}</a>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3" id="auth-slot">
|
||||
<a href="/login" class="hover:opacity-60 transition-opacity duration-200">Login</a>
|
||||
<a href="/register" class="hover:opacity-60 transition-opacity duration-200">Signup</a>
|
||||
<div class="flex items-center gap-3" id="auth-slot">
|
||||
<div class="flex items-center gap-2 normal-case" aria-label="Language">
|
||||
{LOCALES.map((l) => (
|
||||
<a
|
||||
href={translatePath(currentPath, l)}
|
||||
aria-current={l === locale ? 'true' : undefined}
|
||||
class:list={[
|
||||
'cursor-pointer hover:opacity-60 transition-opacity duration-200',
|
||||
l === locale ? 'opacity-100 underline underline-offset-4' : 'opacity-40',
|
||||
]}
|
||||
>{LOCALE_NAMES[l]}</a>
|
||||
))}
|
||||
</div>
|
||||
<span class="opacity-25">/</span>
|
||||
<a href={translatePath('/login')} class="hover:opacity-60 transition-opacity duration-200">{t('Login')}</a>
|
||||
<a href={translatePath('/register')} class="hover:opacity-60 transition-opacity duration-200">{t('Signup')}</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="flex-1 flex flex-col">
|
||||
@ -196,26 +239,30 @@ const categories: { label: string; value: PortfolioCategory | 'Trending Colors'
|
||||
|
||||
<div class="flex items-center gap-6">
|
||||
<a
|
||||
href="/about"
|
||||
href={translatePath('/about')}
|
||||
class="nav-btn hover:text-black cursor-pointer uppercase font-bold"
|
||||
>
|
||||
About
|
||||
{t('About')}
|
||||
</a>
|
||||
<a
|
||||
href="/contact"
|
||||
href={translatePath('/contact')}
|
||||
class="nav-btn hover:text-black cursor-pointer uppercase font-bold"
|
||||
>
|
||||
Contact
|
||||
{t('Contact')}
|
||||
</a>
|
||||
<button
|
||||
id="back-to-top"
|
||||
class="flex items-center gap-1 font-bold text-black hover:opacity-60 transition-opacity cursor-pointer uppercase"
|
||||
>
|
||||
<span>Back to top</span>
|
||||
<span>{t('Back to top')}</span>
|
||||
<!-- <ArrowUp class="w-3.5 h-3.5" /> -->
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 text-[11px] font-mono text-zinc-400 uppercase tracking-widest">
|
||||
{t('© 2026 PORTFOLIO STUDIO. ALL RIGHTS RESERVED.')}
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user