Statamic Peak

Article

Translate Laravel Jetstream and Inertia

How to translate a new projet based on Laravel Jetstream and InertiaJs with vue-i18n or Matice ? Discover all possibilities !

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. The package is still young, and multilingual lazy loading is not available yet : you choose in a config file the languages you want to include. This is only a concern if your project handle a large number of languages or translations.

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 GENL/Matice

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.

t

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: {
        fr
    }
});

// ajouter après .use(InertiaPlugin)
.use(i18n)

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"
}</i18n>

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('terms.show')" class="underline text-sm text-gray-600 hover:text-gray-900">noparse_b262f3093d421b0598f49ae543c45a43</a>
    </template>
    <template #privacyPolicy>
        <a target="_blank" :href="route('policy.show')" class="underline text-sm text-gray-600 hover:text-gray-900">noparse_0b056d589fb911d197db8823ab246b2b</a>
    </template>
</i18n-t>