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

Lingua Laravel Package

rivalex/lingua

Lingua is a self-hosted translation manager for Laravel 11–13 that stores i18n strings in your database or lang files. It adds a Livewire admin UI plus CSV, JSON, XLSX, and ODS import/export with dry-run previews, RTL support, and 26 bundled locales.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and bootstrap in three commands:

composer require rivalex/lingua
php artisan lingua:install
php artisan config:clear

The installer is interactive and asks for a storage driver. Choose database for day-to-day work — it gives you instant edits without redeploys and is where the Livewire UI shines. It publishes config/lingua.php, runs migrations, and seeds the default locale with the bundled 26-locale dataset. Set LINGUA_STORAGE_DRIVER=database in .env and clear config.

Then log in (routes are under ['web','auth'] by default) and open your-app.test/lingua/languages. The first useful workflow: add a second locale, open /lingua/statistics to see coverage, then go to /lingua/translations/{locale} and fill gaps. That's the loop you'll repeat every sprint.

If you'd rather keep lang/ as the source of truth (typical for a small team that PRs language files), pick file mode instead — the installer writes PHP/JSON files and skips the language_lines table.

Implementation Patterns

Database-first editing, file-export for review. The strongest day-to-day pattern is: translators edit in the UI (DB driver), then you export JSON/CSV from /lingua/transfer (always via the enforced dry-run preview) and commit the artefact to a PR for review. lingua:uninstall also exports before tearing down, which is a useful escape hatch if you ever migrate away.

Bi-directional sync is the bridge. Push local PHP/JSON lang files into the DB (or pull DB back to files) when you need to reconcile a release branch with what product edited in the UI. Treat one direction as authoritative per environment — usually pull DB → files locally and push files → DB in a fresh environment's seed step.

Bulk and machine translation. For new locales, bulk AI-translate via Lingua Pro (suppress_pro_nudge and pro_upgrade_url are the only config surface — everything else lives in the add-on). Route missing-key drilling from the statistics page into the translations editor for spot fixes.

Programmatic access. Use the Lingua facade in seeders, custom Artisan commands, and tests to read/write translations and manage languages without going through Livewire. This is the right seam for test fixtures — seed known translations in a base test case and assert against them.

Selector in the host app. Pick a selector.mode in config/lingua.php: sidebar, modal, dropdown, or headless (zero-CSS semantic HTML — the right choice if you have a design system). Flags toggle independently. For RTL locales nothing extra is needed; it's first-class.

Routing and layout. Override routes_prefix, add routes_extra_parameters for multi-tenant scoping (e.g. {team?}), and set layout when you want Lingua pages to render inside your app shell rather than the default livewire.layout. Set navigate: true only if your host app has Livewire SPA navigation enabled.

Authorization. Wire a Gate and set LINGUA_GATE=manage-translations. Middleware defaults to ['web','auth'], and when gate is set, can:{gate} is automatically appended to admin routes — don't duplicate it.

Vendor translations. Lingua manages published vendor lang files alongside your own, so you can patch a broken package string without forking. Keep vendor translation edits in a note or PR comment — they'll be overwritten on the next vendor sync.

UI niceties that pay off. ui.sticky_top (int → rem, or CSS string) fixes the filter bar under a fixed host header. Rich text (headings, bold, code-block, etc.) is configurable per project and also overridable from the Settings UI — keep the config as the baseline so dev environments match.

Gotchas and Tips

  • Cache is forever per (locale, group). Translations are cached indefinitely with prefix lingua.trans. After seeding, bulk-importing, or editing programmatically, clear the relevant cache or you'll debug a phantom. LINGUA_CACHE_STORE lets you isolate this from the app's cache.
  • Switching drivers is not a merge. Changing LINGUA_STORAGE_DRIVER between database and file doesn't sync — the two stores are independent. Run the sync first if you want a snapshot. Also config:clear after switching; the driver is read from env.
  • File mode skips the language_lines migration. If you toggle to database later, run php artisan migrate again — the table won't exist.
  • gate with an undefined Gate = 403 for everyone, including you. Define the ability before setting LINGUA_GATE, or you'll lock out the UI in staging.
  • layout: null delegates to livewire.layout. If the host app doesn't define that, full-page Lingua renders will fail. Set an explicit layout in any non-standard Laravel skeleton.
  • Force dark mode is a blunt instrument. LINGUA_FORCE_DARK_MODE=true overrides the OS/host theme entirely — fine for embedding, bad for a user who explicitly picked light. Prefer the class strategy (<html class="dark">) and let the host own the toggle.
  • XLSX/ODS need openspout/openspout. CSV/JSON work without it. Install it before telling translators to use spreadsheets.
  • Import has a mandatory dry-run preview. This is a feature, not friction — always review the diff, especially on multi-locale scopes.
  • Extension points are real seams. translation_loaders, model, and translation_manager are swappable via config. Override the model if you need soft deletes or extra columns; override LinguaManager only if you need to change driver behaviour — that's the deepest hook and the least stable.
  • Multi-database is supported (SQLite/MySQL/PostgreSQL/SQL Server), but test on your production driver — the JSON columns and index behaviour on SQLite can mask issues.
  • This is a young package (0 dependents, single maintainer, recent release). Day-to-day leverage is high, but pin the version, read the CHANGELOG before upgrades, and keep an export of translations committed to the repo so you can recover if a release regresses. The 780+ test suite and MIT licence help, but adoption should be paired with that export safety net.
  • session_variable defaults to locale — if your app already uses that key for something else, rename it before the two fight.
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.
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log
hsluv/hsluv
seampact/codegen
seampact/hooks
seampact/ir
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table