How to use Laravel translations in JS (vue) files?
Tags: laravel, translation, javascript, vue
Published: 2019-10-04 15:31:06

After some research on this topic, I found out that none of the existing solutions satisfy my needs. Most of them required either recompilation of JS assets or running some artisan command after editing or adding new translations and I don't like that approach. Maybe there are already some solutions, but I remember that I have seen something that I need in Laravel Nova.

So I checked the sources of Laravel Nova and found out that Laravel translations were loaded as JSON from translation files and then passed to the Blade template. In the Blade it was a simple assignment to global config variable.

The problem with that solution was, that it loaded only [JSON](https://laravel.com/docs/6.x/localization#using-translation-strings-as-keys) translations and Laravel also has support for [PHP](https://laravel.com/docs/6.x/localization#using-short-keys) phrases.

After some googling, I found an [article](https://pineco.de/using-laravels-localization-js/), where the author showed how to load PHP language phrases to JS.

I mixed both approaches from Laravel Nova sources and from the article above and in the end I got, I think, the simplest way to use Laravel translation strings in JS files.

First of all create a translations service provider:
```php
<?php

namespace App\Providers;

use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\ServiceProvider;

class TranslationServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        Cache::rememberForever('translations', function () {
            $translations = collect();

            foreach (['en', 'kg', 'ru'] as $locale) { // suported locales
                $translations[$locale] = [
                    'php' => $this->phpTranslations($locale),
                    'json' => $this->jsonTranslations($locale),
                ];
            }

            return $translations;
        });
    }

    private function phpTranslations($locale)
    {
        $path = resource_path("lang/$locale");

        return collect(File::allFiles($path))->flatMap(function ($file) use ($locale) {
            $key = ($translation = $file->getBasename('.php'));

            return [$key => trans($translation, [], $locale)];
        });
    }

    private function jsonTranslations($locale)
    {
        $path = resource_path("lang/$locale.json");

        if (is_string($path) && is_readable($path)) {
            return json_decode(file_get_contents($path), true);
        }

        return [];
    }
}
```

Register it in `config/app.php` file:
```php
'providers' => [
    // your other providers
    App\Providers\TranslationServiceProvider::class,
],
```

Then you have to pass translation strings to JS in blade template. I did it in the default `layouts/app.blade.php` file:
```html
<script>
    window._locale = '{{ app()->getLocale() }}';
    window._translations = {!! cache('translations') !!};
</script>
```

Now you need some js function to retrieve translations and apply replacements. To do that I have created a `trans.js` file:
```js
module.exports = {
    methods: {
        /**
         * Translate the given key.
         */
        __(key, replace) {
            let translation, translationNotFound = true

            try {
                translation = key.split('.').reduce((t, i) => t[i] || null, window._translations[window._locale].php)

                if (translation) {
                    translationNotFound = false
                }
            } catch (e) {
                translation = key
            }

            if (translationNotFound) {
                translation = window._translations[window._locale]['json'][key]
                    ? window._translations[window._locale]['json'][key]
                    : key
            }

            _.forEach(replace, (value, key) => {
                translation = translation.replace(':' + key, value)
            })

            return translation
        }
    },
}

```

It's a bit modified version of `base.js` from Laravel Nova which also loads PHP translations. In short, the logic is: First try to find translation string in PHP translations, if not found, then try to find in JSON translations. If translation was not found at all, then it will show the key itself.

And the last step is to include the method as a mixin:
```js
Vue.mixin(require('./trans'))
```

That's it. Now you can use translations in Vue components like so:

```vue
<template>
<div class="card">
    <div class="card-header">{{ __('Example Component') }}</div>

    <div class="card-body">
        {{ __("I'm an example component.") }}
    </div>
</div>
</template>

<script>
export default {
    mounted() {
        console.log(this.__('Component mounted.'))
    }
}
</script>
```

With this solution, the only thing you have to do after editing/adding new translations is to run `cache:clear` artisan command. Laravel Nova (which we use in our projects) has packages, that allow executing such commands right from the admin panel, so that is not an issue at all.

**Update 25.02.2020**

The previous solution was working fine only on single locale. Thanks to @morpheus_ro to pointing out. Now the solution covers all the locales specified in the app.