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

cyberwolfstudio/lingua

Lingua is a Laravel package for managing translations in a database. It scans your project for translation keys, provides an editable UI for adding and updating strings, and caches translations for fast runtime performance. Simplify multilingual app localization without hand-editing language files.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install: composer require cyberwolfstudio/lingua. The service provider is auto-discovered on Laravel 5.5+; no manual registration needed.
  2. Publish the config first — that's your map of the package:
    php artisan vendor:publish --tag=lingua-config
    
    Open config/lingua.php. It defines the source (lang/ / resources/lang/), the target (a JS/JSON file under resources/js or public/), and the output format (global object vs ESM export). Almost every customization you'll ever need lives here.
  3. Generate once to confirm the contract:
    php artisan lingua:generate
    
    Inspect the emitted file before wiring it into your frontend — this tells you exactly what shape (window.Lingua = {...}, export default {...}, per-locale files, etc.) your JS code needs to consume.
  4. First use case — Vue 3 + vue-i18n:
    import { createI18n } from 'vue-i18n';
    import messages from './lang/generated.js'; // the Lingua output
    
    const i18n = createI18n({
      locale: document.documentElement.lang || 'en',
      fallbackLocale: 'en',
      messages,
    });
    
    For React + i18next it's the same idea — feed the generated object into i18next.init({ resources }).

Look first at config/lingua.php and the single generated artifact; if those two are right, the rest of the package is just a file-copy step.

Implementation Patterns

Wire generation into the build, not into your fingers. The package is a code generator; treat it like ziggy:generate or ide-helper. The pattern that survives in real projects:

// package.json
{
  "scripts": {
    "predev": "php artisan lingua:generate",
    "dev": "vite",
    "prebuild": "php artisan lingua:generate",
    "build": "vite build"
  }
}

Or, if you're already on Vite, use spatie/laravel-vite-plugin's refresh paths plus a small custom plugin that re-runs artisan lingua:generate when files under lang/ change — otherwise HMR will not pick up translation edits and you'll be reloading manually all day.

Per-locale lazy loading. If the config supports splitting output by locale (most versions of this pattern do — check for a per_locale / files option), generate lang/en.js, lang/de.js, and dynamic-import the file matching the current locale:

const load = (locale) => import(`./lang/${locale}.js`);

const { default: messages } = await load(currentLocale);

This keeps the initial bundle small and matches Laravel's own app()->getLocale() semantics — pass the current locale down from Blade:

<script>window.__LOCALE__ = "{{ app()->getLocale() }}";</script>
@vite('resources/js/app.js')

Blade-first vs SPA. For a server-rendered app with pockets of JS, generate the global-object format and skip a JS build entirely — a <script src="/js/lang.js"> in app.blade.php is often all you need. For a full SPA, generate ESM and import it.

CI drift check. Since the generated file is derived state, the durable pattern is:

php artisan lingua:generate
git diff --exit-code resources/js/lang/

Run this in CI to fail when a developer edits lang/*.php but forgets to regenerate. Pair it with a .gitignore rule if and only if you also add the generation step to every environment's build (Docker build stages especially — a cached image without translations is a nasty bug).

Testing. In tests/TestCase.php or a trait, run the command in setUp before booting your JS-side fixtures, or point lingua.source at a fixture directory so tests are hermetic and fast.

Gotchas and Tips

  • Generated files are stale files. The #1 issue with this class of package is a translation that exists in lang/de.json but not in the JS bundle because someone never re-ran the command. Make generation part of the build, not a documented manual step.
  • Nested vs flat keys. Laravel's lang/*.php arrays are naturally nested. Vue-i18n and i18next both accept nested objects — but keys containing literal dots (e.g., 'user.name' as a flat key) will be split into a path. Prefer dot-notation in JS templates or flatten consistently; test one file with real nesting before trusting it across the whole app.
  • Vendor / published translations. lang/vendor/* and lang/{locale}/passwords.php/validation.php are frequently missed if your source glob doesn't include them — validation messages are usually the first thing a QA tester notices is untranslated.
  • Escaping. Translations containing apostrophes, {}, or @ can break a generated .js file that uses raw string interpolation. If the package offers a JSON output mode, prefer it over a hand-rolled JS wrapper.
  • Output location must be Vite-visible. Files written to public/ will be served but not hashed or cache-busted; files written to resources/js/ must sit inside Vite's root. Pick deliberately, and update vite.config.js build.rollupOptions.input if you emit a separate entry.
  • Don't forget fallbackLocale. Laravel's fallback logic is server-side; vue-i18n/i18next fallback is separate. Set it explicitly or users on a partially translated locale see raw keys.
  • Cache clearing after config changes. If you tweak config/lingua.php, run php artisan config:clear before regenerating — cached config will silently ignore your edits in dev.
  • Low bus factor (26 stars, 0 dependents). The package is a thin, well-understood bridge. Read the command source once (~100 lines) — you can vendor or reimplement it in an afternoon if it's ever abandoned. Don't build architecture that only makes sense because Lingua exists.
  • Migration path. If you outgrow it, laravel-vue-i18n and laravel-lang/publisher cover the same ground with larger communities. Keeping the generated artifact as a plain JS module means swapping generators is a one-line change in your build script.
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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle