PHPackages                             lingualayer/lingualayer - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Localization &amp; i18n](/categories/localization)
4. /
5. lingualayer/lingualayer

ActiveLibrary[Localization &amp; i18n](/categories/localization)

lingualayer/lingualayer
=======================

Bidirectional AI translation middleware for Laravel — translate any app without touching existing code.

v1.7.1(today)00MITPHPPHP ^8.2

Since Jul 1Pushed todayCompare

[ Source](https://github.com/vitaldoceurope-droid/lingualayer-oss)[ Packagist](https://packagist.org/packages/lingualayer/lingualayer)[ Docs](https://viataling.com)[ RSS](/packages/lingualayer-lingualayer/feed)WikiDiscussions master Synced today

READMEChangelogDependencies (10)Versions (3)Used By (0)

LinguaLayer
===========

[](#lingualayer)

Bidirectional AI translation middleware for Laravel. Drop it into any existing app and every page becomes multilingual — **without changing a single controller, view, or database column**.

[![PHP](https://camo.githubusercontent.com/187240af044d09d5b14a1d9d9ebdf3f7a993e4c7bc09bdb46b4ba661a891bf5b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e322532422d626c7565)](https://php.net)[![Laravel](https://camo.githubusercontent.com/0171c541f6433dcec81b1736e87bba993ce6d1b126d0954ece853c872cc659f7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c61726176656c2d31312532422d726564)](https://laravel.com)[![License](https://camo.githubusercontent.com/f8df3091bbe1149f398a5369b2c39e896766f9f6efba3477c63e9b4aa940ef14/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e)](LICENSE)

---

Two modes
---------

[](#two-modes)

LinguaLayer ships with **two operating modes**. Pick one based on whether you want to manage your own LLM API keys.

**Standalone****Gateway**Who pays the LLMYou, directly to GoogleLinguaLayer (subscription)SetupSet `LINGUA_GEMINI_KEY`Set `LINGUA_LICENSE_KEY`Monthly feeNoneSubscription tierCachePer-host (your DB)Per-host **+ global network cache**Best forPersonal projects, small appsProduction, multi-app, network-effect savingsPick interactively with `php artisan lingua:configure`.

---

Network effect (Gateway mode only)
----------------------------------

[](#network-effect-gateway-mode-only)

LinguaLayer's Gateway has a unique billing model: **the more clients use it, the cheaper translations get for everyone**.

Imagine you want to translate "Bienvenido" into French. Three scenarios:

- **You're the first client to ever request it:** you pay 1 word.
- **You're client #100 to request it (cached from someone else's translation):** you pay **0 words** — *network effect free*.
- **You request it 5 times yourself:** you pay `1 + (4 × 0.5) = 3 words` instead of 5 — *own-repetition discount*.

Every fresh translation you contribute becomes free for the next clients who need it. Every cached translation those clients seeded is free for you.

Read the breakdown in real time: `php artisan lingua:test` shows mode + savings, and the dashboard at `/lingua/quality` displays a Network Effect Savings panel.

---

How it works
------------

[](#how-it-works)

```
User requests /patients in French
        ↓
TranslateResponse middleware intercepts the HTML response
        ↓
Extracts text from h1–h6, p, button, td, li, a, label…
        ↓
Sends batch to Gemini 2.5-flash — atomic: all chunks must succeed
        ↓
Stores complete translated HTML in full-page cache
        ↓
Next user requesting same URL + language → instant cache hit

```

For form submissions, `lingua.js` intercepts the submit event, POSTs the field values to `/lingua/translate-input`, swaps them with the source-language equivalents, then lets the form submit normally. Identity fields (`firstname`, `address`, `company`…) are never touched.

---

Requirements
------------

[](#requirements)

RequirementVersionPHP8.2+Laravel11 or 12Gemini API keyFree tier works---

Quick start
-----------

[](#quick-start)

```
composer require lingualayer/lingualayer
php artisan lingua:install
php artisan lingua:configure   # interactive: pick standalone or gateway
php artisan lingua:test        # verify
```

That's it. Visit any page of your app and you'll see the language selector in the top-right corner.

### Autonomous agent — one cron line (opt-in)

[](#autonomous-agent--one-cron-line-opt-in)

LinguaLayer can **pre-translate ("warm") your pages automatically** so visitors never wait, and auto-translate any newly-enabled language within ~a minute — with **no queue worker**. Because it spends your LLM budget on a schedule, **it is off by default** — enable it explicitly:

```
LINGUA_AGENT_ENABLED=true
```

Then give it Laravel's standard scheduler by adding this **single line** to your server's crontab:

```
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
```

This is the exact line [Laravel documents for task scheduling](https://laravel.com/docs/scheduling#running-the-scheduler) — if you already have it, you're done. **Even with the agent off, translation still works** (pages are translated live on first visit); the agent just makes them instant and keeps your translations fresh. `php artisan lingua:configure` offers to enable it for you.

### Path A — Standalone (your own Gemini key)

[](#path-a--standalone-your-own-gemini-key)

```
LINGUA_MODE=standalone
LINGUA_GEMINI_KEY=your-gemini-api-key
# Source language defaults to your app locale (config/app.php). Set this only
# when your content is written in a different language:
# LINGUA_SOURCE_LANG=en
```

You pay Google directly per token. No subscription.

### Path B — Gateway (managed)

[](#path-b--gateway-managed)

```
LINGUA_MODE=gateway
LINGUA_LICENSE_KEY=LL-XXXX-XXXX-XXXX-XXXX
LINGUA_GATEWAY_URL=https://api.lingualayer.com
# Source language defaults to your app locale; override only if needed:
# LINGUA_SOURCE_LANG=en
```

You pay a fixed monthly subscription. Network-effect cache means your effective cost drops as more clients use the system.

### Switching modes

[](#switching-modes)

Run `php artisan lingua:configure` again. The wizard rewrites the relevant lines in `.env` without touching anything else.

---

Configuration
-------------

[](#configuration)

After publishing, edit `config/lingua.php`. The most common options:

```
'source_language'     => 'en',            // Your app's base language (defaults to config('app.locale'))
'supported_languages' => [                // Languages users can choose
    'es' => ['name' => 'Español',  'flag' => '🇪🇸'],
    'en' => ['name' => 'English',  'flag' => '🇬🇧'],
    'fr' => ['name' => 'Français', 'flag' => '🇫🇷'],
    'ar' => ['name' => 'العربية',  'flag' => '🇸🇦'],
],
'gemini_model'        => 'gemini-2.5-flash',
'cache_ttl'           => 86400,            // Cache in minutes (60 days)
'selector_position'   => 'top-right',      // top-right | top-left | bottom-right | bottom-left
```

Full `.env` reference: see [`.env.example`](.env.example).

### Choosing an AI provider

[](#choosing-an-ai-provider)

Standalone mode is not tied to Gemini. Set `LINGUA_PROVIDER`:

```
# Google Gemini (default)
LINGUA_PROVIDER=gemini
LINGUA_GEMINI_KEY=your-gemini-key

# …or any OpenAI-compatible endpoint
LINGUA_PROVIDER=openai
LINGUA_OPENAI_KEY=sk-...
LINGUA_OPENAI_MODEL=gpt-4o-mini
LINGUA_OPENAI_BASE_URL=https://api.openai.com/v1
```

`openai` speaks the standard `/chat/completions` schema, so it also drives **Groq, Together, Mistral, or a self-hosted model** (Ollama, vLLM, LocalAI) — just point `LINGUA_OPENAI_BASE_URL` at it (a local model needs no API key). All providers share the same caching, batching, retry and preservation logic; adding a new one is a single `callModel()` method.

### Translation quality &amp; faithful placeholders

[](#translation-quality--faithful-placeholders)

The engine is built to translate *input and output* without corrupting your app:

- **Placeholders are guaranteed.** Laravel `:placeholders`, `{curly}` vars, `%s`/`%d` printf tokens, `@mentions`, `#tags`, inline URLs and emails are masked before the text reaches the model and restored verbatim afterwards — the LLM literally cannot translate or drop them. If a model mangles one, that fragment falls back to the source instead of shipping a broken variable.
- **Domain &amp; register.** `LINGUA_DOMAIN` (generic, medical, legal, ecommerce, technical, finance, hospitality, or free text) and `LINGUA_FORMALITY` (formal/informal/neutral) tune terminology and tone.
- **Brand terms &amp; glossary.** `LINGUA_BRAND_TERMS="ViataLing,Acme"` keeps names untouched; `lingua.translation.glossary` pins authoritative term translations per language.

---

Translation memory
------------------

[](#translation-memory)

Every translation is persisted in `lingua_translations` and resolved **DB → cache → LLM**, so repeat content costs zero API calls. That growing library is a portable asset you own:

```
php artisan lingua:memory                  # stats: entries per language, coverage
php artisan lingua:memory export tm.jsonl  # back up / move the memory
php artisan lingua:memory import tm.jsonl  # seed a fresh install with an existing corpus
```

Seeding a brand-new client install from an export gives it instant coverage instead of paying the LLM to re-learn everything.

---

Features
--------

[](#features)

### Full-page caching

[](#full-page-caching)

The first visitor to any URL+language combination triggers translation. Every subsequent visitor gets the pre-translated HTML instantly from cache — zero API calls. Cache TTL is configurable (default: 60 days).

### Atomic translations — no partial pages

[](#atomic-translations--no-partial-pages)

Every translation request is split into chunks of 40 texts. If any chunk fails, it retries 3 times with exponential backoff (1s → 2s → 4s). If all retries fail, the page is served in the original language rather than half-translated. The user always sees a coherent page.

### Async mode (opt-in)

[](#async-mode-opt-in)

By default LinguaLayer translates each page **inline** (atomic) on first visit and page-caches the result, so repeat visits are instant **without any worker** — this is the right default for almost everyone.

If you run a dedicated queue worker you can opt into async mode, where the first visit returns immediately in the source language and a background job fills the cache:

1. First visit → served immediately in source language + "Translating…" banner
2. Background job translates + stores in cache
3. JS polls `/lingua/status/{hash}` every 2s → auto-reloads when ready
4. All subsequent visitors → instant cache hit

To enable, set `LINGUA_ASYNC=true`, use a non-sync queue (`QUEUE_CONNECTION=database` or `redis`) **and run a worker**:

```
php artisan queue:work --queue=lingua
```

> **Why opt-in?** Laravel 11/12 default `QUEUE_CONNECTION` to `database`. Earlier versions auto-enabled async whenever the driver was non-sync — which silently dispatched background jobs on hosts that had no worker, leaving pages untranslated and the banner spinning. Async is now explicit and never enabled in **gateway mode** (which has no local worker).

### Smart form field detection

[](#smart-form-field-detection)

Fields whose names contain identity-related substrings (`name`, `firstname`, `lastname`, `address`, `company`, `dni`…) are never translated — the user's actual name should reach the backend unchanged. Configure in `translate_field_patterns.skip`.

### RTL support

[](#rtl-support)

The language selector automatically mirrors its position for right-to-left languages (Arabic, Hebrew, Farsi, Urdu). A `top-right` selector becomes `top-left` when the page `dir="rtl"`.

### Translation quality scoring (optional)

[](#translation-quality-scoring-optional)

Enable with `LINGUA_AUTO_SCORE=true`. After each background translation, a sample of 3–5 texts is sent back to Gemini for quality evaluation (1–10). Translations scoring below 7 are invalidated and re-translated on the next visit. View all scores at `/lingua/quality`.

---

Artisan commands
----------------

[](#artisan-commands)

```
# Verify installation and API connectivity
php artisan lingua:test

# Inspect / export / import the translation memory
php artisan lingua:memory
php artisan lingua:memory export memory.jsonl
php artisan lingua:memory import memory.jsonl

# Publish assets (run after package updates)
php artisan vendor:publish --tag=lingua-assets --force
```

---

Excluded routes
---------------

[](#excluded-routes)

By default, LinguaLayer skips `api/*` and `lingua/*`. Add more in `config/lingua.php`:

```
'excluded_routes' => [
    'api/*',
    'admin/*',
    'webhooks/*',
    'lingua/*',
],
```

---

Excluded form fields
--------------------

[](#excluded-form-fields)

Fields that are never translated regardless of name:

```
'excluded_fields' => ['password', 'password_confirmation', '_token', 'email', 'phone'],
```

---

Quality dashboard
-----------------

[](#quality-dashboard)

Access at `/lingua/quality` (protect it with `LINGUA_QUALITY_SECRET`):

```
/lingua/quality?key=your-secret

```

Shows: average score per language, low-score count, and a full history of scored translations.

---

Running tests
-------------

[](#running-tests)

```
composer test
```

Or with coverage:

```
./vendor/bin/pest --coverage
```

---

Security
--------

[](#security)

- The public `/lingua/*` endpoints are CSRF-exempt (so your forms never 419) and rate-limited per IP — `LINGUA_THROTTLE_INPUT` (default 200/min) and `LINGUA_THROTTLE_DOM` (default 600/min). Tune them in `.env`. Each request is also hard-capped at `LINGUA_MAX_FIELDS` (200) fields and `LINGUA_MAX_BYTES`(100 KB) — oversized requests are served back unchanged, never billed.
- The demo/preview routes are **auto-disabled when `APP_ENV=production`** — nothing to remove by hand.
- Set `LINGUA_QUALITY_SECRET` to protect the quality dashboard (it 404s without it).
- The full-page cache is never shared across sessions: responses carrying a CSRF token (or an authenticated session) are never page-cached.
- In production (`APP_ENV=production`), SSL certificate verification on provider/ gateway API calls is enforced.

---

License
-------

[](#license)

MIT © [Mohamed Hounaini — ViataLing](mailto:contact@viataling.com)

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance100

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity47

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

0d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/7954fa9da0b654465865dc56454928dfa5994f192b6ba38f7c71bcf584c8c1c7?d=identicon)[vitaldoceurope-droid](/maintainers/vitaldoceurope-droid)

---

Top Contributors

[![medhounaini](https://avatars.githubusercontent.com/u/217502759?v=4)](https://github.com/medhounaini "medhounaini (1 commits)")

---

Tags

middlewarelaravellocalizationinternationalizationi18ntranslationaimultilingualopenaiGemini

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/lingualayer-lingualayer/health.svg)

```
[![Health](https://phpackages.com/badges/lingualayer-lingualayer/health.svg)](https://phpackages.com/packages/lingualayer-lingualayer)
```

###  Alternatives

[laravel/pulse

Laravel Pulse is a real-time application performance monitoring tool and dashboard for your Laravel application.

1.7k15.1M129](/packages/laravel-pulse)[psalm/plugin-laravel

Psalm plugin for Laravel

3355.3M345](/packages/psalm-plugin-laravel)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9762.4M125](/packages/roots-acorn)[laravel/scout

Laravel Scout provides a driver based solution to searching your Eloquent models.

1.7k55.0M610](/packages/laravel-scout)[nuwave/lighthouse

A framework for serving GraphQL from Laravel

3.5k11.8M116](/packages/nuwave-lighthouse)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

77022.3M142](/packages/laravel-mcp)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
