PHPackages                             i18nagent/laravel-locale-chain - 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. i18nagent/laravel-locale-chain

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

i18nagent/laravel-locale-chain
==============================

Configurable locale fallback chains for Laravel — fixes both single-fallback limitation and JSON fallback bug

v1.0.0(5mo ago)00MITPHPPHP ^8.1

Since Mar 17Pushed 1mo agoCompare

[ Source](https://github.com/i18n-agent/i18n-laravel-locale-chain)[ Packagist](https://packagist.org/packages/i18nagent/laravel-locale-chain)[ Docs](https://github.com/i18n-agent/i18n-laravel-locale-chain)[ RSS](/packages/i18nagent-laravel-locale-chain/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (1)Dependencies (4)Versions (2)Used By (0)

i18n-laravel-locale-chain
=========================

[](#i18n-laravel-locale-chain)

Smart locale fallback chains for Laravel -- because pt-BR users deserve pt-PT translations, not English.

The Problem
-----------

[](#the-problem)

Laravel's translation system has two limitations:

1. **Single fallback locale.** The `fallback_locale` config only supports one language. There is no intermediate fallback. If a user requests `pt-BR` and you only have `pt-PT` translations, Laravel skips `pt-PT` entirely and shows English (or whatever your `fallback_locale` is).
2. **JSON translation files ignore fallback entirely.** This is a [known Laravel bug](https://github.com/laravel/framework/issues/41565). When you use JSON translation files (`lang/pt-BR.json`), Laravel does **not** check the fallback locale's JSON file. PHP files (`lang/pt-BR/messages.php`) respect `fallback_locale`, but JSON files do not.

**Example:** A user's browser sends `Accept-Language: pt-BR`. Your Laravel app has `pt-PT` translations but no `pt-BR` locale. Laravel skips `pt-PT` entirely and shows English.

The same thing happens with `es-MX` -&gt; `es`, `fr-CA` -&gt; `fr`, `de-AT` -&gt; `de`, and every other regional variant.

Your users see English when a perfectly good translation exists in a sibling locale.

The Solution
------------

[](#the-solution)

One service provider. Zero code changes. Laravel auto-discovery handles everything.

`i18n-laravel-locale-chain` replaces Laravel's `TranslationServiceProvider` with a `ChainTranslator` that walks a configurable fallback chain for **both** PHP and JSON translation files. Missing keys in the primary locale are resolved from fallback locales before reaching the app's default language. Your existing translation calls just work:

- `__('key')` helper
- `trans('key')` helper
- `@lang('key')` Blade directive
- `{{ __('key') }}` in Blade templates
- All `Illuminate\Translation` functions

Installation
------------

[](#installation)

```
composer require i18nagent/laravel-locale-chain
```

That's it. Laravel auto-discovers the service provider. All 75 default fallback chains are active immediately.

Quick Start
-----------

[](#quick-start)

### 1. Install (auto-discovery does the rest)

[](#1-install-auto-discovery-does-the-rest)

```
composer require i18nagent/laravel-locale-chain
```

No configuration needed. A `pt-BR` user will now see `pt-PT` translations when `pt-BR` is not available, and JSON translation files will correctly fall back through the chain.

### 2. (Optional) Publish the config file

[](#2-optional-publish-the-config-file)

```
php artisan vendor:publish --tag=locale-chain-config
```

This creates `config/locale-chain.php` where you can customize chains or disable the package.

### 3. (Optional) Add custom chains

[](#3-optional-add-custom-chains)

```
// config/locale-chain.php

return [
    'chains' => [
        'pt-BR' => ['pt-PT', 'pt'],
        'es-MX' => ['es-419', 'es'],
        'ja-JP' => ['ja'],
    ],
];
```

Your custom chains are merged with the 75 built-in defaults. Keys you specify replace the corresponding default chain.

Configuration Modes
-------------------

[](#configuration-modes)

### Default (zero config)

[](#default-zero-config)

Just install the package. Uses all 75 built-in fallback chains covering Chinese, Portuguese, Spanish, French, German, Italian, Dutch, English, Arabic, Norwegian, and Malay regional variants.

### Config file

[](#config-file)

```
// config/locale-chain.php

return [
    'enabled' => true,

    // Custom chains merged with defaults
    'chains' => [
        'pt-BR' => ['pt-PT', 'pt'],
        'ja-JP' => ['ja'],
    ],

    // Set to false to use ONLY your custom chains
    'merge_defaults' => true,
];
```

### Programmatic

[](#programmatic)

```
// At runtime, via the translator instance
app('translator')->setChains([
    'pt-BR' => ['pt-PT', 'pt'],
]);

// Or merge with defaults
use I18nAgent\LocaleChain\FallbackMap;

app('translator')->setChains(
    FallbackMap::merge(['pt-BR' => ['pt']])
);
```

**Priority order** (highest to lowest):

1. `setChains()` call (programmatic API)
2. `config/locale-chain.php` config file
3. Built-in defaults (zero-config)

API Reference
-------------

[](#api-reference)

### `FallbackMap::defaults()`

[](#fallbackmapdefaults)

Returns the 75 built-in fallback chains as `array`.

### `FallbackMap::merge(?array $overrides = null, bool $mergeDefaults = true)`

[](#fallbackmapmergearray-overrides--null-bool-mergedefaults--true)

Merge overrides on top of the default chains. Returns a new array -- the defaults are never mutated.

ParameterTypeDefaultDescription`$overrides``array|null``null`Per-locale chains that replace defaults`$mergeDefaults``bool``true`If false, return only overrides### `ChainTranslator`

[](#chaintranslator)

Extends `Illuminate\Translation\Translator`. Overrides `get()` to walk a configurable fallback chain for both PHP and JSON files.

MethodDescription`setChains(array $chains)`Set the fallback chains at runtime`getChains()`Get the current fallback chains### `LocaleChainServiceProvider`

[](#localechainserviceprovider)

Extends `TranslationServiceProvider`. Replaces the `translator` singleton with `ChainTranslator`. Auto-discovered by Laravel.

### Config Reference

[](#config-reference)

KeyTypeDefaultDescription`enabled``bool``true`Set to false to disable without removing the package`chains``array``[]`Custom fallback chains, merged with built-in defaults`merge_defaults``bool``true`If false, use only custom chainsDefault Fallback Map
--------------------

[](#default-fallback-map)

### Chinese (Traditional)

[](#chinese-traditional)

LocaleFallback Chainzh-Hant-HKzh-Hant-TW -&gt; zh-Hant -&gt; (app locale)zh-Hant-MOzh-Hant-HK -&gt; zh-Hant-TW -&gt; zh-Hant -&gt; (app locale)zh-Hant-TWzh-Hant -&gt; (app locale)### Chinese (Simplified)

[](#chinese-simplified)

LocaleFallback Chainzh-Hans-SGzh-Hans -&gt; (app locale)zh-Hans-MYzh-Hans -&gt; (app locale)### Portuguese

[](#portuguese)

LocaleFallback Chainpt-BRpt-PT -&gt; pt -&gt; (app locale)pt-PTpt -&gt; (app locale)pt-AOpt-PT -&gt; pt -&gt; (app locale)pt-MZpt-PT -&gt; pt -&gt; (app locale)### Spanish

[](#spanish)

LocaleFallback Chaines-419es -&gt; (app locale)es-MXes-419 -&gt; es -&gt; (app locale)es-ARes-419 -&gt; es -&gt; (app locale)es-COes-419 -&gt; es -&gt; (app locale)es-CLes-419 -&gt; es -&gt; (app locale)es-PEes-419 -&gt; es -&gt; (app locale)es-VEes-419 -&gt; es -&gt; (app locale)es-ECes-419 -&gt; es -&gt; (app locale)es-GTes-419 -&gt; es -&gt; (app locale)es-CUes-419 -&gt; es -&gt; (app locale)es-BOes-419 -&gt; es -&gt; (app locale)es-DOes-419 -&gt; es -&gt; (app locale)es-HNes-419 -&gt; es -&gt; (app locale)es-PYes-419 -&gt; es -&gt; (app locale)es-SVes-419 -&gt; es -&gt; (app locale)es-NIes-419 -&gt; es -&gt; (app locale)es-CRes-419 -&gt; es -&gt; (app locale)es-PAes-419 -&gt; es -&gt; (app locale)es-UYes-419 -&gt; es -&gt; (app locale)es-PRes-419 -&gt; es -&gt; (app locale)### French

[](#french)

LocaleFallback Chainfr-CAfr -&gt; (app locale)fr-BEfr -&gt; (app locale)fr-CHfr -&gt; (app locale)fr-LUfr -&gt; (app locale)fr-MCfr -&gt; (app locale)fr-SNfr -&gt; (app locale)fr-CIfr -&gt; (app locale)fr-MLfr -&gt; (app locale)fr-CMfr -&gt; (app locale)fr-MGfr -&gt; (app locale)fr-CDfr -&gt; (app locale)### German

[](#german)

LocaleFallback Chainde-ATde -&gt; (app locale)de-CHde -&gt; (app locale)de-LUde -&gt; (app locale)de-LIde -&gt; (app locale)### Italian

[](#italian)

LocaleFallback Chainit-CHit -&gt; (app locale)### Dutch

[](#dutch)

LocaleFallback Chainnl-BEnl -&gt; (app locale)### English

[](#english)

LocaleFallback Chainen-GBen -&gt; (app locale)en-AUen-GB -&gt; en -&gt; (app locale)en-NZen-AU -&gt; en-GB -&gt; en -&gt; (app locale)en-INen-GB -&gt; en -&gt; (app locale)en-CAen -&gt; (app locale)en-ZAen-GB -&gt; en -&gt; (app locale)en-IEen-GB -&gt; en -&gt; (app locale)en-SGen-GB -&gt; en -&gt; (app locale)### Arabic

[](#arabic)

LocaleFallback Chainar-SAar -&gt; (app locale)ar-EGar -&gt; (app locale)ar-AEar -&gt; (app locale)ar-MAar -&gt; (app locale)ar-DZar -&gt; (app locale)ar-IQar -&gt; (app locale)ar-KWar -&gt; (app locale)ar-QAar -&gt; (app locale)ar-BHar -&gt; (app locale)ar-OMar -&gt; (app locale)ar-JOar -&gt; (app locale)ar-LBar -&gt; (app locale)ar-TNar -&gt; (app locale)ar-LYar -&gt; (app locale)ar-SDar -&gt; (app locale)ar-YEar -&gt; (app locale)### Norwegian

[](#norwegian)

LocaleFallback Chainnbno -&gt; (app locale)nnnb -&gt; no -&gt; (app locale)### Malay

[](#malay)

LocaleFallback Chainms-MYms -&gt; (app locale)ms-SGms -&gt; (app locale)ms-BNms -&gt; (app locale)How It Works
------------

[](#how-it-works)

1. Laravel auto-discovers `LocaleChainServiceProvider`, which extends Laravel's `TranslationServiceProvider`.
2. The service provider replaces the `translator` singleton with `ChainTranslator`, which extends `Illuminate\Translation\Translator`.
3. `ChainTranslator` overrides the `get()` method to walk a configurable fallback chain.
4. For each key lookup, it tries the primary locale first, then each locale in the chain, then the configured `fallback_locale`.
5. **Crucially, it checks JSON translation files at each step** -- fixing Laravel's bug where JSON files ignore fallback entirely.
6. The original terminal fallback (your `fallback_locale`) is preserved at the end of the chain.
7. Your existing `__()`, `trans()`, and `@lang()` calls work without any changes.

Example
-------

[](#example)

A working example route is included in the `example/` directory. Copy it to your `routes/web.php`:

```
Route::get('/locale-chain-test', function () {
    App::setLocale('pt-BR');

    return response()->json([
        'locale' => App::getLocale(),
        'php_greeting' => __('messages.greeting'),
        'json_welcome' => __('Welcome'),
    ]);
});
```

Then test:

```
curl http://localhost:8000/locale-chain-test
```

See `example/README.md` for full details.

FAQ
---

[](#faq)

**Is this production-ready?**Yes. The library extends Laravel's `Translator` class using its public API. No monkey-patching, no private API access.

**Performance impact?**Negligible. Translation files are loaded once per locale per request via Laravel's built-in `FileLoader` caching. The chain walking adds only a few array lookups per missing key.

**Does it fix the JSON fallback bug?**Yes. This is one of the two main features. Laravel's stock translator does not check JSON fallback files -- `ChainTranslator` checks JSON files at every step of the fallback chain.

**Can I use a non-English default locale?**Yes. The fallback chains are independent of your app's `locale` and `fallback_locale`. They only control which sibling locales are checked before the default language.

**Can I disable it?**Yes. Set `'enabled' => false` in `config/locale-chain.php`, or remove the package entirely.

**Does it work with Laravel Livewire / Inertia.js?**Yes. Both use Laravel's translation system under the hood, so fallback chains work automatically.

**Does it work with API resources?**Yes. Any code that uses `__()`, `trans()`, or the `Translator` service will benefit from fallback chains.

**Minimum Laravel version?**Laravel 10 (LTS). Also supports Laravel 11 and Laravel 12.

Contributing
------------

[](#contributing)

- Open issues for bugs or feature requests.
- PRs welcome, especially for adding new locale fallback chains.
- Run tests with: `./vendor/bin/phpunit`

License
-------

[](#license)

MIT License - see [LICENSE](LICENSE) file.

Built by [i18nagent.ai](https://i18nagent.ai)

###  Health Score

34

—

LowBetter than 74% of packages

Maintenance82

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity43

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

Unknown

Total

1

Last Release

162d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/e0c87e2fd347981bc88b09dd1d88735965ec9b910aedf54ea17f34177890ee4a?d=identicon)[i18nagent](/maintainers/i18nagent)

---

Top Contributors

[![kwunlokng](https://avatars.githubusercontent.com/u/2629491?v=4)](https://github.com/kwunlokng "kwunlokng (7 commits)")

---

Tags

fallbacki18nl10nlaravellocalelocalizationphplaravellocalizationi18ntranslationlocalefallback

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/i18nagent-laravel-locale-chain/health.svg)

```
[![Health](https://phpackages.com/badges/i18nagent-laravel-locale-chain/health.svg)](https://phpackages.com/packages/i18nagent-laravel-locale-chain)
```

###  Alternatives

[kkomelin/laravel-translatable-string-exporter

Translatable String Exporter for Laravel

3291.6M23](/packages/kkomelin-laravel-translatable-string-exporter)[erag/laravel-lang-sync-inertia

A powerful Laravel package for syncing and managing language translations across backend and Inertia.js (Vue/React/Svelte) frontends, offering effortless localization, auto-sync features, and smooth multi-language support for modern Laravel applications.

5031.3k](/packages/erag-laravel-lang-sync-inertia)[opgginc/codezero-laravel-localized-routes

A convenient way to set up, manage and use localized routes in a Laravel app.

29128.7k1](/packages/opgginc-codezero-laravel-localized-routes)[niels-numbers/laravel-localizer

Detects the user’s preferred language and redirects to the matching localized URL.

216.7k](/packages/niels-numbers-laravel-localizer)

PHPackages © 2026

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