stackmeister
stackmeister

Reputation: 239

Internationalization for vue 3 vite with i18n

I'm currently trying to internationalize my vue 3 vite project with "@intlify/vite-plugin-vue-i18n". The problem I am facing here, is that i currently have to import and setup the t variable for every component to use it.
example component:

<template>
  t('translation')
</template>

<script>
import { useI18n } from 'vue-i18n'
export default {
  setup(){
    const {t} = useI18n();
    return {t}
  },
};
</script>

My question is, if its possible, what is the best way to make the variable "t" global? I cant find any examples/help on this, since they all import it into every compoenent. All help would be apreciated! :) For reference, here are the relevant files.

export default defineConfig({
  plugins: [
    vue(),
    vueI18n({
      include: path.resolve(__dirname, './src/locales/**')
    })
  ]
})

main.ts:

import i18n from './i18n';
const app = createApp(App);
app.use(i18n);
app.mount("#app");

i18n.js:

import { createI18n } from 'vue-i18n'
import messages from '@intlify/vite-plugin-vue-i18n/messages'

export default createI18n({
  legacy: false,
  locale: 'no',
  messages
})

Upvotes: 9

Views: 34318

Answers (2)

agm1984
agm1984

Reputation: 17168

I have an additional example to show of accessing the global composer instance in vue-i18n v9:

i18n.js

import { createI18n } from 'vue-i18n';
import en from './locales/en';
import fr from './locales/fr';

const i18n = createI18n({
    legacy: false,
    locale: 'en',
    fallbackLocale: 'en',
    messages: {
        en,
        fr,
    },
});

export default i18n;

main.js

import i18n from './i18n.js';

...

app.use(i18n);

Then you can import the instance into any file, such as vue-router's beforeEnter hook or vuex, etc.

import i18n from '../i18n.js';

console.log('i18n', i18n.global);

// to change locale:
i18n.global.locale.value = 'en';

You access it via i18n.global. it is the same instance that is returned via useI18n() from the 'vue-i18n' package.

EDIT: here is my current router.beforeEach function:

const routes = [
    {
        path: '/',
        redirect: 'lang'
    },
    {
        path: '/:lang',
        name: 'lang',
        component: HomePage,
    },
    {
        path: '/:lang/content/:contentId',
        name: 'guide.content',
        component: () => import('@/views/ContentRoot.vue'),
        children: [
            {
                path: '',
                name: 'guide.content.root',
                component: ContentPage,
            },
            {
                path: 'video',
                name: 'guide.content.video',
                component: ShowVideo,
            },
        ],
    },
    {
        path: '/:catchAll(.*)',
        component: () => import('@/views/404.vue'),
        hidden: true,
    },
];

router.beforeEach((to, from, next) => {
    const locale = to.params.lang; // Retrieve the current locale set in the URL

    // Check if the locale the user is trying to access is authorized.
    // In a larger application that supports lots of languages, you may want to store
    // all the locales in a separate array
    if (!['en', 'es'].includes(locale)) {
        return next(i18n.global.locale.value);
    }

    // Changing the language from the URL (either manually or with a link) is possible this way
    i18n.global.locale.value = locale;

    return next();
});

and here is my localeswitcher component:

<script setup>
import { watch } from 'vue';
import { IonSelect, IonSelectOption } from '@ionic/vue';
import { useRoute, useRouter } from 'vue-router';
import i18n from '@/i18n';

const route = useRoute();
const router = useRouter();

watch(() => i18n.global.locale.value, () => {
    router.replace({
        name: route.name,
        params: {
            lang: i18n.global.locale.value,
        },
    });
});
</script>

<template>
    <ion-select v-model="$i18n.locale" class="w-20" aria-label="Locale">
        <ion-select-option value="en">English</ion-select-option>
        <ion-select-option value="es">Spanish</ion-select-option>
    </ion-select>
</template>

It's Ionic in this example, but you can easily port that to a <select> and <option>

Upvotes: 17

Boussadjra Brahim
Boussadjra Brahim

Reputation: 1

The i18n plugin registering using app.use(i18n) make a global function $t available for all the children components :

<template>
  {{$t('translation')}}
</template>

This function is also available in option api and you could it like :

mounted() {
  console.log(this.$t('translation'))
}

But you should add globalInjection: true, to i18n config as follows :

import { createI18n } from 'vue-i18n'
import messages from '@intlify/vite-plugin-vue-i18n/messages'

export default createI18n({
  legacy: false,
  locale: 'no',
  globalInjection: true,
  messages
})

BONUS :

Change the locale by watching the getter inside App.vue then set locale:

<script>

import { defineComponent, onMounted, watch } from "vue";
import { useI18n } from "vue-i18n";
import { useStore } from "vuex";


export default defineComponent({
  name: "app",
  data() {
    return {};
  },

  setup() {
    const i18n = useI18n();
    const store = useStore();

    watch(()=>store.getters.currentLang,(newVal) => { //watch the getter
      i18n.locale.value = store.getters.currentLang;
    },{
      immediate:true
    });
  },
});
</script>

Upvotes: 24

Related Questions