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:
- 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:
- 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.
- 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?

