feat: 全站20个模板添加国际化(i18n)支持 - 简体中文/繁体中文/英文三语切换

- 所有18套前端模板 + 管理后台共20个HTML文件添加i18n引擎
- 前端JavaScript方案:I18N_LOCALES翻译字典 + data-i18n属性 + t()函数调用
- 语言切换器UI:下拉菜单选择,匹配各模板主题风格
- 语言持久化:localStorage存储 + URL参数覆盖 + 浏览器语言自动检测
- onLangChange回调:语言切换时动态重新渲染所有内容
- 支持语言:zh-CN(简体中文) / zh-TW(繁体中文) / en(English)
This commit is contained in:
2026-09-09 16:58:40 +08:00
parent 69c59f4fbd
commit a84129330e
20 changed files with 9343 additions and 3855 deletions

View File

@@ -58,6 +58,34 @@ img { max-width:100%; display:block; }
.nav-actions svg { width:22px; height:22px; color:var(--text); }
.cart-badge { position:absolute; top:2px; right:2px; background:var(--primary); color:#fff; font-size:11px; font-weight:700; min-width:18px; height:18px; border-radius:9px; display:flex; align-items:center; justify-content:center; padding:0 4px; }
/* ========== 语言切换器 ========== */
.lang-switcher{position:relative;margin-left:8px}
.lang-btn{
display:flex;align-items:center;gap:6px;padding:8px 14px;border-radius:10px;
font-size:13px;font-weight:600;color:rgba(255,255,255,0.85);cursor:pointer;
background:rgba(255,255,255,0.06);border:1px solid rgba(255,255,255,0.1);
transition:all .25s cubic-bezier(.4,0,.2,1);
}
.lang-btn:hover{color:#fff;background:rgba(255,255,255,0.12);border-color:rgba(255,255,255,0.2)}
.lang-btn svg{width:16px;height:16px}
.lang-dropdown{
position:absolute;top:calc(100% + 8px);right:0;min-width:160px;
background:rgba(10,10,15,0.96);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);
border:1px solid rgba(255,255,255,0.12);border-radius:var(--radius-sm);
box-shadow:0 12px 40px rgba(0,0,0,0.5);overflow:hidden;
opacity:0;transform:translateY(-8px);pointer-events:none;
transition:all .25s cubic-bezier(.4,0,.2,1);z-index:1001;
}
.lang-dropdown.open{opacity:1;transform:none;pointer-events:auto}
.lang-option{
display:flex;align-items:center;gap:10px;padding:12px 16px;
font-size:14px;color:rgba(255,255,255,0.6);cursor:pointer;transition:all .2s;
}
.lang-option:hover{background:rgba(230,57,70,0.1);color:#fff}
.lang-option.active{color:var(--primary);font-weight:600}
.lang-option .check{width:16px;height:16px;opacity:0;transition:opacity .2s}
.lang-option.active .check{opacity:1}
/* ========== Hero 动态壁纸 ========== */
.hero { position:relative; width:100%; height:100vh; min-height:640px; overflow:hidden; background:#0a0a0f; }
#heroCanvas { position:absolute; inset:0; width:100%; height:100%; z-index:1; }
@@ -275,9 +303,9 @@ img { max-width:100%; display:block; }
<!-- 顶部公告栏 -->
<div class="topbar">
<span>🚀 全球直邮 · 满 $99 免运费</span>
<span>📦 7天无理由退换</span>
<span>🔒 安全支付 · <a href="#">查看详情</a></span>
<span data-i18n="topbar_shipping">🚀 全球直邮 · 满 $99 免运费</span>
<span data-i18n="topbar_return">📦 7天无理由退换</span>
<span data-i18n="topbar_payment">🔒 安全支付 · <a href="#" data-i18n="topbar_details">查看详情</a></span>
</div>
<!-- 导航栏 -->
@@ -288,23 +316,44 @@ img { max-width:100%; display:block; }
GlobalShop
</div>
<ul class="nav-links">
<li><a href="#" class="active" onclick="resetPage();return false;">首页</a></li>
<li><a href="#products" onclick="scrollToProducts();return false;">所有商品</a></li>
<li><a href="#featured" onclick="loadFeatured();return false;">热卖推荐</a></li>
<li><a href="#deals" onclick="filterDeals();return false;">限时特惠</a></li>
<li><a href="#" class="active" onclick="resetPage();return false;" data-i18n="nav_home">首页</a></li>
<li><a href="#products" onclick="scrollToProducts();return false;" data-i18n="nav_all">所有商品</a></li>
<li><a href="#featured" onclick="loadFeatured();return false;" data-i18n="nav_featured">热卖推荐</a></li>
<li><a href="#deals" onclick="filterDeals();return false;" data-i18n="nav_deals">限时特惠</a></li>
</ul>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
<input type="text" id="searchInput" placeholder="搜索商品..." oninput="handleSearch(event)">
<input type="text" id="searchInput" data-i18n-placeholder="search_placeholder" placeholder="搜索商品..." oninput="handleSearch(event)">
</div>
<div class="nav-actions">
<button title="收藏">
<button title="收藏" data-i18n-title="btn_fav">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></svg>
</button>
<button title="购物车" onclick="toggleCart()">
<button data-i18n-title="btn_cart" onclick="toggleCart()">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/></svg>
<span class="cart-badge" id="cartBadge" style="display:none">0</span>
</button>
<div class="lang-switcher" id="langSwitcher">
<button class="lang-btn" id="langBtn" onclick="toggleLangDropdown()">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M2 12h20M12 2a15 15 0 0 1 0 20 15 15 0 0 1 0-20z"/></svg>
<span id="langLabel">中文</span>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:12px;height:12px"><path d="M6 9l6 6 6-6"/></svg>
</button>
<div class="lang-dropdown" id="langDropdown">
<div class="lang-option" data-lang="zh-CN" onclick="switchLang('zh-CN')">
<span>🇨🇳</span><span>简体中文</span>
<svg class="check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg>
</div>
<div class="lang-option" data-lang="zh-TW" onclick="switchLang('zh-TW')">
<span>🇹🇼</span><span>繁體中文</span>
<svg class="check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg>
</div>
<div class="lang-option" data-lang="en" onclick="switchLang('en')">
<span>🌐</span><span>English</span>
<svg class="check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg>
</div>
</div>
</div>
</div>
</div>
</nav>
@@ -315,25 +364,25 @@ img { max-width:100%; display:block; }
<div class="hero-overlay"></div>
<div class="hero-light" id="heroLight"></div>
<div class="hero-content">
<div class="hero-badge"><span class="dot"></span> 全球直邮 · 2026 新季上线</div>
<h1>全球好物<br><span class="accent">跨境直达</span></h1>
<p>精选 18+ 品类好物,覆盖智能电子、家居生活、时尚运动,全球直邮,品质保障。沉浸式购物体验,由此开启。</p>
<div class="hero-badge"><span class="dot"></span> <span data-i18n="hero_badge">全球直邮 · 2026 新季上线</span></div>
<h1><span data-i18n="hero_title_line1">全球好物</span><br><span class="accent" data-i18n="hero_title_line2">跨境直达</span></h1>
<p data-i18n="hero_desc">精选 18+ 品类好物,覆盖智能电子、家居生活、时尚运动,全球直邮,品质保障。沉浸式购物体验,由此开启。</p>
<div class="hero-actions">
<button class="hero-btn-primary" onclick="scrollToProducts()">
立即选购
<span data-i18n="hero_cta">立即选购</span>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:20px;height:20px"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
</button>
<button class="hero-btn-ghost" onclick="loadFeatured()">探索精选</button>
<button class="hero-btn-ghost" onclick="loadFeatured()" data-i18n="hero_explore">探索精选</button>
</div>
</div>
<div class="hero-stats">
<div class="hero-stat"><div class="num">18<span>+</span></div><div class="label">品类好物</div></div>
<div class="hero-stat"><div class="num">50<span>万</span></div><div class="label">全球用户</div></div>
<div class="hero-stat"><div class="num">99<span>%</span></div><div class="label">好评率</div></div>
<div class="hero-stat"><div class="num">7<span>天</span></div><div class="label">无理由退换</div></div>
<div class="hero-stat"><div class="num">18<span>+</span></div><div class="label" data-i18n="stat_categories">品类好物</div></div>
<div class="hero-stat"><div class="num">50<span data-i18n="stat_users_unit">万</span></div><div class="label" data-i18n="stat_users">全球用户</div></div>
<div class="hero-stat"><div class="num">99<span>%</span></div><div class="label" data-i18n="stat_rating">好评率</div></div>
<div class="hero-stat"><div class="num">7<span data-i18n="stat_return_unit">天</span></div><div class="label" data-i18n="stat_return">无理由退换</div></div>
</div>
<div class="hero-scroll">
<span>向下滚动</span>
<span data-i18n="scroll_hint">向下滚动</span>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M5 12l7 7 7-7"/></svg>
</div>
</div>
@@ -342,58 +391,58 @@ img { max-width:100%; display:block; }
<div class="features-strip">
<div class="feature-item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="3" width="15" height="13"/><path d="M16 8h4l3 3v5h-7V8z"/><circle cx="5.5" cy="18.5" r="2.5"/><circle cx="18.5" cy="18.5" r="2.5"/></svg>
<div><h4>全球直邮</h4><p>满 $99 免运费</p></div>
<div><h4 data-i18n="feat1_title">全球直邮</h4><p data-i18n="feat1_desc">满 $99 免运费</p></div>
</div>
<div class="feature-item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>
<div><h4>正品保障</h4><p>品牌授权直供</p></div>
<div><h4 data-i18n="feat2_title">正品保障</h4><p data-i18n="feat2_desc">品牌授权直供</p></div>
</div>
<div class="feature-item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg>
<div><h4>7天退换</h4><p>无理由退货</p></div>
<div><h4 data-i18n="feat3_title">7天退换</h4><p data-i18n="feat3_desc">无理由退货</p></div>
</div>
<div class="feature-item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="1" y="4" width="22" height="16" rx="2"/><line x1="1" y1="10" x2="23" y2="10"/></svg>
<div><h4>安全支付</h4><p>多渠道加密</p></div>
<div><h4 data-i18n="feat4_title">安全支付</h4><p data-i18n="feat4_desc">多渠道加密</p></div>
</div>
</div>
<!-- 商品列表区 -->
<div class="section" id="products">
<div class="section-header">
<div class="section-title">全部商品</div>
<div class="section-title" data-i18n="section_all_products">全部商品</div>
</div>
<div class="products-layout">
<!-- 侧边筛选 -->
<aside class="filters-sidebar">
<h3>筛选</h3>
<h3 data-i18n="filter_title">筛选</h3>
<div class="filter-group">
<h4>分类</h4>
<h4 data-i18n="filter_category">分类</h4>
<div id="categoryFilters"></div>
</div>
<div class="filter-group">
<h4>价格区间</h4>
<div class="filter-option"><input type="radio" name="price" id="p0" value="0" checked onchange="applyFilters()"><label for="p0">全部</label></div>
<h4 data-i18n="filter_price">价格区间</h4>
<div class="filter-option"><input type="radio" name="price" id="p0" value="0" checked onchange="applyFilters()"><label for="p0" data-i18n="price_all">全部</label></div>
<div class="filter-option"><input type="radio" name="price" id="p1" value="0-30" onchange="applyFilters()"><label for="p1">$0 - $30</label></div>
<div class="filter-option"><input type="radio" name="price" id="p2" value="30-60" onchange="applyFilters()"><label for="p2">$30 - $60</label></div>
<div class="filter-option"><input type="radio" name="price" id="p3" value="60-100" onchange="applyFilters()"><label for="p3">$60 - $100</label></div>
<div class="filter-option"><input type="radio" name="price" id="p4" value="100-9999" onchange="applyFilters()"><label for="p4">$100+</label></div>
</div>
<div class="filter-group">
<h4>排序</h4>
<div class="filter-option"><input type="radio" name="sort" id="s1" value="default" checked onchange="applyFilters()"><label for="s1">默认排序</label></div>
<div class="filter-option"><input type="radio" name="sort" id="s2" value="sales" onchange="applyFilters()"><label for="s2">销量优先</label></div>
<div class="filter-option"><input type="radio" name="sort" id="s3" value="price_asc" onchange="applyFilters()"><label for="s3">价格升序</label></div>
<div class="filter-option"><input type="radio" name="sort" id="s4" value="price_desc" onchange="applyFilters()"><label for="s4">价格降序</label></div>
<div class="filter-option"><input type="radio" name="sort" id="s5" value="rating" onchange="applyFilters()"><label for="s5">评分最高</label></div>
<h4 data-i18n="filter_sort">排序</h4>
<div class="filter-option"><input type="radio" name="sort" id="s1" value="default" checked onchange="applyFilters()"><label for="s1" data-i18n="sort_default">默认排序</label></div>
<div class="filter-option"><input type="radio" name="sort" id="s2" value="sales" onchange="applyFilters()"><label for="s2" data-i18n="sort_sales">销量优先</label></div>
<div class="filter-option"><input type="radio" name="sort" id="s3" value="price_asc" onchange="applyFilters()"><label for="s3" data-i18n="sort_price_asc">价格升序</label></div>
<div class="filter-option"><input type="radio" name="sort" id="s4" value="price_desc" onchange="applyFilters()"><label for="s4" data-i18n="sort_price_desc">价格降序</label></div>
<div class="filter-option"><input type="radio" name="sort" id="s5" value="rating" onchange="applyFilters()"><label for="s5" data-i18n="sort_rating">评分最高</label></div>
</div>
</aside>
<!-- 商品网格 -->
<div class="products-main">
<div class="products-toolbar">
<div class="products-count" id="productsCount">加载中...</div>
<div class="products-count" id="productsCount" data-i18n="loading">加载中...</div>
<div class="category-nav" style="margin:0;padding:0;overflow:visible;border:none" id="categoryChips"></div>
</div>
<div class="products-grid" id="productsGrid">
@@ -410,16 +459,16 @@ img { max-width:100%; display:block; }
<!-- 购物车抽屉 -->
<div class="cart-drawer" id="cartDrawer">
<div class="cart-header">
<h3>购物车 (<span id="cartCount">0</span>)</h3>
<h3><span data-i18n="cart_title">购物车</span> (<span id="cartCount">0</span>)</h3>
<button class="cart-close" onclick="toggleCart()">&times;</button>
</div>
<div class="cart-items" id="cartItems"></div>
<div class="cart-footer">
<div class="cart-total">
<span class="cart-total-label">合计</span>
<span class="cart-total-label" data-i18n="cart_total">合计</span>
<span class="cart-total-value" id="cartTotal">$0.00</span>
</div>
<button class="checkout-btn" id="checkoutBtn" onclick="checkout()">去结算</button>
<button class="checkout-btn" id="checkoutBtn" onclick="checkout()" data-i18n="cart_checkout">去结算</button>
</div>
</div>
@@ -431,7 +480,7 @@ img { max-width:100%; display:block; }
<!-- Toast -->
<div class="toast" id="toast">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
<span id="toastMsg">已加入购物车</span>
<span id="toastMsg" data-i18n="toast_added">已加入购物车</span>
</div>
<!-- 页脚 -->
@@ -439,42 +488,363 @@ img { max-width:100%; display:block; }
<div class="footer-content">
<div class="footer-brand">
<h3>GlobalShop</h3>
<p>全球好物,跨境直达。我们精选全球优质商品,为您提供安全、便捷、愉悦的跨境购物体验。覆盖智能电子、家居生活、时尚服饰、运动户外、美妆个护等品类。</p>
<p data-i18n="footer_desc">全球好物,跨境直达。我们精选全球优质商品,为您提供安全、便捷、愉悦的跨境购物体验。覆盖智能电子、家居生活、时尚服饰、运动户外、美妆个护等品类。</p>
</div>
<div class="footer-col">
<h4>购物指南</h4>
<h4 data-i18n="footer_guide">购物指南</h4>
<ul>
<li><a href="#">购物流程</a></li>
<li><a href="#">会员介绍</a></li>
<li><a href="#">常见问题</a></li>
<li><a href="#">联系客服</a></li>
<li><a href="#" data-i18n="footer_guide_process">购物流程</a></li>
<li><a href="#" data-i18n="footer_guide_member">会员介绍</a></li>
<li><a href="#" data-i18n="footer_guide_faq">常见问题</a></li>
<li><a href="#" data-i18n="footer_guide_support">联系客服</a></li>
</ul>
</div>
<div class="footer-col">
<h4>配送与退换</h4>
<h4 data-i18n="footer_shipping">配送与退换</h4>
<ul>
<li><a href="#">配送方式</a></li>
<li><a href="#">运费规则</a></li>
<li><a href="#">退换货政策</a></li>
<li><a href="#">退款说明</a></li>
<li><a href="#" data-i18n="footer_ship_methods">配送方式</a></li>
<li><a href="#" data-i18n="footer_ship_rules">运费规则</a></li>
<li><a href="#" data-i18n="footer_ship_return">退换货政策</a></li>
<li><a href="#" data-i18n="footer_ship_refund">退款说明</a></li>
</ul>
</div>
<div class="footer-col">
<h4>关于我们</h4>
<h4 data-i18n="footer_about">关于我们</h4>
<ul>
<li><a href="#">公司简介</a></li>
<li><a href="#">加入我们</a></li>
<li><a href="#">隐私政策</a></li>
<li><a href="#">用户协议</a></li>
<li><a href="#" data-i18n="footer_about_company">公司简介</a></li>
<li><a href="#" data-i18n="footer_about_join">加入我们</a></li>
<li><a href="#" data-i18n="footer_about_privacy">隐私政策</a></li>
<li><a href="#" data-i18n="footer_about_terms">用户协议</a></li>
</ul>
</div>
</div>
<div class="footer-bottom">
© 2026 GlobalShop. All rights reserved. 全球好物 · 跨境直达
© 2026 GlobalShop. All rights reserved. <span data-i18n="footer_suffix">全球好物 · 跨境直达</span>
</div>
</footer>
<script>
// ========== i18n 国际化引擎 ==========
const I18N_LOCALES = {
'zh-CN': {
topbar_shipping: '🚀 全球直邮 · 满 $99 免运费',
topbar_return: '📦 7天无理由退换',
topbar_payment: '🔒 安全支付 · ',
topbar_details: '查看详情',
nav_home: '首页',
nav_all: '所有商品',
nav_featured: '热卖推荐',
nav_deals: '限时特惠',
search_placeholder: '搜索商品...',
btn_fav: '收藏',
btn_cart: '购物车',
hero_badge: '全球直邮 · 2026 新季上线',
hero_title_line1: '全球好物',
hero_title_line2: '跨境直达',
hero_desc: '精选 18+ 品类好物,覆盖智能电子、家居生活、时尚运动,全球直邮,品质保障。沉浸式购物体验,由此开启。',
hero_cta: '立即选购',
hero_explore: '探索精选',
stat_categories: '品类好物',
stat_users: '全球用户',
stat_users_unit: '万',
stat_rating: '好评率',
stat_return: '无理由退换',
stat_return_unit: '天',
scroll_hint: '向下滚动',
feat1_title: '全球直邮',
feat1_desc: '满 $99 免运费',
feat2_title: '正品保障',
feat2_desc: '品牌授权直供',
feat3_title: '7天退换',
feat3_desc: '无理由退货',
feat4_title: '安全支付',
feat4_desc: '多渠道加密',
section_all_products: '全部商品',
filter_title: '筛选',
filter_category: '分类',
filter_price: '价格区间',
price_all: '全部',
filter_sort: '排序',
sort_default: '默认排序',
sort_sales: '销量优先',
sort_price_asc: '价格升序',
sort_price_desc: '价格降序',
sort_rating: '评分最高',
category_all: '全部分类',
category_all_chips: '全部',
loading: '加载中...',
cart_title: '购物车',
cart_total: '合计',
cart_checkout: '去结算',
cart_empty: '购物车是空的',
cart_empty_hint: '快去挑选心仪的商品吧',
cart_remove: '删除',
toast_added: '已加入购物车',
toast_adding: '正在加入...',
toast_add_fail: '添加失败',
toast_deals: '正在筛选限时特惠商品...',
toast_featured_fail: '加载推荐失败',
toast_order_submitted: '订单已提交! 合计 ',
error_load: '商品加载失败,请稍后重试',
error_no_match: '未找到匹配的商品',
error_detail_fail: '商品信息加载失败',
product_sold: '已售 ',
product_save: '省 ',
product_rating: '评分',
product_sales: '销量',
product_stock: '库存',
product_stock_unit: ' 件',
modal_add_cart: '加入购物车',
featured_prefix: '热卖推荐 · ',
footer_desc: '全球好物,跨境直达。我们精选全球优质商品,为您提供安全、便捷、愉悦的跨境购物体验。覆盖智能电子、家居生活、时尚服饰、运动户外、美妆个护等品类。',
footer_guide: '购物指南',
footer_guide_process: '购物流程',
footer_guide_member: '会员介绍',
footer_guide_faq: '常见问题',
footer_guide_support: '联系客服',
footer_shipping: '配送与退换',
footer_ship_methods: '配送方式',
footer_ship_rules: '运费规则',
footer_ship_return: '退换货政策',
footer_ship_refund: '退款说明',
footer_about: '关于我们',
footer_about_company: '公司简介',
footer_about_join: '加入我们',
footer_about_privacy: '隐私政策',
footer_about_terms: '用户协议',
footer_suffix: '全球好物 · 跨境直达'
},
'en': {
topbar_shipping: '🚀 Global Direct · Free Shipping on $99+',
topbar_return: '📦 7-Day Free Returns',
topbar_payment: '🔒 Secure Payment · ',
topbar_details: 'View Details',
nav_home: 'Home',
nav_all: 'All Products',
nav_featured: 'Top Picks',
nav_deals: 'Flash Deals',
search_placeholder: 'Search products...',
btn_fav: 'Favorites',
btn_cart: 'Cart',
hero_badge: 'Global Direct · 2026 New Season',
hero_title_line1: 'Global Finds',
hero_title_line2: 'Delivered Worldwide',
hero_desc: 'Explore 18+ categories covering smart electronics, home & living, fashion & sports. Global shipping with quality guarantee. Start your immersive shopping journey.',
hero_cta: 'Shop Now',
hero_explore: 'Explore Picks',
stat_categories: 'Categories',
stat_users: 'Global Users',
stat_users_unit: '',
stat_rating: 'Satisfaction',
stat_return: 'Free Returns',
stat_return_unit: 'Day',
scroll_hint: 'Scroll Down',
feat1_title: 'Global Shipping',
feat1_desc: 'Free on orders $99+',
feat2_title: 'Authentic Guarantee',
feat2_desc: 'Brand authorized',
feat3_title: '7-Day Returns',
feat3_desc: 'No questions asked',
feat4_title: 'Secure Payment',
feat4_desc: 'Multi-channel encryption',
section_all_products: 'All Products',
filter_title: 'Filters',
filter_category: 'Category',
filter_price: 'Price Range',
price_all: 'All',
filter_sort: 'Sort By',
sort_default: 'Default',
sort_sales: 'Best Selling',
sort_price_asc: 'Price: Low to High',
sort_price_desc: 'Price: High to Low',
sort_rating: 'Top Rated',
category_all: 'All Categories',
category_all_chips: 'All',
loading: 'Loading...',
cart_title: 'Cart',
cart_total: 'Total',
cart_checkout: 'Checkout',
cart_empty: 'Your cart is empty',
cart_empty_hint: 'Start adding some items!',
cart_remove: 'Remove',
toast_added: 'Added to cart',
toast_adding: 'Adding...',
toast_add_fail: 'Failed to add',
toast_deals: 'Filtering flash deals...',
toast_featured_fail: 'Failed to load recommendations',
toast_order_submitted: 'Order submitted! Total: ',
error_load: 'Failed to load products, please try again',
error_no_match: 'No matching products found',
error_detail_fail: 'Failed to load product details',
product_sold: 'Sold ',
product_save: 'Save ',
product_rating: 'Rating',
product_sales: 'Sales',
product_stock: 'Stock',
product_stock_unit: '',
modal_add_cart: 'Add to Cart',
featured_prefix: 'Top Picks · ',
footer_desc: 'Global finds, delivered worldwide. We curate quality products from around the world for a safe, convenient, and enjoyable cross-border shopping experience. Covering smart electronics, home & living, fashion, sports, beauty, and more.',
footer_guide: 'Shopping Guide',
footer_guide_process: 'How to Order',
footer_guide_member: 'Membership',
footer_guide_faq: 'FAQ',
footer_guide_support: 'Contact Support',
footer_shipping: 'Shipping & Returns',
footer_ship_methods: 'Shipping Methods',
footer_ship_rules: 'Shipping Policy',
footer_ship_return: 'Return Policy',
footer_ship_refund: 'Refund Info',
footer_about: 'About Us',
footer_about_company: 'Company Profile',
footer_about_join: 'Join Us',
footer_about_privacy: 'Privacy Policy',
footer_about_terms: 'Terms of Service',
footer_suffix: 'Global Finds · Delivered Worldwide'
},
'zh-TW': {
topbar_shipping: '🚀 全球直郵 · 滿 $99 免運費',
topbar_return: '📦 7天無理由退換',
topbar_payment: '🔒 安全支付 · ',
topbar_details: '查看詳情',
nav_home: '首頁',
nav_all: '所有商品',
nav_featured: '熱賣推薦',
nav_deals: '限時特惠',
search_placeholder: '搜尋商品...',
btn_fav: '收藏',
btn_cart: '購物車',
hero_badge: '全球直郵 · 2026 新季上線',
hero_title_line1: '全球好物',
hero_title_line2: '跨境直達',
hero_desc: '精選 18+ 品類好物,涵蓋智慧電子、居家生活、時尚運動,全球直郵,品質保障。沉浸式購物體驗,由此開啟。',
hero_cta: '立即選購',
hero_explore: '探索精選',
stat_categories: '品類好物',
stat_users: '全球用戶',
stat_users_unit: '萬',
stat_rating: '好評率',
stat_return: '無理由退換',
stat_return_unit: '天',
scroll_hint: '向下捲動',
feat1_title: '全球直郵',
feat1_desc: '滿 $99 免運費',
feat2_title: '正品保障',
feat2_desc: '品牌授權直供',
feat3_title: '7天退換',
feat3_desc: '無理由退貨',
feat4_title: '安全支付',
feat4_desc: '多渠道加密',
section_all_products: '全部商品',
filter_title: '篩選',
filter_category: '分類',
filter_price: '價格區間',
price_all: '全部',
filter_sort: '排序',
sort_default: '預設排序',
sort_sales: '銷量優先',
sort_price_asc: '價格升序',
sort_price_desc: '價格降序',
sort_rating: '評分最高',
category_all: '全部分類',
category_all_chips: '全部',
loading: '載入中...',
cart_title: '購物車',
cart_total: '合計',
cart_checkout: '去結算',
cart_empty: '購物車是空的',
cart_empty_hint: '快去挑選心儀的商品吧',
cart_remove: '刪除',
toast_added: '已加入購物車',
toast_adding: '正在加入...',
toast_add_fail: '新增失敗',
toast_deals: '正在篩選限時特惠商品...',
toast_featured_fail: '載入推薦失敗',
toast_order_submitted: '訂單已提交! 合計 ',
error_load: '商品載入失敗,請稍後重試',
error_no_match: '未找到相符的商品',
error_detail_fail: '商品資訊載入失敗',
product_sold: '已售 ',
product_save: '省 ',
product_rating: '評分',
product_sales: '銷量',
product_stock: '庫存',
product_stock_unit: ' 件',
modal_add_cart: '加入購物車',
featured_prefix: '熱賣推薦 · ',
footer_desc: '全球好物,跨境直達。我們精選全球優質商品,為您提供安全、便捷、愉悅的跨境購物體驗。涵蓋智慧電子、居家生活、時尚服飾、運動戶外、美妝個護等品類。',
footer_guide: '購物指南',
footer_guide_process: '購物流程',
footer_guide_member: '會員註冊',
footer_guide_faq: '常見問題',
footer_guide_support: '聯絡客服',
footer_shipping: '配送與退換',
footer_ship_methods: '配送方式',
footer_ship_rules: '運費規則',
footer_ship_return: '退換貨政策',
footer_ship_refund: '退款說明',
footer_about: '關於我們',
footer_about_company: '公司簡介',
footer_about_join: '加入我們',
footer_about_privacy: '隱私政策',
footer_about_terms: '用戶協議',
footer_suffix: '全球好物 · 跨境直達'
}
};
const LANG_LABELS = {'zh-CN':'中文','en':'EN','zh-TW':'繁中'};
let currentLang = (function(){
try{
var u=new URLSearchParams(location.search).get('lang');
if(u&&I18N_LOCALES[u]) return u;
}catch(e){}
try{var s=localStorage.getItem('lang');if(s&&I18N_LOCALES[s]) return s;}catch(e){}
var b=navigator.language||navigator.userLanguage||'';
if(b.startsWith('zh-TW')||b.startsWith('zh-Hant')) return 'zh-TW';
if(b.startsWith('zh')) return 'zh-CN';
return 'en';
})();
function t(key){return (I18N_LOCALES[currentLang]&&I18N_LOCALES[currentLang][key])||(I18N_LOCALES['zh-CN'][key])||key;}
function applyI18n(){
document.querySelectorAll('[data-i18n]').forEach(function(el){
var key=el.getAttribute('data-i18n');
el.textContent=t(key);
});
document.querySelectorAll('[data-i18n-placeholder]').forEach(function(el){
var key=el.getAttribute('data-i18n-placeholder');
el.placeholder=t(key);
});
document.querySelectorAll('[data-i18n-title]').forEach(function(el){
var key=el.getAttribute('data-i18n-title');
el.title=t(key);
});
document.documentElement.lang=currentLang==='zh-TW'?'zh-TW':currentLang==='zh-CN'?'zh-CN':'en';
document.getElementById('langLabel').textContent=LANG_LABELS[currentLang]||currentLang;
document.querySelectorAll('.lang-option').forEach(function(opt){
opt.classList.toggle('active',opt.getAttribute('data-lang')===currentLang);
});
}
function switchLang(lang){
if(!I18N_LOCALES[lang]) return;
currentLang=lang;
try{localStorage.setItem('lang',lang);}catch(e){}
applyI18n();
if(typeof onLangChange==='function') onLangChange(lang);
document.getElementById('langDropdown').classList.remove('open');
}
function toggleLangDropdown(){
document.getElementById('langDropdown').classList.toggle('open');
}
document.addEventListener('click',function(e){
var sw=document.getElementById('langSwitcher');
if(sw&&!sw.contains(e.target)) document.getElementById('langDropdown').classList.remove('open');
});
// ========== 全局状态 ==========
let allCategories = [];
let currentPage = 1;
@@ -504,7 +874,7 @@ function productIconSVG(catId) {
async function fetchJSON(url) {
const res = await fetch(url);
const json = await res.json();
if (json.code !== 0) throw new Error(json.message || '请求失败');
if (json.code !== 0) throw new Error(json.message || t('error_load'));
return json.data;
}
@@ -513,7 +883,7 @@ async function loadCategories() {
allCategories = await fetchJSON('/api/v1/shop/categories');
renderCategoryFilters();
renderCategoryChips();
} catch(e) { console.error('分类加载失败:', e); }
} catch(e) { console.error('Categories load failed:', e); }
}
async function loadProducts() {
@@ -539,7 +909,7 @@ async function loadProducts() {
renderPagination(data.total || 0);
} catch(e) {
grid.innerHTML = `<div style="grid-column:1/-1;text-align:center;padding:60px;color:var(--text-muted)">
<p>商品加载失败,请稍后重试</p></div>`;
<p>${t('error_load')}</p></div>`;
}
}
@@ -549,7 +919,7 @@ function renderCategoryFilters() {
container.innerHTML = `
<div class="filter-option">
<input type="radio" name="cat-filter" id="cat0" value="0" checked onchange="selectCategory(0)">
<label for="cat0">全部分类</label>
<label for="cat0">${t('category_all')}</label>
</div>
` + allCategories.map(c => `
<div class="filter-option">
@@ -561,18 +931,18 @@ function renderCategoryFilters() {
function renderCategoryChips() {
const container = document.getElementById('categoryChips');
container.innerHTML = `<span class="category-chip ${selectedCategory === 0 ? 'active' : ''}" onclick="selectCategory(0)">全部</span>` +
container.innerHTML = `<span class="category-chip ${selectedCategory === 0 ? 'active' : ''}" onclick="selectCategory(0)">${t('category_all_chips')}</span>` +
allCategories.map(c => `<span class="category-chip ${selectedCategory === c.id ? 'active' : ''}" onclick="selectCategory(${c.id})">${c.name}</span>`).join('');
}
function renderProducts(products, total) {
const grid = document.getElementById('productsGrid');
document.getElementById('productsCount').textContent = `共 ${total} 件商品`;
document.getElementById('productsCount').textContent = `${t('section_all_products')} ${total} 件商品`;
if (products.length === 0) {
grid.innerHTML = `<div style="grid-column:1/-1;text-align:center;padding:60px;color:var(--text-muted)">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" style="width:48px;height:48px;margin:0 auto 12px;opacity:0.3"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
<p>未找到匹配的商品</p></div>`;
<p>${t('error_no_match')}</p></div>`;
return;
}
@@ -581,7 +951,7 @@ function renderProducts(products, total) {
let tags = [];
try { tags = JSON.parse(p.tags || '[]'); } catch(e) {}
const tagHtml = tags.slice(0, 2).map(t => {
const cls = t.includes('热') || t.includes('爆') ? 'tag-hot' : t.includes('新') ? 'tag-new' : 'tag-sale';
const cls = t.includes('热') || t.includes('爆') || t.includes('Hot') ? 'tag-hot' : t.includes('新') || t.includes('New') ? 'tag-new' : 'tag-sale';
return `<span class="product-tag ${cls}">${t}</span>`;
}).join('');
@@ -606,10 +976,10 @@ function renderProducts(products, total) {
<span class="stars">${'★'.repeat(Math.round(p.rating))}</span>
<span>${p.rating.toFixed(1)}</span>
</div>
<span class="product-sales">已售 ${formatSales(p.sales)}</span>
<span class="product-sales">${t('product_sold')}${formatSales(p.sales)}</span>
</div>
</div>
<button class="add-cart-btn" onclick="event.stopPropagation();addToCart(${p.id})" title="加入购物车">
<button class="add-cart-btn" onclick="event.stopPropagation();addToCart(${p.id})" title="${t('modal_add_cart')}">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/></svg>
</button>
</div>`;
@@ -648,10 +1018,6 @@ function selectCategory(catId) {
}
function applyFilters() {
// 侧边栏排序与价格筛选
const priceRadio = document.querySelector('input[name="price"]:checked');
const sortRadio = document.querySelector('input[name="sort"]:checked');
// 同步排序到主工具栏的 sort
currentPage = 1;
loadProducts();
}
@@ -684,9 +1050,8 @@ function resetPage() {
}
function filterDeals() {
// 筛选有折扣的商品(通过搜索价格降序再筛选不太精确,这里简单跳转)
scrollToProducts();
showToast('正在筛选限时特惠商品...');
showToast(t('toast_deals'));
}
async function loadFeatured() {
@@ -694,16 +1059,16 @@ async function loadFeatured() {
const products = await fetchJSON('/api/v1/shop/featured?limit=8');
renderProducts(products, products.length);
document.getElementById('pagination').innerHTML = '';
document.getElementById('productsCount').textContent = `热卖推荐 · ${products.length} 件商品`;
document.getElementById('productsCount').textContent = `${t('featured_prefix')}${products.length} 件商品`;
document.getElementById('products').scrollIntoView({ behavior: 'smooth' });
} catch(e) { showToast('加载推荐失败'); }
} catch(e) { showToast(t('toast_featured_fail')); }
}
// ========== 商品详情 ==========
async function showProductDetail(id) {
const modal = document.getElementById('productModal');
const content = document.getElementById('modalContent');
content.innerHTML = '<div style="padding:60px;text-align:center;color:var(--text-muted)">加载中...</div>';
content.innerHTML = `<div style="padding:60px;text-align:center;color:var(--text-muted)">${t('loading')}</div>`;
modal.classList.add('open');
try {
@@ -729,21 +1094,21 @@ async function showProductDetail(id) {
<div class="modal-price">
<span class="price-current" style="color:var(--primary);font-size:32px;font-weight:800">$${p.price.toFixed(2)}</span>
${p.original_price > p.price ? `<span class="price-original" style="font-size:16px;color:var(--text-muted);text-decoration:line-through">$${p.original_price.toFixed(2)}</span>` : ''}
${discount > 0 ? `<span style="background:var(--primary);color:#fff;padding:2px 8px;border-radius:4px;font-size:12px;font-weight:700">省 ${discount}%</span>` : ''}
${discount > 0 ? `<span style="background:var(--primary);color:#fff;padding:2px 8px;border-radius:4px;font-size:12px;font-weight:700">${t('product_save')}${discount}%</span>` : ''}
</div>
<div class="modal-tags">${tags.map(t => `<span class="product-tag ${t.includes('热') ? 'tag-hot' : t.includes('新') ? 'tag-new' : 'tag-sale'}">${t}</span>`).join('')}</div>
<div class="modal-tags">${tags.map(t => `<span class="product-tag ${t.includes('热') || t.includes('Hot') ? 'tag-hot' : t.includes('新') || t.includes('New') ? 'tag-new' : 'tag-sale'}">${t}</span>`).join('')}</div>
<p class="modal-desc">${p.description}</p>
<div class="modal-meta">
<div class="modal-meta-item"><span>评分</span><span>${p.rating.toFixed(1)} ★</span></div>
<div class="modal-meta-item"><span>销量</span><span>${formatSales(p.sales)}</span></div>
<div class="modal-meta-item"><span>库存</span><span>${p.stock} 件</span></div>
<div class="modal-meta-item"><span>${t('product_rating')}</span><span>${p.rating.toFixed(1)} ★</span></div>
<div class="modal-meta-item"><span>${t('product_sales')}</span><span>${formatSales(p.sales)}</span></div>
<div class="modal-meta-item"><span>${t('product_stock')}</span><span>${p.stock}${t('product_stock_unit')}</span></div>
</div>
<button class="modal-add-cart" onclick="addToCart(${p.id});closeModal()">加入购物车</button>
<button class="modal-add-cart" onclick="addToCart(${p.id});closeModal()">${t('modal_add_cart')}</button>
</div>
</div>
`;
} catch(e) {
content.innerHTML = '<div style="padding:60px;text-align:center;color:var(--text-muted)">商品信息加载失败</div>';
content.innerHTML = `<div style="padding:60px;text-align:center;color:var(--text-muted)">${t('error_detail_fail')}</div>`;
}
}
@@ -761,7 +1126,7 @@ async function addToCart(productId) {
try {
product = await fetchJSON(`/api/v1/shop/products/${productId}`);
productCache[productId] = product;
} catch(e) { showToast('添加失败'); return; }
} catch(e) { showToast(t('toast_add_fail')); return; }
}
const existing = cart.find(i => i.id === productId);
@@ -771,7 +1136,7 @@ async function addToCart(productId) {
cart.push({ id: productId, name: product.name, price: product.price, qty: 1, category_id: product.category_id, image: product.image || '' });
}
updateCartUI();
showToast(`已加入购物车: ${product.name.substring(0, 15)}...`);
showToast(`${t('toast_added')}: ${product.name.substring(0, 15)}...`);
}
function removeFromCart(productId) {
@@ -803,8 +1168,8 @@ function updateCartUI() {
container.innerHTML = `
<div class="cart-empty">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/></svg>
<p>购物车是空的</p>
<p style="font-size:13px;margin-top:8px">快去挑选心仪的商品吧</p>
<p>${t('cart_empty')}</p>
<p style="font-size:13px;margin-top:8px">${t('cart_empty_hint')}</p>
</div>`;
return;
}
@@ -819,7 +1184,7 @@ function updateCartUI() {
<button class="qty-btn" onclick="changeQty(${item.id}, -1)">-</button>
<span class="qty-value">${item.qty}</span>
<button class="qty-btn" onclick="changeQty(${item.id}, 1)">+</button>
<span class="cart-item-remove" onclick="removeFromCart(${item.id})">删除</span>
<span class="cart-item-remove" onclick="removeFromCart(${item.id})">${t('cart_remove')}</span>
</div>
</div>
</div>
@@ -834,7 +1199,7 @@ function toggleCart() {
function checkout() {
if (cart.length === 0) return;
const total = cart.reduce((s, i) => s + i.price * i.qty, 0);
showToast(`订单已提交! 合计 $${total.toFixed(2)}`);
showToast(`${t('toast_order_submitted')}$${total.toFixed(2)}`);
cart = [];
updateCartUI();
toggleCart();
@@ -842,6 +1207,11 @@ function checkout() {
// ========== 工具函数 ==========
function formatSales(n) {
if (currentLang === 'en') {
if (n >= 1000000) return (n / 1000000).toFixed(1) + 'M';
if (n >= 1000) return (n / 1000).toFixed(1) + 'K';
return n.toString();
}
if (n >= 10000) return (n / 10000).toFixed(1) + '万';
return n.toString();
}
@@ -854,8 +1224,17 @@ function showToast(msg) {
window._toastTimer = setTimeout(() => toast.classList.remove('show'), 2500);
}
// ========== 语言切换回调:重新渲染动态内容 ==========
function onLangChange(){
applyI18n();
renderCategoryFilters();
renderCategoryChips();
loadProducts();
}
// ========== 初始化 ==========
async function init() {
applyI18n();
await loadCategories();
await loadProducts();
updateCartUI();
@@ -1100,7 +1479,6 @@ window.addEventListener('scroll', function() {
tiltEvt = e;
if (tiltRaf === null) tiltRaf = requestAnimationFrame(tiltUpdate);
});
// 鼠标真正离开卡片时恢复(relatedTarget 仍在卡片内则保持倾斜,避免子元素间移动闪烁)
document.addEventListener('mouseout', function(e){
var t = e.target;
if (t && t.classList && t.classList.contains('product-card')) {