i18n locale change in url and nested routes

1.8k views Asked by At

Call to help from the depths of Vue Router purgatory! I've now spent a few days combining several resources, but had no success in making internalization work with url routes in my particular set-up.

Here's the gist: I have a project with nested router-views. Structure is like so:

.
├── /step
│   ├── /1 
│   ├── /2 
│   └── /3 
├── /join
└── /success

Here's what I've done so far:

  1. When the app loads, it should show the default locale in the url --> www.app.com/de and also redirect to /step. I've done this in router index.js:
{
        path: '/',
        beforeEnter(to, from, next) {
          next(i18n.locale + '/step')
        }
      },

and in i18n.js:

//global variable to check language support
export const supportedLangs = ["de", "en"];

// check what url user has arrived at, retrieves "en", "de" etc
let langParam = window.location.pathname.slice(1,3)

export default new VueI18n({
  locale: supportedLangs.includes(langParam) ? langParam  : 'de',
  fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en',
  messages: loadLocaleMessages(),
})
  1. Extract language parameter from url (there will be links leading to my app with specific /de or /en param). I've done it in router index.js:
{
    path: '/:lang', 
    component: App,
    beforeEnter(to, from, next) {
      let lang = to.params.lang
      if ( supportedLangs.includes(lang) ) {
        if (i18n.locale !== lang) {
          i18n.locale = lang
        }
        return next()
      }
      return next(i18n.locale)
    }
}

I am facing 2 problems that I could use help with.

PROBLEM 1: Un-nested routes don't render (for example www.app.com/de/join)

PROBLEM 2: Contents of App.vue that are outside get rendered twice, indicating the route nesting is not done correctly (I see double app bar)

I made a simplified playground with the code -> HERE

Fingers crossed someone can perhaps note out where I went wrong!

1

There are 1 answers

0
Anzu Pai On

Okay, after hours and hours of testing I think I found a solution with the correct structure. What I was missing was an empty component with for my dynamic language route.


const routes = [
  {
    path: "/",
    beforeEnter(to, from, next) {
      next(i18n.locale + "/step");
    },
    component: App
  },

  {
    path: "/:lang",
    component: { template: "<router-view />" },
    beforeEnter(to, from, next) {
      let lang = to.params.lang;
      if (supportedLangs.includes(lang)) {
        if (i18n.locale !== lang) {
          i18n.locale = lang;
        }
        return next();
      }
      return next(i18n.locale);
    },
    children: [
      {
        path: "step",
        redirect: "step/1",
        name: "Start",
        component: Steps,
        children: [
          {
            path: "1",
            name: "step1",
            component: Step1
          },
          {
            path: "2",
            name: "step2",
            component: Step2
          },
          {
            path: "3",
            name: "step3",
            component: Step3
          }
        ]
      },
      {
        path: "join",
        name: "join",
        component: Join
      },
      {
        path: "success",
        name: "success",
        component: Success
      }
    ]
  }
];

Here is the working version codesandbox. No clue WHY exactly it's needed but it made my project work the way I needed so I won't question the universe for now :P