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

Langsys Php Laravel Laravel Package

langsys/langsys-php-laravel

Laravel integration for LangSys translation management. Adds middleware, helpers, Blade directives, and Artisan commands to fetch, cache, and serve localized strings from LangSys, simplifying multilingual Laravel apps and keeping translations synced with your LangSys project.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and verify prerequisites first. The install itself is one command, but two of the requirements fail loudly and late if you skip them:

composer require langsys/langsys-php-laravel
  • Confirm ext-intl is present (php -m | grep intl) before you start, not after the first request 500s in CI. The official php:8.x-fpm images ship without it, so your Dockerfile needs docker-php-ext-install intl (plus libicu-dev) or an equivalent. This is the single most common first-run failure.
  • Confirm your matrix: PHP 8.1+, Laravel 10/11/12. The package pulls langsys/langsys-php ^1.3 transitively — don't pin it yourself.

Where to look first. Read ROADMAP.md before writing any markup. The "two coverage modes, never both" rule is a project-level architectural decision, and it is far cheaper to make on day one than to unwind after your catalog has been polluted. CHANGELOG.md is where the actual behavioural detail lives; the README is thinner than the release notes suggest.

First use case — tagged mode, smallest possible slice. For a first integration, take a single Blade view and a single locale:

  1. Register DetectLocale on your web middleware group so the locale resolves from query → cookie → session → Accept-Language.
  2. Wrap the strings you care about: {{ t('Welcome {name}', ['name' => $user->name]) }} or the @t directive.
  3. Hit the page with ?lang=fr (or whatever your catalog locales are) and confirm the query-string override wins before you trust cookie/session behaviour.
  4. Watch your Langsys dashboard/catalog for the phrase appearing — it arrives after the response, so a page refresh is needed to see it.

Keep the default (tagged) mode for the first pass. Do not enable TranslateResponse until you have something working end-to-end, because it changes the failure surface from "one string is untranslated" to "the whole document is walked".

Implementation Patterns

One catalog, whole stack. The t() signature is deliberately identical to the JS SDKs, so Welcome {name} is one catalog entry, not one per runtime value. The pattern to internalise: interpolation happens in the SDK, not in Laravel. Pass params as an array to the helper; never sprintf a phrase into existence, or you'll fork a catalog entry per user.

{{-- right --}}
{{ t('Welcome {name}', ['name' => $user->name]) }}

{{-- wrong — creates N catalog entries --}}
{{ t("Welcome {$user->name}") }}

Middleware ordering is the integration contract. DetectLocale must run before anything that resolves a phrase, and before TranslateResponse if you opt in. A typical stack:

// bootstrap/app.php (Laravel 11+) or Kernel::$middleware
$middleware->web(append: [
    \Langsys\Laravel\Http\Middleware\DetectLocale::class,
]);

Add TranslateResponse only to the groups/routes that actually need it — putting it on the global stack means it also walks JSON APIs, file downloads and streamed responses, which is pure cost.

Tagged mode is the default recommendation for new projects. You get explicit control, a small diff surface, and no surprise catalog growth. Use it for: marketing pages you author yourself, transactional email templates, validation messages you've already overridden.

Automatic mode is a deliberate trade. It exists for one reason: @t structurally cannot reach text Alpine injects from JS expressions, because that text does not exist at render time. If your app is Alpine-heavy and the copy lives in x-text/x-html, tagged mode gives you false confidence. Choose automatic mode wholesale and exclude the already-resolved subtrees:

<div translate="no">{{ t('Already handled server-side') }}</div>
<span data-notrans>{{ $someResolvedString }}</span>

The rule is per-project, not per-view. Mixing modes in one app is the documented failure mode.

Inertia + SSR. If you're on Inertia with the Vue/React/Svelte SDKs, call InertiaSsrProps::share() so initialTranslations is seeded into the SSR payload — the JS side then hydrates without a second round trip. This pairs with langsys-js-typescript ≥0.6.2; a mismatched JS version is a silent-ish failure (you get base-language text after hydration), so pin and bump them together.

Registration is asynchronous by design. FlushPendingRegistrations sends discovered phrases after the response is flushed. Newly seen phrases therefore do not exist in the catalog during the request that first discovers them — expect the first render of a new string to be base-language, and don't write a test that asserts otherwise. The Octane listener exists precisely because long-lived workers never fire PHP shutdown handlers; if you're on Octane, verify the listener is registered rather than assuming the after-response path covers you.

Testing pattern that works. Assert on the resolved output, not on catalog state:

$this->withSession(['locale' => 'fr'])
     ->get('/')
     ->assertSee('Bienvenue Alice');

Because lookups degrade rather than throw, a test that mocks the SDK to fail and asserts the base phrase + interpolated params is a cheap guard on the "never 500s a page" guarantee — and it will catch anyone later "helpfully" removing the try/catch.

Gotchas and Tips

Never enable both coverage modes. This is the one bug that damages other people's work. In automatic mode, TranslateResponse re-walks nodes already translated by @t and registers the translated string as a source phrase — into the same catalog every Langsys SDK shares. There's no undo button and no per-project isolation. If you inherit a project and see French phrases sitting in the source-language catalog, this is what happened.

ext-intl fails in production, not on your laptop. Homebrew PHP bundles it; the official Docker images don't. Add the extension to your base image and to CI, and add a startup check if you run multiple environments.

Silent degradation is the feature and the hazard. Lookups failing to base language, registration failures swallowed, automatic mode serving the untranslated page — all correct behaviour, all invisible. Nothing 500s, so nothing alerts. Wire the package's logging into your error tracker (or at minimum grep for its log channel in staging) or you will discover a broken Langsys connection weeks later when someone notices the site is English-only in six markets.

@t is a render-time tool; Alpine is not. Text produced by x-text/x-html/JS expressions is invisible to @t. If a design review says "we translated everything" but the interactive components are still English, you're in automatic-mode territory — don't paper over it with translate="no" everywhere.

Locale formats differ between Laravel and the SDK. DetectLocale gives you canonical BCP 47 for Laravel (en-GB, pt-BR) and a normalised form for the SDK. Don't hand-roll locale strings into t() or into catalog config from app()->getLocale() and assume they match what the SDK expects — pass through the middleware's resolution and verify with an actual pt-BR request. Region-only and script-subtag locales (zh-Hant) are where this bites.

Cache and response-middleware interaction. TranslateResponse buffers and rewrites whole HTML bodies. That's incompatible with streamed responses, and it interacts badly with full-page caches (an entry cached under one locale can be served to another if the locale isn't part of the cache key). Include the resolved locale in your cache key, or keep TranslateResponse off cached routes.

Octane checklist. Long-lived workers: (a) confirm the Octane listener is registered, (b) ensure any locale state you set doesn't persist across requests — a locale written to a singleton survives into the next user's request. Prefer the middleware's per-request resolution over App::setLocale() in a service provider.

Exclusion attributes are your emergency valve. translate="no" and data-notrans cost nothing and let you keep automatic mode when one subtree (a code block, a user-generated display name, a currency-formatted string) must not be touched. Reach for them before you consider splitting modes.

Extension points to know about. The response middleware and the post-response flush are the two seams worth understanding before you need them: an excluded subtree plus a custom middleware on the same route is the usual way to handle "translate this page except this widget." If ROADMAP.md lists a gap that affects you, design around it now rather than discovering it after the catalog has a few thousand entries.

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