- Closes #9
- Removed dependency on vue-i18n
- Localization Changes
This commit is contained in:
Shiny Nematoda 2022-09-12 15:00:54 +00:00
parent 44db54737b
commit fcc3d455c6
No known key found for this signature in database
GPG key ID: 6506D50F5613A42D
18 changed files with 143 additions and 270 deletions

128
package-lock.json generated
View file

@ -17,7 +17,6 @@
"shaka-player": "^4.2.1", "shaka-player": "^4.2.1",
"stream-browserify": "^3.0.0", "stream-browserify": "^3.0.0",
"vue": "^3.2.38", "vue": "^3.2.38",
"vue-i18n": "^9.2.2",
"xml-js": "^1.6.11" "xml-js": "^1.6.11"
}, },
"devDependencies": { "devDependencies": {
@ -64,63 +63,6 @@
"node": ">=12" "node": ">=12"
} }
}, },
"node_modules/@intlify/core-base": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.2.2.tgz",
"integrity": "sha512-JjUpQtNfn+joMbrXvpR4hTF8iJQ2sEFzzK3KIESOx+f+uwIjgw20igOyaIdhfsVVBCds8ZM64MoeNSx+PHQMkA==",
"dependencies": {
"@intlify/devtools-if": "9.2.2",
"@intlify/message-compiler": "9.2.2",
"@intlify/shared": "9.2.2",
"@intlify/vue-devtools": "9.2.2"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/@intlify/devtools-if": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.2.2.tgz",
"integrity": "sha512-4ttr/FNO29w+kBbU7HZ/U0Lzuh2cRDhP8UlWOtV9ERcjHzuyXVZmjyleESK6eVP60tGC9QtQW9yZE+JeRhDHkg==",
"dependencies": {
"@intlify/shared": "9.2.2"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/@intlify/message-compiler": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.2.2.tgz",
"integrity": "sha512-IUrQW7byAKN2fMBe8z6sK6riG1pue95e5jfokn8hA5Q3Bqy4MBJ5lJAofUsawQJYHeoPJ7svMDyBaVJ4d0GTtA==",
"dependencies": {
"@intlify/shared": "9.2.2",
"source-map": "0.6.1"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/@intlify/shared": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.2.2.tgz",
"integrity": "sha512-wRwTpsslgZS5HNyM7uDQYZtxnbI12aGiBZURX3BTR9RFIKKRWpllTsgzHWvj3HKm3Y2Sh5LPC1r0PDCKEhVn9Q==",
"engines": {
"node": ">= 14"
}
},
"node_modules/@intlify/vue-devtools": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.2.2.tgz",
"integrity": "sha512-+dUyqyCHWHb/UcvY1MlIpO87munedm3Gn6E9WWYdWrMuYLcoIoOEVDWSS8xSwtlPU+kA+MEQTP6Q1iI/ocusJg==",
"dependencies": {
"@intlify/core-base": "9.2.2",
"@intlify/shared": "9.2.2"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/@swc/helpers": { "node_modules/@swc/helpers": {
"version": "0.3.17", "version": "0.3.17",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.3.17.tgz", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.3.17.tgz",
@ -1147,23 +1089,6 @@
"@vue/shared": "3.2.39" "@vue/shared": "3.2.39"
} }
}, },
"node_modules/vue-i18n": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.2.2.tgz",
"integrity": "sha512-yswpwtj89rTBhegUAv9Mu37LNznyu3NpyLQmozF3i1hYOhwpG8RjcjIFIIfnu+2MDZJGSZPXaKWvnQA71Yv9TQ==",
"dependencies": {
"@intlify/core-base": "9.2.2",
"@intlify/shared": "9.2.2",
"@intlify/vue-devtools": "9.2.2",
"@vue/devtools-api": "^6.2.1"
},
"engines": {
"node": ">= 14"
},
"peerDependencies": {
"vue": "^3.0.0"
}
},
"node_modules/webrtc-adapter": { "node_modules/webrtc-adapter": {
"version": "7.7.1", "version": "7.7.1",
"resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-7.7.1.tgz", "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-7.7.1.tgz",
@ -1210,48 +1135,6 @@
"dev": true, "dev": true,
"optional": true "optional": true
}, },
"@intlify/core-base": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.2.2.tgz",
"integrity": "sha512-JjUpQtNfn+joMbrXvpR4hTF8iJQ2sEFzzK3KIESOx+f+uwIjgw20igOyaIdhfsVVBCds8ZM64MoeNSx+PHQMkA==",
"requires": {
"@intlify/devtools-if": "9.2.2",
"@intlify/message-compiler": "9.2.2",
"@intlify/shared": "9.2.2",
"@intlify/vue-devtools": "9.2.2"
}
},
"@intlify/devtools-if": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.2.2.tgz",
"integrity": "sha512-4ttr/FNO29w+kBbU7HZ/U0Lzuh2cRDhP8UlWOtV9ERcjHzuyXVZmjyleESK6eVP60tGC9QtQW9yZE+JeRhDHkg==",
"requires": {
"@intlify/shared": "9.2.2"
}
},
"@intlify/message-compiler": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.2.2.tgz",
"integrity": "sha512-IUrQW7byAKN2fMBe8z6sK6riG1pue95e5jfokn8hA5Q3Bqy4MBJ5lJAofUsawQJYHeoPJ7svMDyBaVJ4d0GTtA==",
"requires": {
"@intlify/shared": "9.2.2",
"source-map": "0.6.1"
}
},
"@intlify/shared": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.2.2.tgz",
"integrity": "sha512-wRwTpsslgZS5HNyM7uDQYZtxnbI12aGiBZURX3BTR9RFIKKRWpllTsgzHWvj3HKm3Y2Sh5LPC1r0PDCKEhVn9Q=="
},
"@intlify/vue-devtools": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.2.2.tgz",
"integrity": "sha512-+dUyqyCHWHb/UcvY1MlIpO87munedm3Gn6E9WWYdWrMuYLcoIoOEVDWSS8xSwtlPU+kA+MEQTP6Q1iI/ocusJg==",
"requires": {
"@intlify/core-base": "9.2.2",
"@intlify/shared": "9.2.2"
}
},
"@swc/helpers": { "@swc/helpers": {
"version": "0.3.17", "version": "0.3.17",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.3.17.tgz", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.3.17.tgz",
@ -1873,17 +1756,6 @@
"@vue/shared": "3.2.39" "@vue/shared": "3.2.39"
} }
}, },
"vue-i18n": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.2.2.tgz",
"integrity": "sha512-yswpwtj89rTBhegUAv9Mu37LNznyu3NpyLQmozF3i1hYOhwpG8RjcjIFIIfnu+2MDZJGSZPXaKWvnQA71Yv9TQ==",
"requires": {
"@intlify/core-base": "9.2.2",
"@intlify/shared": "9.2.2",
"@intlify/vue-devtools": "9.2.2",
"@vue/devtools-api": "^6.2.1"
}
},
"webrtc-adapter": { "webrtc-adapter": {
"version": "7.7.1", "version": "7.7.1",
"resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-7.7.1.tgz", "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-7.7.1.tgz",

View file

@ -19,7 +19,6 @@
"shaka-player": "^4.2.1", "shaka-player": "^4.2.1",
"stream-browserify": "^3.0.0", "stream-browserify": "^3.0.0",
"vue": "^3.2.38", "vue": "^3.2.38",
"vue-i18n": "^9.2.2",
"xml-js": "^1.6.11" "xml-js": "^1.6.11"
}, },
"devDependencies": { "devDependencies": {

View file

@ -18,15 +18,15 @@ import Prefs from '@/components/Prefs.vue';
/* Composables */ /* Composables */
import { useStore } from '@/scripts/util.js'; import { useStore } from '@/scripts/util.js';
import { useT, useSetupLocale } from '@/scripts/i18n.js';
import { useSetupDB, useUpdatePlaylist } from '@/scripts/db.js'; import { useSetupDB, useUpdatePlaylist } from '@/scripts/db.js';
/* Stores */ /* Stores */
import { useData, usePlayer } from '@/stores/player.js'; import { useData, usePlayer } from '@/stores/player.js';
import { useResults, useArtist } from '@/stores/results.js'; import { useResults, useArtist } from '@/stores/results.js';
import { useNav } from '@/stores/misc.js'; import { useNav, useI18n } from '@/stores/misc.js';
const store = useStore(), const { t, setupLocale } = useI18n(),
store = useStore(),
data = useData(), data = useData(),
player = usePlayer(), player = usePlayer(),
results = useResults(), results = useResults(),
@ -101,7 +101,7 @@ onBeforeMount(() => {
/* Set the default locale if set */ /* Set the default locale if set */
if (store.locale) { if (store.locale) {
useSetupLocale(store.locale); setupLocale(store.locale);
} }
}); });
@ -160,7 +160,7 @@ onMounted(() => {
</div> </div>
</header> </header>
<main class="placeholder" :data-placeholder="useT('info.search')"> <main class="placeholder" :data-placeholder="t('info.search')">
<KeepAlive> <KeepAlive>
<Search v-if="nav.state.page == 'home'" @play-urls="playList" /> <Search v-if="nav.state.page == 'home'" @play-urls="playList" />
</KeepAlive> </KeepAlive>

View file

@ -181,6 +181,7 @@ body {
} }
button { button {
padding: 1px 6px;
border: none; border: none;
background: transparent; background: transparent;
color: var(--color-text); color: var(--color-text);

View file

@ -5,12 +5,13 @@ import { useResults } from '@/stores/results.js';
import { getJsonHyp } from '@/scripts/fetch.js'; import { getJsonHyp } from '@/scripts/fetch.js';
import { useRoute } from '@/scripts/util.js'; import { useRoute } from '@/scripts/util.js';
import { useT } from '@/scripts/i18n.js'; import { useI18n } from '@/stores/misc.js';
import AlbumItem from './AlbumItem.vue'; import AlbumItem from './AlbumItem.vue';
const props = defineProps(['id']), const props = defineProps(['id']),
{ getAlbum } = useResults(); { getAlbum } = useResults(),
{ t } = useI18n();
const data = reactive({ const data = reactive({
title: '', title: '',
@ -57,12 +58,12 @@ onMounted(get);
<template> <template>
<template v-if="data.title"> <template v-if="data.title">
<i class="bi bi-arrow-left back" @click="get"> {{ useT('action.back') }}</i> <i class="bi bi-arrow-left back" @click="get"> {{ t('action.back') }}</i>
<h2 class="head">{{ data.title }}</h2> <h2 class="head">{{ data.title }}</h2>
<template v-for="type in ['featured', 'spotlight', 'community']"> <template v-for="type in ['featured', 'spotlight', 'community']">
<h3 class="head">{{ useT('title.' + type) }}</h3> <h3 class="head">{{ t('title.' + type) }}</h3>
<div class="grid-3"> <div class="grid-3">
<template v-for="i in data[type]"> <template v-for="i in data[type]">
<AlbumItem <AlbumItem
@ -79,7 +80,7 @@ onMounted(get);
</template> </template>
<template v-else> <template v-else>
<h2 v-if="btns.moods.length > 0">{{ useT('title.moods') }}</h2> <h2 v-if="btns.moods.length > 0">{{ t('title.moods') }}</h2>
<div class="btn-grid"> <div class="btn-grid">
<button <button
@ -91,7 +92,7 @@ onMounted(get);
</button> </button>
</div> </div>
<h2 v-if="btns.genres.length > 0">{{ useT('title.genres') }}</h2> <h2 v-if="btns.genres.length > 0">{{ t('title.genres') }}</h2>
<div class="btn-grid"> <div class="btn-grid">
<button <button

View file

@ -1,17 +1,18 @@
<script setup> <script setup>
import { useT } from '@/scripts/i18n.js'; import { useI18n } from '@/stores/misc.js';
import TextModal from './TextModal.vue'; import TextModal from './TextModal.vue';
defineProps(['text']); defineProps(['text']);
const parse = d => const { t } = useI18n(),
new DOMParser().parseFromString(d, 'text/html').body.innerText; parse = d => new DOMParser().parseFromString(d, 'text/html').body.innerText;
</script> </script>
<template> <template>
<TextModal> <TextModal>
<template #content> <template #content>
<pre class="placeholder" :data-placeholder="useT('info.no_info')">{{ <pre class="placeholder" :data-placeholder="t('info.no_info')">{{
text ? parse(text.replaceAll('<br>', '\n')) : '' text ? parse(text.replaceAll('<br>', '\n')) : ''
}}</pre> }}</pre>
</template> </template>

View file

@ -3,11 +3,12 @@ import { ref, watch } from 'vue';
import { getJsonHyp } from '@/scripts/fetch.js'; import { getJsonHyp } from '@/scripts/fetch.js';
import { useData } from '@/stores/player.js'; import { useData } from '@/stores/player.js';
import { useT } from '@/scripts/i18n.js'; import { useI18n } from '@/stores/misc.js';
import TextModal from './TextModal.vue'; import TextModal from './TextModal.vue';
const data = useData(), const { t } = useI18n(),
data = useData(),
text = ref(''), text = ref(''),
source = ref(''), source = ref(''),
status = ref(false); status = ref(false);
@ -51,8 +52,8 @@ watch(
class="placeholder" class="placeholder"
:data-placeholder=" :data-placeholder="
data.state.urls[0]?.url && !status data.state.urls[0]?.url && !status
? useT('info.lyrics.load') ? t('lyrics.load')
: useT('info.lyrics.void') : t('lyrics.void')
" "
>{{ text }}</pre >{{ text }}</pre
> >

View file

@ -7,7 +7,7 @@ import Modal from './Modal.vue';
import { useRand } from '@/scripts/colors.js'; import { useRand } from '@/scripts/colors.js';
import { useStore } from '@/scripts/util.js'; import { useStore } from '@/scripts/util.js';
import { getJsonAuth, getAuthPlaylists } from '@/scripts/fetch.js'; import { getJsonAuth, getAuthPlaylists } from '@/scripts/fetch.js';
import { useT } from '@/scripts/i18n.js'; import { useI18n } from '@/stores/misc.js';
import { import {
useListPlaylists, useListPlaylists,
@ -16,7 +16,8 @@ import {
useUpdatePlaylist, useUpdatePlaylist,
} from '../scripts/db.js'; } from '../scripts/db.js';
const store = useStore(), const { t } = useI18n(),
store = useStore(),
auth = ref(!!store.auth); auth = ref(!!store.auth);
const emit = defineEmits(['play-urls', 'open-playlist']), const emit = defineEmits(['play-urls', 'open-playlist']),
@ -202,7 +203,7 @@ onMounted(async () => {
<Modal <Modal
n="2" n="2"
:display="show.new" :display="show.new"
:title="useT('playlist.create')" :title="t('playlist.create')"
@show=" @show="
e => { e => {
show.new = e; show.new = e;
@ -211,10 +212,10 @@ onMounted(async () => {
<template #content> <template #content>
<div v-if="auth" class="tabs"> <div v-if="auth" class="tabs">
<button :data-active="!user.create" @click="user.create = false"> <button :data-active="!user.create" @click="user.create = false">
{{ useT('title.local') }} {{ t('title.local') }}
</button> </button>
<button :data-active="user.create" @click="user.create = true"> <button :data-active="user.create" @click="user.create = true">
{{ useT('title.remote') }} {{ t('title.remote') }}
</button> </button>
</div> </div>
@ -225,9 +226,9 @@ onMounted(async () => {
v-model="text" /> v-model="text" />
</template> </template>
<template #buttons> <template #buttons>
<button @click="show.new = false">{{ useT('action.cancel') }}</button> <button @click="show.new = false">{{ t('action.cancel') }}</button>
<button @click="user.create ? createPlaylist() : Create()"> <button @click="user.create ? createPlaylist() : Create()">
{{ useT('action.create') }} {{ t('action.create') }}
</button> </button>
</template> </template>
</Modal> </Modal>
@ -235,7 +236,7 @@ onMounted(async () => {
<Modal <Modal
:n="sync.type == 'send' ? 2 : 1" :n="sync.type == 'send' ? 2 : 1"
:display="show.sync" :display="show.sync"
:title="useT('playlist.sync')" :title="t('playlist.sync')"
@show=" @show="
e => { e => {
show.sync = e; show.sync = e;
@ -246,10 +247,10 @@ onMounted(async () => {
<button <button
:data-active="sync.type == 'send'" :data-active="sync.type == 'send'"
@click="sync.type = 'send'"> @click="sync.type = 'send'">
{{ useT('action.send') }} {{ t('action.send') }}
</button> </button>
<button :data-active="sync.type == 'rec'" @click="sync.type = 'rec'"> <button :data-active="sync.type == 'rec'" @click="sync.type = 'rec'">
{{ useT('action.receive') }} {{ t('action.receive') }}
</button> </button>
</div> </div>
@ -267,11 +268,9 @@ onMounted(async () => {
</template> </template>
<template #buttons> <template #buttons>
<button @click="show.sync = false">{{ useT('action.cancel') }}</button> <button @click="show.sync = false">{{ t('action.cancel') }}</button>
<button v-if="sync.type == 'send'" @click="Send"> <button v-if="sync.type == 'send'" @click="Send">
{{ {{ sync.type == 'send' ? t('action.send') : t('action.recieve') }}
sync.type == 'send' ? useT('action.send') : useT('action.recieve')
}}
</button> </button>
</template> </template>
</Modal> </Modal>
@ -284,7 +283,7 @@ onMounted(async () => {
@click="show.sync = true"></div> @click="show.sync = true"></div>
</div> </div>
<h2 v-if="list.length > 0">{{ useT('playlist.local') }}</h2> <h2 v-if="list.length > 0">{{ t('playlist.local') }}</h2>
<div class="grid-3"> <div class="grid-3">
<template v-for="i in list"> <template v-for="i in list">
@ -296,7 +295,7 @@ onMounted(async () => {
</template> </template>
</div> </div>
<h2 class="login-h">{{ useT('playlist.remote') }}</h2> <h2 class="login-h">{{ t('playlist.remote') }}</h2>
<div v-if="auth" class="grid-3"> <div v-if="auth" class="grid-3">
<template v-for="i in user.playlists"> <template v-for="i in user.playlists">
@ -321,7 +320,7 @@ onMounted(async () => {
autocomplete="password" autocomplete="password"
@change="user.password = $event.target.value" @change="user.password = $event.target.value"
required /> required />
<button @click="Login" class="textbox">{{ useT('title.login') }}</button> <button @click="Login" class="textbox">{{ t('title.login') }}</button>
<p> <p>
Don't have an account? register on Don't have an account? register on

View file

@ -1,8 +1,9 @@
<script setup> <script setup>
import { useData, usePlayer } from '@/stores/player.js'; import { useData, usePlayer } from '@/stores/player.js';
import { useT } from '@/scripts/i18n.js'; import { useI18n } from '@/stores/misc.js';
const player = usePlayer(), const { t } = useI18n(),
player = usePlayer(),
data = useData(); data = useData();
defineEmits(['playthis']); defineEmits(['playthis']);
@ -10,7 +11,7 @@ defineEmits(['playthis']);
<template> <template>
<Transition name="fade"> <Transition name="fade">
<div class="pl-modal placeholder" :data-placeholder="useT('playlist.add')"> <div class="pl-modal placeholder" :data-placeholder="t('playlist.add')">
<template v-for="plurl in data.state.urls"> <template v-for="plurl in data.state.urls">
<div class="pl-item" @click="$emit('playthis', plurl)"> <div class="pl-item" @click="$emit('playthis', plurl)">
<span v-if="data.state.url == plurl.url" class="bars-wrap"> <span v-if="data.state.url == plurl.url" class="bars-wrap">

View file

@ -2,10 +2,11 @@
import { ref, onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import { getJson } from '@/scripts/fetch.js'; import { getJson } from '@/scripts/fetch.js';
import { SUPPORTED_LOCALES, useT, useSetupLocale } from '@/scripts/i18n.js'; import { SUPPORTED_LOCALES, useI18n } from '@/stores/misc.js';
import { useStore } from '@/scripts/util.js'; import { useStore } from '@/scripts/util.js';
const instances = ref([]), const { t, setupLocale } = useI18n(),
instances = ref([]),
hypInstances = ref([]), hypInstances = ref([]),
next = ref(false); next = ref(false);
@ -44,7 +45,7 @@ function setTheme(theme) {
} }
function setLang(locale) { function setLang(locale) {
useSetupLocale(locale); setupLocale(locale);
setStore('locale', locale); setStore('locale', locale);
} }
@ -58,7 +59,7 @@ onMounted(() => {
</script> </script>
<template> <template>
<h2>{{ useT('pref.theme') }}</h2> <h2>{{ t('pref.theme') }}</h2>
<select <select
id="pref-theme" id="pref-theme"
:value="getTheme()" :value="getTheme()"
@ -79,7 +80,7 @@ onMounted(() => {
<option v-for="i in SUPPORTED_LOCALES" :value="i.code">{{ i.name }}</option> <option v-for="i in SUPPORTED_LOCALES" :value="i.code">{{ i.name }}</option>
</select> </select>
<h2>{{ useT('pref.player') }}</h2> <h2>{{ t('pref.player') }}</h2>
<div class="left"> <div class="left">
<input <input
@ -88,11 +89,11 @@ onMounted(() => {
id="pref-chk-next" id="pref-chk-next"
@change="setStore('next', $event.target.checked)" @change="setStore('next', $event.target.checked)"
v-model="next" /> v-model="next" />
<label for="pref-chk-next">{{ useT('pref.auto_queue') }}</label> <label for="pref-chk-next">{{ t('pref.auto_queue') }}</label>
</div> </div>
<div class="left"> <div class="left">
<label for="pref-codec">{{ useT('pref.codec') }}</label> <label for="pref-codec">{{ t('pref.codec') }}</label>
<select <select
id="pref-codec" id="pref-codec"
name="pref-codec" name="pref-codec"
@ -106,20 +107,20 @@ onMounted(() => {
</div> </div>
<div class="left"> <div class="left">
<label for="pref-quality">{{ useT('pref.quality') }}</label> <label for="pref-quality">{{ t('pref.quality') }}</label>
<select <select
id="pref-quality" id="pref-quality"
name="pref-quality" name="pref-quality"
:value="getStore('quality') || 'auto'" :value="getStore('quality') || 'auto'"
@change="setStore('quality', $event.target.value)"> @change="setStore('quality', $event.target.value)">
<option value="auto">{{ useT('pref.auto') }}</option> <option value="auto">{{ t('pref.auto') }}</option>
<option value="best">{{ useT('pref.best') }}</option> <option value="best">{{ t('pref.best') }}</option>
<option value="worst">{{ useT('pref.worst') }}</option> <option value="worst">{{ t('pref.worst') }}</option>
</select> </select>
</div> </div>
<div class="left"> <div class="left">
<label for="pref-volume">{{ useT('pref.volume') }}</label> <label for="pref-volume">{{ t('pref.volume') }}</label>
<input <input
type="number" type="number"
name="pref-volume" name="pref-volume"
@ -130,7 +131,7 @@ onMounted(() => {
@change="setStore('vol', $event.target.value)" /> @change="setStore('vol', $event.target.value)" />
</div> </div>
<h2>{{ useT('pref.instances.hyp') }}</h2> <h2>{{ t('instances.hyp') }}</h2>
<select <select
v-if="hypInstances" v-if="hypInstances"
@ -148,8 +149,8 @@ onMounted(() => {
<table> <table>
<thead> <thead>
<tr> <tr>
<th>{{ useT('pref.instances.name') }}</th> <th>{{ t('instances.name') }}</th>
<th>{{ useT('pref.instances.loc') }}</th> <th>{{ t('instances.loc') }}</th>
</tr> </tr>
</thead> </thead>
<tbody v-for="i in hypInstances"> <tbody v-for="i in hypInstances">
@ -165,7 +166,7 @@ onMounted(() => {
</table> </table>
</div> </div>
<h2>{{ useT('pref.instances.piped') }}</h2> <h2>{{ t('instances.piped') }}</h2>
<select <select
v-if="instances" v-if="instances"
:value="getStore('pipedapi') || 'pipedapi.kavin.rocks'" :value="getStore('pipedapi') || 'pipedapi.kavin.rocks'"
@ -178,7 +179,7 @@ onMounted(() => {
</option> </option>
</select> </select>
<h3>{{ useT('pref.instances.auth') }}</h3> <h3>{{ t('instances.auth') }}</h3>
<select <select
v-if="instances" v-if="instances"
@ -196,11 +197,11 @@ onMounted(() => {
<table> <table>
<thead> <thead>
<tr> <tr>
<th>{{ useT('pref.instances.name') }}</th> <th>{{ t('instances.name') }}</th>
<th>{{ useT('pref.instances.loc') }}</th> <th>{{ t('instances.loc') }}</th>
<th>{{ useT('pref.instances.cdn') }}</th> <th>{{ t('instances.cdn') }}</th>
<th>{{ useT('pref.instances.up_to_date') }}</th> <th>{{ t('instances.up_to_date') }}</th>
<th>{{ useT('pref.instances.version') }}</th> <th>{{ t('instances.version') }}</th>
</tr> </tr>
</thead> </thead>
<tbody v-for="i in instances"> <tbody v-for="i in instances">

View file

@ -8,13 +8,13 @@ import AlbumItem from './AlbumItem.vue';
import { getJsonPiped, getPipedQuery } from '@/scripts/fetch.js'; import { getJsonPiped, getPipedQuery } from '@/scripts/fetch.js';
import { useRoute } from '@/scripts/util.js'; import { useRoute } from '@/scripts/util.js';
import { useCreatePlaylist } from '@/scripts/db.js'; import { useCreatePlaylist } from '@/scripts/db.js';
import { useT } from '@/scripts/i18n.js';
import { useResults, useArtist } from '@/stores/results.js'; import { useResults, useArtist } from '@/stores/results.js';
import { useData } from '@/stores/player.js'; import { useData } from '@/stores/player.js';
import { useNav } from '@/stores/misc.js'; import { useNav, useI18n } from '@/stores/misc.js';
const results = useResults(), const { t } = useI18n(),
results = useResults(),
data = useData(), data = useData(),
nav = useNav(), nav = useNav(),
artist = useArtist(); artist = useArtist();
@ -137,14 +137,14 @@ onUpdated(() => {
getSearch(nav.state.search); getSearch(nav.state.search);
" "
:data-active="f == filter"> :data-active="f == filter">
{{ useT('title.' + f.split('_')[1]) }} {{ t('title.' + f.split('_')[1]) }}
</button> </button>
</div> </div>
<div <div
v-if="results.items.songs && results.items.songs.items[0]" v-if="results.items.songs && results.items.songs.items[0]"
class="search-songs"> class="search-songs">
<h2>{{ useT('title.songs') }}</h2> <h2>{{ t('title.songs') }}</h2>
<div class="grid"> <div class="grid">
<template v-for="song in results.items.songs.items"> <template v-for="song in results.items.songs.items">
<SongItem <SongItem
@ -180,14 +180,14 @@ onUpdated(() => {
" "
class="more" class="more"
:href="'/playlist?list=' + artist.state.playlistId" :href="'/playlist?list=' + artist.state.playlistId"
>{{ useT('info.see_all') }}</a >{{ t('info.see_all') }}</a
> >
</div> </div>
<div <div
v-if="results.items.albums && results.items.albums.items[0]" v-if="results.items.albums && results.items.albums.items[0]"
class="search-albums"> class="search-albums">
<h2>{{ useT('title.albums') }}</h2> <h2>{{ t('title.albums') }}</h2>
<div class="grid-3"> <div class="grid-3">
<template v-for="album in results.items.albums.items"> <template v-for="album in results.items.albums.items">
<AlbumItem <AlbumItem
@ -204,7 +204,7 @@ onUpdated(() => {
<div <div
v-if="results.items.singles && results.items.singles.items[0]" v-if="results.items.singles && results.items.singles.items[0]"
class="search-albums"> class="search-albums">
<h2>{{ useT('title.singles') }}</h2> <h2>{{ t('title.singles') }}</h2>
<div class="grid-3"> <div class="grid-3">
<template v-for="single in results.items.singles.items"> <template v-for="single in results.items.singles.items">
<AlbumItem <AlbumItem
@ -225,9 +225,7 @@ onUpdated(() => {
class="search-artists"> class="search-artists">
<h2> <h2>
{{ {{
results.items.artists results.items.artists ? t('title.artists') : t('title.similar_artists')
? useT('title.artists')
: useT('title.similar_artists')
}} }}
</h2> </h2>
<div class="grid-3 circle"> <div class="grid-3 circle">

View file

@ -1,9 +1,9 @@
<script setup> <script setup>
import { ref } from 'vue'; import { ref } from 'vue';
import { useNav } from '@/stores/misc.js'; import { useNav, useI18n } from '@/stores/misc.js';
import { useT } from '@/scripts/i18n.js';
const show = ref(false), const { t } = useI18n(),
show = ref(false),
nav = useNav(); nav = useNav();
</script> </script>
@ -18,7 +18,7 @@ const show = ref(false),
<input <input
type="text" type="text"
aria-label="Search Input" aria-label="Search Input"
:placeholder="useT('title.search') + '...'" :placeholder="t('title.search') + '...'"
@change=" @change="
nav.state.search = $event.target.value; nav.state.search = $event.target.value;
nav.state.page = 'home'; nav.state.page = 'home';

View file

@ -1,7 +1,7 @@
<script setup> <script setup>
import { ref, onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import { useRand } from '../scripts/colors.js'; import { useRand } from '@/scripts/colors.js';
import { useArtist } from '@/stores/results.js'; import { useArtist } from '@/stores/results.js';
import { useData, usePlayer } from '@/stores/player.js'; import { useData, usePlayer } from '@/stores/player.js';

View file

@ -6,11 +6,12 @@ import Modal from './Modal.vue';
import { useStore } from '@/scripts/util.js'; import { useStore } from '@/scripts/util.js';
import { useListPlaylists, useUpdatePlaylist } from '@/scripts/db.js'; import { useListPlaylists, useUpdatePlaylist } from '@/scripts/db.js';
import { getAuthPlaylists, getJsonAuth } from '@/scripts/fetch.js'; import { getAuthPlaylists, getJsonAuth } from '@/scripts/fetch.js';
import { useT } from '@/scripts/i18n.js';
import { useData, usePlayer } from '@/stores/player.js'; import { useData, usePlayer } from '@/stores/player.js';
import { useI18n } from '@/stores/misc.js';
const data = useData(), const { t } = useI18n(),
data = useData(),
player = usePlayer(), player = usePlayer(),
store = useStore(); store = useStore();
@ -108,7 +109,7 @@ function Save() {
</template> </template>
<template #buttons> <template #buttons>
<button aria-label="Cancel" @click="showme.pl = false"> <button aria-label="Cancel" @click="showme.pl = false">
{{ useT('action.cancel') }} {{ t('action.cancel') }}
</button> </button>
<button <button
aria-label="Add Song" aria-label="Add Song"
@ -116,7 +117,7 @@ function Save() {
Save(); Save();
showme.pl = false; showme.pl = false;
"> ">
{{ useT('action.add') }} {{ t('action.add') }}
</button> </button>
</template> </template>
</Modal> </Modal>

View file

@ -41,25 +41,25 @@
"auto": "auto", "auto": "auto",
"best": "best", "best": "best",
"worst": "worst", "worst": "worst",
"volume": "Default Volume", "volume": "Default Volume"
"instances": {
"hyp": "Hyperpipe Instance",
"piped": "Piped Instance",
"auth": "Authentication Instance",
"name": "Name",
"loc": "Locations",
"cdn": "CDN",
"up_to_date": "Up to Date",
"version": "Version"
}
}, },
"info": { "info": {
"see_all": "See All", "see_all": "See All",
"search": "Start Searching", "search": "Start Searching",
"no_info": "No Information Available", "no_info": "No Information Available"
"lyrics": { },
"load": "Fetching Lyrics", "instances": {
"void": "No Lyrics" "hyp": "Hyperpipe Instance",
} "piped": "Piped Instance",
"auth": "Authentication Instance",
"name": "Name",
"loc": "Locations",
"cdn": "CDN",
"up_to_date": "Up to Date",
"version": "Version"
},
"lyrics": {
"load": "Fetching Lyrics",
"void": "No Lyrics"
} }
} }

View file

@ -1,26 +1,12 @@
import { createApp } from 'vue'; import { createApp } from 'vue';
import { createPinia } from 'pinia'; import { createPinia } from 'pinia';
import { createI18n } from 'vue-i18n';
import App from './App.vue'; import App from './App.vue';
import en from '@/locales/en.json';
import('bootstrap-icons/font/bootstrap-icons.css'); import('bootstrap-icons/font/bootstrap-icons.css');
const pinia = createPinia(), const pinia = createPinia(),
i18n = createI18n({
globalInjection: true,
legacy: false,
locale: 'en',
fallbackLocale: 'en',
messages: {
en: en,
},
}),
app = createApp(App); app = createApp(App);
window.i18n = i18n;
app.use(i18n);
app.use(pinia); app.use(pinia);
app.mount('#app'); app.mount('#app');

View file

@ -1,29 +0,0 @@
import { useI18n } from 'vue-i18n';
export const SUPPORTED_LOCALES = [
{
code: 'en',
name: 'English',
},
];
export function useT(path) {
const { messages, locale, fallbackLocale } = useI18n(),
msgs = messages.value?.[locale.value],
fallback = messages.value?.[fallbackLocale.value],
keys = path.split('.'),
translate = msg => keys.reduce((obj, i) => obj?.[i], msg),
translated = translate(msgs) || translate(fallback);
return translated || path;
}
export function useSetupLocale(locale) {
import(`@/locales/${locale}.json`)
.then(mod => mod.default)
.then(mod => {
window.i18n.global.messages.value[locale] = mod;
});
window.i18n.global.locale.value = locale;
}

View file

@ -1,6 +1,19 @@
import { reactive } from 'vue'; import { reactive, ref } from 'vue';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import en from '@/locales/en.json';
export const SUPPORTED_LOCALES = [
{
code: 'en',
name: 'English',
},
{
code: 'fr',
name: 'French',
},
];
export const useNav = defineStore('nav', () => { export const useNav = defineStore('nav', () => {
const state = reactive({ const state = reactive({
search: '', search: '',
@ -9,3 +22,31 @@ export const useNav = defineStore('nav', () => {
return { state }; return { state };
}); });
export const useI18n = defineStore('i18n', () => {
const locale = ref('en'),
map = ref({
en: en,
});
function t(path) {
const msgs = map.value[locale.value],
fallback = map.value['en'],
keys = path.split('.'),
translate = msg => keys.reduce((obj, i) => obj?.[i], msg),
translated = translate(msgs) || translate(fallback);
return translated || path;
}
function setupLocale(code) {
import(`@/locales/${code}.json`)
.then(mod => mod.default)
.then(mod => {
map.value[code] = mod;
locale.value = code;
});
}
return { locale, map, t, setupLocale };
});