feat: use vue-i18n for language switch (#23)

* feat: add config to resolve path alias.

* feat: use vue-i18n for language switch

* feat: add .editorconfig for ide

* fix: add no-referrer to avoid CROB

* fix: setCookie and fix typo

* feat: integrate vue-i18n
This commit is contained in:
Hawtim Zhang 2020-10-19 23:14:26 +08:00 committed by GitHub
parent 00774d3a91
commit ecee495a18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 246 additions and 65 deletions

View File

@ -21,6 +21,7 @@
"vue": "^2.6.11",
"vue-analytics": "^5.22.1",
"vue-global-events": "^1.2.1",
"vue-i18n": "^8.22.0",
"vue-router": "^3.4.3",
"vue-slider-component": "^3.2.5",
"vuex": "^3.4.0"

View File

@ -0,0 +1,3 @@
<svg fill="currentColor" viewBox="0 0 1024 500">
<path d="M152.1 236.2c-3.5-12.1-7.8-33.2-7.8-33.2h-.5s-4.3 21.1-7.8 33.2l-11.1 37.5H163zM616 96H336v320h280c13.3 0 24-10.7 24-24V120c0-13.3-10.7-24-24-24zm-24 120c0 6.6-5.4 12-12 12h-11.4c-6.9 23.6-21.7 47.4-42.7 69.9 8.4 6.4 17.1 12.5 26.1 18 5.5 3.4 7.3 10.5 4.1 16.2l-7.9 13.9c-3.4 5.9-10.9 7.8-16.7 4.3-12.6-7.8-24.5-16.1-35.4-24.9-10.9 8.7-22.7 17.1-35.4 24.9-5.8 3.5-13.3 1.6-16.7-4.3l-7.9-13.9c-3.2-5.6-1.4-12.8 4.2-16.2 9.3-5.7 18-11.7 26.1-18-7.9-8.4-14.9-17-21-25.7-4-5.7-2.2-13.6 3.7-17.1l6.5-3.9 7.3-4.3c5.4-3.2 12.4-1.7 16 3.4 5 7 10.8 14 17.4 20.9 13.5-14.2 23.8-28.9 30-43.2H412c-6.6 0-12-5.4-12-12v-16c0-6.6 5.4-12 12-12h64v-16c0-6.6 5.4-12 12-12h16c6.6 0 12 5.4 12 12v16h64c6.6 0 12 5.4 12 12zM0 120v272c0 13.3 10.7 24 24 24h280V96H24c-13.3 0-24 10.7-24 24zm58.9 216.1L116.4 167c1.7-4.9 6.2-8.1 11.4-8.1h32.5c5.1 0 9.7 3.3 11.4 8.1l57.5 169.1c2.6 7.8-3.1 15.9-11.4 15.9h-22.9a12 12 0 0 1-11.5-8.6l-9.4-31.9h-60.2l-9.1 31.8c-1.5 5.1-6.2 8.7-11.5 8.7H70.3c-8.2 0-14-8.1-11.4-15.9z"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -1,7 +1,7 @@
<template>
<footer>
<ButtonTwoTone :iconClass="'settings'" :color="'grey'">
Settings
{{ $t("footer.settings") }}
</ButtonTwoTone>
</footer>
</template>

View File

@ -10,17 +10,17 @@
</div>
<div class="navigation-links">
<router-link to="/" :class="{ active: this.$route.name === 'home' }"
>Home</router-link
>{{ $t('nav.home') }}</router-link
>
<router-link
to="/explore"
:class="{ active: this.$route.name === 'explore' }"
>Explore</router-link
>{{ $t('nav.explore') }}</router-link
>
<router-link
to="/library"
:class="{ active: this.$route.name === 'library' }"
>Library</router-link
>{{ $t('nav.library') }}</router-link
>
</div>
<div class="right-part">
@ -32,7 +32,7 @@
<svg-icon icon-class="search" />
<div class="input">
<input
:placeholder="inputFocus ? '' : 'Search'"
:placeholder="inputFocus ? '' : $t('nav.search')"
v-model="keywords"
@keydown.enter="goToSearchPage"
@focus="inputFocus = true"
@ -41,6 +41,10 @@
</div>
</div>
</div>
<div class="locale-changer" @click="changeLang">
<svg-icon icon-class="translation" class="translation"
/>
</div>
</div>
</nav>
</template>
@ -57,6 +61,7 @@ export default {
return {
inputFocus: false,
keywords: "",
langs: ['zh-CN', 'en']
};
},
methods: {
@ -70,6 +75,12 @@ export default {
query: { keywords: this.keywords },
});
},
changeLang() {
if (this.$i18n.locale === "zh-CN") {
return this.$i18n.locale = "en";
}
this.$i18n.locale = "zh-CN";
}
},
};
</script>
@ -91,7 +102,6 @@ nav {
backdrop-filter: saturate(180%) blur(30px);
background-color: rgba(255, 255, 255, 0.86);
z-index: 100;
// border-bottom: 1px solid rgba(0, 0, 0, 0.02);
}
.navigation-buttons {
@ -133,6 +143,7 @@ nav {
color: #335eea;
}
}
.search {
.svg-icon {
height: 18px;
@ -193,4 +204,15 @@ nav {
width: 24px;
}
}
.locale-changer {
position: relative;
.translation {
margin-left: 16px;
height: 48px;
width: 48px;
}
}
</style>

12
src/locale/index.js Normal file
View File

@ -0,0 +1,12 @@
import Vue from 'vue';
import VueI18n from 'vue-i18n'
import messages from './messages';
Vue.use(VueI18n)
const i18n = new VueI18n({
locale: 'zh-CN',
messages
});
export default i18n;

144
src/locale/messages.js Normal file
View File

@ -0,0 +1,144 @@
export default {
en: {
nav: {
home: "Home",
explore: "Explore",
library: "Library",
search: "Search"
},
footer: {
settings: 'Settings',
},
home: {
recommendPlaylist: "Recommended Playlist",
recommendArtist: "Recommended Artist",
newAlbum: "Lastest Album",
leaderboard: "Leaderboard",
seeMore: "SEE MORE",
},
library: "Library",
explore: {
explore: "Explore",
loadMore: "Load More",
},
artist: {
latestRelease: "Latest Release",
popularSongs: "Popular Songs",
showMore: "SHOW MORE",
showLess: "SHOW LESS",
EPSingle: "EPs & Singles",
albums: "Albums"
},
album: {
released: "Released",
},
login: {
accessToAll: "Access to all data",
loginText: "Login in Netease",
search: "Search account",
readonly: "Only access to public data",
usernameLogin: "Username Login",
searchHolder: "Your account username",
enterTip: "Press 'enter' to search",
choose: "Choose your account",
confirm: "confirm",
countrycode: 'countrycode',
phone: 'phone',
email: 'email',
password: 'password',
login: 'Login',
usingEmail: 'Using email',
usingPhone: 'Using phone',
// TODO
agreement: `YesPlayMusic promises not to save any of your account information to the cloud.
Your password will be MD5 encrypted locally and then transmitted to NetEase Cloud API.
YesPlayMusic is not the official website of NetEase Cloud, please consider carefully before entering account information. You can also go to <a href="https://github.com/qier222/YesPlayMusic">YesPlayMusic's GitHub repository</a> to build and use the self-hosted NetEase Cloud API.`
},
mv: {
moreVideo: 'More Videos'
},
next: {
nowPlaying: 'Now Playing',
nextUp: 'Next Up'
},
playlist: "Playlists",
play: "PLAY",
likedSong: "Liked Songs",
shortIs: "'s",
is: "'s",
updatedAt: "Updated at",
},
"zh-CN": {
nav: {
home: "首页",
explore: "发现",
library: "歌单",
search: "搜索"
},
footer: {
settings: '设置',
},
home: {
recommendPlaylist: "推荐歌单",
recommendArtist: "推荐歌手",
newAlbum: "新专速递",
leaderboard: "排行榜",
seeMore: "更多",
},
library: "歌单",
explore: {
explore: "发现",
loadMore: "加载更多",
},
artist: {
latestRelease: "最新发布",
popularSongs: "热门歌曲",
showMore: "显示更多",
showLess: "收起",
EPSingle: "专辑及单曲",
albums: "专辑"
},
album: {
released: "发布于",
},
login: {
accessToAll: "可访问全部数据",
loginText: "登录网易云账号",
search: "搜索网易云账号",
readonly: "只能读取账号公开数据",
usernameLogin: '用户名登录',
searchHolder: "请输入你的网易云用户名",
enterTip: "按 Enter 搜索",
choose: "在列表中选中你的账号",
confirm: "确认",
countrycode: '国际区号',
phone: '手机号',
email: '邮箱',
password: '密码',
login: '登录',
usingEmail: '使用邮箱登录',
usingPhone: '使用手机号登录',
// TODO
agreement: `YesPlayMusic 承诺不会保存你的任何账号信息到云端。<br />
你的密码会在本地进行 MD5 加密后再传输到网易云 API<br />
YesPlayMusic 并非网易云官方网站输入账号信息前请慎重考虑 你也可以前往
<a href="https://github.com/qier222/YesPlayMusic"
>YesPlayMusic GitHub 源代码仓库</a
>
自行构建并使用自托管的网易云 API`
},
mv: {
moreVideo: '更多视频'
},
next: {
nowPlaying: '正在播放',
nextUp: '即将播放'
},
playlist: "播放列表",
play: "播放",
likedSong: "我喜欢的音乐",
shortIs: "",
is: "的",
updatedAt: "更新于"
}
};

View File

@ -3,6 +3,7 @@ import VueAnalytics from "vue-analytics";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import i18n from '@/locale';
import "@/assets/icons";
import "@/utils/filters";
import { initMediaSession } from "@/utils/mediaSession";
@ -18,6 +19,7 @@ Vue.config.productionTip = false;
initMediaSession();
new Vue({
i18n,
store,
router,
render: (h) => h(App),

View File

@ -38,24 +38,21 @@
@click.native="playAlbumByID(album.id)"
:iconClass="`play`"
>
PLAY
{{ $t('play') }}
</ButtonTwoTone>
</div>
</div>
</div>
<TrackList :tracks="tracks" :type="'album'" :id="album.id" />
<div class="extra-info">
<div class="album-time"></div>
<div class="release-date">
Released {{ album.publishTime | formatDate("MMMM D, YYYY") }}
{{ $t("album.released") }} {{ album.publishTime | formatDate("MMMM D, YYYY") }}
</div>
<div class="copyright" v-if="album.company !== null">
© {{ album.company }}
</div>
</div>
<transition name="fade">
<div
class="shade"

View File

@ -13,13 +13,13 @@
</div>
<div class="buttons">
<ButtonTwoTone @click.native="playPopularSongs()" :iconClass="`play`">
PLAY
{{ $t('play') }}
</ButtonTwoTone>
</div>
</div>
</div>
<div class="latest-release">
<div class="section-title">Latest Release</div>
<div class="section-title">{{ $t('artist.latestRelease') }}</div>
<div class="release">
<div class="container">
<Cover
@ -53,7 +53,7 @@
</div>
</div>
<div class="popular-tracks">
<div class="section-title">Popular Songs</div>
<div class="section-title">{{ $t('artist.popularSongs') }}</div>
<TrackList
:tracks="popularTracks.slice(0, showMorePopTracks ? 24 : 12)"
:type="'tracklist'"
@ -61,13 +61,13 @@
<div class="show-more">
<button @click="showMorePopTracks = !showMorePopTracks">
<span v-show="!showMorePopTracks">SHOW MORE</span>
<span v-show="showMorePopTracks">SHOW LESS</span>
<span v-show="!showMorePopTracks">{{ $t('artist.showMore') }}</span>
<span v-show="showMorePopTracks">{{ $t('artist.showLess') }}</span>
</button>
</div>
</div>
<div class="albums" v-if="albums.length !== 0">
<div class="section-title">Albums</div>
<div class="section-title">{{ $t('artist.album') }}</div>
<CoverRow
:type="'album'"
:items="albums"
@ -80,7 +80,7 @@
<MvRow :mvs="mvs" subtitle="publishTime" />
</div>
<div class="eps">
<div class="section-title">EPs & Singles</div>
<div class="section-title">{{ $t('artist.EPSingle') }}</div>
<CoverRow
:type="'album'"
:items="eps"

View File

@ -1,6 +1,6 @@
<template>
<div class="explore">
<h1>Explore</h1>
<h1>{{ $t('explore.explore') }}</h1>
<div class="buttons">
<div
class="button"
@ -35,7 +35,7 @@
@click.native="getPlaylist"
color="grey"
:loading="loadingMore"
>Load More</ButtonTwoTone
>{{ $t('explore.loadMore') }}</ButtonTwoTone
>
</div>
</div>

View File

@ -11,11 +11,10 @@
:imageSize="1024"
/>
</div>
<div class="index-row">
<div class="title">
{{ recommendPlaylist.name }}
<router-link to="/explore?category=推荐歌单">SEE MORE</router-link>
{{ $t("home.recommendPlaylist") }}
<router-link to="/explore?category=推荐歌单">{{ $t("home.seeMore") }}</router-link>
</div>
<CoverRow
:type="'playlist'"
@ -23,24 +22,21 @@
:subText="'copywriter'"
/>
</div>
<div class="index-row">
<div class="title">{{ recommendArtists.name }}</div>
<div class="title">{{ $t("home.recommendArtist") }}</div>
<CoverRow type="artist" :items="recommendArtists.items" />
</div>
<div class="index-row">
<div class="title">
{{ newReleasesAlbum.name }}
<router-link to="/new-album">SEE MORE</router-link>
{{ $t("home.newAlbum") }}
<router-link to="/new-album">{{ $t("home.seeMore") }}</router-link>
</div>
<CoverRow type="album" :items="newReleasesAlbum.items" subText="artist" />
</div>
<div class="index-row">
<div class="title">
{{ topList.name }}
<router-link to="/explore?category=排行榜">SEE MORE</router-link>
{{ $t("home.leaderboard") }}
<router-link to="/explore?category=排行榜">{{ $t("home.seeMore") }}</router-link>
</div>
<CoverRow
:type="'chart'"
@ -58,7 +54,6 @@ import { toplistOfArtists } from "@/api/artist";
import { byAppleMusic } from "@/utils/staticPlaylist";
import { newAlbums } from "@/api/album";
import NProgress from "nprogress";
import CoverRow from "@/components/CoverRow.vue";
export default {
@ -67,15 +62,13 @@ export default {
data() {
return {
show: false,
recommendPlaylist: { name: "推荐歌单", items: [] },
newReleasesAlbum: { name: "新专速递", items: [] },
recommendPlaylist: { items: [] },
newReleasesAlbum: { items: [] },
topList: {
name: "排行榜",
items: [],
ids: [19723756, 180106, 60198, 3812895, 60131],
},
recommendArtists: {
name: "推荐歌手",
items: [],
indexs: [],
},

View File

@ -3,7 +3,7 @@
<h1>
<img class="head" :src="user.profile.avatarUrl | resizeImage" />{{
user.profile.nickname
}}'s Library
}}{{ $t("is") }} {{ $t("library") }}
</h1>
<div class="section-one">
<div class="liked-songs" @click="goToLikedSongsList">
@ -19,7 +19,7 @@
</div>
<div class="bottom">
<div class="titles">
<div class="title">Liked Songs</div>
<div class="title">{{ $t("likedSong") }}</div>
<div class="sub-title">
{{ likedSongsPlaylist.trackCount }} songs
</div>
@ -41,7 +41,7 @@
</div>
<div class="playlists" v-if="playlists.length > 1">
<div class="title">Playlists</div>
<div class="title">{{ $t("playlist") }}</div>
<div>
<CoverRow
:items="playlists.slice(1)"

View File

@ -14,8 +14,8 @@
>
<div class="container" :class="{ active: activeCard === 1 }">
<div class="title-info">
<div class="title">登录网易云账号</div>
<div class="info">可访问全部数据</div>
<div class="title">{{ $t('login.loginText') }}</div>
<div class="info">{{ $t('login.accessToAll') }}</div>
</div>
<svg-icon icon-class="arrow-right"></svg-icon>
</div>
@ -28,8 +28,8 @@
>
<div class="container" :class="{ active: activeCard === 2 }">
<div class="title-info">
<div class="title">搜索网易云账号</div>
<div class="info">只能读取账号公开数据</div>
<div class="title">{{ $t('login.search') }}</div>
<div class="info">{{ $t('login.readonly') }}</div>
</div>
<svg-icon icon-class="arrow-right"></svg-icon>
</div>
@ -105,6 +105,8 @@ export default {
height: 128px;
width: 300px;
transition: all 0.3s;
padding-left: 22px;
box-sizing: border-box;
.active {
.title-info {

View File

@ -3,7 +3,7 @@
<div class="section-1">
<img src="/img/logos/netease-music.png" />
</div>
<div class="title">登录网易云账号</div>
<div class="title">{{ $t("login.loginText") }}</div>
<div class="section-2">
<div class="input-box" v-show="mode === 'phone'">
<div class="container" :class="{ active: inputFocus === 'phone' }">
@ -11,14 +11,14 @@
<div class="inputs">
<input
id="countryCode"
:placeholder="inputFocus === 'phone' ? '' : '国际区号'"
:placeholder="inputFocus === 'phone' ? '' : $t('login.countrycode')"
v-model="countryCode"
@focus="inputFocus = 'phone'"
@blur="inputFocus = ''"
/>
<input
id="phoneNumber"
:placeholder="inputFocus === 'phone' ? '' : '手机号'"
:placeholder="inputFocus === 'phone' ? '' : $t('login.phone')"
v-model="phoneNumber"
@focus="inputFocus = 'phone'"
@blur="inputFocus = ''"
@ -33,7 +33,7 @@
<input
type="email"
id="email"
:placeholder="inputFocus === 'email' ? '' : '邮箱'"
:placeholder="inputFocus === 'email' ? '' : $t('login.email')"
v-model="email"
@focus="inputFocus = 'email'"
@blur="inputFocus = ''"
@ -48,7 +48,7 @@
<input
type="password"
id="password"
:placeholder="inputFocus === 'password' ? '' : '密码'"
:placeholder="inputFocus === 'password' ? '' : $t('login.password')"
v-model="password"
@focus="inputFocus = 'password'"
@blur="inputFocus = ''"
@ -58,7 +58,7 @@
</div>
</div>
<div class="confirm">
<button @click="login" v-show="!processing">登录</button>
<button @click="login" v-show="!processing">{{ $t('login.login') }}</button>
<button v-show="processing" class="loading" disabled>
<span></span>
<span></span>
@ -66,8 +66,8 @@
</button>
</div>
<div class="other-login">
<a v-show="mode === 'phone'" @click="mode = 'email'">使用邮箱登录</a>
<a v-show="mode === 'email'" @click="mode = 'phone'">使用手机号登录</a>
<a v-show="mode === 'phone'" @click="mode = 'email'">{{ $t('login.usingEmail') }}</a>
<a v-show="mode === 'email'" @click="mode = 'phone'">{{ $t('login.usingPhone') }}</a>
</div>
<div class="notice">
YesPlayMusic 承诺不会保存你的任何账号信息到云端<br />
@ -112,7 +112,7 @@ export default {
methods: {
...mapMutations(["updateUser", "updateUserInfo"]),
afterLogin() {
Cookies.set("MUSIC_U", true, { expires: 3650 });
// Cookies.set("MUSIC_U", true, { expires: 3650 });
Cookies.set("loginMode", "account", { expires: 3650 });
userPlaylist({
uid: this.$store.state.settings.user.userId,

View File

@ -1,14 +1,14 @@
<template>
<div class="login">
<div>
<div class="title">用户名登录</div>
<div class="title">{{ $t('login.usernameLogin') }}</div>
<div class="sestion">
<div class="search-box">
<div class="container">
<svg-icon icon-class="search" />
<div class="input">
<input
placeholder="请输入你的网易云用户名"
:placeholder="$t('login.searchHolder')"
v-model="keyword"
@keydown.enter="search"
/>
@ -18,10 +18,10 @@
</div>
<div class="sestion">
<div class="name" v-show="activeUser.nickname === undefined">
按Enter搜索
{{ $t("login.enterTip") }}
</div>
<div class="name" v-show="activeUser.nickname !== undefined">
在列表中选中你的账号
{{ $t("login.choose") }}
</div>
<div class="user-list">
<div
@ -41,7 +41,7 @@
<ButtonTwoTone
@click.native="confirm"
v-show="activeUser.nickname !== undefined"
>确定</ButtonTwoTone
>{{ $t("login.confirm") }}</ButtonTwoTone
>
</div>
</div>

View File

@ -19,7 +19,7 @@
</div>
</div>
<div class="more-video">
<div class="section-title">More Videos</div>
<div class="section-title">{{ $t("mv.moreVideo") }}</div>
<MvRow :mvs="simiMvs" />
</div>
</div>

View File

@ -1,6 +1,6 @@
<template>
<div class="newAlbum">
<h1>新专速递</h1>
<h1>{{ $t("home.newAlbum")}}</h1>
<div class="playlist-row">
<div class="playlists">

View File

@ -1,12 +1,12 @@
<template>
<div class="next-tracks">
<h1>Now Playing</h1>
<h1>{{ $t("next.nowPlaying") }}</h1>
<TrackList
:tracks="[currentTrack]"
:type="'playlist'"
dbclickTrackFunc="none"
/>
<h1>Next Up</h1>
<h1>{{ $t("next.nextUp") }}</h1>
<TrackList
:tracks="sortedTracks"

View File

@ -35,7 +35,7 @@
>
</div>
<div class="date-and-count">
Updated at {{ playlist.updateTime | formatDate }} ·
{{ $t("updatedAt") }} {{ playlist.updateTime | formatDate }} ·
{{ playlist.trackCount }} Songs
</div>
<div class="description" @click="showFullDescription = true">
@ -43,7 +43,7 @@
</div>
<div class="buttons">
<ButtonTwoTone @click.native="playPlaylistByID()" :iconClass="`play`">
PLAY
{{ $t("play") }}
</ButtonTwoTone>
<ButtonTwoTone
v-if="
@ -64,7 +64,7 @@
<h1>
<img class="avatar" :src="settings.user.avatarUrl | resizeImage" />{{
settings.user.nickname
}}'s Liked Songs
}}{{ $t("is") }} {{ $t("likedSong") }}
</h1>
</div>

View File

@ -79,7 +79,7 @@
</div>
<div class="playlists" v-if="result.hasOwnProperty('playList')">
<div class="section-title">Playlists</div>
<div class="section-title">{{ $t("playlist") }}</div>
<div class="albums-list">
<div
class="album"

View File

@ -8535,6 +8535,11 @@ vue-hot-reload-api@^2.3.0:
resolved "https://registry.npm.taobao.org/vue-hot-reload-api/download/vue-hot-reload-api-2.3.4.tgz?cache=0&sync_timestamp=1589682714858&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-hot-reload-api%2Fdownload%2Fvue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2"
integrity sha1-UylVzB6yCKPZkLOp+acFdGV+CPI=
vue-i18n@^8.22.0:
version "8.22.0"
resolved "https://registry.npm.taobao.org/vue-i18n/download/vue-i18n-8.22.0.tgz?cache=0&sync_timestamp=1602001179053&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-i18n%2Fdownload%2Fvue-i18n-8.22.0.tgz#6403381a6eab1d229e515a62addb29b2ff3abfed"
integrity sha1-ZAM4Gm6rHSKeUVpirdspsv86v+0=
"vue-loader-v16@npm:vue-loader@^16.0.0-beta.7":
version "16.0.0-beta.7"
resolved "https://registry.npm.taobao.org/vue-loader/download/vue-loader-16.0.0-beta.7.tgz?cache=0&sync_timestamp=1599639283575&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-loader%2Fdownload%2Fvue-loader-16.0.0-beta.7.tgz#6f2726fa0e2b1fbae67895c47593bbf69f2b9ab8"