How to translate Laravel with Inertia and Vue
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>