How to translate Laravel with Inertia and Vue

Dario Trbovic
3 min readMay 18, 2021

This is how I translated Laravel with Inertia and Vue. I tried to find simple solution but I couldn’t find it.

I’m using Laravel 8 and Vue 3, @inertiajs/inertia 0.8.4 and @inertiajs/inertia-vue3 0.3.5.

Edit config/filesystems.php

Add ‘languages’ disk so you can easily use Laravel’s Storage to access ‘resources/lang’ folder where your languages reside.

config/filesystems.php:

'disks' => [
'languages' => [
'driver' => 'local',
'root' => base_path('resources/lang'),
],
],

Add command for generating en.json

In order to be able to read our php translations we need to convert them to .json file. That file will be placed under ‘resources/lang/en.json’. This command go through all of directories under ‘resources/lang’ and creates .json files for all languages.

For example if under ‘resources/lang’ there is ‘en’ and ‘de’ folder command will create ‘resources/lang/en.json’ and ‘resources/lang/de.json’. Command will follow php array structure and will recreate json files out of it.

app/Console/Commands/CreateJsonTranslationFileCommand.php:

<?php

namespace
App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;

class CreateJsonTranslationFileCommand extends Command
{
/**
* The name and signature of the console command.
*
*
@var string
*/
protected $signature = 'translation:generate-json';

/**
* The console command description.
*
*
@var string
*/
protected $description = 'This commands go through en lang files and creates en.json file';

/**
* Execute the console command.
*
*
@return int
*/
public function handle()
{
$storage = Storage::disk('languages');
$directories = $storage->directories();
foreach ($directories as $directory) {
$files = $storage->allFiles($directory);
$array = [];
foreach ($files as $file) {
//do not include "api" directory, because api is only for API calls
if (str_starts_with($file, $directory.'/api')) {
continue;
}
$baseName = str_replace('.php', '', basename($file));
$contents = require $storage->path($file);
$array[$baseName] = $contents;
}

$filePath = sprintf('%s.json', $directory);
$storage->put($filePath, json_encode($array, JSON_PRETTY_PRINT));
}

$this->info('Files generated');

return 0;
}
}

Create helpers.php file

In order to be able to fetch en.json file, its content and share with Inertia we need one helper function. I added it under “helpers” folder in root directory of my project.

helpers/helpers.php:

<?php


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

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

Update composer.json

We need to tell composer.json to include our helpers.php file in autoload.

composer.json:

...
"autoload"
: {
"files": [
"helpers/helpers.php"
]
}
...

Now run: composer dump-autoload

Edit HandleInertiaRequests class

Now that we have our en.json and translations helper function we need to share data. The server-side adapters provide a way to preassign shared data for each request. This is typically done outside of your controllers. Shared data will be automatically merged with the page props provided in your controller. (https://inertiajs.com/shared-data)

app/Http/Middleware/HandleInertiaRequests.php:

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

Create translation.js file

Create translation.js file under ‘resources/js/translation.js’. This file hold JS logic for fetching data from our en.json file. It recursively goes through en.json and looks for keys.

So if we have json like one below we can fetch our text with “test.hello” syntax (more about it later in this article):

{
"test": {
"hello": "This is my text"
}
}

translation.js:

module.exports = {
methods: {
/**
* Translate the given key.
*/
__(key, replace = {}) {
keys = key.split('.');
var translation = this.$page.props.language;
keys.forEach(function(keyTmp){
translation = translation[keyTmp]
? translation[keyTmp]
: keyTmp
});

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

return translation
},

/**
* Translate the given key with basic pluralization.
*/
__n(key, number, replace = {}) {
var options = key.split('|');

key = options[1];
if(number == 1) {
key = options[0];
}

return tt(key, replace);
},
},
}

Modify resources/js/app.js

Modify resources/js/app.js and add .mixin(require(‘./translation’)) like below:

createApp({
render: () =>
h(InertiaApp, {
initialPage: JSON.parse(el.dataset.page),
resolveComponent: (name) => require(`./Pages/${name}`).default,
}),
})
.mixin({ methods: { route } })
.mixin(require('./translation')) //ADD THIS
.use(InertiaPlugin)
.mount(el);

We are done, try it out

If you haven’t run: npm run dev

Do you remember our sample en.json file:

{
"test": {
"hello": "This is my text"
}
}

Open any .vue file and

<jet-label for="phone_number" v-bind:value="__('test.hello')" class="mb-5" />

or

<div>
{{__('test.hello')}}
</div>

--

--