rivalex / lingua
Laravel package for multilingual management with database, Livewire UI and translation synchronization
Fund package maintenance!
Requires
- php: ^8.4
- illuminate/contracts: ^12.0
- laravel-lang/common: ^6.7
- league/html-to-markdown: ^5.1
- livewire/flux: ^2.12
- livewire/livewire: ^4.1
- outhebox/blade-flags: ^1.6
- spatie/laravel-package-tools: ^1.92
- spatie/laravel-translation-loader: ^2.8
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.8
- orchestra/testbench: ^10.9
- pestphp/pest: ^4.4
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- phpunit/phpunit: ^12.5
- spatie/laravel-ray: ^1.35
README
π Lingua
The complete multilingual management system for Laravel
Lingua brings database-driven translations to Laravel with a beautiful Livewire + Flux UI β install languages, manage translations, and sync everything with a single command.
Features Β· Installation Β· Configuration Β· Artisan Commands Β· Publishing Β· UI Guide Β· Architecture
β¨ Features
| Feature | Description |
|---|---|
| Database-backed translations | All translations stored in the database, editable instantly without deployments |
| Livewire UI | Reactive, real-time language and translation management interface |
| Flux UI components | Modern, accessible UI built with Livewire Flux |
| Bi-directional sync | Push translations to the database or pull them back to local PHP/JSON files |
| Laravel Lang integration | Install 70+ languages with one command, auto-updated via laravel-lang |
| Rich text support | Translations can be plain text, HTML, or Markdown |
| Language selector | Configurable sidebar, dropdown, or modal language switcher for users |
| Progress tracking | Per-language completion percentage and missing-translation counts |
| RTL support | First-class right-to-left language handling |
| Vendor translations | Manage package translations alongside your own |
| Database-agnostic | Full support for SQLite, MySQL, PostgreSQL, and SQL Server |
| Fully tested | 150+ tests with Pest, covering commands, Livewire components, Blade components and helpers class |
π¦ Requirements
- PHP 8.4+
- Laravel 12+
- Livewire 4.1+
- Livewire Flux 2.12+
π Installation
1. Install via Composer
composer require rivalex/lingua
2. Run the interactive installer
php artisan lingua:install
The installer will:
- Publish the configuration file to
config/lingua.php - Publish and run the database migrations
- Seed the database with your default language and its translations
- Optionally star the repo on GitHub β
That's it β Lingua is ready.
3. Access the UI
| Page | URL | Route name |
|---|---|---|
| Languages | your-app.test/lingua/languages |
lingua.languages |
| Translations | your-app.test/lingua/translations/{locale?} |
lingua.translations |
βοΈ Configuration
After installation, config/lingua.php gives you full control:
return [ // Directory where local language files are stored 'lang_dir' => lang_path(), // Application default locale 'default_locale' => config('app.locale', 'en'), // Fallback locale when a translation is missing 'fallback_locale' => config('app.fallback_locale', 'en'), // Middleware applied to Lingua's routes 'middleware' => ['web'], // URL prefix for Lingua's management pages 'routes_prefix' => 'lingua', // Session key used to store the active locale 'session_variable' => 'locale', // Language selector widget settings 'selector' => [ 'mode' => 'sidebar', // 'sidebar' | 'modal' | 'dropdown' 'show_flags' => true, ], // Rich-text editor toolbar options 'editor' => [ 'headings' => false, 'bold' => true, 'italic' => true, 'underline' => true, 'strikethrough' => false, 'bullet' => true, 'ordered' => true, 'clear' => true, // ... more options available ], ];
π Artisan Commands
Lingua ships with a complete command suite for terminal-driven language and translation management.
Language management
| Command | Description |
|---|---|
lingua:add {locale} |
Install a new language: downloads files, creates DB record, syncs translations |
lingua:remove {locale} |
Remove a language: deletes files, cleans DB, reorders remaining languages |
lingua:update-lang |
Update all installed language files via Laravel Lang, then re-sync to database |
# Add Italian php artisan lingua:add it # Add Brazilian Portuguese php artisan lingua:add pt_BR # Remove French (the default language is protected) php artisan lingua:remove fr # Pull the latest translation strings from Laravel Lang and sync to DB php artisan lingua:update-lang
Translation sync
| Command | Description |
|---|---|
lingua:sync-to-database |
Import all local PHP/JSON translation files into the database |
lingua:sync-to-local |
Export all database translations back to local PHP/JSON files |
# Populate the database from existing lang/ files (e.g. after a fresh install) php artisan lingua:sync-to-database # Write database translations to lang/ files (e.g. for version control or deployment) php artisan lingua:sync-to-local
Install command
# Interactive first-time setup wizard
php artisan lingua:install
π€ Publishing
Lingua ships several publishable groups so you can override only what you need.
Publish everything at once
php artisan vendor:publish --provider="Rivalex\Lingua\LinguaServiceProvider"
Publish individual tags
lingua-config
Publishes the configuration file to config/lingua.php.
php artisan vendor:publish --tag="lingua-config"
Use this when you want to customise routes, middleware, the language selector mode, the rich-text editor toolbar, or any other package option. The file is well-commented and safe to edit β Lingua reads it on every request.
lingua-migrations
Publishes the database migrations to database/migrations/.
php artisan vendor:publish --tag="lingua-migrations"
Use this when you need to modify the languages or language_lines table schema β for example to add indexes, change column types, or integrate with an existing translations table. After publishing, run php artisan migrate as normal.
Note: The
lingua:installwizard publishes and runs the migrations automatically. Only publish manually if you need to customise the schema before running them.
lingua-translations
Publishes the package's own UI translation strings to lang/vendor/lingua/.
php artisan vendor:publish --tag="lingua-translations"
This exposes all the labels, headings, buttons, and messages used in the Lingua UI (e.g. lingua::lingua.languages.title, lingua::lingua.translations.save). Override any string to translate the interface into your application's language or to adapt the wording to your project's style.
The published files follow the standard Laravel vendor translation structure:
lang/
βββ vendor/
βββ lingua/
βββ en/
βββ lingua.php
lingua-views
Publishes all Blade and Livewire views to resources/views/vendor/lingua/.
php artisan vendor:publish --tag="lingua-views"
Use this to customise the look and layout of the Languages page, Translations page, individual translation rows, modals, or the language selector component. Laravel will use your published views instead of the package's defaults.
The full view tree is:
resources/views/vendor/lingua/
βββ components/ # Blade anonymous components
β βββ autocomplete.blade.php
β βββ clipboard.blade.php
β βββ editor.blade.php
β βββ language-flag.blade.php
β βββ menu-group.blade.php
β βββ message.blade.php
βββ livewire/ # Livewire component views
βββ languages.blade.php
βββ language-selector.blade.php
βββ translations.blade.php
βββ translation/
βββ create.blade.php
βββ delete.blade.php
βββ row.blade.php
βββ update.blade.php
Tip: Only publish views you intend to change. Unpublished views are served directly from the package and will receive upstream updates automatically.
lingua-assets
Publishes the compiled CSS and JavaScript assets to public/vendor/lingua/.
php artisan vendor:publish --tag="lingua-assets"
This is required only if you serve assets from public/ rather than loading them via Vite or a CDN. Re-run this command after every Lingua upgrade to keep the assets in sync with the package version.
Re-publishing after upgrades
After updating Lingua via Composer, re-publish any assets that may have changed:
# Force-overwrite previously published assets php artisan vendor:publish --tag="lingua-assets" --force php artisan vendor:publish --tag="lingua-translations" --force
The --force flag overwrites existing files. Omit it for views and config so your local customisations are not lost.
π₯ UI Guide
Languages page β /lingua/languages
The languages page is your control center for installed locales.
Available actions:
- Add a language β choose from 70+ locales; files are installed and translations synced automatically
- Remove a language β confirmation modal prevents accidental deletion; the default language is protected
- Set the default language β one click sets the new application default
- Reorder languages β drag-and-drop to control display order across the UI
- Sync to database β import all local
lang/files into the database - Sync to local β export database translations back to
lang/files - Update via Laravel Lang β pull the latest strings from upstream
laravel-langpackages
Each language row shows the completion percentage and a count of missing translations so you can prioritise your translation effort.
Translations page β /lingua/translations/{locale?}
Manage individual translation strings with a filterable, paginated table.
Available actions:
- Search by key, group, or value
- Filter by locale, group, or translation type (text / HTML / Markdown)
- Show only missing translations for a locale to focus your translation work
- Create new custom translation entries
- Edit any string inline β the rich-text editor activates automatically for HTML and Markdown types
- Delete translations globally or for a specific locale only
- Copy the translation key to clipboard with one click
Language selector component
Embed a language switcher anywhere in your Blade layouts:
<livewire:lingua::language-selector />
Control the display mode via config or inline props:
{{-- sidebar (default), dropdown, or modal --}} <livewire:lingua::language-selector mode="dropdown" :show-flags="false" />
Note: To show or hide the language flags, set the
lingua.show_flagsconfig option totrueorfalse. Alternatively, use the:show-flagsprop to override the config setting for a specific instance.
π Architecture
How translations are stored
Lingua stores translations in the language_lines table, extending Spatie's laravel-translation-loader. Each row holds all locales in a single JSON text column, eliminating the need for per-locale rows:
group | key | text
------------|--------------|--------------------------------------------------------------
validation | required | {"en": "The :attribute field is required.", "it": "..."}
single | Welcome | {"en": "Welcome", "fr": "Bienvenue", "de": "Willkommen"}
This design allows instant locale switching at runtime without additional queries per language.
Translation types
Each string is classified automatically during sync:
| Type | Use case | Auto-detected when⦠|
|---|---|---|
text |
Plain strings, labels, messages | Default |
html |
Rich content with HTML markup | String contains HTML tags |
markdown |
Markdown-formatted content | String parses as Markdown |
The type drives which editor is shown in the Translations UI.
Bi-directional sync
lang/en/*.php ββ
lang/en.json β lingua:sync-to-database β language_lines (DB)
lang/it/*.php β
lang/it.json ββ€
lang/vendor/β¦ β β lingua:sync-to-local
ββ
sync-to-databaseβ reads every locale file (core + vendor packages) and upserts rows inlanguage_lines, auto-creatinglanguagesrecords for any new locales discovered.sync-to-localβ reads every row inlanguage_linesand writes locale-specific PHP/JSON files back tolang/, including vendor subdirectories.
Translation loading at runtime
Lingua registers a custom LinguaManager as the Laravel translation loader. At runtime it merges:
- File-based translations from
lang/ - Database translations via Spatie's
Dbloader
Database translations take precedence, enabling live overrides without touching source files.
Locale middleware
LinguaMiddleware is automatically appended to the web middleware group on boot. It:
- Reads the active locale from the session (
lingua.session_variable) - Falls back to the database default language
- Calls
app()->setLocale()and stores the locale in the session for the next request
π§ͺ Testing
# Run the full test suite composer test # Run with coverage report composer test-coverage
The suite uses Pest and covers:
- All 5 Artisan commands β happy paths and error handling
- All Livewire components β rendering, interactions, and event dispatching
- Bi-directional sync operations
- All blade components
- Helper functions
π€ Contributing
Contributions are welcome! Please open an issue first to discuss your proposed change, then submit a PR. Run composer lint before pushing.
π License
The MIT License (MIT). Please see LICENSE.md for more information.
Built with β€οΈ by Alessandro Rivolta
Powered by Laravel Β· Livewire Β· Flux Β· Laravel Lang Β· Spatie