Add i18n localization
This commit is contained in:
parent
02bbb653ab
commit
66711ae123
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -0,0 +1 @@
|
|||
export { default as FlagIcon } from './FlagIcon'
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"common": {
|
||||
"loading": "Loading",
|
||||
"error": "error",
|
||||
"close": "close"
|
||||
},
|
||||
"pages": {
|
||||
"header": {
|
||||
"instructions": "Gidelins"
|
||||
},
|
||||
"ceremony": {
|
||||
"titleSherpa": "Sherpa Cash",
|
||||
"titleCeremony": "Trusted Setup Ceremony"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import en from './en.json'
|
||||
import kr from './kr.json'
|
||||
|
||||
export default {
|
||||
en,
|
||||
kr
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"common": {
|
||||
"loading": "Loading",
|
||||
"error": "error",
|
||||
"close": "close"
|
||||
},
|
||||
"pages": {
|
||||
"header": {
|
||||
"instructions": "안내사항"
|
||||
},
|
||||
"ceremony": {
|
||||
"titleSherpa": "Sherpa Cash",
|
||||
"titleCeremony": "Trusted Setup Ceremony"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export { localStorage } from "./localStorage";
|
|
@ -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,
|
||||
}
|
Loading…
Reference in New Issue