mirror of
https://github.com/mmjee/Piped-Material.git
synced 2024-12-06 19:26:36 +01:00
Internationalization upgrades (PR 13)
commit 56e4933c639ff32ea18470ae1534fa4d06b448e3 Author: root <root@git.maharshi.ninja> Date: Tue Jan 4 11:09:22 2022 +0530 Also format dates commit 01d379537b2e41819e2093b33dfd2a026bcb7281 Author: root <root@git.maharshi.ninja> Date: Sun Jan 2 13:48:36 2022 +0530 Another Bengali translation update commit 53063d4265f85db7be14135ffaa095647f233841 Author: root <root@git.maharshi.ninja> Date: Sat Jan 1 11:41:44 2022 +0530 Standardize to BCP47 and adjust commit 1b48f79cce411f8d18cf9e5059916b1c50719cd5 Author: root <root@git.maharshi.ninja> Date: Sat Jan 1 11:17:28 2022 +0530 Localized number formatting + Bengali translations update commit 6b3e30db37338334f03d98b92d124741446fb470 Author: root <root@git.maharshi.ninja> Date: Sat Jan 1 10:41:27 2022 +0530 Fix fontloading commit 5b08962971f8edb5030bc5ba79972bbb209b3d31 Author: root <root@git.maharshi.ninja> Date: Sat Jan 1 10:38:29 2022 +0530 Even more standardization commit bc6a4dfb335647472ea091b302b627f475bed879 Author: root <root@git.maharshi.ninja> Date: Sat Jan 1 10:25:29 2022 +0530 Rename as per BCP-47 and standardize commit 106f84fa0486d19504fd244aaab41f9cd4a8a513 Author: root <root@git.maharshi.ninja> Date: Sat Jan 1 10:13:32 2022 +0530 i18n on steroids
This commit is contained in:
+7
-7
@@ -110,20 +110,20 @@ export default {
|
||||
|
||||
data: () => ({
|
||||
languageOptions: [
|
||||
{ value: 'zh_Hant', text: 'Chinese (Traditional)' },
|
||||
{ value: 'en', text: 'English' },
|
||||
{ value: 'fr', text: 'French' },
|
||||
{ value: 'de', text: 'German' },
|
||||
{ value: 'el', text: 'Greek' },
|
||||
{ value: 'zh_Hans', text: 'Chinese (Simplified, only loads the font)' },
|
||||
{ value: 'jp', text: 'Japanese (only loads fonts)' },
|
||||
{ value: 'kr', text: 'Korean (only loads fonts)' },
|
||||
{ value: 'cyrl', text: 'Cyrillic Languages (fonts)' },
|
||||
{ value: 'zh-Hans', text: 'Chinese (Simplified, only loads the font)' },
|
||||
{ value: 'zh-Hant', text: 'Chinese (Traditional)' },
|
||||
{ value: 'ja', text: 'Japanese (only loads fonts)' },
|
||||
{ value: 'ko', text: 'Korean (only loads fonts)' },
|
||||
{ value: 'ru', text: 'Russian (only loads fonts)' },
|
||||
{ value: 'lt', text: 'Lithuanian' },
|
||||
{ value: 'ml', text: 'Malayalam' },
|
||||
{ value: 'nb_NO', text: 'Norwegian Bokmål' },
|
||||
{ value: 'nb-NO', text: 'Norwegian Bokmål' },
|
||||
{ value: 'tr', text: 'Turkish' },
|
||||
{ value: 'bn_beng', text: 'Bengali (বাংলা)' }
|
||||
{ value: 'bn-Beng', text: 'Bengali (বাংলা)' }
|
||||
// Incomplete, DO NOT USE
|
||||
/* { value: 'bn_latn', text: 'Bengali (Latin)' }, */
|
||||
].sort((a, b) => {
|
||||
|
||||
@@ -10,18 +10,16 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LibPiped } from '@/tools/libpiped'
|
||||
|
||||
export default {
|
||||
name: 'ExpandableDate',
|
||||
props: ['date'],
|
||||
computed: {
|
||||
timeAgo () {
|
||||
return LibPiped.timeAgo(this.date)
|
||||
return this.$store.getters['i18n/fmtRelative'](this.date)
|
||||
},
|
||||
|
||||
formattedDate () {
|
||||
return LibPiped.formatFullDate(this.date)
|
||||
return this.$store.getters['i18n/fmtDateTime'](this.date)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
<v-divider class="my-2" />
|
||||
|
||||
<v-icon>mdi-thumb-up</v-icon> {{ numberFormat(comment.likeCount) }}
|
||||
<v-icon>mdi-thumb-up</v-icon> {{ this.$store.getters['i18n/fmtNumber'](comment.likeCount) }}
|
||||
<v-icon v-if="comment.hearted" class="ml-4">mdi-heart</v-icon>
|
||||
<br />
|
||||
<v-btn class="mt-1" text :loading="requestInProgress" :disabled="requestInProgress" @click="loadReplies" v-if="!subComment && childComments.length === 0 && comment.repliesPage != null">Load Replies</v-btn>
|
||||
@@ -45,9 +45,6 @@ export default {
|
||||
childComments: []
|
||||
}),
|
||||
methods: {
|
||||
numberFormat (...args) {
|
||||
return LibPiped.numberFormat(...args)
|
||||
},
|
||||
async loadReplies () {
|
||||
let nextpage = null
|
||||
this.requestInProgress = true
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<h5 @click="navigate" @keypress.enter="navigate" role="link">{{ video.uploaderName || video.uploader }}</h5>
|
||||
</router-link>
|
||||
<slot />
|
||||
{{ $tc('counts.views', video.views, { n: numberFormat(video.views) }) }}<br />
|
||||
{{ $tc('counts.views', video.views, { n: this.$store.getters['i18n/fmtNumber'](video.views) }) }}<br />
|
||||
{{ video.uploadedDate }} <br />
|
||||
{{ timeFormat(video.duration) }}
|
||||
</v-card-text>
|
||||
@@ -74,11 +74,6 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
numberFormat (...args) {
|
||||
return LibPiped.numberFormat(...args)
|
||||
},
|
||||
|
||||
timeFormat (...args) {
|
||||
return LibPiped.timeFormat(...args)
|
||||
}
|
||||
|
||||
+10
-8
@@ -4,7 +4,7 @@ import App from './App.vue'
|
||||
import '@mdi/font/css/materialdesignicons.min.css'
|
||||
import '@/styles/main.scss'
|
||||
|
||||
import { i18n } from '@/plugins/i18n'
|
||||
import { i18n, i18nInitialized } from '@/plugins/i18n'
|
||||
import vuetify from '@/plugins/vuetify'
|
||||
import store from '@/store'
|
||||
import router from '@/router'
|
||||
@@ -13,10 +13,12 @@ import './registerServiceWorker'
|
||||
|
||||
Vue.config.productionTip = false
|
||||
|
||||
new Vue({
|
||||
vuetify,
|
||||
i18n,
|
||||
store,
|
||||
router,
|
||||
render: h => h(App)
|
||||
}).$mount('#app')
|
||||
i18nInitialized.then(() => {
|
||||
new Vue({
|
||||
vuetify,
|
||||
i18n,
|
||||
store,
|
||||
router,
|
||||
render: h => h(App)
|
||||
}).$mount('#app')
|
||||
})
|
||||
|
||||
+37
-16
@@ -1,6 +1,8 @@
|
||||
import Vue from 'vue'
|
||||
import VueI18n from 'vue-i18n'
|
||||
import TimeAgo from 'javascript-time-ago'
|
||||
|
||||
import store from '@/store'
|
||||
import ENTranslations from '@/translations/en.json'
|
||||
|
||||
Vue.use(VueI18n)
|
||||
@@ -15,6 +17,10 @@ export const i18n = new VueI18n({
|
||||
messages
|
||||
})
|
||||
|
||||
const TIME_AGO_EXCEPTIONS = {
|
||||
'bn-Beng': 'bn'
|
||||
}
|
||||
|
||||
async function syncStylesPerLanguage (locale) {
|
||||
switch (locale) {
|
||||
// All the latin languages
|
||||
@@ -25,43 +31,43 @@ async function syncStylesPerLanguage (locale) {
|
||||
case 'lt':
|
||||
case 'ml':
|
||||
case 'tr':
|
||||
case 'bn_latn':
|
||||
case 'bn-Latn':
|
||||
// Don't need to import fonts because Latin fonts are always loaded
|
||||
document.body.classList.remove(...document.body.classList)
|
||||
document.body.classList.add('latin')
|
||||
break
|
||||
// Bengali script
|
||||
case 'bn_beng':
|
||||
// Bengali script
|
||||
case 'bn-Beng':
|
||||
await import('@fontsource/hind-siliguri/bengali.css')
|
||||
document.body.classList.remove(...document.body.classList)
|
||||
document.body.classList.add('bengali')
|
||||
break
|
||||
// Other languages
|
||||
// NOTE: if you are a native speaker & want to see a different font, just email me or join the channel
|
||||
case 'zh_Hant':
|
||||
// Other languages
|
||||
// NOTE: if you are a native speaker & want to see a different font, just email me or join the channel
|
||||
case 'zh-Hant':
|
||||
await import('@fontsource/noto-sans-tc/chinese-traditional.css')
|
||||
document.body.classList.remove(...document.body.classList)
|
||||
document.body.classList.add('traditional-chinese')
|
||||
break
|
||||
case 'zh_Hans':
|
||||
case 'zh-Hans':
|
||||
await import('@fontsource/noto-sans-sc/chinese-simplified.css')
|
||||
document.body.classList.remove(...document.body.classList)
|
||||
document.body.classList.add('simplified-chinese')
|
||||
break
|
||||
case 'jp':
|
||||
case 'ja':
|
||||
await import('@fontsource/noto-sans-jp/japanese.css')
|
||||
document.body.classList.remove(...document.body.classList)
|
||||
document.body.classList.add('japanese')
|
||||
break
|
||||
case 'kr':
|
||||
case 'ko':
|
||||
await import('@fontsource/noto-sans-kr/korean.css')
|
||||
document.body.classList.remove(...document.body.classList)
|
||||
document.body.classList.add('korean')
|
||||
break
|
||||
case 'cyrl':
|
||||
case 'ru':
|
||||
await import('@fontsource/nunito-sans/cyrillic.css')
|
||||
document.body.classList.remove(...document.body.classList)
|
||||
document.body.classList.add('cyrillic')
|
||||
document.body.classList.add('russian')
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -71,7 +77,7 @@ export async function loadLocale (locale) {
|
||||
return
|
||||
}
|
||||
// load locale messages with dynamic import
|
||||
const messages = await import(/* webpackChunkName: "locale-[request]" */ `@/translations/${locale}.json`)
|
||||
const messages = await import(/* webpackChunkName: "translations-[request]" */ `@/translations/${locale}.json`)
|
||||
|
||||
// set locale and locale message
|
||||
i18n.setLocaleMessage(locale, messages.default)
|
||||
@@ -79,9 +85,24 @@ export async function loadLocale (locale) {
|
||||
return Vue.nextTick()
|
||||
}
|
||||
|
||||
async function loadFormatting (locale) {
|
||||
const tal = TIME_AGO_EXCEPTIONS[locale] || locale
|
||||
const data = await import(/* webpackChunkName: "timeago-[request]" */ `javascript-time-ago/locale/${tal}.json`)
|
||||
TimeAgo.addLocale(data)
|
||||
const timeAgo = new TimeAgo(locale)
|
||||
|
||||
store.commit('i18n/updateState', {
|
||||
intlLocale: locale,
|
||||
timeAgo
|
||||
})
|
||||
}
|
||||
|
||||
export async function changeLocale (lang) {
|
||||
await loadLocale(lang)
|
||||
await syncStylesPerLanguage(lang)
|
||||
await Promise.all([
|
||||
loadLocale(lang),
|
||||
syncStylesPerLanguage(lang),
|
||||
loadFormatting(lang)
|
||||
])
|
||||
i18n.locale = lang
|
||||
window.localStorage.setItem('LOCALE', lang)
|
||||
}
|
||||
@@ -92,7 +113,7 @@ function initializeLocalLocale () {
|
||||
// Default language
|
||||
lang = 'en'
|
||||
}
|
||||
changeLocale(lang).catch(e => console.error(e))
|
||||
return changeLocale(lang)
|
||||
}
|
||||
|
||||
initializeLocalLocale()
|
||||
export const i18nInitialized = initializeLocalLocale()
|
||||
|
||||
@@ -111,10 +111,6 @@ export default {
|
||||
})
|
||||
},
|
||||
|
||||
numberFormat (...args) {
|
||||
return LibPiped.numberFormat(...args)
|
||||
},
|
||||
|
||||
timeFormat (...args) {
|
||||
return LibPiped.timeFormat(...args)
|
||||
},
|
||||
|
||||
@@ -17,19 +17,19 @@
|
||||
<v-card outlined color="bgTwo">
|
||||
<v-card-title class="text-h5">{{ video.title }}</v-card-title>
|
||||
<v-card-subtitle class="text-subtitle-1">
|
||||
<v-row>
|
||||
<v-col md="5" align-self="start">
|
||||
{{ $tc('counts.views', video.views, { n: addCommas(video.views) }) }}
|
||||
<v-row align="center">
|
||||
<v-col>
|
||||
{{ $tc('counts.views', video.views, { n: $store.getters['i18n/fmtNumber'](video.views) }) }}
|
||||
•
|
||||
{{ video.uploadDate }}
|
||||
{{ $store.getters['i18n/fmtDate'](new Date(video.uploadDate)) }}
|
||||
<!-- TODO make translatable -->
|
||||
<span v-if="lastWatch">
|
||||
•
|
||||
Last watched till {{ lastWatchDurationH }}
|
||||
{{ $t('misc.lastWatchedTill', { t: lastWatchDurationH }) }}
|
||||
<ExpandableDate :date="lastWatch.timestamp" />
|
||||
</span>
|
||||
</v-col>
|
||||
<v-col offset-md="4" md="3" align-self="end">
|
||||
<v-col style="text-align: right">
|
||||
<v-icon>mdi-thumb-up</v-icon>
|
||||
<b class="ml-2">{{ addCommas(video.likes) }}</b>
|
||||
<v-icon class="ml-2">mdi-thumb-down</v-icon>
|
||||
@@ -222,10 +222,6 @@ export default {
|
||||
window.location.href = url.href
|
||||
},
|
||||
|
||||
numberFormat (...args) {
|
||||
return LibPiped.numberFormat(...args)
|
||||
},
|
||||
|
||||
addCommas (...args) {
|
||||
return LibPiped.addCommas(...args)
|
||||
},
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
export const i18nStore = {
|
||||
namespaced: true,
|
||||
state: () => ({
|
||||
fullFormatter: null,
|
||||
dateFormatter: null,
|
||||
NF: null,
|
||||
timeAgo: null
|
||||
}),
|
||||
|
||||
mutations: {
|
||||
updateState (state, { intlLocale, NF, timeAgo }) {
|
||||
state.fullFormatter = new Intl.DateTimeFormat(intlLocale, {
|
||||
dateStyle: 'full',
|
||||
timeStyle: 'full'
|
||||
})
|
||||
state.dateFormatter = new Intl.DateTimeFormat(intlLocale, {
|
||||
dateStyle: 'long'
|
||||
})
|
||||
state.NF = new Intl.NumberFormat(intlLocale)
|
||||
|
||||
state.timeAgo = timeAgo
|
||||
}
|
||||
},
|
||||
|
||||
getters: {
|
||||
fmtRelative: state => time => {
|
||||
return state.timeAgo.format(time)
|
||||
},
|
||||
|
||||
fmtNumber: state => num => {
|
||||
return state.NF.format(num)
|
||||
},
|
||||
|
||||
fmtDate: state => dt => {
|
||||
return state.dateFormatter.format(dt)
|
||||
},
|
||||
|
||||
fmtDateTime: state => dt => {
|
||||
return state.fullFormatter.format(dt)
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -3,13 +3,15 @@ import Vuex from 'vuex'
|
||||
|
||||
import { PrefsStore, initializePrefEvents } from '@/store/prefs-store'
|
||||
import { AuthenticationStore, initializeAuthEvents } from '@/store/authentication-store'
|
||||
import { i18nStore } from '@/store/i18n-store'
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
const store = new Vuex.Store({
|
||||
modules: {
|
||||
prefs: PrefsStore,
|
||||
auth: AuthenticationStore
|
||||
auth: AuthenticationStore,
|
||||
i18n: i18nStore
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
@include addFonts('Noto Sans KR');
|
||||
}
|
||||
|
||||
.cyrillic {
|
||||
.russian {
|
||||
@include addFonts('Nunito Sans');
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,6 @@
|
||||
import DOMPurify from 'dompurify'
|
||||
|
||||
import TimeAgo from 'javascript-time-ago'
|
||||
import en from 'javascript-time-ago/locale/en'
|
||||
|
||||
TimeAgo.addDefaultLocale(en)
|
||||
const timeAgo = new TimeAgo('en-US')
|
||||
|
||||
class LibPiped {
|
||||
intlDTF = new Intl.DateTimeFormat([], {
|
||||
dateStyle: 'full',
|
||||
timeStyle: 'full'
|
||||
})
|
||||
|
||||
pad (num, size) {
|
||||
return ('000' + num).slice(size * -1)
|
||||
}
|
||||
@@ -31,40 +20,6 @@ class LibPiped {
|
||||
return str
|
||||
}
|
||||
|
||||
formatFullDate (date) {
|
||||
return this.intlDTF.format(date)
|
||||
}
|
||||
|
||||
numberFormat (num) {
|
||||
const digits = 2
|
||||
const si = [
|
||||
{
|
||||
value: 1,
|
||||
symbol: ''
|
||||
},
|
||||
{
|
||||
value: 1e3,
|
||||
symbol: 'K'
|
||||
},
|
||||
{
|
||||
value: 1e6,
|
||||
symbol: 'M'
|
||||
},
|
||||
{
|
||||
value: 1e9,
|
||||
symbol: 'B'
|
||||
}
|
||||
]
|
||||
const rx = /\.0+$|(\.[0-9]*[1-9])0+$/
|
||||
let i
|
||||
for (i = si.length - 1; i > 0; i--) {
|
||||
if (num >= si[i].value) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return (num / si[i].value).toFixed(digits).replace(rx, '$1') + si[i].symbol
|
||||
}
|
||||
|
||||
addCommas (num) {
|
||||
num = parseInt(num)
|
||||
return num.toLocaleString('en-US')
|
||||
@@ -74,10 +29,6 @@ class LibPiped {
|
||||
return DOMPurify.sanitize(original)
|
||||
}
|
||||
|
||||
timeAgo (time) {
|
||||
return timeAgo.format(time)
|
||||
}
|
||||
|
||||
determineVideoIdFromPath (path) {
|
||||
const loc = new URL(path, 'http://localhost')
|
||||
return loc.searchParams.get('v')
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
"trendingFetchError": "জনপ্রিয় ভিডিও খুঁজে পাওয়া যায়নি। দেখুন আপনার নির্বাচিত দেশ কে ইউটুবে সাপোর্ট করে কি না।"
|
||||
},
|
||||
"misc": {
|
||||
"searchBarLabel": "কি সন্ধান করছেন বলুন?"
|
||||
"searchBarLabel": "কি সন্ধান করছেন বলুন?",
|
||||
"lastWatchedTill": "আপনি {t} পর্যন্ত দেখেছিলেন"
|
||||
},
|
||||
"actions": {
|
||||
"subscribe": "সদস্যতা নিন",
|
||||
@@ -16,7 +16,8 @@
|
||||
"trendingFetchError": "Could not fetch trending videos. Check whether your country is supported by YouTube"
|
||||
},
|
||||
"misc": {
|
||||
"searchBarLabel": "What are you looking for?"
|
||||
"searchBarLabel": "What are you looking for?",
|
||||
"lastWatchedTill": "Last watched till {t}"
|
||||
},
|
||||
"actions": {
|
||||
"subscribe": "Subscribe",
|
||||
|
||||
Reference in New Issue
Block a user