Translate Laravel Jetstream and Inertia with vue-i18n / Matice
3 min read

Translate Laravel Jetstream and Inertia with vue-i18n / Matice

How to translate a new project made with Laravel Jetstream and InertiaJs ? I show you the differents options !
Translate Laravel Jetstream and Inertia with vue-i18n / Matice

In order to translate the various contents included in Laravel and Jetstream, the package laravel-lang/lang gives us pre-made file that we can copy/paste. We only have left the translation of our Vue files.

To do this, we have 2 options :

  • Only manage our translation server-side. Which is done with GENL/Matice.
  • Split the front-end / back-end translations and then use the package vue-i18n

Depending on your team's size or its ability to evolve, you have to pick one solution. Matice is an easy fit for a team used to Blade / Laravel because it works the same as functions in PHP. But the package is still young and is not managing the multilingual lazy loading: Either one language is loaded, or all languages are. On the other hand, vue-i18n is the most popular translation solution in Vue projects, but doesn't offer any specific integration for Laravel.

Use GENLMatice

We add the composer package :

composer require genl/matice

We add the @translations directive in our app.blade.php file before our JS files loading.

On the JS side, a package is also available to add translations functions, inspired by Laravel:

npm install matice --save-dev

Then, the functions and their parameters are explained in the project's README et are similar to Laravel.

Use your Laravel translations in JavaScript. Contribute to GENL/matice development by creating an account on GitHub.

Use Vue-i18n

Install Vue-i18n in a Laravel Jetstream project

For vue-i18n, the solution is only in the front-end.

npm i --save-dev vue-i18n@next

In order to use JSON files or translations in Vue files, it is mandatory to add @intlify/vue-i18n-loader and to configure webpack.

npm i --save-dev @intlify/vue-i18n-loader@next

In webpack.config.js :

module: {
        rules: [
                test: /\\.vue$/,
                loader: 'vue-loader',
                test: /\\.(json5?|ya?ml)$/, // target json, json5, yaml and yml files
                type: 'javascript/auto',
                loader: '@intlify/vue-i18n-loader',
                include: [ // Use `Rule.include` to specify the files of locale messages to be pre-compiled
                    path.resolve(__dirname, 'src/locales')
                resourceQuery: /blockType=i18n/,
                type: 'javascript/auto',
                loader: '@intlify/vue-i18n-loader'

Use of Vue-i18n in a Laravel Jetstream project

We now only have to add vue-i18n to our Vue Instance.

To do this, we modify app.js.

import { createI18n } from 'vue-i18n'

const i18n = createI18n({
    locale: 'fr',
    fallbackLocale: 'fr',
    messages: {

// ajouter après .use(InertiaPlugin)

We are then allowed to create i18n blocks directly in a component :

<i18n locale="fr">{
    "forgot_password": "Mot de passe oublié?",
    "login": "Se connecter",
    "remember_me": "Se souvenir de moi"

Otherwise, we can put our translations into a JS file.

export default {
    "agree": "J'accepte les {tos} et la {privacyPolicy}",
    "email": "Email",
    "name" : "Raison sociale",
    "password": "Mot de passe",

Our install makes accessible a this.$t()  function in our components which uses as parameters the translation key et brings back the content according to the defined language.

For example :

<jet-label for="password_confirmation" :value="$t('confirm_password')" />

Sometimes, this is not enoughI want to display the message

"I accept the Terms of Service and Privacy Policy" and create links to 2 different pages.

For complex situations, vue-i18n allows to create a i18n-t tag, which in out example will replace {tos} and {privacyPolicy} by the templates. In the translation, we just have to write

"agree": "I accept the {tos} and {privacyPolicy}".

<i18n-t keypath="agree">
    <template #tos>
        <a target="_blank" :href="route('')" class="underline text-sm text-gray-600 hover:text-gray-900">{{
    <template #privacyPolicy>
        <a target="_blank" :href="route('')" class="underline text-sm text-gray-600 hover:text-gray-900">{{$t('privacy_policy')}}</a>

Vous aimez mes articles? Inscrivez-vous pour être alerté des nouveautés.