What is the best way to manage localization in vue 3?

Viewed 937

I wrote a small application in Vue 3 which handle the localization using i18n@next. My implementation is pretty simple, essentially I have each url prefixed with the current locale chosen by the user.

main.ts

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import { i18n } from './lang'

createApp(App)
  .use(store)
  .use(router)
  .use(i18n)
  .mount('#app')

lang/index.ts

import { createI18n } from 'vue-i18n'
import en from './translations/en.json'
import it from './translations/it.json'

export const i18n = createI18n({
  locale: 'en',
  fallbackLocale: 'en',
  messages: {
    en,
    it,
  },
})

router/index.ts

import { i18n } from '@/lang'
import { useAuth } from '@/modules/auth'
import App from '@/App.vue'
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'

function load(component: string) {
  return () => import(`@/views/${component}.vue`)
}

const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    name: 'root',
    redirect: process.env.VUE_APP_I18N_LOCALE,
  },
  {
    path: '/:locale',
    component: App,
    beforeEnter: (to, from, next) => {
      const locale = to.params.locale.toString()
      const supportedLocales = process.env.VUE_APP_I18N_SUPPORTED_LOCALE.split(
        ','
      )

      if (!supportedLocales.includes(locale)) return next('en')

      if (i18n.global.locale !== locale) {
        i18n.global.locale = locale
      }

      return next()
    },
    children: [
      {
        path: '',
        name: 'home',
        component: load('Home'),
        meta: { requiresAuth: false },
      },
      {
        path: 'login',
        name: 'login',
        component: load('Auth/Login'),
        meta: { requiresAuth: false },
      },
      {
        path: 'register',
        name: 'register',
        component: load('Auth/Register'),
        meta: { requiresAuth: false },
      },
    ],
  },
]

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes,
})

router.beforeEach((to, from, next) => {
  const { authenticating, user } = useAuth()
  next()
})

export default router

Essentially I can have multiple urls like:

http://localhost:8080/en
http://localhost:8080/en/login
http://localhost:8080/it
http://localhost:8080/it/login

As you can see from router/index.ts, before enter into an url, I set the locale available in the prefix.

I also have this LanguageSwitcher.vue component:

<template>
  <div class="dropdown ms-3">
    <a
      class="dropdown-toggle"
      href="#"
      role="button"
      id="dropdownLanguage"
      data-bs-toggle="dropdown"
      aria-haspopup="true"
      aria-expanded="false"
    >
      <img class="fa-fw me-2" src="assets/images/flags/uk.svg" alt="" />
      {{ $i18n.locale }}
    </a>
    <div class="dropdown-menu mt-2 shadow" aria-labelledby="dropdownLanguage">
      <span class="dropdown-item-text">{{ $t("selectLanguage") }}</span>
      <div class="dropdown-divider"></div>
      <a
        class="dropdown-item"
        v-for="(lang, i) in langs"
        :key="`lang-${i}`"
        v-on:click="changeLanguage(lang)"
      >
        <img class="fa-fw me-2" src="assets/images/flags/uk.svg" alt="" />
        {{ lang }}
      </a>
    </div>
  </div>
</template>

<script>
import { defineComponent } from "vue"

export default defineComponent({
  name: "LanguageSwitcher",
  data() {
    return { langs: process.env.VUE_APP_I18N_SUPPORTED_LOCALE.split(",") }
  },
  methods: {
    changeLanguage(lang) {
      this.$i18n.locale = lang

      // replace url prefix with the selected locale
      this.$router.replace({ params: { locale: lang } })
    },
  },
})
</script>

The code above works well but I have few questions:

  1. Static assets issue:

Each URL is prefixed with the locale chosen by the user. So for load the static assets, I was forced to use this convention in index.html (which is located in the public directory):

<script src="<%= BASE_URL %>assets/vendor/bootstrap/dist/js/bootstrap.bundle.min.js"></script>

The problem is different with the components. Infact I have something like this:

<img
    class="navbar-brand-item"
    v-bind:src="assets/images/logo.png"
    alt="Logo"
    />

when this asset is loaded I get 200 as status code, but nothing is returned back 'cause there is the locale prefix:

http://localhost:8080/en/assets/images/logo.png

How can I load the assets and load everything from:

http://localhost:8080/assets/images/logo.png

I also tried to add a @/assets but same issue, infact I get:

http://localhost:8080/@/en/assets/images/logo.png

Some example from console:

enter image description here

enter image description here

  1. Localize the slug:

How can I translate also the url? Eg: if I'm on english locale, and the load something like http://localhost:8080/en/login when I go to italian locale I just get http://localhost:8080/it/login, so the same slug with the prefix changed. Is there a way to also translate the slug?

Something like: http://localhost:8080/it/accedi. In this way the italian user can understand the page where they are located.

  1. router-link not follow the prefix

Suppose I have:

<router-link class="nav-link" to="/login">
    Login <i class="far fa-user me-2"></i>
</router-link>

if the current url is: http://localhost:8080/en and I click on the login link, I get: http://localhost:8080/login but should be: http://localhost:8080/en/login. How can I manage this?

0 Answers
Related