How to implement translations with JetStream, Intertia and Vue3 using Laravel 10

Published at: 2023-08-15 18:00:05

Laravel 10 comes with internationalization support out of the box, however when we use JetStream starter pack with Inertia the transaction support is not included in the Vue part. Luckily it is quite easy to make it work.

Our use case

A brand new Laravel 10 application, using JetStream with Intertia and Vue3 frontend. The app requires multilanguage support, so we need a solution that we can use in both frontend and backend code. The best would be to use the same language files for both. In the newest Laravel (in time of writing it is version 10) we can use both json files and php files for translations. The php files are located in lang/{locale}/file.php. These files can be separated by "topic" like auth, validation etc. In case of json files, we only have one file per locale, and they are located in resources/lang/{locale}.json We want to use the latter because it is easily to pass the values to the frontend in json format.

Default Laravel translation keys in json

Laravel has a built in command to publish the default language files: php artisan lang:publish Unfortunately this only publishes the php language files. To convert them to json I used a quick and dirty solution and replaced the return in those files by using echo json_encode(['translation' => 'content']), and copied over the json. One caveat here is that the php file name is the translation prefix (for example auth), so in the json you need to add auth. prefix to all the translation keys (for example: auth.password).

Share the translation data from the backend

To be able to use the same language pack in both the frontend and the backend, we need pass the translation data to the frontend. First create a helper function which reads the json file support/helpers.php:


if (! function_exists('translations')) {
    function translations($json)
        if (!file_exists($json)) {
            return [];

        return json_decode(file_get_contents($json), true);

Add this file to the composer json autoload and run composer dump-autoload

    "autoload": {
        "files": [

We use Inertia so we share the translation object from the json by adding the following to the app/Http/Middleware/HandleInertiaRequests.php middleware:

   public function share(Request $request): array
        return array_merge(parent::share($request), [
            'locale' => function () {
                return app()->getLocale();
            'translations' => function () {
                return translations(
                    resource_path('lang/'. app()->getLocale() .'.json')

This makes the translations available as Inertia props.

Create Vue3 plugin for translation

The localization needs to be accessible everywhere so we create a plugin and make it accessible globally. Create a file resources/js/Translator.js

export const Translator = {
    install: (v, translations) => {
        v.config.globalProperties.__ = function(key, replace = {}) {
            let translation = translations[key]
                ? translations[key]
                : key;

            Object.keys(replace).forEach(function (key) {
                translation = translation.replaceAll(':' + key, replace[key])

            return translation

This basically finds the given key in the translations object and replace the placeholders in the translation if there is any. We make our function "__" available globally by adding it to the globalProperties. 

For further details about Vue plugin development check out the docs here:

Now our plugin is ready, let's use it in our app: resources/js/app.js

import './bootstrap';
import '../css/app.css';

import { createApp, h } from 'vue';
import { createInertiaApp } from '@inertiajs/vue3';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
import { ZiggyVue } from '../../vendor/tightenco/ziggy/dist/vue.m';
import {Translator} from './Translator.js';

const appName = import.meta.env.VITE_APP_NAME || 'Laravel';

    title: (title) => `${title} - ${appName}`,
    resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')),
    setup({ el, App, props, plugin }) {

        return createApp({ render: () => h(App, props) })
            .use(ZiggyVue, Ziggy)
            .use(Translator, props.initialPage.props.translations)
    progress: {
        color: '#4B5563',


Using translations

In blade files you can use the __() function as usual in Laravel, it will automatically find the proper json language file according to the app current locale. In Vue components use the same function like this: 


When using options API you can use it in the code like: this.__('auth.failed').

Useful resources

I'd like to thank Dario Trbovic I took the original idea from his blog post:

Vue3 documentation about writing plugins:

Laravel documentation about localization: