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

Laravel Lang Country Laravel Package

involved-group/laravel-lang-country

Laravel Lang Country provides ISO 639-1 language and ISO 3166-1 country data with ready-made validation rules. Access language and country names and codes, validate user input with simple rules, and keep your forms consistent with lightweight, configurable helpers for Laravel applications.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish the config:

composer require involved-group/laravel-lang-country
php artisan vendor:publish --provider="InvolvedGroup\LaravelLangCountry\LangCountryServiceProvider" --tag=config

The published config/lang-country.php is the whole package: a lookup table of countries, the languages spoken there, and the country's date notation. Set sane defaults before writing any code:

'default_country' => 'US',
'default_locale'  => 'en',

The first use case to wire up is not translation — it's date presentation. Pick any screen that renders a date to a user (invoice, order history, profile) and replace $date->format('Y-m-d') with the package's country-aware format:

use InvolvedGroup\LaravelLangCountry\Facades\LangCountry;

LangCountry::setCountry('NL');
LangCountry::setLocale('nl');

LangCountry::getDateFormat();          // e.g. 'd-m-Y'
now()->format(LangCountry::getDateFormat());

If that works, you've validated the config, the facade binding, and the core value proposition. Everything else — middleware, user preferences, language negotiation — is plumbing on top of those two calls. Look first at the LangCountry facade class and the config file; those are the public surface, and the README's examples map directly onto them.

Implementation Patterns

Resolution middleware is the right home for this. Country and locale are per-request context, so resolve them once and set both the package state and Laravel's own locale:

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use InvolvedGroup\LaravelLangCountry\Facades\LangCountry;

class SetLangCountry
{
    public function handle(Request $request, Closure $next)
    {
        $country = $request->user()?->country
            ?? $request->session()->get('country')
            ?? LangCountry::getCountry();

        $locale = $request->user()?->locale
            ?? $this->negotiate($request, $country)
            ?? LangCountry::getLocale();

        LangCountry::setCountry($country);
        LangCountry::setLocale($locale);

        // The package tracks its own state; Laravel and Carbon need telling too.
        app()->setLocale($locale);
        Carbon::setLocale($locale);

        return $next($request);
    }

    private function negotiate(Request $request, string $country): ?string
    {
        $supported = LangCountry::getLanguagesFromCountry($country);

        return $request->getPreferredLanguage($supported) ?: ($supported[0] ?? null);
    }
}

Register it in the web group after StartSession and Authenticate, and in the api group before anything that formats output.

Persist the preference, don't infer it every request. Add country and locale columns to users and expose a small settings form. On save, validate against the package's own lists rather than hardcoding arrays:

'country' => ['required', 'string', Rule::in(array_keys(LangCountry::getAllCountries()))],
'locale'  => ['required', 'string', Rule::in(LangCountry::getAllLanguages())],

Then cross-check that the locale is actually spoken in the chosen country — countries like Belgium, Switzerland, Canada, and India have several, and this package is specifically useful for that check:

$allowed = LangCountry::getLanguagesFromCountry($request->country);
abort_unless(in_array($request->locale, $allowed, true), 422);

Use it as a negotiation source, not a translator. The package answers "which languages are plausible for this country?" and "how does this country write dates?". Hand the chosen locale to Laravel's translator and set Carbon's locale for translatedFormat() / diffForHumans(). Pair it with Accept-Language for anonymous users and the user's stored preference for authenticated ones.

Blade and API exposure. A tiny helper or Blade directive keeps views clean:

@langcountryDate($order->created_at)
// compiles to: $date->format(LangCountry::getDateFormat())

For APIs, expose country and locale in the user payload so clients can mirror formatting. Consider returning the raw ISO timestamp plus the resolved format string rather than pre-formatted dates, so mobile clients can localize natively.

Testing. Set config and facade state in setUp() and assert both the resolved locale and the rendered date:

LangCountry::setCountry('DE');
LangCountry::setLocale('de');
$this->assertSame('d.m.Y', LangCountry::getDateFormat());

Feature-test the middleware: a user with country = 'BE', locale = 'fr' hitting a page should end with app()->getLocale() === 'fr'.

Gotchas and Tips

  • The facade is effectively a singleton. State set by LangCountry::setCountry() survives for the life of the process. In queue workers, Octane, and PHPUnit this leaks between requests and tests. Always set both values at the start of a request/job, never rely on the previous value, and reset in tearDown().
  • LangCountry::setLocale() may not call app()->setLocale() for you. Treat them as two separate switches (plus Carbon::setLocale()). The most common bug report with this package is "translations are still English" because only the package's state was set.
  • Date format is per-country, not per-locale. en_US and en_GB share a language but not m/d/Y vs d/m/Y. This is the package's main differentiator over plain setLocale() — don't defeat it by formatting with a locale-derived pattern.
  • Country codes are uppercase ISO-3166, locales are lowercase. Passing 'nl' as a country or 'NL' as a locale will silently fail lookups and fall through to defaults. Normalize at the boundary (strtoupper, strtolower).
  • Config caching. Run php artisan config:clear after editing the published file; config:cache serves a stale map otherwise. The country table is large but static, so caching it is genuinely beneficial in production.
  • Handle unsupported input explicitly. A country missing from the config or a language not spoken there should trigger your fallback chain, not an exception. Wrap resolution in a service that returns a default and logs the miss — geo-IP and client headers are unreliable.
  • Don't overload it with timezone. Date format and date timezone are orthogonal. Use a separate timezone column or config('app.timezone'); reaching for this package to solve timezone display will frustrate you.
  • Middleware order matters. Session-based country/locale resolution must run after StartSession, and after auth if you're reading $request->user(). In API groups without sessions, guard the session() calls.
  • Verify the facade's method names against your installed version. The API is small (getCountry, setCountry, getLocale, setLocale, getAllCountries, getAllLanguages, getLanguagesFromCountry, getCountriesFromLanguage, date-format accessors), but naming has shifted across releases. The facade class is ~50 lines — read it once rather than guessing from blog posts.
  • Extension point: if you need per-tenant or per-market overrides, don't fork the config. Wrap the facade in your own LocaleResolver service and swap the binding; the package is a lookup table, so it composes cleanly behind an interface.
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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core