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

Languages Bundle Laravel Package

modera/languages-bundle

A Symfony bundle from Modera for managing application languages and locales. It helps define available languages, centralize localization configuration, and integrate language management into Modera-based admin and content workflows.

View on GitHub
Deep Wiki
Context7

Getting Started

modera/languages-bundle is a Symfony bundle backed by Doctrine, last tagged in June 2016 and published as a read-only subtree split of the Modera monorepo. For a Laravel developer, "getting started" means one thing: read it as a spec, do not install it. There is no service provider, no publishable config, no migration set, and the Symfony 2/3 constraints will not resolve against a modern Laravel install.

Minimal path in a Laravel app (the recommended one):

  1. Open the repo and read exactly two files: Entity/Language.php (the mapped class — roughly id, code, name, enabled) and DependencyInjection/ (where the entity class parameter and service aliases live). That is the entire public surface; everything else is bundle wiring.
  2. Port the schema to a Laravel migration:
Schema::create('languages', function (Blueprint $t) {
    $t->increments('id');
    $t->string('code', 5)->unique();   // ISO 639-1, e.g. "en"
    $t->string('name');                // "English"
    $t->boolean('enabled')->default(true);
    $t->timestamps();
});
  1. Seed the data — the bundle ships the table, not the list. Pull names from symfony/intl (Languages::getNames()) or umpirsky/language-list rather than hand-maintaining them.
  2. Keep the default language in config/app.php / a dedicated config file, not in the table.

First use case: a language switcher. Read enabled languages once, build a code => name map, cache it, and render the switcher off that map.

Only if you are in a legacy Symfony 2.x/3.x app: composer require modera/languages-bundle, register new Modera\LanguagesBundle\ModeraLanguagesBundle() in AppKernel, let Doctrine generate the schema, convert the dump to an explicit migration, and seed the ISO list yourself.

Implementation Patterns

Thin model + cached repository. The day-to-day value is a single read-mostly lookup, so wrap it:

final class LanguageRepository
{
    public function enabled(): Collection
    {
        return Cache::rememberForever('languages.enabled', fn () =>
            Language::where('enabled', true)->orderBy('name')->get()
        );
    }

    public function byCode(string $code): ?Language
    {
        return $this->enabled()->firstWhere('code', $code);
    }
}

Bind it behind an interface (LanguageRepositoryInterface) in a service provider. That way, if you ever do need to swap the implementation, it's a container binding rather than a rewrite.

Locale negotiation middleware. This is the piece that actually earns its keep in a Laravel app:

public function handle(Request $request, Closure $next)
{
    $available = app(LanguageRepositoryInterface::class)->enabled()->pluck('code')->all();
    $locale = $request->getPreferredLanguage($available) ?? config('app.locale');
    app()->setLocale($locale);
    return $next($request);
}

Laravel's getPreferredLanguage() already parses Accept-Language including q-values — don't hand-roll this.

Seeder from CLDR data. One artisan seeder, idempotent (updateOrCreate on code), reading Languages::getNames(). Run it in every environment so the language list is never a manual DB step.

Switcher as a Blade component. Read enabled(), emit a form/select posting to a /locale/{code} route guarded by in_array($code, $available). Persist to session so the choice survives navigation, and apply it in the middleware before Accept-Language is consulted.

Per-language configuration. Where the Modera bundle implies default-language settings, do the same with a config array keyed by code (config/languages.php) for fallbacks or formatting overrides — but keep the enabled set in the database so admins can toggle without a deploy. Two sources of truth is fine as long as the split is: DB = which languages, config = how each behaves.

Cache invalidation on write. Hook Language::saved and Language::deleted to Cache::forget('languages.enabled'). Without that, an admin disables a language and the middleware keeps negotiating into it.

Testing pattern. Feature-test the middleware with $this->withHeader('Accept-Language', 'fr-CA,fr;q=0.9,en;q=0.5') and assert app()->getLocale(). Test the fallback path (request a disabled language, assert default) explicitly — it's the failure that shows up in production as wrong-language emails.

Gotchas and Tips

  • It is not a Laravel package. No ServiceProvider, no facade, no publishable config. Any catalog listing it as Laravel-native is mislabeled. Budget zero time trying to composer require it.
  • Composer will fail. 2016-era PHP and Symfony constraints against Laravel 10/11's Symfony 6/7 transitives won't resolve. --ignore-platform-reqs buys a green build and an unverified runtime — don't.
  • Read-only mirror. Issues and PRs have no upstream destination. If you somehow need the original code, your only option is a private fork; there is no support path, 2 stars, and zero dependents.
  • The bundle gives you no data. The table ships empty. Forget the seeder and every language switcher renders blank — this is the most common first-run confusion.
  • enabled filtering is yours to enforce everywhere. The negotiation middleware, the switcher, and any admin CRUD must each filter on it. A partial implementation lets users select a language the app can't serve.
  • Language code vs locale are different fields. en is a language; en_US is a locale. The Modera entity is language-shaped. If you need regional formatting (dates, currency), add a separate locale column or derive it — don't overload code.
  • Doctrine naming != Eloquent naming. If you ever point an Eloquent model at an existing Modera table, expect column and table-name mismatches from the different naming strategies. Verify with toSql() before assuming.
  • Normalize code to lowercase and enforce the unique index. EN and en in the same table will quietly break lookups.
  • Cache correctness, not performance, is the risk. The dataset is tiny and belongs in memory/Redis. The only failure mode worth designing for is a stale cache after an admin change.
  • Fallback chains should never return null. Requested → base language (fr-CA → fr) → config('app.locale'). Anything that can produce an unset locale will surface as untranslated UI, which is customer-visible and easy to miss in tests.
  • No extension points to speak of. Everything useful lives in the entity and the DI extension. Wrap the concept in your own interface and treat the repo purely as a shape reference — the schema above is the whole takeaway.
  • Security scanning. If this arrives transitively in a legacy service, scanners will flag abandoned dependencies. The right ticket is "remove it," not "upgrade it" — there is nothing to upgrade to.
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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package