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

Multilingual Country List Laravel Package

petercoles/multilingual-country-list

View on GitHub
Deep Wiki
Context7

Getting Started

Install via Composer:

composer require petercoles/multilingual-country-list

On Laravel 5.5+ the service provider and Countries facade are auto-discovered. On older versions add the provider and alias manually in config/app.php. If the package ships a config file, publish it and read it before writing any code:

php artisan vendor:publish --provider="PeterColes\\MultilingualCountryList\\CountriesServiceProvider"

Look first at three places:

  1. vendor/petercoles/multilingual-country-list/README.md — the authoritative method list for the version you installed.
  2. The facade's registered alias (Countries) and its underlying class, so you know what's bound in the container.
  3. vendor/umpirsky/country-list/data/ — this is where the actual translated names live; the package is a thin wrapper over it. If a locale or country name looks wrong, that is the source of truth, not your code.

The first use case you'll hit is a country select in a registration or address form. In a controller:

use Countries; // facade alias

public function create()
{
    return view('profile.create', [
        'countries' => Countries::lookup(app()->getLocale()),
    ]);
}

Then render a simple select with the ISO code as the value and the localized name as the label. Confirm the exact method name (lookup, keyValue, all, or similar) against the README once — the API is small but has shifted between releases.

Implementation Patterns

Keep codes in storage, names in the UI. Always persist the ISO 3166-1 alpha-2 code (GB, DE, BR), never the display name. Render the localized name at read time. This is the single most important pattern: it lets you switch locales without a data migration and lets the user change language mid-session.

// store
$validated = $request->validate([
    'country' => ['required', Rule::in(array_keys(Countries::lookup('en')))],
]);

$user->update(['country_code' => $validated['country']]);

Validate against the same source you populate from. Use Rule::in(array_keys($lookup)) rather than a hand-maintained list. If you also need the alpha-3 code, derive it from the same lookup instead of hardcoding a mapping.

Wrap the facade in a small domain service. The package gives you raw arrays; you want {code, name} objects, sorting, and a default locale in one place.

class CountryOptions
{
    public function forLocale(?string $locale = null): array
    {
        $locale = $locale ?? app()->getLocale();

        return collect(Countries::lookup($locale))
            ->map(fn ($name, $code) => ['value' => $code, 'label' => $name])
            ->values()
            ->all();
    }
}

Use this in a Blade component or view composer so every form gets identical behavior, and swap the implementation later if you outgrow the package.

Cache per locale, not globally. The data is static per install, so caching is safe — but a common bug is a single cache key that leaks the first requested locale into subsequent requests.

$countries = Cache::rememberForever("countries:{$locale}", fn () =>
    Countries::lookup($locale)
);

Clear the tag (or the whole cache) after composer update so renames and newly added territories appear.

Fall back deliberately. Use config('app.fallback_locale') (en) when the current locale isn't shipped by umpirsky/country-list, and guard the call so a missing locale returns English rather than an empty array.

In APIs, return a stable shape. Flatten to [{code: 'GB', name: 'United Kingdom'}] and document that name is locale-dependent while code is not. Clients can then cache by (Accept-Language, code).

Test against known fixtures. Assert specific pairs (GB → "United Kingdom", DE in German locale) rather than snapshotting the whole array, which churns on every data update.

Gotchas and Tips

  • Locale identifiers must match the dataset. umpirsky/country-list uses directory names like en, fr, de, and region variants such as zh-CN or pt-BR. Passing en_US when only en exists can yield an empty result or an exception depending on version. Normalize with a small map or a try/catch fallback.
  • Data staleness is real. Country names change (Macedonia → North Macedonia, Swaziland → Eswatini, Turkey → Türkiye) and new territories appear. This package bundles a snapshot via umpirsky/country-list, so refresh with composer update petercoles/multilingual-country-list umpirsky/country-list --with-all-dependencies and review diffs in stored codes before deploying.
  • Composer constraints can lag Laravel. Older releases may not declare support for Laravel 10/11 even when they run fine. Check composer.json in the vendor directory before assuming incompatibility, and pin versions in composer.json rather than relying on ^.
  • Facade alias collisions. If your app already binds something to Countries, alias the package facade explicitly in config/app.php or inject the class instead.
  • Sorting. The underlying data is typically ordered by localized name, which is not the same as byte-order ksort. If you re-sort, use PHP's Collator (new \Collator($locale)) so accented names sort correctly. Sorting by code is only appropriate for internal lists.
  • config:cache and route:cache don't touch this data. If country lists look stale after deploy, it's the vendor snapshot or your app cache key — not the config cache. Run php artisan cache:clear and re-check.
  • Debugging an empty list is almost always one of: locale string mismatch, facade not bound (check app()->bound()), or a typo'd method name for your version. Dump app()->getLocale() and the raw call result side by side.
  • Extension points are thin by design. The realistic customization surface is: publish/override config, decorate the facade with your own service (as above), or replace the binding in a service provider. If you need admin-editable countries, a database table, or per-tenant availability, wrap this package rather than fork it — the wrapper is small enough that your abstraction will outlive it.
  • Don't use it for validation of addresses. It validates that a country code exists, not that it matches a shipping zone, tax region, or payment provider restriction. Layer those rules separately.
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.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core