Vue i18n - adding locale to the URL using routerview - vue.js

I am using a scafolded AspNetCore 2.1 site with VueJS using TypeScript.
I'm trying to integrate the kazupon i18n plugin with the router-view. Without the URL integration, it works just fine.
I am not able to get the proper redirects working like http://localhost/en/product and http://localhost/fr/product
This is the initial boot.ts, that uses VueI18n
import Vue from 'vue';
import VueRouter from 'vue-router';
import VueI18n from 'vue-i18n'
Vue.use(VueRouter);
Vue.use(VueI18n);
import { messages, defaultLocale } from './lang/i18n';
const i18n = new VueI18n({
locale: defaultLocale,
fallbackLocale: 'en',
messages
})
const routes = [
{ path: '/', component: require('./components/home/home.vue.html') },
{ path: '/product', component: require('./components/product/product.vue.html') },
];
const router = new VueRouter({
mode: 'history',
routes: routes
});
new Vue({
el: '#app-root',
router: router,
i18n: i18n,
render: h => h(require('./components/app/app.vue.html'))
});
So far I have tried prefixing routes but it just breaks the functionality:
const routes = [{
path: '/',
redirect: `/${defaultLocale}`,
},
{
path: '/:locale',
children: [
{ path: '/', component: require('./components/home/home.vue.html') },
{ path: '/counter', component: require('./components/product/product.vue.html') },
]
}];
I've also tried relying on router.beforeEach to set the locale.
router.beforeEach((to, from, next) => {
let language = to.params.locale;
if (!language) {
language = 'en';
}
i18n.locale = language;
next();
});
This does not function either.
I've drawn inspiration from github.com/ashour/vuejs-i18n-demo and vue-i18n-sap-multilingual-best-practice, but it seems that during the i18n migration, some samples may have been obsolete or lost and those use-cases no longer function.

what you primarily needed is to specify the base option in the vue-router constructor.
But first, you need to extract the locale from the url:
let locale = window.location.pathname.replace(/^\/([^\/]+).*/i,'$1');
and then specify the base in your VueRouter constructor:
const router = new VueRouter({
...
base: (locale.trim().length && locale != "/") ? '/' + locale : undefined
...
});
and last but not the least, pass the locale into your VueI18n constructor:
const i18n = new VueI18n({
locale: (locale.trim().length && locale != "/") ? locale : defaultLocale,
fallbackLocale: 'en',
messages
})
See the updated example:
import Vue from 'vue';
import VueRouter from 'vue-router';
import VueI18n from 'vue-i18n'
Vue.use(VueRouter);
Vue.use(VueI18n);
import { messages, defaultLocale } from './lang/i18n';
var locale = window.location.pathname.replace(/^\/([^\/]+).*/i,'$1');
const i18n = new VueI18n({
locale: (locale.trim().length && locale != "/") ? locale : defaultLocale ,
fallbackLocale: 'en',
messages
})
const routes = [
{ path: '/', component: require('./components/home/home.vue.html') },
{ path: '/product', component: require('./components/product/product.vue.html') },
];
const router = new VueRouter({
base: (locale.trim().length && locale != "/") ? '/' + locale : undefined,
mode: 'history',
routes: routes
});
new Vue({
el: '#app-root',
router: router,
i18n: i18n,
render: h => h(require('./components/app/app.vue.html'))
});

Thank you very much #Julian Paolo Dayag, there were three problems with the approach in my question:
it requires a redirect to "/" + defaultLocale + to.path
the children routes shouldn't start with /, they should be product instead of /product
The router.beforeEach... is no longer needed
I ended up with this code:
const routes = [
{
path: "/",
redirect: `/${defaultLocale}`
},
{
path: `/(fr|en)`,
component: require("./components/app/routertemplate.vue.html"),
children: [
{ path: "", component: require("./components/home/home.vue.html") },
{
path: "product",
component: require("./components/product/product.vue.html")
}
]
},
{
path: "/(.*)",
redirect: (to: any) => {
return "/" + defaultLocale + to.path;
}
}
];

Related

Autoload routes as modules

I've managed to create an autoloader for vuex store files, but I can't get it to work for vue-router files.
I always get the error [vue-router] "path" is required in a route configuration.
index.js autoload file
import camelCase from 'lodash/camelCase';
const requireRoute = require.context('.', false, /\.js$/); // extract js files inside modules folder
const routes = {};
requireRoute.keys().forEach((fileName) => {
if (fileName === './index.js') return; // reject the index.js file
const routeName = camelCase(fileName.replace(/(\.\/|\.js)/g, '')); //
routes[routeName] = requireRoute(fileName).default;
});
export default routes;
router.js
import Vue from 'vue';
import Router from 'vue-router';
import Home from '../views/Home.vue';
import routes from '#/routes/modules';
Vue.use(Router);
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home,
meta: {
icon: 'fa-tachometer',
title: 'Dashboard',
inMenu: true,
},
},
routes,
],
});
Can anybody point me in the right direction?
Try to spread your routes object, so your main routes will contain not a single object, but a bunch of objects:
routes: [
{
path: '/',
name: 'home',
component: Home,
meta: {
icon: 'fa-tachometer',
title: 'Dashboard',
inMenu: true,
},
},
...routes,
],
I had to set const routes = []; and had to .push() it to the array.
So the following code works:
index.js
const requireRoute = require.context('.', false, /\.js$/); // extract js files inside modules folder
const routes = [];
requireRoute.keys().forEach((fileName) => {
if (fileName === './index.js') return; // reject the index.js file
routes.push(requireRoute(fileName).default);
});
export default routes;
router.js
import Vue from 'vue';
import Router from 'vue-router';
import routes from '#/routes/modules';
Vue.use(Router);
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes,
});

Load routes from api in vue

I'm trying to load routes in a Vue application from my API. I tried pushing data to routes variable and using addRoutes method. But no luck. I think there could be an issue with async. But why the addRoutes() not working?
Here's my code:
import Vue from 'vue';
import VueRouter from 'vue-router';
import axios from 'axios';
/**
* Routes
*/
import item_index from '../../app/Tenant/Item/Views/Index.vue';
import contact_index from '../../app/Tenant/Contact/Views/Index.vue';
import eav_index from '../../app/Tenant/Eav/Views/create.vue';
import eav_create from '../../app/Tenant/Eav/Views/create.vue';
var routes = [
{ path: '/items', component: item_index, name: 'item_index' },
{ path: '/contact', component: eav_index , name: 'contact_index' , props: { entity_type_id: 1 }},
];
Vue.use(VueRouter);
const router = new VueRouter({
mode: 'history',
linkActiveClass: 'active',
routes
});
axios
.get('http://c1.fmt.dev/api/eav/entity_types')
.then(response => {
for (var i = 0; i < response.data.length; i++) {
var type = response.data[i];
var route = {
path: '/' + type.name,
component: eav_index,
name: type.name + '_index',
props: {
entity_type_id: type.id
},
};
router.addRoutes([route]);
alert(router.options.routes);
// alert(JSON.stringify(routes));
}
})
.catch(error => {
console.log(error)
});
new Vue({
el: '.v-app',
data(){
return {
page_header: '',
page_header_small: '',
}
},
router, axios
});
Try this improved code. Without postponing Vue instance creation, so without unnecessary delaying page interactivity:
import Vue from 'vue'
import VueRouter from 'vue-router'
import axios from 'axios'
import item_index from '../../app/Tenant/Item/Views/Index.vue'
import contact_index from '../../app/Tenant/Contact/Views/Index.vue'
import eav_index from '../../app/Tenant/Eav/Views/create.vue'
import eav_create from '../../app/Tenant/Eav/Views/create.vue'
Vue.use(VueRouter)
const router = new VueRouter({
mode: 'history',
linkActiveClass: 'active',
routes: [{
path: '/items',
component: item_index,
name: 'item_index'
}, {
path: '/contact',
component: eav_index ,
name: 'contact_index' ,
props: {entity_type_id: 1}
}]
})
new Vue({
el: '.v-app',
router,
data () {
return {
page_header: '',
page_header_small: '',
}
},
methods: {
getDynamicRoutes (url) {
axios
.get(url)
.then(this.processData)
.catch(err => console.log(err))
},
processData: ({data}) => {
data.forEach(this.createAndAppendRoute)
},
createAndAppendRoute: route => {
let newRoute = {
path: `/${route.name}`,
component: eav_index,
name: `${route.name}_index`,
props: {entity_type_id: route.id}
}
this.$router.addRoutes([newRoute])
}
},
created () {
this.getDynamicRoutes('http://c1.fmt.dev/api/eav/entity_types')
}
})
For better code structure and readability, move router definition to separate file:
In your main file, leave just this code:
// main.js
import Vue from 'vue'
import router from '#/router'
import axios from 'axios'
new Vue({
el: '.v-app',
router,
data () {
return {
page_header: '',
page_header_small: '',
}
},
methods: {
getDynamicRoutes (url) {
axios
.get(url)
.then(this.processData)
.catch(err => console.log(err))
},
processData: ({data}) => {
data.forEach(this.createAndAppendRoute)
},
createAndAppendRoute: route => {
let newRoute = {
path: `/${route.name}`,
component: eav_index,
name: `${route.name}_index`,
props: {entity_type_id: route.id}
}
this.$router.addRoutes([newRoute])
}
},
created () {
this.getDynamicRoutes('http://c1.fmt.dev/api/eav/entity_types')
}
})
And in same folder as main file lies, create subfolder 'router' with 'index.js' within:
// router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import item_index from '../../../app/Tenant/Item/Views/Index.vue'
import contact_index from '../../../app/Tenant/Contact/Views/Index.vue'
import eav_index from '../../../app/Tenant/Eav/Views/create.vue'
import eav_create from '../../../app/Tenant/Eav/Views/create.vue'
Vue.use(VueRouter)
export default new VueRouter({
mode: 'history',
linkActiveClass: 'active',
routes: [{
path: '/items',
component: item_index,
name: 'item_index'
}, {
path: '/contact',
component: eav_index ,
name: 'contact_index' ,
props: {entity_type_id: 1}
}]
})
The vue instance is already initiated when you try to add the routes (same problem as in this question: How to use addroutes method in Vue-router? ). You could postpone the vue initalization after the routes are loaded:
//imports and stuff...
axios
.get('http://c1.fmt.dev/api/eav/entity_types')
.then(response => {
for (var i = 0; i < response.data.length; i++) {
var type = response.data[i];
var route = {
path: '/' + type.name,
component: eav_index,
name: type.name + '_index',
props: {
entity_type_id: type.id
},
};
// extend routes array prior to router init
routes.push(route);
}
// init router when all routes are known
const router = new VueRouter({
mode: 'history',
linkActiveClass: 'active',
routes
});
// init vuw instance when router is ready
new Vue({
el: '.v-app',
data(){
return {
page_header: '',
page_header_small: '',
}
},
router, axios
});
})
.catch(error => {
console.log(error)
});

correct setting prerender-spa-plugin

I am trying to configure a prerender-spa-plugin for my application.
Need to configure for multiple pages.
I do, as in documentation (Vue.js 2 Router)
I added the necessary parameters to file webpack.prod.config.js
const PrerenderSPAPlugin = require('prerender-spa-plugin')
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer
const webpackConfig = merge(baseWebpackConfig, {
...
plugins: [
// == PRERENDER SPA PLUGIN == //
new PrerenderSPAPlugin({
staticDir: path.join(__dirname, '../dist'),
routes: ['/', '/test'],
renderer: new Renderer({
inject: {
foo: 'bar'
},
headless: false,
renderAfterDocumentEvent: 'render-event'
})
}),
...
Also in the file main.js
new Vue({
el: '#app',
router,
render: h => h(App),
mounted () {
// You'll need this for renderAfterDocumentEvent.
document.dispatchEvent(new Event('render-event'))
}
})
Now about the main problem.
At the root(/) of the project, pre-rendering is working.
If you go to the page /test, then in the source code (ctrl + u) will show the code from the site root(/).
I can't find content from /test in any way.
Tell me what I'm doing wrong, and how to fix the problem.
Thank!
UPD:routes.js
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '#/components/HelloWorld'
import test from '#/components/test'
Vue.use(Router)
export default new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
},
{
path: '/test',
name: 'test',
component: test
}
]
})

VueJS : Routing confusion

probably a stupid question especially as I am not sure if I am using VueJS or VueJS 2.0 but I am trying to get simple routing working where I can pick up the parameters / the path of the URL.
For example http://127.0.0.1/search/*****
Here is my main.js
import Vue from 'vue'
import App from './components/App'
import VueRouter from 'vue-router'
const routes = [
{ path: '/', name: 'Home', component: App },
{ path: 'search/:id', name: 'Search', component: App }
];
const router = new VueRouter({ mode: 'history', routes: routes });
Vue.config.productionTip = false
new Vue({
el: '#app',
router,
render: h => h(App)
})
And on my App.component I am trying to get the :id
created: function() {
//this.filterTutorials();
this.searchTerm = this.$route.query.id;
if (this.searchTerm == null) {
this.searchTerm = this.$route.params.id;
}
console.log(this.searchTerm)
}
UPDATE
App and search were the same component but I have not split them out (same directory)
New main.js. It is not even calling the search page
import Vue from 'vue'
import App from './components/App'
import VueRouter from 'vue-router'
const routes = [
{ path: '/', name: 'Home', component: App },
{ path: '/search/:id', name: 'Search', component: () => import(/* webpackChunkName: "search" */ './components/Search.vue'), props: true }
];
const router = new VueRouter({ mode: 'history', routes: routes });
Vue.config.productionTip = false
new Vue({
el: '#app',
router,
render: h => h(App)
})
I have also updated webpacks
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
vue: 'vue/dist/vue.js'
}
},
In your case, App is statically created before the route even resolves, so the created lifecycle hook would check for route parameter before it even existed (i.e., it would be undefined). I noticed both /search and / point to App, but you probably meant a component name like Search.
You can either dynamically import Search:
const routes = [
{
path: '/search/:id',
name: 'Search',
component: () => import(/* webpackChunkName: "search" */ './views/Search.vue')
}
]
Or you could use VueRouter's props: true to automatically set Search's id prop on navigation, obviating the check for the route parameters from created().
const routes = [
{
path: '/search/:id',
name: 'Search',
component: () => import(/* webpackChunkName: "search" */ './views/Search.vue'),
props: true,
}
]
demo

How do I link the route to the component using vue-router?

I'm trying to use vue-router to link url's to their corresponding components.
My issue is that only the root url ('/') can link to the correct component and any other url (e.g. '/first') does not link to their component. All of them link to the component which belongs to the '/' url.
When I use "a" tag in the vue file it will route to the right component - it's only when I input the url directly into the browser that it doesn't work
main.js:
import Vue from 'vue'
import router from './router/index.js'
Vue.use(ElementUI)
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
template: '<App/>',
// components: { App },
render: h => h(App)
})
index.js:
import Vue from 'vue'
import Router from 'vue-router'
import Hello from '../components/Hello.vue'
import First from '../components/firstPage.vue'
import Login from '../components/logIn.vue'
Vue.use(Router)
const routes =[
{
path: '/',
component: Login
},
{
path:'/first',
component:Hello
}
]
const router = new Router({
routes
})
export default router
I think you can also try as below. It's works, just try it! You can add mode: 'hash', to give default # in all urls.
import Vue from 'vue';
import Router from 'vue-router'
import Hello from '../components/Hello.vue'
import First from '../components/firstPage.vue'
import Login from '../components/logIn.vue'
Vue.use(VueRouter);
const router = new VueRouter({
mode: 'hash',
base: '/your_base_name_if_you_want',
linkActiveClass: 'active-tab', //if you w
routes: [
{
path: '/',
redirect: '/if_you_want_to_redirect',
name: 'Login', //Whatever
component: {
render (c) { return c('router-view'); }
},
children: [
{
path: '/',
component: Login
name: 'Login',
meta: { title: 'Your title name' }
},
{
path:'/first',
component:Hello
name: 'Hello',
meta: { title: 'Your title name' }
}
]
}
]
})
export default router
You can also remove it from your urls using:
var router = new VueRouter({
history: true
});
Hope this helps you!
The default mode for vue-router is hash mode
This is why you can't get the 'Hello' component when your url is '/first'. Instead you can try input '/#/first'.
If you want to use history mode like '/first', set the mode attribute for your route object like this.
const router = new Router({
mode: 'history',
routes
})
Hope it helps you.