kg-bot/laravel-localization-to-vue
Exports your Laravel localization files into JavaScript so Vue components and vue-i18n can reuse the same translations. It supports PHP and JSON lang files, generates a JS object or module, can watch for changes, and keeps backend and frontend strings in sync without duplicate translation work.
This package is a build-time generator, not a runtime dependency: it reads your Laravel lang/ files and emits a JS/JSON bundle that vue-i18n consumes. Treat it accordingly.
composer require --dev kg-bot/laravel-localization-to-vue
resources/assets/js:
php artisan vendor:publish
# then open config/laravel-localization-to-vue.php
Check two keys first: the search directories (should include your lang/ path — resources/lang pre-Laravel 9, lang/ after) and the output path.resources/js/locales/) and generate:
php artisan vue-i18n:generate
vue-i18n in app.js:
import { createApp } from 'vue'
import { createI18n } from 'vue-i18n'
import messages from './locales/vue-i18n-locales.generated.js'
const i18n = createI18n({
locale: document.documentElement.lang || 'en',
messages,
})
createApp(App).use(i18n).mount('#app')
If you're on Laravel 11/12, v2.3 and v2.4 are interchangeable for you — v2.4 only widens the framework constraint and applies StyleCI formatting. Upgrade is a drop-in; no config or output changes.
Generate as part of the build, not by hand. The most common failure mode is a stale generated file. Hook it into package.json:
"scripts": {
"i18n": "php artisan vue-i18n:generate",
"dev": "npm run i18n && vite",
"build": "npm run i18n && vite build"
}
Watch mode during development. The package has no file watcher. Either re-run the artisan command manually, or add a watcher over lang/ (chokidar, nodemon --watch lang --exec "php artisan vue-i18n:generate") so new keys show up without a browser refresh killing your flow.
CI guard against drift. If you commit the generated file (recommended for apps that deploy JS separately from PHP), assert it's current:
php artisan vue-i18n:generate
git diff --exit-code resources/js/locales || (echo "Locales out of date" && exit 1)
Multiple search directories for modular lang files. Large apps split translations across lang/, a domain package, and vendor/ overrides. List each in the config's search directories rather than maintaining a second export step.
Lazy-load per locale once the bundle gets big — generate to one file per locale and dynamic-import in the router/i18n setup, rather than shipping every language to every user:
const messages = { en: await import('./locales/en.js') }
Blade + Vue share the same source. Because output derives from lang/, __('messages.welcome') in Blade and $t('messages.welcome') in Vue stay in sync by construction. Keep that invariant: never hand-edit the generated bundle, and route all user-facing strings through lang/.
Server-provided locale. Emit the active locale into the page (meta tag or window.__LOCALE__) so the client's initial createI18n({ locale }) matches what Laravel resolved — otherwise you get a flash of the wrong language on first paint.
:name; vue-i18n uses {name}. The generator does not rewrite these — a string like Welcome :name renders literally with a dangling colon in Vue. Standardise on {name} in lang/ (Laravel's replacer tolerates it) or post-process the output.apple|apples pipe-separated form collides with vue-i18n's own | plural syntax and its {count} requirement. Laravel's simple two-form plurals often survive, but anything with explicit ranges ({0} none|{1} one|[2,*] many) must be authored in vue-i18n syntax and loses Laravel-side correctness. Test pluralised keys explicitly.return ['a' => ['b' => 'c']] becomes a nested object; vue-i18n resolves a.b against it fine, but a literal dotted key ('a.b' => 'c') will not resolve as expected. Avoid dots inside key names.resources/js/... and not resources/assets/js/.... Vite will silently fail to resolve the import and you'll blame the wrong thing.env() in the published config breaks under config:cache. If you customise paths with env(), switch to literals or config()-safe values — deploying with a cached config otherwise yields an empty or wrong output path.vendor/kg-bot/.../config/ against yours after each bump. (Not needed for v2.4 — config is unchanged.)base_path('lang')) and that your lang files return arrays/JSON as expected. dd() the output file contents — an empty object almost always means the directory scan found nothing.composer.json.vue-i18n:generate.How can I help you explore Laravel packages today?