matice
matice copied to clipboard
this.$forceUpdate does not refresh the whole page when choosing another language
Expected behavior
All Vue components on the page are updated when the language is changed.
Current behavior
Only the language selector is updated and the rest of the page remains the same. this.$forceUpdate()
seems not to work.
Versions
- Laravel: 8.43.0
- Matice: 1.1.4
- Vue: 3.0.5
Description
The backend package works fine and the translations on the frontend, when I change the language on the Laravel config side, works wonderfully too. The problem here is that when I change the language with a selector I have created on the frontend, the this.$forceUpdate()
method won't refresh all components on the page, so the translation only applies to the selector itself and nowhere else on the page.
I am using Vue3 if that matters. Here is my code on app.js:
...
createApp({
render: () =>
h(InertiaApp, {
initialPage: JSON.parse(el.dataset.page),
resolveComponent: (name) => require(`./Pages/${name}`).default,
}),
})
.mixin({
methods: {
route,
$trans: trans,
$__: __,
$transChoice: transChoice,
$setLocale(locale) {
if (this.$locale() !== locale) {
setLocale(locale);
this.$forceUpdate(); // Refresh the vue instance(The whole app in case of SPA) after the locale changes.
}
},
// The current locale
$locale() {
return getLocale()
},
// A listing of the available locales
$locales() {
return locales()
}
}
})
.use(InertiaPlugin)
.mount(el);
...
and this is the code that calls the $setLocale(locale)
method on my language selector component:
...
<select v-on:change="$setLocale($event.target.value)" v-model="selectedLang">
<option v-for="(lang, code) in languages" :value="code">
{{ lang }}
</option>
</select>
...
In vue 3. If you want to refresh the app, store an instance of the app, then call $forceUpdate
on that instance.
const MaticeMixin = {
methods: {
route,
$trans: trans,
$__: __,
$transChoice: transChoice,
$setLocale(locale) {
if (locale !== getLocale()) {
setLocale(locale);
app.$forceUpdate() // Refresh the vue instance after locale change.
}
},
// The current locale
$locale() {
return getLocale()
},
$locales() {
return locales()
},
},
}
const root = document.querySelector('#app');
let app = createApp({
render: () => h(App, {
initialPage: JSON.parse(root.dataset.page),
resolveComponent: (name) => require(`./Pages/${name}`).default,
}),
}).use(plugin)
.mixin(MaticeMixin)
.mount(root)
Also, I suggest you upgrade to the latest version of matice. There are no breaking changes, but improved performance and more features,
This solution didn't work for me with this combination:
- "genl/matice": "^1.1",
- "inertiajs/inertia-laravel": "^0.4.3",
- "laravel/framework": "^8.54",
- "laravel/jetstream": "^2.3",
But I made it work using Inertia:
// resoures/js/app.js
require('./bootstrap');
import { createApp, h } from 'vue';
import { createInertiaApp } from '@inertiajs/inertia-vue3';
import { InertiaProgress } from '@inertiajs/progress';
import '@fortawesome/fontawesome-free/js/all.js';
import { __, trans, setLocale, getLocale, transChoice, MaticeLocalizationConfig, locales } from "matice"
const appName = window.document.getElementsByTagName('title')[0]?.innerText || 'Laravel';
const MaticeMixin = {
methods: {
route,
$trans: trans,
$__: __,
$transChoice: transChoice,
$setLocale(locale) {
if (locale !== getLocale()) {
setLocale(locale);
this.$inertia.get(route('locale', {'locale': locale}));
this.$inertia.reload();
}
},
$locale() {
return getLocale()
},
$locales() {
return locales()
},
},
}
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) => require(`./Pages/${name}.vue`),
setup({ el, app, props, plugin }) {
return createApp({ render: () => h(app, props) })
.use(plugin)
.mixin({
methods: {
route,
}
})
.mixin(MaticeMixin)
.mount(el);
},
});
InertiaProgress.init({ color: '#2980B9' });
Using this.$inertia.reload();
works like a charm on main pages:
https://ibb.co/B345zYg
but not on auth pages: https://ibb.co/0DB220z
Any hint to fix this?
Can you share the error output if any? First check if the issue comes from Matice or inertia. If the error is related to Matice, then make sure it is well loaded on your auth page. The best way of doing that is to check if a single component updates when the language local changes.
Please share with me the code of both pages.
Also make sure sure to use the latest version of Matice, v1.6.
On the links you provided, I connot interact with the local field. So it's impossible to test,
There's no error output, everything works fine, but the view doesn't reload (only on auth pages). Anyway, this is not so important, I just wanted to help others using Inertia like me. But if you have any suggestion to make my view work, it would be great!
You cannot interact with my links because are images, I cannot publish my application at the moment
UPDATE: Sometimes the "non-reload" happens on labels and buttons on other pages too. Maybe is something related to Vue or Inertia themselves, I don't know, but I'm writing it down here to make it work for everybody
Share your portion of the code with me, please.
This one is the same as before:
// resoures/js/app.js
require('./bootstrap');
import { createApp, h } from 'vue';
import { createInertiaApp } from '@inertiajs/inertia-vue3';
import { InertiaProgress } from '@inertiajs/progress';
import '@fortawesome/fontawesome-free/js/all.js';
import { __, trans, setLocale, getLocale, transChoice, MaticeLocalizationConfig, locales } from "matice"
const appName = window.document.getElementsByTagName('title')[0]?.innerText || 'Laravel';
const MaticeMixin = {
methods: {
route,
$trans: trans,
$__: __,
$transChoice: transChoice,
$setLocale(locale) {
if (locale !== getLocale()) {
setLocale(locale);
this.$inertia.get(route('locale', {'locale': locale}));
this.$inertia.reload();
}
},
$locale() {
return getLocale()
},
$locales() {
return locales()
},
},
}
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) => require(`./Pages/${name}.vue`),
setup({ el, app, props, plugin }) {
return createApp({ render: () => h(app, props) })
.use(plugin)
.mixin({
methods: {
route,
}
})
.mixin(MaticeMixin)
.mount(el);
},
});
InertiaProgress.init({ color: '#2980B9' });
This is my locale selector (imported inside my layout):
<template>
<select v-on:change="$setLocale($event.target.value)">
<option v-for="(lang) in $locales()" :value="lang" :key="lang" :selected="$locale() == lang">
{{ lang }}
</option>
</select>
</template>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
props: [],
methods: {
}
})
</script>
And this is my login page:
<template>
<Head :title="$__('auth.login.title')" />
<jet-authentication-card>
<template #logo>
<jet-authentication-card-logo />
</template>
<jet-validation-errors class="mb-4" />
<div v-if="status" class="mb-4 font-medium text-sm text-green-600">
{{ status }}
</div>
<form @submit.prevent="submit">
<div>
<jet-label for="email" :value="$__('auth.login.email-or-username')" />
<jet-input id="email" type="text" class="mt-1 block w-full" v-model="form.email" required autofocus />
</div>
<div class="mt-4">
<jet-label for="password" :value="$__('auth.login.password')" />
<jet-input id="password" type="password" class="mt-1 block w-full" v-model="form.password" required autocomplete="current-password" />
</div>
<div class="block mt-4">
<label class="flex items-center">
<jet-checkbox name="remember" v-model:checked="form.remember" />
<span class="ml-2 text-sm text-gray-600">
{{ $__('auth.login.remember-me') }}
</span>
</label>
</div>
<div class="flex items-center justify-end mt-4">
<Link v-if="canResetPassword" :href="route('password.request')" class="underline text-sm text-gray-600 hover:text-gray-900">
{{ $__('auth.login.forgot-password') }}
</Link>
<jet-button class="ml-4" :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
{{ $__('auth.login.login') }}
</jet-button>
</div>
</form>
<hr class="my-5">
<div class="text-center px-5" v-html="$__('auth.login.register', { args: { url: route('register') } })">
</div>
</jet-authentication-card>
</template>
<script>
import { defineComponent } from 'vue'
import JetAuthenticationCard from '@/Jetstream/AuthenticationCard.vue'
import JetAuthenticationCardLogo from '@/Jetstream/AuthenticationCardLogo.vue'
import JetButton from '@/Jetstream/Button.vue'
import JetInput from '@/Jetstream/Input.vue'
import JetCheckbox from '@/Jetstream/Checkbox.vue'
import JetLabel from '@/Jetstream/Label.vue'
import JetValidationErrors from '@/Jetstream/ValidationErrors.vue'
import { Head, Link } from '@inertiajs/inertia-vue3';
export default defineComponent({
components: {
Head,
JetAuthenticationCard,
JetAuthenticationCardLogo,
JetButton,
JetInput,
JetCheckbox,
JetLabel,
JetValidationErrors,
Link,
},
props: {
canResetPassword: Boolean,
status: String
},
data() {
return {
form: this.$inertia.form({
email: '',
password: '',
remember: false
})
}
},
methods: {
submit() {
this.form
.transform(data => ({
... data,
remember: this.form.remember ? 'on' : ''
}))
.post(this.route('login'), {
onFinish: () => this.form.reset('password'),
});
}
}
})
</script>
That's all
$setLocale(locale) {
if (locale !== getLocale()) {
setLocale(locale);
this.$inertia.get(route('locale', {'locale': locale}));
this.$inertia.reload();
}
}
Matice is a frontend package to handle Laravel's translations.
Make sure everything works fine locally without the backend call.
Then, you should wait for the GET
call to finish before calling inertia reload
.
I plan to make a release, to change the code to make the locale update reactive. So it will not be required to reload the page anymore. Just give me a couple of days.
I tried to put reload function call inside a callback (onSuccess and onFinish), but it causes a modal to appear because of Inertia/Jetstream functionalities. I also tried with a simple setTimeout, but nothing changed. I'll wait for your update! Thanks a lot for your support
And this is the reason why I make a GET call:
Route::get('/locale/{locale}', function ($locale) {
app()->setLocale($locale);
session()->put('locale', $locale);
$user = Auth::user();
if($user){
$user->profile->locale_code = $locale;
$user->profile->save();
}
})->name('locale');