Skip to content

Commit

Permalink
feat: add language support
Browse files Browse the repository at this point in the history
  • Loading branch information
jmpsf committed Jul 11, 2022
1 parent 6372a61 commit ef526dc
Show file tree
Hide file tree
Showing 13 changed files with 294 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import AppVersion from './AppVersion.vue'
export default {
name: 'UserProfileDropdownMenu',
components: {
AppVersion
AppVersion,
},
computed: {
user () {
Expand All @@ -30,6 +30,10 @@ export default {
const params = { username: this.user.username }
return { name: 'user-retrieve-bookmarks', params }
},
languageRoute () {
const params = { username: this.user.username }
return { name: 'user-retrieve-language', params }
},
historyRoute () {
const params = { username: this.user.username }
return { name: 'user-retrieve-history', params }
Expand Down Expand Up @@ -78,6 +82,14 @@ export default {
<truck-icon class="user-profile-dropdown-menu__item__icon" />
{{ $t('userProfileDropdownMenu.help') }}
</b-dropdown-item>
<b-dropdown-item
id="user-profile-dropdown-menu__item--language"
:to="languageRoute"
class="user-profile-dropdown-menu__item user-profile-dropdown-menu__item--language"
>
<globe-icon class="user-profile-dropdown-menu__item__icon" />
{{ $t('userRetrieveLanguage.title.yours') + ' (' + $i18n.locale.toUpperCase() + ')' }}
</b-dropdown-item>
<b-dropdown-item :href="$config.get('logoutUrl')" class="user-profile-dropdown-menu__item user-profile-dropdown-menu__item--logout">
<log-out-icon class="user-profile-dropdown-menu__item__icon" />
{{ $t('userProfileDropdownMenu.logOut') }}
Expand All @@ -101,6 +113,7 @@ export default {
&__icon {
margin-right: $spacer;
}
}
}
</style>
13 changes: 12 additions & 1 deletion prophecies/apps/frontend/src/core/Core.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// BootstrapVue recommends using this
import 'mutationobserver-shim'

import compose from 'lodash/fp/compose'
import Murmur from '@icij/murmur'
import BootstrapVue from 'bootstrap-vue'
import Vue from 'vue'
Expand All @@ -9,6 +10,8 @@ import VueRouter from 'vue-router'
import VueScrollTo from 'vue-scrollto'
import VueWait from 'vue-wait'

import LocaleMixin from './LocaleMixin'

import Setting from '@/models/Setting'
import User from '@/models/User'
import router from '@/router'
Expand All @@ -21,16 +24,22 @@ import App from '@/views/App'
import vShortkey from '@/directives/shortkey'
import Shortkey from '@/plugins/Shortkey'

class Base {}
const Behaviors = compose(LocaleMixin)(Base)

/**
@class
@classdesc Class representing the core application.
@mixes LocaleMixin
@typicalname prophecies
*/
class Core {
class Core extends Behaviors {
/**
* Create an application
* @param {Object} LocalVue - The Vue class to instantiate the application with.
*/
constructor (LocalVue = Vue) {
super(LocalVue)
this.LocalVue = LocalVue
// Disable production tip when not in production
this.LocalVue.config.productionTip = process.env.NODE_ENV === 'development'
Expand Down Expand Up @@ -171,6 +180,8 @@ class Core {
this.config.merge(await this.getSettings())
// Finally, load the user into a specific attribute.
this.config.set('user', await this.getUser())
// Load the locale
this.loadLocale(this.store.state.app.locale ?? 'en')
// Old a promise that is resolved when the core is configured
return this.ready && this._readyResolve(this)
} catch (error) {
Expand Down
41 changes: 41 additions & 0 deletions prophecies/apps/frontend/src/core/LocaleMixin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@

/**
Mixin class extending the core to add helpers to manage the internationalization.
@mixin LocaleMixin
@typicalname prophecies
*/

const LocaleMixin = superclass => class extends superclass {
/**
* Set the i18n language from the locale
* @param {String} locale - Locale code to load
* @memberof LocaleMixin.prototype
*/
setI18nLanguage (locale) {
this.i18n.locale = locale
return locale
}
/**
* Load the application language from the locale
* @param {String} locale - Locale code to load
* @memberof LocaleMixin.prototype
*/
loadLocale (locale) {
if (this.loadedLocales === undefined) {
this.loadedLocales = []
}
if (this.i18n.locale !== locale) {
if (!this.loadedLocales.includes(locale)) {
return import(/* webpackChunkName: "[request]" */ '@/messages/' + locale + '.json').then(messages => {
this.i18n.setLocaleMessage(locale, messages.default)
this.loadedLocales.push(locale)
return this.setI18nLanguage(locale)
})
}
return Promise.resolve(this.setI18nLanguage(locale))
}
return Promise.resolve(locale)
}
}

export default LocaleMixin
8 changes: 7 additions & 1 deletion prophecies/apps/frontend/src/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -327,10 +327,16 @@
},
"noHistory": "Empty history"
},
"userRetrieveLanguage": {
"title": {
"yours": "Language",
"others": "Language"
}
},
"userTaskStatsCard": {
"allOpenTasks": "All open tasks",
"allRecords": "All records",
"noActivity": "No activity recorded over this period",
"noTask": "No tasks assigned yet"
}
}
}
7 changes: 7 additions & 0 deletions prophecies/apps/frontend/src/router/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import TipRetrieve from '@/views/TipRetrieve.vue'
import StatsList from '@/views/StatsList.vue'
import UserRetrieve from '@/views/UserRetrieve.vue'
import UserRetrieveProfile from '@/views/UserRetrieveProfile.vue'
import UserRetrieveLanguage from '@/views/UserRetrieveLanguage.vue'
import UserRetrieveActivity from '@/views/UserRetrieveActivity.vue'
import UserRetrieveTeam from '@/views/UserRetrieveTeam.vue'
import UserRetrieveBookmarks from '@/views/UserRetrieveBookmarks.vue'
Expand Down Expand Up @@ -74,6 +75,12 @@ export const router = {
path: 'history',
component: UserRetrieveHistory,
props: true
},
{
name: 'user-retrieve-language',
path: 'language',
component: UserRetrieveLanguage,
props: true
}
]
},
Expand Down
8 changes: 8 additions & 0 deletions prophecies/apps/frontend/src/settings/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ export default {
{
key: 'en',
label: 'English'
},
{
key: 'es',
label: 'Español'
},
{
key: 'fr',
label: 'Français'
}
],
loginUrl: '/login/provider/?next=/',
Expand Down
5 changes: 3 additions & 2 deletions prophecies/apps/frontend/src/store/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ export function createStore () {
createPersistedState({
paths: [
'app.redirectAfterLogin',
'app.showTutorial'
'app.showTutorial',
'app.locale'
],
filter (mutation) {
// Only for some mutations
Expand All @@ -40,4 +41,4 @@ export function createStore () {
})
}

export default createStore()
export default createStore()
9 changes: 8 additions & 1 deletion prophecies/apps/frontend/src/store/modules/app.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export const state = () => ({
redirectAfterLogin: null,
showTutorial: true
showTutorial: true,
locale: 'en'
})

export const mutations = {
Expand All @@ -11,6 +12,9 @@ export const mutations = {
},
setShowTutorial (state, showTutorial) {
state.showTutorial = showTutorial
},
setLocale (state, locale) {
state.locale = locale
}
}

Expand All @@ -22,6 +26,9 @@ export const actions = {
showTutorial ({ state: { showTutorial }, commit }, isShown) {
commit('setShowTutorial', isShown)
return showTutorial
},
locale ({ commit }, locale) {
commit('setLocale', locale)
}
}

Expand Down
1 change: 1 addition & 0 deletions prophecies/apps/frontend/src/utils/icons.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export {
FilmIcon,
FilterIcon,
FrownIcon,
GlobeIcon,
GridIcon,
HomeIcon,
HelpCircleIcon,
Expand Down
55 changes: 48 additions & 7 deletions prophecies/apps/frontend/src/views/UserRetrieve.vue
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ export default {
const params = { username: this.username }
return { name: 'user-retrieve-history', params }
},
languageRoute () {
const params = { username: this.username }
return { name: 'user-retrieve-language', params }
},
fetchUserLoader () {
return uniqueId('load-user-')
},
Expand All @@ -78,6 +82,9 @@ export default {
historyTitle () {
return this.routeTitle(this.historyRoute.name)
},
languageTitle () {
return this.routeTitle(this.languageRoute.name)
},
user () {
return User.find(this.username)
},
Expand All @@ -96,6 +103,8 @@ export default {
return 'BellIcon'
case this.historyTitle:
return 'ClockIcon'
case this.languageTitle:
return 'GlobeIcon'
default:
return 'UserIcon'
}
Expand Down Expand Up @@ -142,34 +151,66 @@ export default {
<div class="user-retrieve d-flex align-items-start">
<app-sidebar class="w-100 sticky-top">
<template #items>
<b-nav-item :to="profileRoute" exact>
<b-nav-item
:to="profileRoute"
exact
:class="{'font-weight-bold': title === profileTitle}"
>
<user-icon class="mr-3" />
{{ profileTitle }}
</b-nav-item>
<b-nav-item :to="activityRoute" exact>
<b-nav-item
:to="activityRoute"
exact
:class="{'font-weight-bold': title === activityTitle}"
>
<activity-icon class="mr-3" />
{{ activityTitle }}
</b-nav-item>
<b-nav-item :to="notificationsRoute" exact v-if="isMe">
<b-nav-item
:to="notificationsRoute"
exact v-if="isMe"
:class="{'font-weight-bold': title === notificationsTitle}"
>
<bell-icon class="mr-3" />
{{ notificationsTitle }}
</b-nav-item>
<b-nav-item :to="bookmarksRoute" exact>
<b-nav-item
:to="bookmarksRoute"
exact
:class="{'font-weight-bold': title === bookmarksTitle}"
>
<bookmark-icon class="mr-3" />
{{ bookmarksTitle }}
</b-nav-item>
<b-nav-item :to="historyRoute" exact>
<b-nav-item
:to="historyRoute"
exact
:class="{'font-weight-bold': title === historyTitle}"
>
<clock-icon class="mr-3" />
{{ historyTitle }}
</b-nav-item>
<b-nav-item :to="teamRoute" exact>
<b-nav-item
:to="teamRoute"
exact
:class="{'font-weight-bold': title === teamTitle}"
>
<users-icon class="mr-3" />
{{ teamTitle }}
</b-nav-item>
<b-nav-item :href="$config.get('helpLink')">
<truck-icon class="mr-3" />
{{ $t('userProfileDropdownMenu.help') }}
</b-nav-item>
<b-nav-item
:to="languageRoute"
exact
:class="{'font-weight-bold': title === languageTitle}"
>
<globe-icon class="mr-3" />
{{ languageTitle + ' (' + $i18n.locale.toUpperCase() + ')'}}
</b-nav-item>
<b-nav-item :href="$config.get('logoutUrl')">
<log-out-icon class="mr-3" />
{{ $t('userProfileDropdownMenu.logOut') }}
Expand All @@ -181,7 +222,7 @@ export default {
<div class="container-fluid">
<page-header :title="title" :icon="icon" class="mb-5" />
<app-waiter :loader="fetchUserLoader" waiter-class="my-5 mx-auto d-block">
<router-view />
<router-view :class="{'px-5': title === languageTitle}"/>
</app-waiter>
</div>
</div>
Expand Down
Loading

0 comments on commit ef526dc

Please sign in to comment.