How to stop i18n from reverting to default locale on page reload on Vue? - vue.js

I'm using i18n to translate my app on Vue. I've created a component name LocaleChanger to allow the user to change the app language. I'm using it in two different places: in the login page and the dashboard page.
The default locale language is Spanish.
I'm in the login page and I change the language to English and log in and push to dashboard. The language remains English. But if I reload the page the language changes to Spanish. How can I stop this from happening?
This is my component
<template>
<v-menu offset-y>
<template v-slot:activator="{ on }">
<v-btn class="transparent" rounded outlined v-on="on">
<v-icon class="pr-3">mdi-web</v-icon>
{{ $store.getters.getAppLanguage }}
<v-icon class="pl-3">mdi-menu-down</v-icon>
</v-btn>
</template>
<v-list>
<v-list-item-group>
<v-list-item
v-for="(item, i) in items"
:key="i"
#click="setLanguage(item)"
>
<v-list-item-content class="text-center">
<v-list-item-title v-text="item.text"></v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list-item-group>
</v-list>
</v-menu>
</template>
<script>
export default {
name: 'LocaleChanger',
data () {
return {
selectedLanguage: this.$store.getters.getAppLanguage,
items: [
{
text: 'ES',
},
{
text: 'EN',
},
],
}
},
methods: {
// Set local i18n language acording to the language user has selected
setLanguage (item) {
if (item.text == 'ES') {
i18n.locale = 'es'
this.$store.commit('setAppLanguage', 'ES')
} else if (item.text == 'EN') {
i18n.locale = 'en'
this.$store.commit('setAppLanguage', 'EN')
}
},
},
}
</script>
And this i18n.js
Vue.use(VueI18n)
const messages = {
en: {
...require('#/locales/en.json'),
$vuetify: en,
},
es: {
...require('#/locales/es.json'),
$vuetify: es,
},
}
export default new VueI18n({
locale: process.env.VUE_APP_I18N_LOCALE || 'es',
fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en',
messages,
})

You could use localstorage to persist the information.
In your store try to get appLanguage from the localStorage.
export default new Vuex.Store({
state: {
appLanguage: localStorage.getItem("appLanguage") || process.env.VUE_APP_I18N_LOCALE || 'es'
},
getters: {
getAppLanguage: (state) => state.appLanguage
},
mutations: {
setAppLanguage(state, language) {
state.appLanguage = language;
localStorage.setItem("appLanguage", language); // Whenever we change the appLanguage we save it to the localStorage
}
}
})
And then initialize localStorage with the value from your store.
import store from "./store.js";
export default new VueI18n({
locale: store.getters.getAppLanguage,
messages,
})
https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage

Related

Page display and refresh problem in vueJs

I have a small problem with the display of the Authentication with VueJs page.
Once on the authentication User page I also have the menu which remains displayed, and once authenticated it redirects me to the Home page however when I disconnect I have the same concerns, it redirects me to the authenticationUser page but with the menu that remains displayed.
and if I do a refresh on the authenticationUser page the page remains blank without error.
I don't know if you have any idea for the cause of the problem.
Thanks in advance.
Page App.vu
<template>
<v-app id='inspire'>
<div v-if="user && user.sub">
<Menu />
</div>
</v-app>
</template>
<script>
import AuthentificationUser from "./views/AuthentificationUser";
import AuthenticatedUserUtils from "./utils/AuthenticatedUserUtils";
import Header from './components/common/Header';
import Menu from './components/common/Menu';
export default {
name: "app",
data: () => ({
user: AuthenticatedUserUtils.user
}),
components: {
AuthentificationUser,
Menu
},
};
</script>
enter code here
Router.ts
import User from '../models/User'
import Vue from 'vue'
import VueRouter, { RouteConfig } from 'vue-router'
import AuthenticatedUserUtils from '../utils/AuthenticatedUserUtils'
Vue.use(VueRouter)
const routes: Array<RouteConfig> = [
{
path: '/',
meta: {
requireAuth: true,
}
},
{
path: '/inventory',
name: 'Inventory',
meta: {
requireAuth: true,
}
},
{
path: '/inventory/authentificationUser',
name: 'AuthentificationUser',
component: () => import('../views/AuthentificationUser.vue')
},
{
path: '/inventory/home',
name: 'Home',
component: () => import('../views/Home.vue'),
meta: {
requireAuth: true,
}
},
{
path: '/inventory/parametrage',
name: 'Parametrage',
component: () => import('../views/Parametrage.vue'),
meta: {
requireAuth: true,
}
},
{
path: '/inventory/rfid',
name: 'RFID',
component: () => import('../views/RFID.vue'),
meta: {
requireAuth: true,
}
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
router.beforeEach((to, from, next) => {
if (!!to && !!to.meta && to.meta.requireAuth) {
// TODO : voir si le cookie existe et est valide
if (document && document.cookie) {
const potentialCookie = document.cookie
.split('; ')
.find(row => row.startsWith('access_token='));
let token = '';
if (potentialCookie) {
token = potentialCookie.split('=')[1];
}
const parsedJwt = parseJwt(token);
if (new Date() <= new Date(parsedJwt.exp * 1000)) {
const usr:User = new User();
usr.created = parsedJwt.created;
usr.sub =JSON.parse(JSON.stringify(parsedJwt.sub));
usr.exp = parsedJwt.exp;
AuthenticatedUserUtils.user = usr;
next();
} else {
AuthenticatedUserUtils.user = undefined;
// token non valide sur une page qui a besoin de l'authent
router.push('/inventory/authentificationUser').catch(()=>{});;
}
} else {
router.push('/inventory/authentificationUser').catch(()=>{});;
}
} else {
// appInsights.trackPageView({ name: to.name });
next();
}
});
function parseJwt (token: string) {
var base64Url = token.split('.')[1];
var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
var jsonPayload = decodeURIComponent(atob(base64).split('').map(function(c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
return JSON.parse(jsonPayload);
};
export default router
enter code here
Menu.ts
<template>
<v-app>
<v-navigation-drawer color="#80e27e" app v-model='drawer'>
<div style="background-color: #F5F5F5; width:260px;height:63px;">
<v-list-item-title class="text-h6" style="text-align:center; padding-top: 5%; color: #087f23;">{{ $t("label.inventaire") }}</v-list-item-title>
</div>
<v-list dense nav>
<template v-for='item in items'>
<v-list-item :key='item.text' #click="goToRoute(item.to)">
<v-list-item-icon>
<v-icon v-text="item.icon" style="color: #087f23;" />
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title v-text="item.title" style="color: #087f23;"/>
</v-list-item-content>
</v-list-item>
</template>
</v-list>
<template>
<div style="color: #087f23; margin-top: 150%;">
<b><span block style="color: #087f23; margin-left: 23%; ">{{ $t("label.bienvenue") }} Frédéric</span></b>
<div style="width:150px;height:35px;border:0px solid #000;border-radius:5px;-webkit-border-radius:5px;margin-left: 20%;background-color: #F5F5F5;">
<img v-if="checkValueCodeLangueMenu() === true" style="width:35px; height:35px; margin-left:39%; border-radius:5px;-webkit-border-radius:5px;" src="#/assets/fra.png"/>
<img v-if="checkValueCodeLangueMenu() === false" style="width:27px; height:27px; margin-left:39%; margin-top:2%; border-radius:5px;-webkit-border-radius:5px;" src="#/assets/eng.png"/>
</div>
<v-btn style="color: #087f23; margin-top: 10%;width:200px;height:35px; margin-left: 10%;" #click="logout()">
<v-icon>mdi-logout</v-icon>
<label>{{ $t("label.deconnexion") }}</label>
</v-btn>
</div>
</template>
</v-navigation-drawer>
<v-app-bar app color="#4caf50">
<v-app-bar-nav-icon #click="drawer = !drawer" style="color: #087f23;"></v-app-bar-nav-icon>
<v-spacer></v-spacer>`
<v-menu offset-y>`enter code here`
<template v-slot:activator="{ on }">
<div class="text-center">
<v-btn icon v-on="on">
<v-icon> mdi-earth </v-icon>
</v-btn>
</div>
</template>
<v-list>
<v-list-item
v-for="langue in languesDispo"
:key="langue.codeLangue"
#click="changeLocale(langue)"
>
<v-list-item-title>
<img v-if="checkValueCodeLangue(langue.codeLangue) === true" style="width:26px; height:26px;border-radius:5px;-webkit-border-radius:8px;vertical-align:middle" src="#/assets/fra.png"/>
<img v-if="checkValueCodeLangue(langue.codeLangue) === false" style="width:26px; height:26px;border-radius:5px;-webkit-border-radius:8px;vertical-align:middle" src="#/assets/eng.png"/>
</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
<v-icon v-on:click="logout()">mdi-logout</v-icon>
</v-app-bar>
<v-main >
<v-container fluid>
<router-view/>
</v-container>
</v-main>
</v-app>
</template>
<script lang="ts">
import Vue from 'vue'
import i18n from '#/plugins/i18n';
import Langue from '#/models/Langue';
import PaysUtils from '#/utils/PaysUtils';
export default Vue.extend({
name: 'Menu',
data: () => ({
currentPays: 'fr',
languesDispo: PaysUtils.lstPays,
drawer: null,
imgLangue: null,
items: [
{ title: 'Accueil', icon: 'mdi-home', to: '/' },
{ title: 'Paramétrage', icon: 'mdi-cog', to: '/inventory/parametrage' },
{ title: 'RFID', icon: 'mdi-help-box', to: '/inventory/rfid' },
],
}),
methods: {
logout (){
document.cookie = "";
this.$router.push('/inventory/authentificationUser').catch(()=>{});
},
goToRoute(route: string): void {
this.$router.push(route);
},
changeLocale(langue: Langue): void {
if (langue.codeLangue && langue.codePays) {
this.$vuetify.lang.current = langue.codeLangue;
i18n.locale = langue.codeLangue;
this.currentPays = langue.codePays;
}
},
checkValueCodeLangue : function(langue: String) {
if(langue === 'fra') {
return true
}
return false
},
checkValueCodeLangueMenu : function() {
if(this.currentPays === 'fr'){
return true
}
return false
}
},
})
</script>

I want to add editing functions using Nuxt.js

What I want to come true
I am creating TodoLists.
I tried to implement the following editing features, but it didn't work and I'm having trouble.
Click the edit button to display the edit text in the input field
If you click the save button after entering the changes in the input field, the changes will be reflected in the first position.
Code
<v-row v-for="(todo,index) in todos" :key="index">
<v-text-field
filled
readonly
:value="todo.text"
class="ma-3"
auto-grow
/>
<v-menu
top
rounded
>
<template #activator="{ on, attrs }">
<v-btn
v-bind="attrs"
icon
class="mt-6"
v-on="on"
>
<v-icon>
mdi-dots-vertical
</v-icon>
</v-btn>
</template>
<v-list>
<v-list-item
link
>
<v-list-item-title #click="toEdit(todos)">
<v-icon>mdi-pencil</v-icon>
Edit
</v-list-item-title>
</v-list-item>
</v-list>
<v-list>
<v-list-item
link
>
<v-list-item-title #click="removeTodo(todo)">
<v-icon>mdi-delete</v-icon>
Delete
</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-row>
<v-text-field
v-model="itemText"
filled
color="pink lighten-3"
auto-grow
#keyup.enter="addTodo"
/>
<v-btn
:disabled="disabled"
#click="addTodo"
>
Save
</v-btn>
data () {
return {
editIndex: false,
hidden: false,
itemText: '',
items: [
{ title: 'Edit', icon: 'mdi-pencil' },
{ title: 'Delete', icon: 'mdi-delete' }
]
}
},
computed: {
todos () {
return this.$store.state.todos.list
},
disabled () {
return this.itemText.length === 0
}
},
methods: {
addTodo (todo) {
if (this.editIndex === false) {
this.$store.commit('todos/add', this.itemText)
this.itemText = ''
} else {
this.$store.commit('todos/edit', this.itemText, todo)
this.itemText = ''
}
},
toEdit (todo) {
this.editIndex = true
this.itemText = this.todos
},
removeTodo (todo) {
this.$store.commit('todos/remove', todo)
}
}
}
</script>
export const state = () => ({
list: []
})
export const mutations = {
add (state, text) {
state.list.push({
text
})
},
remove (state, todo) {
state.list.splice(state.list.indexOf(todo), 1)
},
edit (state, text, todo) {
state.list.splice(state.list.indexOf(todo), 1, text)
}
}
Error
Click the edit button and it will look like this
What I tried myself
//methods
toEdit (todo) {
this.editIndex = true
this.itemText = this.todos.text //add
},
// Cannot read property 'length' of undefined
For some reason I get an error that I couldn't see before
The properties/data types in your code are a bit mixed up.
Here you're accessing state.todos.list...
todos () {
return this.$store.state.todos.list
},
...but in your store the const state doesn't include todos:
export const state = () => ({
list: []
})
Furthermore, you're writing to itemText the content of todos, which should be a string but actually is an object - which leads to the output of [object Object].
toEdit (todo) {
this.editIndex = true
this.itemText = this.todos
},
Also, please check out kissu's comment about the mutations.

Vuejs Passing Data through the same component

Im a noob in vuejs and i want to pass some data : profile that you can find inside of created() into
<span v-if="isLoggedIn">{{this.profile.username}}</span>
I know i'm missing some basics behind how vue works but im still learnig:)
<template>
<v-card class="mx-auto" color="dark" dark>
<div>
<v-app-bar clipped-left dark app>
<v-app-bar-nav-icon class="grey--text" #click="drawer= !drawer"></v-app-bar-nav-icon>
<v-toolbar-title class="grey--text">
<span class="font-weight-light">anime</span>
<span>Art</span>
</v-toolbar-title>
<v-spacer></v-spacer>
<span v-if="isLoggedIn">hi{{profile.username}}</span>
<v-btn icon>
<v-icon>mdi-heart</v-icon>
</v-btn>
<v-btn icon to="/login">
<v-icon>mdi-account-outline</v-icon>
</v-btn>
<v-btn icon v-if="isLoggedIn">
<v-icon v-on:click="logout">mdi-logout-variant</v-icon>
</v-btn>
</v-app-bar>
</div>
<v-navigation-drawer app expand-on-hover clipped v-model="drawer">
<v-divider></v-divider>
<v-list nav>
<v-list-item v-for="item in items" :key="item.title" :to="item.path" link>
<v-list-item-icon>
<v-icon>{{ item.icon }}</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
</v-navigation-drawer>
</v-card>
</template>
<script>
import firebase from "firebase";
// import db from "#/Firebase/firebaseInit";
export default {
data() {
return {
profile: {},
name: "navbar",
isLoggedIn: false,
currentUser: false,
drawer: false,
items: [
{ title: "Anime Home", icon: "mdi-view-dashboard", path: "/" },
{
title: "Trends",
icon: "mdi-chart-line-stacked",
path: "/trends"
},
{ title: "Save", icon: "mdi-bookmark", path: "/save" },
{
title: "Profile",
icon: "mdi-badge-account-horizontal",
path: "/profile"
},
{ title: "About", icon: "mdi-label", path: "/about" }
],
right: null
};
},
created() {
this.profile = {username:'hello'}
var user = firebase.auth().currentUser;
// var name, email,uid;
//, photoUrl, emailVerified
if (user != null) {
this.isLoggedIn = true;
this.profile = {
username: user.displayName,
useremail: user.email,
userid: user.uid,
photoUrl: user.photoURL,
emailVerified: user.emailVerified
};
console.log(profile.username);
// The user's ID, unique to the Firebase project. Do NOT use
// this value to authenticate with your backend server, if
// you have one. Use User.getToken() instead.
}
// console.log(user)
},
methods: {
logout: function() {
firebase
.auth()
.signOut()
.then(function() {
// Sign-out successful.
if (!firebase.auth().currentUser) {
alert("Signed out successfuly ");
}
})
.catch(function(error) {
// An error happened.
alert(error.message);
});
this.isLoggedIn = false;
this.$router.go({ path: this.$router.path });
}
}
};
</script>
in your html :
<span v-if="isLoggedIn">{{profile.username}}</span>
in your script
<script>
import firebase from "firebase";
export default {
data() {
return {
profile: {},
//all your stuff
},
created() {
var user = firebase.auth().currentUser;
if (user != null) {
this.isLoggedIn = true;
this.profile = {
username:user.displayName,
useremail :user.email,
userid:user.uid,
photoUrl : user.photoURL,
emailVerified: user.emailVerified
}
}
// console.log(user)
},
methods: {//all your stuff }
};
</script>

Vue-i18n: how to save the selected locale

There is a site on vue to which i18n library is connected.There is a button for switching languages.
<div class="locale-changer">
<v-menu v-model="changeLocaleState" offset-y>
<template v-slot:activator="{ on }">
<v-btn
color="primary"
dark
v-on="on"
/>
</template>
<v-list>
<v-list-tile
v-for="(lang, i) in langs"
:key="`lang.lacale${i}`"
:value="lang.locale"
#click="$i18n.locale = lang.locale"
>
<v-list-tile-title><img :src="lang.img">{{ lang.locale }}</v-list-tile-title>
</v-list-tile>
</v-list>
</v-menu>
export default {
name: 'VeLanguageSwitcher',
data () {
return { langs: [
{
name: 'en',
locale: 'en-US',
img: '/assets/img/flag/GB.png'
},
{
name: 'ru',
locale: 'ru-RU',
img: '/assets/img/flag/RU.png'
}
] }
},
computed: {
changeLocaleState: {
get () {
return this.$store.state.ui.changeLocale
},
set (val) {
this.$store.commit('ui/setChangeLocale', val)
}
}
}
}
</script>
The selected locale is not saved.Theoretically, I understand that when choosing a language you make SET in vuex, and GET displays a picture of the current locale inside the button.But I don't know how to do it

How to make dynamic routes on the vue router?

My MainNavBar component like this :
<template>
...
<v-list-item
v-for="(item, index) in listMenu"
:key="index"
#click="goTo(item)"
>
<v-list-item-content>
<v-list-item-title>{{ item }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
...
</template>
<script>
export default {
...
methods: {
goTo(key) {
this.$router.push({ name: key });
},
...mapActions("dataStore", ["getMenu"])
},
computed: {
...mapGetters("dataStore", ["listMenu"])
}
};
</script>
listMenu taken from API. It is a menu list
My router configuration like this :
import Vue from "vue";
import Router from "vue-router";
import Application from "#/pages/Application"
import MainNavbar from "#/layout/MainNavbar";
...
import { APPLICATION_ROUTE as RouteConfig } from "./route-config";
Vue.use(Router);
export default new Router({
mode: 'history',
routes: [
{
path: "/menu-first",
name: RouteConfig.APPLICATION_NAME_1.NAME,
components: { default: Application, header: MainNavbar }
},
{
path: "/menu-two",
name: RouteConfig.APPLICATION_NAME_2.NAME,
components: { default: Application, header: MainNavbar }
},
...
]
});
My RouterConfig like this :
export const APPLICATION_ROUTE = {
APPLICATION_NAME_1: {
NAME: "menu-first"
},
APPLICATION_NAME_2: {
NAME: "menu-two"
},
...
};
And my application component like this :
<template>
<v-flex xs12>
<v-card dark color="light-blue darken-4">
<v-card-title>
<v-flex xs4>
<p class="title">Name</p>
<p class="body-2 margin-sub-text">{{detailMenu.name}}</p>
</v-flex>
<v-flex xs4>
<p class="title">URL</p>
<p class="body-2 margin-sub-text">{{detailMenu.url}}</p>
</v-flex>
<v-flex xs4>
...
</v-flex>
</v-card-title>
</v-card>
</v-flex>
</template>
<script>
import { mapActions, mapState, mapGetters } from "vuex";
export default {
..
created() {
this.getDetailMenu(this.$route.path);
},
computed: mapState({
data: state => state.dataStore.data,
...mapGetters("dataStore", ["detailMenu"])
}),
methods: {
...mapActions("dataStore", ["getDetailMenu"]),
},
watch: {
$route() {
this.getDetailMenu(this.$route.path);
}
}
};
</script>
From the configuration router, my router is not dynamic. I want to make my router dynamic. So the path in the router configuration is taken from listMenu (API)
How do I do that?
I suppose getDetailMenu is calling API method to get listMenu.
You can create route dynamically using addRoutes method
Pseudo code
created() {
this.getDetailMenu(this.$route.path)
.then((listMenu) => {
// you need to return listMenu from getDetailMenu
listMenu.forEach((item, index) => createAndAppendRoute(item, index))
})
},
methods: {
createAndAppendRoute: (item, index) => {
console.log(item, index)
// Base on your data, you should be able to create router name and path from item and index
// here is just an example
let newRoute = {
path: `/${item}`,
name: `${item}_${index}`,
components: { default: Application, header: MainNavbar },
}
this.$router.addRoutes([newRoute])
}
}
This link should help :
https://router.vuejs.org/guide/essentials/navigation.html
goTo(key) {
this.$router.push({ path: key });
}
you can use the router.addRoutes method (https://router.vuejs.org/api/#router-addroutes):
router.addRoutes(routes: Array<RouteConfig>)
Dynamically add more routes to the router. The argument must be an Array using the same route config format with the routes constructor option.