fix(i18n): 补全 MV/排行榜/歌单/搜索/专辑页面缺失的国际化

- 新增 comp.pages 命名空间,包含页面描述、地区分类、加载状态等 i18n 键
- toplist: 标题和描述文本国际化
- mv: 描述、加载状态、6 个地区分类标签国际化
- list: 描述、加载/无更多状态国际化,提取每日推荐常量
- search: 描述文本国际化
- album: 5 个地区分类标签国际化
- 覆盖全部 5 种语言 (zh-CN/en-US/ja-JP/ko-KR/zh-Hant)
This commit is contained in:
alger
2026-03-15 15:57:17 +08:00
parent 239229a60c
commit 24aa574176
10 changed files with 215 additions and 37 deletions

View File

@@ -280,5 +280,39 @@ export default {
home: 'Home', home: 'Home',
search: 'Search', search: 'Search',
album: 'Album', album: 'Album',
localMusic: 'Local Music' localMusic: 'Local Music',
pages: {
toplist: {
desc: 'The most authoritative music charts, discover the hottest music'
},
mv: {
desc: 'Explore amazing video content',
loadingMore: 'Loading more...',
noMore: '— All content loaded —',
area: {
all: 'All',
mainland: 'Mainland',
hktw: 'HK/TW',
western: 'Western',
japan: 'Japan',
korea: 'Korea'
}
},
list: {
desc: 'Discover more great playlists',
dailyRecommend: 'Daily Picks'
},
search: {
desc: 'Explore the hottest search trends'
},
album: {
area: {
all: 'All',
chinese: 'Chinese',
western: 'Western',
korea: 'Korea',
japan: 'Japan'
}
}
}
}; };

View File

@@ -280,5 +280,39 @@ export default {
home: 'ホーム', home: 'ホーム',
search: '検索', search: '検索',
album: 'アルバム', album: 'アルバム',
localMusic: 'ローカル音楽' localMusic: 'ローカル音楽',
pages: {
toplist: {
desc: '最も権威ある音楽チャート、今一番ホットな音楽を発見'
},
mv: {
desc: '素晴らしい動画コンテンツを探索',
loadingMore: 'もっと読み込み中...',
noMore: '— すべて読み込みました —',
area: {
all: 'すべて',
mainland: '中国大陸',
hktw: '香港・台湾',
western: '欧米',
japan: '日本',
korea: '韓国'
}
},
list: {
desc: 'もっと素敵なプレイリストを発見',
dailyRecommend: 'デイリーおすすめ'
},
search: {
desc: '今最もホットな検索トレンドを探索'
},
album: {
area: {
all: 'すべて',
chinese: '中華圏',
western: '欧米',
korea: '韓国',
japan: '日本'
}
}
}
}; };

View File

@@ -279,5 +279,39 @@ export default {
home: '홈', home: '홈',
search: '검색', search: '검색',
album: '앨범', album: '앨범',
localMusic: '로컬 음악' localMusic: '로컬 음악',
pages: {
toplist: {
desc: '가장 권위 있는 음악 차트, 지금 가장 핫한 음악을 발견하세요'
},
mv: {
desc: '멋진 영상 콘텐츠 탐색',
loadingMore: '더 불러오는 중...',
noMore: '— 모든 콘텐츠 로드 완료 —',
area: {
all: '전체',
mainland: '중국 대륙',
hktw: '홍콩/대만',
western: '서양',
japan: '일본',
korea: '한국'
}
},
list: {
desc: '더 많은 멋진 플레이리스트를 발견하세요',
dailyRecommend: '오늘의 추천'
},
search: {
desc: '지금 가장 핫한 검색 트렌드를 탐색하세요'
},
album: {
area: {
all: '전체',
chinese: '중화권',
western: '서양',
korea: '한국',
japan: '일본'
}
}
}
}; };

View File

@@ -272,5 +272,39 @@ export default {
home: '首页', home: '首页',
search: '搜索', search: '搜索',
album: '专辑', album: '专辑',
localMusic: '本地音乐' localMusic: '本地音乐',
pages: {
toplist: {
desc: '最具权威的音乐榜单,发现当下最热门的音乐'
},
mv: {
desc: '探索精彩视频内容',
loadingMore: '加载更多中...',
noMore: '— 已加载全部内容 —',
area: {
all: '全部',
mainland: '内地',
hktw: '港台',
western: '欧美',
japan: '日本',
korea: '韩国'
}
},
list: {
desc: '发现更多好听的歌单',
dailyRecommend: '每日推荐'
},
search: {
desc: '探索当下最热门的搜索趋势'
},
album: {
area: {
all: '全部',
chinese: '华语',
western: '欧美',
korea: '韩国',
japan: '日本'
}
}
}
}; };

View File

@@ -272,5 +272,39 @@ export default {
home: '首頁', home: '首頁',
search: '搜尋', search: '搜尋',
album: '專輯', album: '專輯',
localMusic: '本地音樂' localMusic: '本地音樂',
pages: {
toplist: {
desc: '最具權威的音樂榜單,發現當下最熱門的音樂'
},
mv: {
desc: '探索精彩影片內容',
loadingMore: '載入更多中...',
noMore: '— 已載入全部內容 —',
area: {
all: '全部',
mainland: '內地',
hktw: '港台',
western: '歐美',
japan: '日本',
korea: '韓國'
}
},
list: {
desc: '發現更多好聽的播放清單',
dailyRecommend: '每日推薦'
},
search: {
desc: '探索當下最熱門的搜尋趨勢'
},
album: {
area: {
all: '全部',
chinese: '華語',
western: '歐美',
korea: '韓國',
japan: '日本'
}
}
}
}; };

View File

@@ -133,13 +133,13 @@ const route = useRoute();
const TOTAL_ITEMS = 30; // 每页数量 const TOTAL_ITEMS = 30; // 每页数量
const areas = [ const areas = computed(() => [
{ name: '全部', value: 'ALL' }, { name: t('comp.pages.album.area.all'), value: 'ALL' },
{ name: '华语', value: 'ZH' }, { name: t('comp.pages.album.area.chinese'), value: 'ZH' },
{ name: '欧美', value: 'EA' }, { name: t('comp.pages.album.area.western'), value: 'EA' },
{ name: '韩国', value: 'KR' }, { name: t('comp.pages.album.area.korea'), value: 'KR' },
{ name: '日本', value: 'JP' } { name: t('comp.pages.album.area.japan'), value: 'JP' }
]; ]);
const albumList = ref<any[]>([]); const albumList = ref<any[]>([]);
const page = ref(0); const page = ref(0);
@@ -149,7 +149,8 @@ const loading = ref(false);
const currentArea = ref((route.query.area as string) || 'ALL'); const currentArea = ref((route.query.area as string) || 'ALL');
const currentAreaName = computed( const currentAreaName = computed(
() => areas.find((a) => a.value === currentArea.value)?.name || '全部' () =>
areas.value.find((a) => a.value === currentArea.value)?.name || t('comp.pages.album.area.all')
); );
const contentScrollbarRef = ref(); const contentScrollbarRef = ref();

View File

@@ -23,7 +23,7 @@
<h1 class="text-2xl md:text-3xl font-bold text-neutral-900 dark:text-white mb-2"> <h1 class="text-2xl md:text-3xl font-bold text-neutral-900 dark:text-white mb-2">
{{ listTitle }} {{ listTitle }}
</h1> </h1>
<p class="text-neutral-500 dark:text-neutral-400">发现更多好听的歌单</p> <p class="text-neutral-500 dark:text-neutral-400">{{ t('comp.pages.list.desc') }}</p>
</div> </div>
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-6"> <div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-6">
@@ -96,10 +96,10 @@
<!-- 加载更多 --> <!-- 加载更多 -->
<div v-if="isLoadingMore" class="flex justify-center items-center py-8"> <div v-if="isLoadingMore" class="flex justify-center items-center py-8">
<n-spin size="small" /> <n-spin size="small" />
<span class="ml-2 text-neutral-500">加载中...</span> <span class="ml-2 text-neutral-500">{{ t('common.loading') }}</span>
</div> </div>
<div v-if="!hasMore && recommendList.length > 0" class="text-center py-8 text-neutral-500"> <div v-if="!hasMore && recommendList.length > 0" class="text-center py-8 text-neutral-500">
没有更多了 {{ t('common.noMore') }}
</div> </div>
</div> </div>
</n-scrollbar> </n-scrollbar>
@@ -108,6 +108,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onDeactivated, onMounted, reactive, ref, watch } from 'vue'; import { nextTick, onDeactivated, onMounted, reactive, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { getPlaylistCategory } from '@/api/home'; import { getPlaylistCategory } from '@/api/home';
@@ -121,6 +122,7 @@ defineOptions({
name: 'List' name: 'List'
}); });
const { t } = useI18n();
const TOTAL_ITEMS = 42; // 每页数量 const TOTAL_ITEMS = 42; // 每页数量
const recommendList = ref<any[]>([]); const recommendList = ref<any[]>([]);
@@ -142,7 +144,8 @@ const openPlaylist = (item: any) => {
}; };
const route = useRoute(); const route = useRoute();
const listTitle = ref((route.query.type as string) || '每日推荐'); const DEFAULT_CAT = '每日推荐';
const listTitle = ref((route.query.type as string) || t('comp.pages.list.dailyRecommend'));
const loading = ref(false); const loading = ref(false);
const loadList = async (type: string, isLoadMore = false) => { const loadList = async (type: string, isLoadMore = false) => {
@@ -159,7 +162,7 @@ const loadList = async (type: string, isLoadMore = false) => {
try { try {
const params = { const params = {
cat: type === '每日推荐' ? '' : type, cat: type === DEFAULT_CAT ? '' : type,
limit: TOTAL_ITEMS, limit: TOTAL_ITEMS,
offset: page.value * TOTAL_ITEMS offset: page.value * TOTAL_ITEMS
}; };
@@ -190,7 +193,7 @@ const handleScroll = (e: any) => {
// 添加歌单分类相关的代码 // 添加歌单分类相关的代码
const playlistCategory = ref<IPlayListSort>(); const playlistCategory = ref<IPlayListSort>();
const currentType = ref((route.query.type as string) || '每日推荐'); const currentType = ref((route.query.type as string) || DEFAULT_CAT);
const contentScrollbarRef = ref(); const contentScrollbarRef = ref();
@@ -201,7 +204,7 @@ const loadPlaylistCategory = async () => {
...data, ...data,
sub: [ sub: [
{ {
name: '每日推荐', name: DEFAULT_CAT,
category: 0 category: 0
}, },
...data.sub ...data.sub
@@ -227,10 +230,10 @@ watch(
() => route.query, () => route.query,
async (newParams) => { async (newParams) => {
if (route.path !== '/list') return; if (route.path !== '/list') return;
const newType = (newParams.type as string) || '每日推荐'; const newType = (newParams.type as string) || DEFAULT_CAT;
// 如果路由参数变化,且与当前类型不同,则重新加载 // 如果路由参数变化,且与当前类型不同,则重新加载
if (newType !== currentType.value) { if (newType !== currentType.value) {
listTitle.value = newType; listTitle.value = newType === DEFAULT_CAT ? t('comp.pages.list.dailyRecommend') : newType;
currentType.value = newType; currentType.value = newType;
loading.value = true; loading.value = true;
loadList(newType); loadList(newType);

View File

@@ -23,7 +23,7 @@
> >
MV MV
</h1> </h1>
<p class="text-neutral-500 dark:text-neutral-400">探索精彩视频内容</p> <p class="text-neutral-500 dark:text-neutral-400">{{ t('comp.pages.mv.desc') }}</p>
</div> </div>
<!-- MV Grid Container --> <!-- MV Grid Container -->
@@ -96,14 +96,14 @@
<div v-if="loadingMore" class="flex flex-col items-center gap-4"> <div v-if="loadingMore" class="flex flex-col items-center gap-4">
<n-spin size="small" /> <n-spin size="small" />
<span class="text-xs text-neutral-400 font-medium tracking-widest uppercase"> <span class="text-xs text-neutral-400 font-medium tracking-widest uppercase">
加载更多中... {{ t('comp.pages.mv.loadingMore') }}
</span> </span>
</div> </div>
<div v-if="!hasMore && !initLoading" class="text-center"> <div v-if="!hasMore && !initLoading" class="text-center">
<span <span
class="text-xs text-neutral-400 font-medium tracking-widest uppercase opacity-50" class="text-xs text-neutral-400 font-medium tracking-widest uppercase opacity-50"
> >
已加载全部内容 {{ t('comp.pages.mv.noMore') }}
</span> </span>
</div> </div>
</div> </div>
@@ -122,7 +122,8 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, ref, watch } from 'vue'; import { computed, onMounted, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { getAllMv, getTopMv } from '@/api/mv'; import { getAllMv, getTopMv } from '@/api/mv';
@@ -137,6 +138,7 @@ defineOptions({
name: 'Mv' name: 'Mv'
}); });
const { t } = useI18n();
const showMv = ref(false); const showMv = ref(false);
const mvList = ref<Array<IMvItem>>([]); const mvList = ref<Array<IMvItem>>([]);
const playMvItem = ref<IMvItem>(); const playMvItem = ref<IMvItem>();
@@ -147,14 +149,14 @@ const offset = ref(0);
const limit = ref(40); // 调整为40方便4列布局 (10行) const limit = ref(40); // 调整为40方便4列布局 (10行)
const hasMore = ref(true); const hasMore = ref(true);
const categories = [ const categories = computed(() => [
{ label: '全部', value: '全部' }, { label: t('comp.pages.mv.area.all'), value: '全部' },
{ label: '内地', value: '内地' }, { label: t('comp.pages.mv.area.mainland'), value: '内地' },
{ label: '港台', value: '港台' }, { label: t('comp.pages.mv.area.hktw'), value: '港台' },
{ label: '欧美', value: '欧美' }, { label: t('comp.pages.mv.area.western'), value: '欧美' },
{ label: '日本', value: '日本' }, { label: t('comp.pages.mv.area.japan'), value: '日本' },
{ label: '韩国', value: '韩国' } { label: t('comp.pages.mv.area.korea'), value: '韩国' }
]; ]);
const selectedCategory = ref('全部'); const selectedCategory = ref('全部');
const router = useRouter(); const router = useRouter();

View File

@@ -12,7 +12,7 @@
> >
{{ t('search.title.hotSearch') }} {{ t('search.title.hotSearch') }}
</h1> </h1>
<p class="text-neutral-500 dark:text-neutral-400">探索当下最热门的搜索趋势</p> <p class="text-neutral-500 dark:text-neutral-400">{{ t('comp.pages.search.desc') }}</p>
</div> </div>
</div> </div>

View File

@@ -7,10 +7,10 @@
<h1 <h1
class="text-3xl md:text-4xl font-bold tracking-tight text-neutral-900 dark:text-white mb-2" class="text-3xl md:text-4xl font-bold tracking-tight text-neutral-900 dark:text-white mb-2"
> >
排行榜 {{ t('comp.toplist') }}
</h1> </h1>
<p class="text-neutral-500 dark:text-neutral-400"> <p class="text-neutral-500 dark:text-neutral-400">
最具权威的音乐榜单发现当下最热门的音乐 {{ t('comp.pages.toplist.desc') }}
</p> </p>
</div> </div>
@@ -94,6 +94,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { getToplist } from '@/api/list'; import { getToplist } from '@/api/list';
@@ -104,6 +105,7 @@ defineOptions({
name: 'Toplist' name: 'Toplist'
}); });
const { t } = useI18n();
const router = useRouter(); const router = useRouter();
const topList = ref<any[]>([]); const topList = ref<any[]>([]);
const loading = ref(false); const loading = ref(false);