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:
300
web/dist/music2/index.html
vendored
300
web/dist/music2/index.html
vendored
@@ -124,6 +124,34 @@ ul{list-style:none}
|
||||
font-size:0.9rem;width:100%;padding:4px 0;
|
||||
}
|
||||
.search-bar input::placeholder{color:var(--text-muted);font-style:italic}
|
||||
|
||||
/* Language switcher - vinyl theme */
|
||||
.lang-switcher{position:relative;flex-shrink:0}
|
||||
.lang-btn{
|
||||
display:flex;align-items:center;gap:6px;padding:6px 12px;
|
||||
border-radius:var(--radius);font-family:var(--mono);font-size:0.78rem;
|
||||
color:var(--text-secondary);cursor:pointer;
|
||||
background:rgba(26,20,16,0.6);border:1px solid var(--border-dim);
|
||||
transition:all 0.3s;letter-spacing:1px;
|
||||
}
|
||||
.lang-btn:hover{color:var(--gold);border-color:var(--border-warm)}
|
||||
.lang-btn .ico{font-size:14px}
|
||||
.lang-dropdown{
|
||||
position:absolute;top:calc(100% + 8px);right:0;min-width:150px;z-index:1001;
|
||||
background:var(--bg-card);border:1px solid var(--border-warm);border-radius:var(--radius);
|
||||
padding:6px;box-shadow:var(--shadow-deep),var(--glow-gold);
|
||||
opacity:0;visibility:hidden;transform:translateY(-8px);transition:.25s;
|
||||
}
|
||||
.lang-dropdown.open{opacity:1;visibility:visible;transform:translateY(0)}
|
||||
.lang-option{
|
||||
display:flex;align-items:center;gap:10px;padding:9px 14px;border-radius:4px;
|
||||
font-size:0.82rem;color:var(--text-secondary);cursor:pointer;transition:all 0.2s;
|
||||
}
|
||||
.lang-option:hover{background:rgba(212,160,23,0.08);color:var(--gold)}
|
||||
.lang-option.active{color:var(--gold);font-weight:bold}
|
||||
.lang-option .check{font-size:13px;opacity:0;transition:opacity 0.2s}
|
||||
.lang-option.active .check{opacity:1}
|
||||
|
||||
.header-fm{
|
||||
font-family:var(--mono);font-size:0.7rem;color:var(--gold-dim);
|
||||
letter-spacing:1px;border-left:1px solid var(--border-dim);padding-left:12px;
|
||||
@@ -837,19 +865,36 @@ ul{list-style:none}
|
||||
<div class="logo">
|
||||
<div class="logo-icon"></div>
|
||||
<div>
|
||||
<div class="logo-text">黑胶电台</div>
|
||||
<div class="logo-text" data-i18n="site_name">黑胶电台</div>
|
||||
<div class="logo-sub">VINYL RADIO</div>
|
||||
</div>
|
||||
</div>
|
||||
<nav class="nav">
|
||||
<a class="nav-link active" data-scroll="hero-section">首页</a>
|
||||
<a class="nav-link" data-scroll="library-section">唱片架</a>
|
||||
<a class="nav-link" data-scroll="hot-section">排行榜</a>
|
||||
<a class="nav-link" data-scroll="new-section">新碟</a>
|
||||
<a class="nav-link active" data-scroll="hero-section" data-i18n="nav_home">首页</a>
|
||||
<a class="nav-link" data-scroll="library-section" data-i18n="nav_library">唱片架</a>
|
||||
<a class="nav-link" data-scroll="hot-section" data-i18n="nav_hot">排行榜</a>
|
||||
<a class="nav-link" data-scroll="new-section" data-i18n="nav_new">新碟</a>
|
||||
</nav>
|
||||
<div class="search-bar">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
|
||||
<input type="text" id="header-search" placeholder="搜索歌曲、歌手...">
|
||||
<input type="text" id="header-search" data-i18n-placeholder="search_placeholder" placeholder="搜索歌曲、歌手...">
|
||||
</div>
|
||||
<div class="lang-switcher" id="langSwitcher">
|
||||
<button class="lang-btn" id="langBtn" onclick="toggleLangDropdown()">
|
||||
<span class="ico">🌐</span>
|
||||
<span id="langLabel">中文</span>
|
||||
</button>
|
||||
<div class="lang-dropdown" id="langDropdown">
|
||||
<div class="lang-option" data-lang="zh-CN" onclick="switchLang('zh-CN')">
|
||||
<span class="check">✓</span><span>简体中文</span>
|
||||
</div>
|
||||
<div class="lang-option" data-lang="zh-TW" onclick="switchLang('zh-TW')">
|
||||
<span class="check">✓</span><span>繁體中文</span>
|
||||
</div>
|
||||
<div class="lang-option" data-lang="en" onclick="switchLang('en')">
|
||||
<span class="check">✓</span><span>English</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-fm">
|
||||
<span>FM 88.8</span>
|
||||
@@ -865,23 +910,23 @@ ul{list-style:none}
|
||||
</div>
|
||||
<div class="container hero-inner">
|
||||
<div class="hero-content">
|
||||
<div class="hero-tag">经典之声 · 原音重现</div>
|
||||
<h1 class="hero-title">聆听<span class="accent">经典</span>之声<br>重温黑胶岁月</h1>
|
||||
<p class="hero-subtitle">在数字时代里,保留一份模拟的温暖。<br>每一张唱片,都是一段值得珍藏的时光记忆。</p>
|
||||
<div class="hero-tag" data-i18n="hero_tag">经典之声 · 原音重现</div>
|
||||
<h1 class="hero-title"><span data-i18n="hero_title_1">聆听</span><span class="accent" data-i18n="hero_title_2">经典</span><span data-i18n="hero_title_3">之声</span><br><span data-i18n="hero_title_4">重温黑胶岁月</span></h1>
|
||||
<p class="hero-subtitle" data-i18n="hero_subtitle">在数字时代里,保留一份模拟的温暖。<br>每一张唱片,都是一段值得珍藏的时光记忆。</p>
|
||||
<div class="hero-actions">
|
||||
<button class="btn-primary" id="hero-play-btn">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor"><polygon points="5 3 19 12 5 21 5 3"/></svg>
|
||||
开始播放
|
||||
<span data-i18n="hero_play">开始播放</span>
|
||||
</button>
|
||||
<button class="btn-ghost" data-scroll="library-section">
|
||||
浏览曲库
|
||||
<span data-i18n="hero_browse">浏览曲库</span>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="hero-stats" id="hero-stats">
|
||||
<div class="hero-stat"><div class="num">—</div><div class="label">唱片收藏</div></div>
|
||||
<div class="hero-stat"><div class="num">—</div><div class="label">艺术家</div></div>
|
||||
<div class="hero-stat"><div class="num">—</div><div class="label">总播放量</div></div>
|
||||
<div class="hero-stat"><div class="num">—</div><div class="label" data-i18n="stat_records">唱片收藏</div></div>
|
||||
<div class="hero-stat"><div class="num">—</div><div class="label" data-i18n="stat_artists">艺术家</div></div>
|
||||
<div class="hero-stat"><div class="num">—</div><div class="label" data-i18n="stat_plays">总播放量</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero-vinyl-wrap">
|
||||
@@ -892,7 +937,7 @@ ul{list-style:none}
|
||||
</div>
|
||||
<div class="hero-vinyl">
|
||||
<div class="vinyl-label">
|
||||
<div class="lbl-title">黑胶电台</div>
|
||||
<div class="lbl-title" data-i18n="vinyl_label">黑胶电台</div>
|
||||
<div class="lbl-sub">SIDE A</div>
|
||||
<div class="lbl-rpm">33⅓ RPM</div>
|
||||
</div>
|
||||
@@ -910,7 +955,7 @@ ul{list-style:none}
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 6h18M3 12h18M3 18h18"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="section-title">音乐分类</div>
|
||||
<div class="section-title" data-i18n="section_genres">音乐分类</div>
|
||||
<div class="section-subtitle">GENRES</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -933,11 +978,11 @@ ul{list-style:none}
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="section-title">热门金曲</div>
|
||||
<div class="section-title" data-i18n="section_hot">热门金曲</div>
|
||||
<div class="section-subtitle">HOT CHART</div>
|
||||
</div>
|
||||
</div>
|
||||
<a class="section-more" data-scroll="library-section">查看全部 →</a>
|
||||
<a class="section-more" data-scroll="library-section"><span data-i18n="see_all">查看全部</span> →</a>
|
||||
</div>
|
||||
<div class="section-divider"></div>
|
||||
<div class="chart-list" id="hot-list">
|
||||
@@ -958,11 +1003,11 @@ ul{list-style:none}
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="section-title">新碟速递</div>
|
||||
<div class="section-title" data-i18n="section_new">新碟速递</div>
|
||||
<div class="section-subtitle">NEW RELEASES</div>
|
||||
</div>
|
||||
</div>
|
||||
<a class="section-more" data-scroll="library-section">查看全部 →</a>
|
||||
<a class="section-more" data-scroll="library-section"><span data-i18n="see_all">查看全部</span> →</a>
|
||||
</div>
|
||||
<div class="section-divider"></div>
|
||||
<div class="album-grid" id="new-grid">
|
||||
@@ -983,7 +1028,7 @@ ul{list-style:none}
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="section-title">音乐图书馆</div>
|
||||
<div class="section-title" data-i18n="section_library">音乐图书馆</div>
|
||||
<div class="section-subtitle">MUSIC LIBRARY</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -992,19 +1037,19 @@ ul{list-style:none}
|
||||
<div class="library-controls reveal">
|
||||
<div class="library-search">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
|
||||
<input type="text" id="library-search" placeholder="搜索歌曲、歌手、专辑...">
|
||||
<input type="text" id="library-search" data-i18n-placeholder="search_placeholder_lib" placeholder="搜索歌曲、歌手、专辑...">
|
||||
</div>
|
||||
<select class="sort-select" id="sort-select">
|
||||
<option value="">默认排序</option>
|
||||
<option value="plays">播放量最高</option>
|
||||
<option value="likes">收藏最多</option>
|
||||
<option value="newest">最新发布</option>
|
||||
<option value="" data-i18n="sort_default">默认排序</option>
|
||||
<option value="plays" data-i18n="sort_plays">播放量最高</option>
|
||||
<option value="likes" data-i18n="sort_likes">收藏最多</option>
|
||||
<option value="newest" data-i18n="sort_newest">最新发布</option>
|
||||
</select>
|
||||
<div class="library-count" id="library-count">— 首歌曲</div>
|
||||
<div class="library-count" id="library-count">— <span data-i18n="tracks_unit">首歌曲</span></div>
|
||||
</div>
|
||||
<div class="library-table" id="library-table">
|
||||
<div class="library-table-head">
|
||||
<div>#</div><div></div><div>歌曲</div><div>艺术家</div><div>播放量</div><div style="text-align:right">时长</div><div></div>
|
||||
<div>#</div><div></div><div data-i18n="col_song">歌曲</div><div data-i18n="col_artist">艺术家</div><div data-i18n="col_plays">播放量</div><div style="text-align:right" data-i18n="col_duration">时长</div><div></div>
|
||||
</div>
|
||||
<div id="library-rows">
|
||||
<div class="skel-lib-row"><div class="skeleton" style="width:20px;height:14px"></div><div class="skeleton skel-lib-cover"></div><div class="skeleton skel-line"></div><div class="skeleton skel-line short"></div><div class="skeleton" style="width:60px;height:12px"></div><div class="skeleton" style="width:30px;height:12px"></div><div></div></div>
|
||||
@@ -1023,40 +1068,40 @@ ul{list-style:none}
|
||||
<div class="footer-brand">
|
||||
<div class="logo">
|
||||
<div class="logo-icon"></div>
|
||||
<div class="logo-text">黑胶电台</div>
|
||||
<div class="logo-text" data-i18n="site_name">黑胶电台</div>
|
||||
</div>
|
||||
<p class="footer-desc">在数字洪流中,留存一份模拟的温度。每一道纹路,都是时光的低语。</p>
|
||||
<p class="footer-desc" data-i18n="footer_desc">在数字洪流中,留存一份模拟的温度。每一道纹路,都是时光的低语。</p>
|
||||
</div>
|
||||
<div class="footer-col">
|
||||
<h4>探索</h4>
|
||||
<h4 data-i18n="footer_explore">探索</h4>
|
||||
<ul>
|
||||
<li><a data-scroll="hot-section">热门金曲</a></li>
|
||||
<li><a data-scroll="new-section">新碟速递</a></li>
|
||||
<li><a data-scroll="library-section">音乐图书馆</a></li>
|
||||
<li><a data-scroll="category-section">分类浏览</a></li>
|
||||
<li><a data-scroll="hot-section" data-i18n="nav_hot">热门金曲</a></li>
|
||||
<li><a data-scroll="new-section" data-i18n="section_new">新碟速递</a></li>
|
||||
<li><a data-scroll="library-section" data-i18n="section_library">音乐图书馆</a></li>
|
||||
<li><a data-scroll="category-section" data-i18n="section_genres">分类浏览</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer-col">
|
||||
<h4>关于</h4>
|
||||
<h4 data-i18n="footer_about">关于</h4>
|
||||
<ul>
|
||||
<li><a>关于我们</a></li>
|
||||
<li><a>使用条款</a></li>
|
||||
<li><a>隐私政策</a></li>
|
||||
<li><a>版权声明</a></li>
|
||||
<li><a data-i18n="footer_about_us">关于我们</a></li>
|
||||
<li><a data-i18n="footer_terms">使用条款</a></li>
|
||||
<li><a data-i18n="footer_privacy">隐私政策</a></li>
|
||||
<li><a data-i18n="footer_copyright">版权声明</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer-col">
|
||||
<h4>连接</h4>
|
||||
<h4 data-i18n="footer_connect">连接</h4>
|
||||
<ul>
|
||||
<li><a>意见反馈</a></li>
|
||||
<li><a>商务合作</a></li>
|
||||
<li><a>加入我们</a></li>
|
||||
<li><a>开发者API</a></li>
|
||||
<li><a data-i18n="footer_feedback">意见反馈</a></li>
|
||||
<li><a data-i18n="footer_business">商务合作</a></li>
|
||||
<li><a data-i18n="footer_join">加入我们</a></li>
|
||||
<li><a data-i18n="footer_api">开发者API</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-bottom">
|
||||
<div class="footer-copy">© 2026 VINYL RADIO · 黑胶电台 · 保留所有权利</div>
|
||||
<div class="footer-copy">© 2026 VINYL RADIO · <span data-i18n="site_name">黑胶电台</span> · <span data-i18n="footer_rights">保留所有权利</span></div>
|
||||
<div class="footer-decor">♫ ♪ ♩ ♬</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1115,7 +1160,7 @@ ul{list-style:none}
|
||||
</button>
|
||||
</div>
|
||||
<div class="np-lyrics-wrap">
|
||||
<div class="np-lyrics-title">歌词</div>
|
||||
<div class="np-lyrics-title" data-i18n="lyrics_title">歌词</div>
|
||||
<div class="np-lyrics" id="np-lyrics">暂无歌词</div>
|
||||
</div>
|
||||
<div class="np-footer">
|
||||
@@ -1143,6 +1188,129 @@ ul{list-style:none}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/* ============================================================
|
||||
i18n 国际化引擎
|
||||
============================================================ */
|
||||
const I18N_LOCALES = {
|
||||
'zh-CN': {
|
||||
title: '黑胶电台 - 复古音乐平台', site_name: '黑胶电台',
|
||||
nav_home: '首页', nav_library: '唱片架', nav_hot: '排行榜', nav_new: '新碟',
|
||||
search_placeholder: '搜索歌曲、歌手...', search_placeholder_lib: '搜索歌曲、歌手、专辑...',
|
||||
hero_tag: '经典之声 · 原音重现',
|
||||
hero_title_1: '聆听', hero_title_2: '经典', hero_title_3: '之声', hero_title_4: '重温黑胶岁月',
|
||||
hero_subtitle: '在数字时代里,保留一份模拟的温暖。每一张唱片,都是一段值得珍藏的时光记忆。',
|
||||
hero_play: '开始播放', hero_browse: '浏览曲库',
|
||||
stat_records: '唱片收藏', stat_artists: '艺术家', stat_plays: '总播放量',
|
||||
vinyl_label: '黑胶电台',
|
||||
section_genres: '音乐分类', section_hot: '热门金曲', section_new: '新碟速递', section_library: '音乐图书馆',
|
||||
see_all: '查看全部',
|
||||
col_song: '歌曲', col_artist: '艺术家', col_plays: '播放量', col_duration: '时长',
|
||||
sort_default: '默认排序', sort_plays: '播放量最高', sort_likes: '收藏最多', sort_newest: '最新发布',
|
||||
tracks_unit: '首歌曲', lyrics_title: '歌词', no_lyrics: '暂无歌词',
|
||||
ctrl_play: '播放/暂停', ctrl_expand: '展开', ctrl_collapse: '收起',
|
||||
cat_all: '全部', cat_pop: '流行', cat_rock: '摇滚', cat_jazz: '爵士',
|
||||
cat_classical: '古典', cat_folk: '民谣', cat_electronic: '电子', cat_rnb: 'R&B', cat_hiphop: '嘻哈',
|
||||
empty_categories: '暂无分类', empty_hot: '暂无热门歌曲', empty_new: '暂无新碟', empty_library: '未找到匹配的歌曲',
|
||||
toast_now_playing: '正在播放: ', toast_cannot_load: '无法加载歌曲信息', toast_loading: '歌曲加载中,请稍候...',
|
||||
toast_no_songs: '暂无可播放的歌曲', toast_select_song: '请先选择一首歌曲',
|
||||
toast_cancel_like: '已取消收藏', toast_add_like: '已加入收藏',
|
||||
toast_no_liked: '还没有收藏的歌曲,去点亮一颗心吧', toast_liked_count: '已收藏 ',
|
||||
footer_desc: '在数字洪流中,留存一份模拟的温度。每一道纹路,都是时光的低语。',
|
||||
footer_explore: '探索', footer_about: '关于', footer_connect: '连接',
|
||||
footer_about_us: '关于我们', footer_terms: '使用条款', footer_privacy: '隐私政策', footer_copyright: '版权声明',
|
||||
footer_feedback: '意见反馈', footer_business: '商务合作', footer_join: '加入我们', footer_api: '开发者API',
|
||||
footer_rights: '保留所有权利',
|
||||
},
|
||||
'zh-TW': {
|
||||
title: '黑膠電台 - 復古音樂平台', site_name: '黑膠電台',
|
||||
nav_home: '首頁', nav_library: '唱片架', nav_hot: '排行榜', nav_new: '新碟',
|
||||
search_placeholder: '搜尋歌曲、歌手...', search_placeholder_lib: '搜尋歌曲、歌手、專輯...',
|
||||
hero_tag: '經典之聲 · 原音重現',
|
||||
hero_title_1: '聆聽', hero_title_2: '經典', hero_title_3: '之聲', hero_title_4: '重溫黑膠歲月',
|
||||
hero_subtitle: '在數位時代裡,保留一份類比的溫度。每一張唱片,都是一段值得珍藏的時光記憶。',
|
||||
hero_play: '開始播放', hero_browse: '瀏覽曲庫',
|
||||
stat_records: '唱片收藏', stat_artists: '藝術家', stat_plays: '總播放量',
|
||||
vinyl_label: '黑膠電台',
|
||||
section_genres: '音樂分類', section_hot: '熱門金曲', section_new: '新碟速遞', section_library: '音樂圖書館',
|
||||
see_all: '查看全部',
|
||||
col_song: '歌曲', col_artist: '藝術家', col_plays: '播放量', col_duration: '時長',
|
||||
sort_default: '預設排序', sort_plays: '播放量最高', sort_likes: '收藏最多', sort_newest: '最新發布',
|
||||
tracks_unit: '首歌曲', lyrics_title: '歌詞', no_lyrics: '暫無歌詞',
|
||||
ctrl_play: '播放/暫停', ctrl_expand: '展開', ctrl_collapse: '收起',
|
||||
cat_all: '全部', cat_pop: '流行', cat_rock: '搖滾', cat_jazz: '爵士',
|
||||
cat_classical: '古典', cat_folk: '民謠', cat_electronic: '電子', cat_rnb: 'R&B', cat_hiphop: '嘻哈',
|
||||
empty_categories: '暫無分類', empty_hot: '暫無熱門歌曲', empty_new: '暫無新碟', empty_library: '未找到匹配的歌曲',
|
||||
toast_now_playing: '正在播放: ', toast_cannot_load: '無法載入歌曲資訊', toast_loading: '歌曲載入中,請稍候...',
|
||||
toast_no_songs: '暫無可播放的歌曲', toast_select_song: '請先選擇一首歌曲',
|
||||
toast_cancel_like: '已取消收藏', toast_add_like: '已加入收藏',
|
||||
toast_no_liked: '還沒有收藏的歌曲,去點亮一顆心吧', toast_liked_count: '已收藏 ',
|
||||
footer_desc: '在數位洪流中,留存一份類比的溫度。每一道紋路,都是時光的低語。',
|
||||
footer_explore: '探索', footer_about: '關於', footer_connect: '連接',
|
||||
footer_about_us: '關於我們', footer_terms: '使用條款', footer_privacy: '隱私政策', footer_copyright: '版權聲明',
|
||||
footer_feedback: '意見回饋', footer_business: '商務合作', footer_join: '加入我們', footer_api: '開發者API',
|
||||
footer_rights: '保留所有權利',
|
||||
},
|
||||
'en': {
|
||||
title: 'Vinyl Radio - Retro Music Platform', site_name: 'Vinyl Radio',
|
||||
nav_home: 'Home', nav_library: 'Library', nav_hot: 'Charts', nav_new: 'New',
|
||||
search_placeholder: 'Search songs, artists...', search_placeholder_lib: 'Search songs, artists, albums...',
|
||||
hero_tag: 'Classic Sound · Pure Audio',
|
||||
hero_title_1: 'Listen to', hero_title_2: 'Classic', hero_title_3: 'Sounds', hero_title_4: 'Relive the Vinyl Era',
|
||||
hero_subtitle: 'In the digital age, preserve the warmth of analog. Every record is a cherished memory of time.',
|
||||
hero_play: 'Start Playing', hero_browse: 'Browse Library',
|
||||
stat_records: 'Records', stat_artists: 'Artists', stat_plays: 'Total Plays',
|
||||
vinyl_label: 'Vinyl Radio',
|
||||
section_genres: 'Genres', section_hot: 'Hot Chart', section_new: 'New Releases', section_library: 'Music Library',
|
||||
see_all: 'See All',
|
||||
col_song: 'Song', col_artist: 'Artist', col_plays: 'Plays', col_duration: 'Duration',
|
||||
sort_default: 'Default', sort_plays: 'Most Played', sort_likes: 'Most Liked', sort_newest: 'Newest',
|
||||
tracks_unit: 'tracks', lyrics_title: 'Lyrics', no_lyrics: 'No lyrics available',
|
||||
ctrl_play: 'Play/Pause', ctrl_expand: 'Expand', ctrl_collapse: 'Collapse',
|
||||
cat_all: 'All', cat_pop: 'Pop', cat_rock: 'Rock', cat_jazz: 'Jazz',
|
||||
cat_classical: 'Classical', cat_folk: 'Folk', cat_electronic: 'Electronic', cat_rnb: 'R&B', cat_hiphop: 'Hip-Hop',
|
||||
empty_categories: 'No categories', empty_hot: 'No hot songs yet', empty_new: 'No new releases yet', empty_library: 'No songs match your search',
|
||||
toast_now_playing: 'Now playing: ', toast_cannot_load: 'Cannot load song info', toast_loading: 'Loading song, please wait...',
|
||||
toast_no_songs: 'No songs available', toast_select_song: 'Select a song first',
|
||||
toast_cancel_like: 'Removed from liked', toast_add_like: 'Added to liked',
|
||||
toast_no_liked: 'No liked songs yet, go show some love', toast_liked_count: 'Liked ',
|
||||
footer_desc: 'In the digital flood, preserve a touch of analog warmth. Every groove is a whisper of time.',
|
||||
footer_explore: 'Explore', footer_about: 'About', footer_connect: 'Connect',
|
||||
footer_about_us: 'About Us', footer_terms: 'Terms of Service', footer_privacy: 'Privacy Policy', footer_copyright: 'Copyright',
|
||||
footer_feedback: 'Feedback', footer_business: 'Business', footer_join: 'Join Us', footer_api: 'Developer API',
|
||||
footer_rights: 'All Rights Reserved',
|
||||
}
|
||||
};
|
||||
const LANG_LABELS = {'zh-CN':'中文','en':'EN','zh-TW':'繁中'};
|
||||
const CATEGORY_NAME_KEY = {流行:'cat_pop',摇滚:'cat_rock',爵士:'cat_jazz',古典:'cat_classical',民谣:'cat_folk',电子:'cat_electronic','R&B':'cat_rnb',嘻哈:'cat_hiphop'};
|
||||
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('music2_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){ el.textContent=t(el.getAttribute('data-i18n')); });
|
||||
document.querySelectorAll('[data-i18n-placeholder]').forEach(function(el){ el.placeholder=t(el.getAttribute('data-i18n-placeholder')); });
|
||||
document.querySelectorAll('[data-i18n-title]').forEach(function(el){ el.title=t(el.getAttribute('data-i18n-title')); });
|
||||
document.documentElement.lang=currentLang==='zh-TW'?'zh-TW':currentLang==='zh-CN'?'zh-CN':'en';
|
||||
document.title=t('title');
|
||||
var ll=document.getElementById('langLabel'); if(ll) ll.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('music2_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'); });
|
||||
|
||||
/* ============================================================
|
||||
* 黑胶电台 - 复古音乐平台
|
||||
* 单文件HTML模板
|
||||
@@ -1403,15 +1571,17 @@ function showToast(msg) {
|
||||
function renderCategories(cats) {
|
||||
const container = document.getElementById('category-tabs');
|
||||
if (!cats || cats.length === 0) {
|
||||
container.innerHTML = '<div style="color:var(--text-muted);padding:10px">暂无分类</div>';
|
||||
container.innerHTML = '<div style="color:var(--text-muted);padding:10px">' + t('empty_categories') + '</div>';
|
||||
return;
|
||||
}
|
||||
cats.sort((a,b) => (a.sort||0) - (b.sort||0));
|
||||
let html = '<div class="category-tab active" data-cat-id=""><span class="cat-icon">\u266B</span>全部</div>';
|
||||
let html = '<div class="category-tab active" data-cat-id=""><span class="cat-icon">\u266B</span>' + t('cat_all') + '</div>';
|
||||
cats.forEach(function(c) {
|
||||
const nameKey = CATEGORY_NAME_KEY[c.name];
|
||||
const displayName = nameKey ? t(nameKey) : c.name;
|
||||
html += '<div class="category-tab" data-cat-id="' + c.id + '">' +
|
||||
'<span class="cat-icon">' + (c.icon || '\u266A') + '</span>' +
|
||||
'<span>' + c.name + '</span></div>';
|
||||
'<span>' + displayName + '</span></div>';
|
||||
});
|
||||
container.innerHTML = html;
|
||||
container.querySelectorAll('.category-tab').forEach(function(tab) {
|
||||
@@ -1430,7 +1600,7 @@ function renderCategories(cats) {
|
||||
function renderHot(songs) {
|
||||
const container = document.getElementById('hot-list');
|
||||
if (!songs || songs.length === 0) {
|
||||
container.innerHTML = '<div class="empty-state"><div class="empty-icon">\u266B</div><div class="empty-text">暂无热门歌曲</div></div>';
|
||||
container.innerHTML = '<div class="empty-state"><div class="empty-icon">\u266B</div><div class="empty-text">' + t('empty_hot') + '</div></div>';
|
||||
return;
|
||||
}
|
||||
let html = '';
|
||||
@@ -1469,7 +1639,7 @@ function renderHot(songs) {
|
||||
function renderNew(songs) {
|
||||
const container = document.getElementById('new-grid');
|
||||
if (!songs || songs.length === 0) {
|
||||
container.innerHTML = '<div class="empty-state" style="grid-column:1/-1"><div class="empty-icon">\u266C</div><div class="empty-text">暂无新碟</div></div>';
|
||||
container.innerHTML = '<div class="empty-state" style="grid-column:1/-1"><div class="empty-icon">\u266C</div><div class="empty-text">' + t('empty_new') + '</div></div>';
|
||||
return;
|
||||
}
|
||||
let html = '';
|
||||
@@ -1505,13 +1675,13 @@ function renderLibrary(data) {
|
||||
const countEl = document.getElementById('library-count');
|
||||
|
||||
if (!data || !data.list || data.list.length === 0) {
|
||||
rowsContainer.innerHTML = '<div class="empty-state" style="grid-column:1/-1"><div class="empty-icon">\u2669</div><div class="empty-text">未找到匹配的歌曲</div></div>';
|
||||
countEl.textContent = '0 首歌曲';
|
||||
rowsContainer.innerHTML = '<div class="empty-state" style="grid-column:1/-1"><div class="empty-icon">\u2669</div><div class="empty-text">' + t('empty_library') + '</div></div>';
|
||||
countEl.textContent = '0 ' + t('tracks_unit');
|
||||
renderPagination(0, 1, state.library.page_size);
|
||||
return;
|
||||
}
|
||||
|
||||
countEl.textContent = data.total + ' 首歌曲';
|
||||
countEl.textContent = data.total + ' ' + t('tracks_unit');
|
||||
state.library.total = data.total;
|
||||
|
||||
let html = '';
|
||||
@@ -1604,7 +1774,7 @@ function renderHeroStats(songs) {
|
||||
/* ==================== Now Playing ==================== */
|
||||
async function playSong(songId) {
|
||||
const song = await apiGet('/songs/' + songId);
|
||||
if (!song) { showToast('无法加载歌曲信息'); return; }
|
||||
if (!song) { showToast(t('toast_cannot_load')); return; }
|
||||
|
||||
state.nowPlaying = song;
|
||||
state.currentTime = 0;
|
||||
@@ -1640,7 +1810,7 @@ async function playSong(songId) {
|
||||
lyricsEl.textContent = song.lyrics;
|
||||
lyricsEl.classList.remove('empty');
|
||||
} else {
|
||||
lyricsEl.textContent = '暂无歌词';
|
||||
lyricsEl.textContent = t('no_lyrics');
|
||||
lyricsEl.classList.add('empty');
|
||||
}
|
||||
|
||||
@@ -1667,7 +1837,7 @@ async function playSong(songId) {
|
||||
// Start progress simulation
|
||||
startPlayback();
|
||||
|
||||
showToast('正在播放: ' + song.title);
|
||||
showToast(t('toast_now_playing') + song.title);
|
||||
}
|
||||
|
||||
function togglePlay() {
|
||||
@@ -1815,7 +1985,7 @@ function bindEvents() {
|
||||
if (state.hotSongs && state.hotSongs.length > 0) {
|
||||
playSong(state.hotSongs[0].id);
|
||||
} else {
|
||||
showToast('歌曲加载中,请稍候...');
|
||||
showToast(t('toast_no_songs'));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1869,8 +2039,20 @@ function setupScrollReveal() {
|
||||
});
|
||||
}
|
||||
|
||||
/* ==================== Language Change Callback ==================== */
|
||||
function onLangChange(lang){
|
||||
applyI18n();
|
||||
if(state.categories.length>0) renderCategories(state.categories);
|
||||
if(state.hotSongs.length>0) renderHot(state.hotSongs);
|
||||
if(state.newSongs.length>0) renderNew(state.newSongs);
|
||||
if(state.library.list.length>0 || state.library.total>0){
|
||||
renderLibrary({list:state.library.list,total:state.library.total,page:state.library.page,page_size:state.library.page_size});
|
||||
}
|
||||
}
|
||||
|
||||
/* ==================== Init ==================== */
|
||||
async function init() {
|
||||
applyI18n();
|
||||
bindEvents();
|
||||
setupScrollReveal();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user