Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Js Translation Bundle Laravel Package

firmaprofesional/js-translation-bundle

Symfony bundle that exposes server-side translation catalogs to JavaScript, letting frontend code reuse the same messages as your PHP templates. Generates JS translation dictionaries from Symfony's translator for consistent multilingual UI text across your app.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony bundle, not a Laravel package — there is no service provider, no config/*.php to publish, and composer require firmaprofesional/js-translation-bundle will not register anything in a Laravel app. Treat it as a reference implementation and a set of reusable pieces (the dumper shape and the translator.js runtime) rather than a drop-in dependency.

What to look at first, in this order:

  1. Controller/ — the route that serializes translations to JSON.
  2. Dumper/ — how the message catalogue is flattened per locale/domain.
  3. Resources/public/js/translator.js — the client-side Translator.trans() / transChoice() API you would actually call from Vue/Alpine/plain JS.
  4. DependencyInjection/Configuration.php — the config keys (locale_fallback, default_domain, translation_show_untranslated).

The bundle's actual contract is simple: a JSON tree of locale → domain → key → message, plus a tiny JS function that substitutes placeholders and picks plural forms. You can reproduce that in Laravel in about 30 lines.

Minimal Laravel port — a route that mirrors what the bundle's controller does:

// routes/web.php
Route::get('/js/translations/{locale}.json', function (string $locale) {
    abort_unless(in_array($locale, config('app.supported_locales')), 404);

    $domains = [];
    foreach (glob(lang_path("{$locale}/*.php")) as $file) {
        $domains[basename($file, '.php')] = require $file;
    }

    return response()->json([$locale => $domains])
        ->header('Cache-Control', 'public, max-age=3600');
});

Then in a Blade layout, before your app bundle:

<script>
  window.__translations = @json($translations ?? []);
  window.__locale = "{{ app()->getLocale() }}";
</script>
<script src="{{ asset('js/translator.js') }}"></script>

First real use case: a Vue or Alpine component that currently hardcodes English strings. Swap them for Translator.trans('messages.welcome', { name: user.name }) and you get locale-aware copy without a full laravel-vue-i18n setup.

Implementation Patterns

Build-time dump over runtime route. The bundle supports dumping translations to a static asset (bazinga:js-translation:dump). In Laravel the equivalent is an artisan command writing public/build/translations.{locale}.json, imported by Vite. This is preferable for production: no PHP hit per locale request, and the file is versioned with your JS bundle.

// app/Console/Commands/DumpJsTranslations.php
foreach (config('app.supported_locales') as $locale) {
    $out = [];
    foreach (glob(lang_path("{$locale}/*.php")) as $file) {
        $out[basename($file, '.php')] = require $file;
    }
    File::put(public_path("build/translations.{$locale}.json"), json_encode([$locale => $out]));
}

Hook it into post-autoload-dump or your deploy script so it never goes stale.

Placeholder translation between the two ecosystems. This is the single biggest friction point. Symfony uses %name%; Laravel uses :name. translator.js from this bundle expects %name%. Either normalize at dump time:

$message = preg_replace('/:(\w+)/', '%$1%', $message);

…or fork translator.js to accept the Laravel style. Pick one and enforce it in a test — mixed conventions produce silent %name% literals on screen.

Pluralization adapter. The bundle's transChoice uses Symfony's interval syntax ({0} none|{1} one|]1,Inf[ many). Laravel uses pipe-delimited none|one|many with trans_choice. Convert at dump time, or keep plural strings in a separate domain and route them through a small JS helper that reuses Laravel's choice boundaries. For simple two-form cases, a {0}…|{1}… → 0…|1… rewrite is enough; for ranges, write the mapping explicitly and test it — this is where most porting bugs live.

Domain filtering. The bundle lets you scope which domains are exposed. Mirror that: only dump messages, auth, validation — never domains containing internal copy, admin-only labels, or anything that leaks product decisions you don't want in a public JSON file.

Locale switching at runtime. The bundle's Translator is a singleton keyed to the current locale. In a SPA, load all locale JSON files up front (they're small) and swap Translator.locale on change; avoid a network round-trip per toggle.

Shared keys between server and client. Keep the source of truth in lang/{locale}/*.php and let both __() and the JS dump read from it. Do not maintain a parallel JS translation file — the two will drift within a sprint.

Testing parity. Add a Pest/PHPUnit test asserting the dumped JSON has exactly the same key set as the PHP files for every supported locale. This catches the "works in English, blank in German" class of bug before deploy.

Gotchas and Tips

  • It will not install usefully in Laravel. Composer may resolve it, but nothing boots. Don't waste time trying to register the bundle; port the JSON shape and the JS runtime instead. Check the upstream willdurand/BazingaJsTranslationBundle for newer commits — this fork appears to be a low-activity mirror (0 stars, 0 dependents).
  • Symfony-version drift. If you ever run this in a genuine Symfony app or a hybrid, verify against the Symfony version in use. The bundle's API surface predates Symfony 4.2's transChoice deprecation; older tags will break on modern symfony/translation.
  • % in messages. Any literal percent sign in a translation (e.g. "Save 20%") collides with the %placeholder% parser. Escape or avoid % in dumped messages.
  • Inline injection is an XSS surface. Never interpolate translations into a <script> block with string concatenation. Use @json() (or Js::from()) so quotes and </script> sequences are escaped correctly.
  • Missing keys fail silently. Translator.trans() returns the key itself when a message is absent, exactly like Laravel's __(). Enable the bundle's "show untranslated" behaviour during development (render the key in a contrasting colour) so missing strings are visible, then turn it off in production.
  • Stale dumps. The most common production bug with this pattern is a translation file edited and deployed without re-running the dump command. Wire the dump into your deploy pipeline and include a hash in the filename (translations.{locale}.{hash}.json) to force CDN invalidation.
  • Caching the runtime route. If you choose the route approach over build-time dumping, cache aggressively per locale but key the cache on the max filemtime of lang/{locale} — otherwise editors won't see changes until the cache TTL expires.
  • Extension point worth stealing: the bundle separates dumper from loader. In Laravel, mirror that with a TranslationDumper contract and a DatabaseTranslationDumper for teams that store translations in the DB — same JSON output, different source, and the JS side never changes.
  • Debugging: open the network tab and inspect the JSON payload directly. The expected shape is { "en": { "messages": { "welcome": "Hello %name%" } } }. If your keys are flat (messages.welcome), flattening is happening at the wrong layer — the bundle nests by domain, and Translator.trans('messages.welcome') resolves domain= messages, key = welcome.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console