How to transfer value from Vuex Store to i18n.js? - javascript

I am trying to make the language that I chose was saved when the program was restarted.
How to save the value of locale:'' in i18n when switching languages?
I want to transfer this value from VUEX Store to i18n, but I don’t know how to do it right.
The default local: 'en'.
When the language is changed, the value in locale is overwritten and stored in localStorage, and when the program is restarted, the value at the created () stage is taken from localStorage.
Maybe at least an example exists where a similar function is implemented?
<template>
...
<f7-menu-dropdown-item href="#" #click="setLocale('en')">
<f7-menu-dropdown-item href="#" #click="setLocale('ru')">
...
</template>
export default {
methods: {
setLocale(locale){
import(`../langs/${locale}.json`).then((msgs) => {
this.$i18n.setLocaleMessage(locale, msgs)
this.$i18n.locale = locale
localStorage.setItem('language', locale);
})
},
created(){
if(localStorage.getItem('language') !== null){
localStorage.setItem('language', navigator.language || navigator.userLanguage);
}
}
}
}
i18n.js
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import en from '../langs/en.json'
import ru from '../langs/ru.json'
Vue.use(VueI18n)
export const i18n = new VueI18n({
locale: window.localStorage.getItem("language"),
fallbackLocale: 'ru',
messages:{
en,
ru
}
})

Related

I can't set i18n.locale in the javascript part of an inertiajs app

I want to use vue-i18n (version 9.1) in an interiajs app (laravel9 + vue3).
Since I need to do several things when switching languages, I need to outsource setting the i18n locale in the javascript part.
Unfortunately, this does not work:
I have already tried using $i18n.locale or this.$i18n.locale, without success (Error-message: this.$i18n / $i18n is not defined).
I am missing a hint how to set $i18n.locale in the javascript.
Here is my code:
app.js setup of i18n
require('./bootstrap');
import { createApp, h } from 'vue';
import { createI18n } from 'vue-i18n'
import { createInertiaApp } from '#inertiajs/inertia-vue3';
import { InertiaProgress } from '#inertiajs/progress';
const appName = window.document.getElementsByTagName('title')[0]?.innerText || 'Laravel';
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) => require(`./Pages/${name}.vue`),
setup({ el, app, props, plugin }) {
const i18n = createI18n({
locale: "de",
// locale: props.initialPage.props.locale, // user locale by props
fallbackLocale: "en", // set fallback locale
messages: loadLocaleMessages(),
});
return createApp({ render: () => h(app, props) })
.use(plugin)
.use(i18n)
.mixin({ methods: { route } })
.mount(el);
},
});
Authenticated.vue switch locale
<script setup>
import {ref, onMounted} from 'vue';
import BreezeDropdown from '#/Components/Dropdown.vue';
import BreezeNavLink from '#/Components/NavLink.vue';
import BreezeResponsiveNavLink from '#/Components/ResponsiveNavLink.vue';
import ProjectVersion from '#/Components/ProjectVersion.vue';
import {Link} from '#inertiajs/inertia-vue3';
import { usePage } from '#inertiajs/inertia-vue3';
import { computed } from 'vue';
function switchLanguage(key){
console.log('switchLanguage to ' + key);
this.$i18n.locale = key; // not working
}
</script>
<template>
<div v-if="$i18n.locale == 'en'">
<a #click.prevent="switchLanguage('de')" href="#">
Deutsch
</a>
</div>
<div v-if="$i18n.locale == 'de'">
<a #click.prevent="switchLanguage('en')" href="#">
English
</a>
</div>
</template>
I am happy about every hint.
In fact, this would not work in the way you're using it, since it's not a reference to the component instance inside that function. The composition API docs for i18n even says that we need "to replace access to this"
I haven't tried it, but I believe this would look something like this:
<script setup>
import { useI18n } from 'vue-i18n'
const { t, locale } = useI18n({
locale: 'en',
messages: {
en: {
// ...
},
de: {
// ...
}
}
})
const switchLanguage = (key) => {
locale.value = key
}
</script>

Vue i18n translation for single file components

I'm using laravel and currently trying to do multilanguage pages,
So i've found this pretty neat plugin called VueI18N for translations and got it working (somehow) by installing it via npm and then putting the following code in my app.js
//app.js
window.Vue = require('vue');
import VueI18n from 'vue-i18n'
Vue.use(VueI18n)
//tons of more components here
Vue.component('vue-test', require('./components/VueTestFileForLocalization.vue').default);
const messages = {
en: {
message: {
hello: 'Hello, {name}!'
}
},
de: {
message: {
hello: 'Guten Tag, {name}!'
}
}
};
const i18n = new VueI18n({
locale: 'de',
messages
});
const app = new Vue({
el: '#vue-app',
i18n
});
Then in my vue-test i tried outputting this successfully:
<template>
<div>{{ $t('message.hello', { name: 'John' }) }}</div>
</template>
<script>
export default {
data() {
return {};
},
created() {
this.$i18n.locale = 'en';
}
};
</script>
and by changing the locale i can also display the other language. Great.
Now I think with so many components, I might have a problem defining all the localization inside app.js , and its not beautiful either. So I tried looking up This link here to the docs for single file components but unsuccessfully, unfortunately.
I copy-pasted the code, (vue-i18n-loader should also be installed by laravel by default) and modified the webpack file. The error I get seems pretty common after research but I cannot seem to fix it.
Value of key 'hello' is not a string!
Cannot translate the value of keypath 'hello'. Use the value of keypath as default
It does simply output whatever the key is i specify in message.
Does any of you out there have an idea, what I might have done wrong or forgot to setup?
Any hints would be appreciated very very much.
Thank you for your time
Best regards,
Desory
While not a direct answer to your question I recently found another approach to the same problem that is less effort when it comes to maintaining translations.
I put all my translations in JSON files so I can share the same translations between Laravel backend and Vue front end.
I did this based on this:
https://www.codeandweb.com/babeledit/tutorials/how-to-translate-your-vue-app-with-vue-i18n
So as per: https://laravel.com/docs/7.x/localization#using-translation-strings-as-keys
Create resources/lang/en.json etc. with contents:
{
"my_message": "This is my message in english",
...
}
I'd create resources/js/i18n.js containing:
import Vue from "vue";
import VueI18n from "vue-i18n";
Vue.use(VueI18n);
function loadLocaleMessages() {
const locales = require.context(
"../lang",
true,
/[A-Za-z0-9-_,\s]+\.json$/i
);
const messages = {};
locales.keys().forEach(key => {
const matched = key.match(/([A-Za-z0-9-_]+)\./i);
if (matched && matched.length > 1) {
const locale = matched[1];
messages[locale] = locales(key);
}
});
return messages;
}
export default new VueI18n({
locale: process.env.VUE_APP_I18N_LOCALE || "en",
fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || "en",
messages: loadLocaleMessages()
});
and in app.js import that as follows:
//Localise
import i18n from "./i18n";
Vue.use(i18n);
/**
* Next, we will create a fresh Vue application instance and attach it to
* the page. Then, you may begin adding components to this application
* or customize the JavaScript scaffolding to fit your unique needs.
*/
const app = new Vue({
i18n,
el: "#app"
});
You can then use the same translations in your blade templates with the __ helper and in Vue with $t(...)
Try the changes below for app.js and your code should work fine:
import Vue from 'vue';
import VueI18n from 'vue-i18n';
import App from './components/VueTestFileForLocalization.vue';
Vue.use(VueI18n);
const messages = {
en: {
message: {
hello: 'Hello, {name}!'
}
},
de: {
message: {
hello: 'Guten Tag, {name}!'
}
}
};
const i18n = new VueI18n({
locale: 'de',
messages
});
new Vue({
i18n,
render: h => h(App)
}).$mount('#vue-app');
I had the same problem, i solved it by restarting the server.
Run npm run serve again.
Hope it helps someone in the future..

Vuex - store state Object is of type unknown

/src/middlewares/auth.ts file:
import store from '#/store'
export default {
guest(): void {
if (store.state.auth.authenticated === false) {
// do some action
}
}
}
/src/store.ts file:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
auth
}
})
/src/store/auth.ts:
import { Module, VuexModule, Mutation, Action } from 'vuex-module-decorators'
#Module
export default class Auth extends VuexModule {
public authenticated: boolean = false
}
However i'm getting TS error: Object is of type 'unknown'
Then my npm run build fails. How can i type store so this error would disappear?
UPDATE 1
Problems is in line: if (store.state.auth.authenticated === false)
UPDATE 2
My directory structure for these 3 files:
/src/middlewares/auth.ts
/src/store.ts
/src/store/auth.ts
UPDATE 3
if i change store.state.auth.authenticated to store.state then compiler stops complaining, i think its related to my store auth.ts file, i need to type definition it somehow.
Take a look: logrocket
export default function auth ({ next, store }){
if(!store.getters.auth.loggedIn){
return next({
name: 'login'
})
}
return next()
}
Your first argument sohuld be an object defining { store }
export default guest ({ store }, to: any ...){
It's hard to tell where store comes from in your scenario that you posted. If you are importing it directly then it would be something along the lines of:
import store from "store/index";
Also, by the nature of the store it's not fully typed. You could type it with something like
if ((store as object).state?.auth?.isLogged) {}
Though this won't give you type safety it would tell the compiler that you don't know.

Why does the language load only after reloading the page? i18n

When you first start the application, the application language should be installed on the system. But this only works when the page is reload. How to make the language load the first time?
<template>
...
<f7-menu-dropdown-item href="#" #click="setLocale('en')">
<f7-menu-dropdown-item href="#" #click="setLocale('ru')">
...
</template>
export default {
methods: {
setLocale(locale){
import(`../langs/${locale}.json`).then((msgs) => {
this.$i18n.setLocaleMessage(locale, msgs)
this.$i18n.locale = locale
localStorage.setItem('language', locale);
})
},
//Before creating, I check if there is any value in localStore and if not, then I install the system language.
beforeCreate(){
if(localStorage.getItem('language') === null){
let lng = window.navigator.systemLanguage || window.navigator.language;
lng = lng.substr(0, 2).toLowerCase();
localStorage.setItem('language', lng);
}
}
}
}
}
i18n
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import en from '../langs/en.json'
import ru from '../langs/ru.json'
Vue.use(VueI18n)
export const i18n = new VueI18n({
locale: window.localStorage.getItem("language"),
fallbackLocale: 'en',
messages:{
en,
ru
}
})

Avoid that when refreshing the page, the global status is lost with Vuex and VueRouter

I'm creating a SPA, and as a basic example of sessions on the client side, I'm using Vuex to store a boolean state if the user is logged in or not, it works fine while not manually updating the browser. As the state restarts to its initial state, is there any way to prevent this or should it always use localstorage? If so, how to get the initial state of storage?
Component Navbar
<template>
<div>
<ul v-if="!isLogued">
<router-link :to="{ name:'login'}" class="nav-link">Login</router-link>
</ul>
<ul v-if="isLogued">
Profile
Salir
</ul>
</div>
</template>
<script>
import {mapState,mapMutations } from 'vuex';
export default{
computed : mapState(['isLogued']),
methods:{
...mapMutations(['logout']),
}
}
</script>
Store.js
export default {
state: {
userLogued: {},
api_token : '',
isLogued : false
},
mutations: {
login( state){
state.userLogued = JSON.parse(localStorage.getItem('usuario'));
state.api_token = localStorage.getItem('api_token');
state.isLogued = true
},
logout(state){
state.userLogued = {}
state.isLogued = false
state.api_token = null
localStorage.clear()
}
}
};
App.JS
Vue.use(VueRouter)
Vue.use(Vuex)
import store from './vuex/store';
import routes from './routes';
const router = new VueRouter({
mode: 'history',
routes
})
const app = new Vue({
router,
store : new Vuex.Store(store)
}).$mount('#app')
In my login component, I do post with axios and if it is correct I do the following
methods : {
...mapMutations(['login']),
sendLogin(){
axios.post('/api/login' , this.form)
.then(res =>{
localStorage.setItem('api_token', res.data.api_token);
localStorage.setItem('user_logued', JSON.stringify(res.data.usuario));
this.login();
this.$router.push('/');
})
You will need to always be using a persistent storage mechanism if you want to preserve state across page reloads, browser sessions, etc. You can use localStorage, sessionStorage, cookies, indexeddb... whichever best suits your needs, although sessionStorage is of course only good for the current session.
To restore the initial state, use a plugin for Vuex, such as vuex-peristedstate. This will take case of the saving/restoring vuex to storage for you.
For example, creating an instance of vuex-persistedstate for use in your store is as easy as:
import createPersistedState from 'vuex-persistedstate'
const store = new Vuex.Store({
// ...
plugins: [createPersistedState()]
})

Categories

Resources