import Vue from 'vue'
import VueI18n from 'vue-i18n'
import fr from './i18n.fr'
import en from './i18n.en'
import nl from './i18n.nl'
import es from './i18n.es'
import VueCookie from 'vue-cookie'
import moment from 'moment'
import { getQueryStringValueOnce } from '@/utils/querystring'
Vue.use(VueCookie)
Vue.use(VueI18n)
const availableLanguages = ['fr', 'en', 'nl', 'es']
const i18nMessages = {
fr,
en,
nl,
es,
}
const localeFromCookie = VueCookie.get('locale')
const isLocaleFromCookieValid = isValidLocaleCode(localeFromCookie)
let defaultLocale = (isLocaleFromCookieValid ? localeFromCookie : 'fr') || 'fr'
let initialized = false
let localeFromQueryString =
getQueryStringValueOnce('locale') ||
getQueryStringValueOnce('lang') ||
getQueryStringValueOnce('lg')
if (localeFromQueryString) {
defaultLocale = localeFromQueryString
console.info('i18n default locale from querystring', defaultLocale)
}
const i18n = new VueI18n({
locale: defaultLocale,
fallbackLocale: 'fr',
messages: i18nMessages,
})
i18n.availableLanguages = availableLanguages
export default i18n
Vue.use(
(() => {
return {
install() {
if (!isLocaleFromCookieValid) {
console.warn(
`Invalid cookie locale (${localeFromCookie}) skipped (expected: ${availableLanguages.join(
', '
)}). Fallback to ${defaultLocale}`
)
}
/**
* Translate falling back to a hardcoded default value instead of the value of the default language
* @param {*} code
* @param {*} defaultValue
* @param {*} options
* @returns
*/
Vue.prototype.$translation = function (
code,
defaultValue = null,
options = {}
) {
if (this.$te(code)) {
return this.$t(code, options.variables || {})
} else {
return defaultValue || code
}
}
//Inject a better $t version that support multiple i18n keys (fallback keys)
Vue.prototype.$tOriginal = Vue.prototype.$t
Vue.prototype.$t = function (codeOrCodes, options = {}) {
if (codeOrCodes instanceof Array) {
let firstAvailableCode = codeOrCodes.find((c) => this.$te(c))
if (firstAvailableCode) {
return this.$tOriginal(firstAvailableCode, options)
} else {
//i.g: ['feature_title','common_title'] => It should fallback to common_title in default language
return this.$tOriginal(
codeOrCodes[codeOrCodes.length - 1],
options
)
}
} else {
return this.$tOriginal(codeOrCodes, options)
}
}
/**
* Access to raw i18n translations (selected language)
*/
Object.defineProperty(Vue.prototype, '$translations', {
get: function () {
let currentLanguageCode =
(this.$root && this.$root.$i18n && this.$root.$i18n.locale) ||
'fr'
let obj = i18nMessages[currentLanguageCode]
if (obj === undefined) {
obj = i18nMessages.fr
console.warn(
'Unable to get i18n messages from current language',
{
currentLanguageCode,
}
)
}
reduceAttributesIntoNestedProperty(obj, 'datatable')
reduceAttributesIntoNestedProperty(obj, 'datepicker')
return obj
},
})
/**
* Access to current language
*/
Object.defineProperty(Vue.prototype, '$locale', {
get: function () {
return this.$root.$i18n.locale
},
set: function (localeCode) {
setNewLocaleCode(localeCode)
},
})
},
}
})()
)
/**
* @unit
* @param {String} code i.g es, fr, nc
* @returns
*/
export function isValidLocaleCode(code) {
return availableLanguages.includes((code || '').toString().toLowerCase())
}
/**
* @unit
* @returns
*/
export function setNewLocaleCode(localeCode) {
if (!initialized && localeFromQueryString) {
localeCode = localeFromQueryString //First assignment will use query param if available
console.debug('i18n locale first assignment from querystring', localeCode)
}
initialized = true
if (!isValidLocaleCode(localeCode)) {
return console.warn(
`$locale::ignore-invalid-locale-during-set: ${localeCode} (Expected ${availableLanguages.join(
', '
)})`
)
}
i18n.locale = localeCode
//WIP: Datepicker vue2-datepicker translation issues (dynamic language switch)
//window.activateLocale = (str) => import('vue2-datepicker/locale/' + str)
//window.activateLocale(localeCode)
VueCookie.set('locale', localeCode)
if (['en', 'nl', 'fr', 'es'].includes(localeCode)) {
moment.locale(localeCode)
}
//console.info(`Locale changed to ${localeCode}`)
}
/**
* Used by Datatable library.
* Datatable expects a language property which is a nested object: https://www.datatables.net/plug-ins/i18n/English
* But we store translations in a plain object:
* - datatable.paginate.next
* - datatable.paginate.prev
* This function will collect all the keys starting with datatable. and build a nested object similar to the one expected by datatable.
* @example:
* reduceAttributesIntoNestedProperty({foo.bar:1,foo.world:2,bar:2,foo:3})
* Output: {foo.bar:1,foo.world:2,bar:2,foo:3, foo:{bar:1,world:2} }
* @param {Object} obj Plain object
* @param {String} attribute Object attribute (Computed nested object)
*/
export function reduceAttributesIntoNestedProperty(obj, attribute = '') {
if (!attribute) {
throw new Error('ATTRIBUTE_REQUIRED')
}
obj[attribute] = Object.keys(obj).includes(`${attribute}.0`) ? [] : {}
Object.keys(obj)
.filter(
(key) =>
key.includes(attribute + '.') && key.indexOf(attribute + '.') === 0
)
.forEach((key) => {
let count = 0
let fullKey = key
let cursor = obj[attribute] //i18n[days]
key = key.split(attribute + '.').join('') //0
do {
let arr = key.split('.') //[datepicker, days, 0], [days, 0] //[0]
let first = arr.shift() //datepicker, days 0
key = arr.join('.') //days.0, 0 ''
let structureValue =
fullKey.includes(`${first}.0`) + fullKey.includes(`${first}.1`)
? []
: {}
if (key === '') {
if (cursor instanceof Array) {
cursor.push(obj[fullKey])
} else {
if (cursor[first] instanceof Array) {
cursor[first].push(obj[fullKey])
} else {
cursor[first] = obj[fullKey] //assign value
}
}
} else {
let parent = cursor //parent = i18n, parent = datepicker
cursor = cursor[first] || structureValue //ig datepicker = {}, datepicker[days]
parent[first] = cursor //ig i18n[datepicker] = {}
}
if (count > 100) {
key = ''
console.error('Short circuit', {
attribute,
})
return
}
} while (key !== '')
})
obj[attribute] = Object.freeze(JSON.parse(JSON.stringify(obj[attribute])))
}
Source