visiosoft/language_switcher-plugin
Visiosoft Language Switcher Plugin adds a simple, configurable language switcher to Laravel/PHP applications, letting users toggle between available locales from a clean UI. Easy to install and integrate into Visiosoft-powered sites.
This is a PyroCMS / Streams Platform addon (Laravel under the hood) that renders a language switcher for multi-locale sites. It is a plugin, not a standalone package, so it expects a working Streams/PyroCMS application.
Minimal steps:
composer require visiosoft/language_switcher-plugin
php artisan addon:install visiosoft.language_switcher-plugin
Replace with the exact namespace printed by php artisan addon:list — PyroCMS addon namespaces are dot-separated, and the slug in composer.json (language_switcher-plugin) is what the manager resolves.// config/streams.php (or config/streams/locales.php depending on version)
'locales' => [
'enabled' => true,
'default' => 'en',
'supported' => ['en', 'fr', 'de'],
],
Where to look first, in order:
src/*PluginServiceProvider.php — registers the addon with AddonManager, may register a block, a view namespace, routes, or a Twig function. This is the file that tells you how the plugin is meant to be consumed.resources/views/ — the actual switcher markup. Read it before styling anything; most customisation happens here.resources/lang/ — labels/accessibility strings.composer.json — streams-addon type, Laravel/PyroCMS version constraints, and the autoloaded namespace (critical for view includes).First use case: drop the switcher into a header partial and confirm it lists the configured locales. Depending on how the service provider registers, that is usually one of:
{{-- Blade --}}
@include('visiosoft.language_switcher-plugin::partials/switcher')
{# Twig (Streams templates) #}
{{ include('visiosoft.language_switcher-plugin::partials/switcher') }}
or, if the plugin registers a block, via Control Panel → Pages → (edit page) → Blocks → Language Switcher, assigned to a layout partial. Check the service provider's blocks()/register() method before assuming the include path — the view namespace matches the addon namespace, not the Composer package name slashes-for-dots.
Layout-level rendering (most common). The switcher belongs in a header/nav partial rendered on every page. Put the include in resources/views/partials/header.blade.php (or the Twig equivalent) rather than in every page, so a single change covers the whole site.
Block-based placement in the CP. If the plugin registers a Streams block, editors can place and reorder the switcher per page without touching templates. Use this when different page groups need the switcher in different positions. You still override the block's view to control markup.
Locale persistence. Decouple the switcher from routing. Two reliable patterns:
// Route-based: /{locale}/...
Route::group(['prefix' => '{locale}', 'middleware' => SetLocale::class], function () { ... });
// Session-based: switcher POSTs to a route, stores locale, redirects back
Route::post('/locale', function (Request $request) {
session(['locale' => $request->validate(['locale' => 'in:en,fr,de'])['locale']]);
return back();
});
The plugin typically only renders links — it does not guarantee the locale is applied on the next request. Wire persistence yourself (middleware or the localization package) and treat the plugin's view as a presentation layer.
View overrides instead of edits. Never edit vendor/. Publish or override the switcher view so Composer updates don't discard your markup:
php artisan vendor:publish --tag=public --provider="Visiosoft\LanguageSwitcherPlugin\..."
# then override under resources/views/vendor/...
Conditional rendering. Hide the widget when there is nothing to switch to — avoids dead UI and duplicate content on single-locale installs:
@if (count(config('streams::locales.supported', [])) > 1)
@include('visiosoft.language_switcher-plugin::partials/switcher')
@endif
SEO wiring. The switcher itself will not emit hreflang. Add alternates in your <head> partial alongside it, driven by the same streams::locales.supported config so the two never drift:
@foreach (config('streams::locales.supported', []) as $locale)
<link rel="alternate" hreflang="{{ $locale }}" href="{{ url($locale . '/' . request()->path()) }}">
@endforeach
Styling. The plugin ships minimal CSS (if any). Treat the switcher as unstyled markup: add your own classes in the override, and use the CP block's wrapper/ID if it has one so editors can target it. Don't rely on the plugin's own CSS — it may be registered conditionally via AddonManager.
Cache integration. If you run route or response caching, the locale must be part of the cache key, or one visitor's language will be served to another. With a /{locale}/ prefix this is automatic; with session-based locale it is not — skip full-page caching for those routes.
composer.json constraints before installing; on newer PyroCMS/Streams and Laravel versions you'll likely hit a dependency conflict. Lock the version and test on a branch.vendor/.visiosoft/language_switcher-plugin resolves to a dot-separated addon namespace (e.g. visiosoft.language_switcher-plugin). If @include(...) throws "View not found", run php artisan addon:list and grep the service provider for the registered namespace instead of guessing.addon:install is required. Composer alone won't register it; Class '...ServiceProvider' not found after composer require usually means the addon was never installed/registered. Also re-run php artisan addon:publish if assets are registered via the addon manager.streams::locales.enabled is false or supported has one entry, the switcher renders empty or only the current locale. Verify config before debugging the plugin.SetLocale middleware first, not the plugin.php artisan view:clear && php artisan cache:clear && php artisan config:clear. PyroCMS's addon manager caches addon manifests too — php artisan addon:refresh if the addon disappears from the CP.storage/logs/laravel.log for the view/route miss; the AddonManager registration logs at debug level; in the CP, the addons screen confirms install status. If the block doesn't appear in the editor, the block wasn't registered — read the service provider's blocks() method.routes/ or the provider's routes() method first.<a href lang="..."> with readable locale names, not just icons."visiosoft/language_switcher-plugin": "1.x.y") rather than ^1.0 so a future tag (or lack of one) can't silently change behaviour on composer update.How can I help you explore Laravel packages today?