Add i18n localization

This commit is contained in:
benber86 2021-05-12 13:31:03 +10:00
parent 02bbb653ab
commit 66711ae123
11 changed files with 215 additions and 71 deletions

View File

@ -8,8 +8,64 @@
</template>
<template slot="end">
<b-navbar-item tag="router-link" to="/instructions">
Instructions
<div>{{ $t('pages.header.instructions') }}</div>
</b-navbar-item>
<b-navbar-item>
<b-dropdown
v-model="$i18n.locale"
@change="langChange"
class="dropdown-langs"
position="is-top-left"
aria-role="list"
>
<b-button slot="trigger" type="is-icon">
<FlagIcon :code="$i18n.locale" :class="'is-active-locale-' + $i18n.locale" />
</b-button>
<b-dropdown-item
v-for="locale in locales"
:key="locale"
:value="locale"
aria-role="listitem"
>
<FlagIcon :code="locale" />
{{ printLang(locale) }}
</b-dropdown-item>
</b-dropdown>
</b-navbar-item>
</template>
</b-navbar>
</template>
<script>
import { FlagIcon } from '@/components/icons'
export default {
components: {
FlagIcon
},
computed: {
locales() {
return this.$i18n.availableLocales
}
},
methods: {
langChange(lang) {
localStorage.setItem('lang', lang)
if (lang === 'zh') {
lang += '-cn'
}
},
printLang(lang) {
let code = lang
switch (code) {
case 'zh':
code = 'cn'
break
}
return code.toUpperCase()
}
}
}
</script>

View File

@ -0,0 +1,26 @@
<template>
<i v-if="code" :class="flagIconClass" class="flag-icon"></i>
</template>
<script>
export default {
name: 'FlagIcon',
props: {
code: { type: String, default: null }
},
computed: {
flagIconClass() {
let code = this.code
switch (code) {
case 'zh':
code = 'cn'
break
case 'en':
code = 'gb'
break
}
return 'flag-icon-' + code
}
}
}
</script>

View File

@ -0,0 +1 @@
export { default as FlagIcon } from './FlagIcon'

16
langs/en.json Normal file
View File

@ -0,0 +1,16 @@
{
"common": {
"loading": "Loading",
"error": "error",
"close": "close"
},
"pages": {
"header": {
"instructions": "Gidelins"
},
"ceremony": {
"titleSherpa": "Sherpa Cash",
"titleCeremony": "Trusted Setup Ceremony"
}
}
}

7
langs/index.js Normal file
View File

@ -0,0 +1,7 @@
import en from './en.json'
import kr from './kr.json'
export default {
en,
kr
}

16
langs/kr.json Normal file
View File

@ -0,0 +1,16 @@
{
"common": {
"loading": "Loading",
"error": "error",
"close": "close"
},
"pages": {
"header": {
"instructions": "안내사항"
},
"ceremony": {
"titleSherpa": "Sherpa Cash",
"titleCeremony": "Trusted Setup Ceremony"
}
}
}

View File

@ -66,7 +66,7 @@ module.exports = {
/*
** Customize the progress-bar color
*/
loading: { color: '#94febf', height: '5px', duration: 5000 },
loading: { color: '#138198', height: '5px', duration: 5000 },
/*
** Global CSS
*/
@ -74,7 +74,7 @@ module.exports = {
/*
** Plugins to load before mounting the App
*/
plugins: [{ src: '~plugins/phase2', ssr: false }, '~plugins/highlight'],
plugins: [{ src: '~plugins/phase2', ssr: false }, '~plugins/highlight', '~plugins/i18n.js'],
/*
** Nuxt.js dev-modules
*/

View File

@ -29,7 +29,8 @@
"oauth": "^0.9.15",
"sequelize": "^5.21.3",
"twitter": "^1.7.1",
"vue-highlightjs": "^1.3.3"
"vue-highlightjs": "^1.3.3",
"vue-i18n": "^8.24.2"
},
"devDependencies": {
"@nuxtjs/eslint-config": "^1.0.1",

View File

@ -1,54 +1,13 @@
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import messages from '../langs/index'
/**
* @param choice {number} a choice index given by the input to $tc: `$tc('path.to.rule', choiceIndex)`
* @param choicesLength {number} an overall amount of available choices
* @returns a final choice index to select plural word by
**/
VueI18n.prototype.getChoiceIndex = function (choice, choicesLength) {
// this === VueI18n instance, so the locale property also exists here
// add the word "only" if the value is greater than 1 and less than 5
if (this.locale !== 'ru') {
if (choice === 0 || choice === 1) {
return choice
}
if (choice > 1 && choice < 5) {
return 2
}
return 3
}
// comply with the rules of the Russian language
if (choice === 0) {
return 0
}
const teen = choice > 10 && choice < 20
const endsWithOne = choice % 10 === 1
if (!teen && endsWithOne) {
return 1
}
if (!teen && choice % 10 >= 2 && choice % 10 <= 4) {
return 2
}
return choicesLength < 4 ? 2 : 3
}
Vue.use(VueI18n)
let lang = 'en'
if (process.browser) {
const locale =
localStorage.getItem('lang') ||
navigator.language.substr(0, 2).toLowerCase()
const locale = localStorage.getItem('lang') || navigator.language.substr(0, 2).toLowerCase()
lang = !messages[locale] ? 'en' : locale
}
@ -61,8 +20,8 @@ const dateTimeFormats = {
weekday: 'long',
hour: 'numeric',
minute: 'numeric',
hour12: true,
},
hour12: true
}
},
fr: {
long: {
@ -72,8 +31,8 @@ const dateTimeFormats = {
weekday: 'long',
hour: 'numeric',
minute: 'numeric',
hour12: true,
},
hour12: true
}
},
tr: {
long: {
@ -83,8 +42,8 @@ const dateTimeFormats = {
weekday: 'long',
hour: 'numeric',
minute: 'numeric',
hour12: true,
},
hour12: true
}
},
jp: {
long: {
@ -94,8 +53,8 @@ const dateTimeFormats = {
weekday: 'long',
hour: 'numeric',
minute: 'numeric',
hour12: true,
},
hour12: true
}
},
kr: {
long: {
@ -105,37 +64,37 @@ const dateTimeFormats = {
weekday: 'long',
hour: 'numeric',
minute: 'numeric',
hour12: true,
},
},
hour12: true
}
}
}
const numberFormats = {
en: {
compact: {
notation: 'compact',
},
notation: 'compact'
}
},
fr: {
compact: {
notation: 'compact',
},
notation: 'compact'
}
},
jp: {
compact: {
notation: 'compact',
},
notation: 'compact'
}
},
kr: {
compact: {
notation: 'compact',
},
notation: 'compact'
}
},
tr: {
compact: {
notation: 'compact',
},
},
notation: 'compact'
}
}
}
// Create VueI18n instance with options
@ -146,12 +105,10 @@ export default ({ app, route, store }) => {
messages,
silentFallbackWarn: true,
dateTimeFormats,
numberFormats,
numberFormats
})
if (lang === 'zh') {
lang += '-cn'
}
// app.$moment.locale(lang)
}

1
utils/index.js Normal file
View File

@ -0,0 +1 @@
export { localStorage } from "./localStorage";

63
utils/localStorage.js Normal file
View File

@ -0,0 +1,63 @@
let isLocalStorageEnabled = null
try {
window.localStorage.setItem('test', 'test')
window.localStorage.removeItem('test')
isLocalStorageEnabled = true
} catch (e) {
isLocalStorageEnabled = false
}
const setItem = (key, value) => {
if (isLocalStorageEnabled) {
window.localStorage.setItem(key, JSON.stringify(value))
}
}
const getItem = (key) => {
if (isLocalStorageEnabled) {
const value = window.localStorage.getItem(key)
try {
return JSON.parse(String(value))
} catch (err) {
return value
}
}
return undefined
}
const removeItem = (key) => {
if (isLocalStorageEnabled) {
return window.localStorage.removeItem(key)
}
}
const clear = () => {
if (isLocalStorageEnabled) {
window.localStorage.clear()
}
}
const subscribe = (key, originalListener) => {
const listener = (event) => {
if (event.storageArea === window.localStorage && event.key === key) {
originalListener(event.newValue, event.oldValue)
}
}
window.addEventListener('storage', listener, false)
return listener
}
const unsubscribe = (listener) => {
window.removeEventListener('storage', listener, false)
}
export const localStorage = {
setItem,
getItem,
removeItem,
clear,
subscribe,
unsubscribe,
}