From 0656291c523e1bc99a310feef4b5f32f129b84cb Mon Sep 17 00:00:00 2001 From: Grzegorz Leoniec Date: Mon, 17 Dec 2018 21:56:30 +0100 Subject: [PATCH] added i18n and basic locales --- locales/de.json | 75 +++++++++++++++++++++++++++++++++++++++++++ locales/en.json | 75 +++++++++++++++++++++++++++++++++++++++++++ nuxt.config.js | 27 ++-------------- package.json | 5 ++- plugins/i18n.js | 84 +++++++++++++++++++++++++++++++++++++++++++++++++ yarn.lock | 30 ++++++++++++++++++ 6 files changed, 271 insertions(+), 25 deletions(-) create mode 100644 locales/de.json create mode 100644 locales/en.json create mode 100644 plugins/i18n.js diff --git a/locales/de.json b/locales/de.json new file mode 100644 index 000000000..7da8a3931 --- /dev/null +++ b/locales/de.json @@ -0,0 +1,75 @@ +{ + "settings": { + "name": "Einstellungen", + "data": { + "name": "Deine Daten" + }, + "security": { + "name": "Sicherheit" + }, + "invites": { + "name": "Einladungen" + }, + "download": { + "name": "Daten herunterladen" + }, + "delete": { + "name": "Konto löschen" + }, + "organizations": { + "name": "Meine Organisationen" + }, + "languages": { + "name": "Sprachen" + } + }, + "admin": { + "name": "Systemverwaltung", + "dashboard": { + "name": "Startzentrale", + "users": "Benutzer", + "posts": "Beiträge", + "comments": "Kommentare", + "notifications": "Benachrichtigungen", + "organizations": "Organisationen", + "projects": "Projects", + "invites": "Einladungen", + "follows": "Folgen", + "shouts": "Shouts" + }, + "organizations": { + "name": "Organisationen" + }, + "users": { + "name": "Benutzer" + }, + "pages": { + "name": "Seiten" + }, + "notifications": { + "name": "Benachrichtigungen" + }, + "categories": { + "name": "Kategorien", + "categoryName": "Name", + "postCount": "Beiträge" + }, + "tags": { + "name": "Schlagworte", + "tagCountUnique": "Benutzer", + "tagCount": "Beiträge" + }, + "settings": { + "name": "Einstellungen" + } + }, + "post": { + "name": "Beitrag", + "moreInfo": { + "name": "Mehr Info" + }, + "takeAction": { + "name": "Aktiv werde" + } + } +} diff --git a/locales/en.json b/locales/en.json new file mode 100644 index 000000000..7a5eb9428 --- /dev/null +++ b/locales/en.json @@ -0,0 +1,75 @@ +{ + "settings": { + "name": "Settings", + "data": { + "name": "Your data" + }, + "security": { + "name": "Security" + }, + "invites": { + "name": "Invites" + }, + "download": { + "name": "Download Data" + }, + "delete": { + "name": "Delete Account" + }, + "organizations": { + "name": "My Organizations" + }, + "languages": { + "name": "Languages" + } + }, + "admin": { + "name": "Admin", + "dashboard": { + "name": "Dashboard", + "users": "Users", + "posts": "Posts", + "comments": "Comments", + "notifications": "Notifications", + "organizations": "Organizations", + "projects": "Projekte", + "invites": "Invites", + "follows": "Follows", + "shouts": "Shouts" + }, + "organizations": { + "name": "Organizations" + }, + "users": { + "name": "Users" + }, + "pages": { + "name": "Pages" + }, + "notifications": { + "name": "Notifications" + }, + "categories": { + "name": "Categories", + "categoryName": "Name", + "postCount": "Posts" + }, + "tags": { + "name": "Tags", + "tagCountUnique": "Users", + "tagCount": "Posts" + }, + "settings": { + "name": "Settings" + } + }, + "post": { + "name": "Post", + "moreInfo": { + "name": "More info" + }, + "takeAction": { + "name": "Take action" + } + } +} diff --git a/nuxt.config.js b/nuxt.config.js index ea5b8b52f..890f83140 100644 --- a/nuxt.config.js +++ b/nuxt.config.js @@ -59,6 +59,7 @@ module.exports = { ** Plugins to load before mounting the App */ plugins: [ + { src: '~/plugins/i18n.js', ssr: true }, { src: '~/plugins/keep-alive.js', ssr: false }, { src: '~/plugins/design-system.js', ssr: true }, { src: '~/plugins/vue-directives.js', ssr: false }, @@ -71,30 +72,6 @@ module.exports = { middleware: ['authenticated'], linkActiveClass: 'router-active-link' }, - /* router: { - routes: [ - { - name: 'index', - path: '/', - component: 'pages/index.vue' - }, - { - name: 'post-slug', - path: '/post/:slug', - component: 'pages/post/_slug.vue', - children: [ - { - path: 'more-info', - component: 'pages/post/_slug.vue' - }, - { - path: 'take-action', - component: 'pages/post/_slug.vue' - } - ] - } - ] - }, */ /* ** Nuxt.js modules @@ -102,6 +79,7 @@ module.exports = { modules: [ ['@nuxtjs/dotenv', { only: envWhitelist }], ['nuxt-env', { keys: envWhitelist }], + 'cookie-universal-nuxt', '@nuxtjs/apollo', '@nuxtjs/axios', [ @@ -172,6 +150,7 @@ module.exports = { */ build: { /* + * TODO: import the polyfill instead of using the deprecated vendor key * Polyfill missing ES6 & 7 Methods to work on older Browser */ vendor: ['@babel/polyfill'], diff --git a/package.json b/package.json index cb55207e5..bf3571388 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "@nuxtjs/axios": "^5.3.6", "@nuxtjs/dotenv": "^1.3.0", "accounting": "^0.4.1", + "cookie-universal-nuxt": "^2.0.11", "cross-env": "^5.2.0", "date-fns": "^2.0.0-alpha.26", "express": "^4.16.3", @@ -52,7 +53,9 @@ "nuxt-env": "^0.0.4", "v-tooltip": "^2.0.0-rc.33", "vue-count-to": "^1.0.13", - "vue-izitoast": "1.1.2" + "vue-i18n": "~8.5.0", + "vue-izitoast": "1.1.2", + "vuex-i18n": "^1.10.5" }, "devDependencies": { "@vue/eslint-config-prettier": "^4.0.1", diff --git a/plugins/i18n.js b/plugins/i18n.js new file mode 100644 index 000000000..03ef61776 --- /dev/null +++ b/plugins/i18n.js @@ -0,0 +1,84 @@ +import Vue from 'vue' +import Vuex from 'vuex' +import vuexI18n from 'vuex-i18n/dist/vuex-i18n.umd.js' +import { debounce, isEmpty } from 'lodash' + +/** + * TODO: Refactor and simplify browser detection + * and implement the user preference logic + */ +export default ({ app, req, cookie, store }) => { + const debug = app.$env.NODE_ENV !== 'production' + const key = 'locale' + + const changeHandler = debounce((mutation, store) => { + if (process.server) return + + const currentLocale = app.$cookies.get(mutation.payload.locale) + const isDifferent = mutation.payload.locale !== currentLocale + if (isDifferent) { + app.$cookies.set(key, mutation.payload.locale) + } + + const user = store.getters['auth/user'] + const token = store.getters['auth/token'] + // persist language if it differs from last value + if (isDifferent && user && user._id && token) { + // TODO: SAVE LOCALE + // store.dispatch('usersettings/patch', { + // uiLanguage: mutation.payload.locale + // }, { root: true }) + } + }, 500) + + const i18nStore = new Vuex.Store({ + strict: debug + }) + + Vue.use(vuexI18n.plugin, i18nStore, { + onTranslationNotFound: function(locale, key) { + console.warn(`vuex-i18n :: Key '${key}' not found for locale '${locale}'`) + } + }) + + // register the locales + Vue.i18n.add('en', require('~/locales/en.json')) + + let userLocale = 'en' + // const localeCookie = app.$cookies.get(key) + // const userSettings = store.getters['auth/userSettings'] + // if (userSettings && userSettings.uiLanguage) { + // // try to get saved user preference + // userLocale = userSettings.uiLanguage + // } else if (!isEmpty(localeCookie)) { + // userLocale = localeCookie + // } else { + // userLocale = process.browser ? (navigator.language || navigator.userLanguage) : req.locale + // if (userLocale && !isEmpty(userLocale.language)) { + // userLocale = userLocale.language.substr(0, 2) + // } + // } + + const availableLocales = ['de', 'en'] + const locale = availableLocales.indexOf(userLocale) >= 0 ? userLocale : 'en' + + if (locale !== 'en') { + Vue.i18n.add(locale, require(`~/locales/${locale}.json`)) + } + + // Set the start locale to use + Vue.i18n.set(locale) + Vue.i18n.fallback('en') + + if (process.client) { + i18nStore.subscribe((mutation, s) => { + if (mutation.type === 'i18n/SET_LOCALE') { + changeHandler(mutation, store) + } + }) + } + + app.$i18n = Vue.i18n + + return i18nStore +} diff --git a/yarn.lock b/yarn.lock index b6c4b2070..4b4936d68 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1225,6 +1225,11 @@ dependencies: "@types/node" "*" +"@types/cookie@^0.3.1": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.3.2.tgz#453f4b14b25da6a8ea4494842dedcbf0151deef9" + integrity sha512-aHQA072E10/8iUQsPH7mQU/KUyQBZAGzTVRCUvnSz8mSvbrYsP4xEO2RSA0Pjltolzi0j8+8ixrm//Hr4umPzw== + "@types/cors@^2.8.4": version "2.8.4" resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.4.tgz#50991a759a29c0b89492751008c6af7a7c8267b0" @@ -3788,6 +3793,21 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= +cookie-universal-nuxt@^2.0.11: + version "2.0.11" + resolved "https://registry.yarnpkg.com/cookie-universal-nuxt/-/cookie-universal-nuxt-2.0.11.tgz#f7f2ea1d63a497b24d5e630cee4e999475e777f4" + integrity sha512-GmzXjkIpCRAe+fIpYavHyFliEDRofiJMbBd17kZJia+nLVEt2CJxjOqcQRqUOXmvtuB0oHDYy+fVflxs863T0w== + dependencies: + "@types/cookie" "^0.3.1" + cookie-universal "^2.0.11" + +cookie-universal@^2.0.11: + version "2.0.11" + resolved "https://registry.yarnpkg.com/cookie-universal/-/cookie-universal-2.0.11.tgz#525f6324b6698a713b46be77a3440daff68e4a6a" + integrity sha512-5IA+uAPwBuphUfuk+lpFEajfrUTZA3RmMXfoT/KWs29WMd2ZifHOafAr+w8WdlSixddjTEkOgUWrk27+ErueUg== + dependencies: + cookie "^0.3.1" + cookie@0.3.1, cookie@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" @@ -11556,6 +11576,11 @@ vue-hot-reload-api@^2.3.0: resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.1.tgz#b2d3d95402a811602380783ea4f566eb875569a2" integrity sha512-AA86yKZ5uOKz87/q1UpngEXhbRkaYg1b7HMMVRobNV1IVKqZe8oLIzo6iMocVwZXnYitlGwf2k4ZRLOZlS8oPQ== +vue-i18n@~8.5.0: + version "8.5.0" + resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-8.5.0.tgz#e6679b7fd1a13124c05565dabc28302b7cc23af0" + integrity sha512-2wz2E9iFeFZ2zAimbZwzU8X/e27fW3UvwVMHox74dp8RXexs6GfW93LU1kttgsmA/06/WjQlSK60NCMkNX6Y6A== + vue-izitoast@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vue-izitoast/-/vue-izitoast-1.1.2.tgz#0cf8290f045f8a389ccce4c238836c75a130eb03" @@ -11662,6 +11687,11 @@ vue@^2.5.17: resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.17.tgz#0f8789ad718be68ca1872629832ed533589c6ada" integrity sha512-mFbcWoDIJi0w0Za4emyLiW72Jae0yjANHbCVquMKijcavBGypqlF7zHRgMa5k4sesdv7hv2rB4JPdZfR+TPfhQ== +vuex-i18n@^1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/vuex-i18n/-/vuex-i18n-1.10.5.tgz#635ea2204e0aa3f8fd512f0fab7f6b994d3f666c" + integrity sha1-Y16iIE4Ko/j9US8Pq39rmU0/Zmw= + vuex@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.0.1.tgz#e761352ebe0af537d4bb755a9b9dc4be3df7efd2"