Country Picker
A country field that reads well in every language. The names come from the locale data PHP already ships with, they are sorted the way the reader's language sorts them, and every country shows its flag in the list and in the field.
It needs one thing: PHP's intl extension, which most PHP builds include. The flag artwork ships with WireKit.
Recipe
Building the Options
Build the list once, where the page's data is prepared, for example in a Livewire component's mount():
use Illuminate\Support\Facades\App;
use Pushery\WireKit\Support\FlagPackage;
// 1. The reader's language decides both the names and their order.
$locale = App::getLocale();
$collator = new \Collator($locale);
// 2. Every ISO 3166-1 country the flags package carries, so no row draws an empty box.
$countries = collect(FlagPackage::isoCodes())
->map(fn (string $code): array => [
// 3. The submitted value: the upper-case code most APIs and databases expect.
'value' => strtoupper($code),
// 4. The name in the reader's language, from the locale data PHP ships with.
'label' => \Locale::getDisplayRegion('-'.$code, $locale),
'flag' => $code,
// 5. Typing the code or the English name finds the country as well.
'keywords' => [strtoupper($code), \Locale::getDisplayRegion('-'.$code, 'en')],
])
// 6. Sorted the way the reader's language sorts, so in German Österreich follows Oman.
->sort(fn (array $a, array $b): int => (int) $collator->compare($a['label'], $b['label']))
->values()
->all();
FlagPackage::isoCodes() reads the manifest that ships with WireKit, so the list is there without installing anything.
Why the Names Come From intl
The names, their translations and the sort order all come from the Unicode Common Locale Data Repository. The intl extension already carries that data, so this recipe adds no dependency. A Composer package that bundles the same data is a download of several megabytes, and it adds nothing the extension does not already provide.
Sorting with Collator rather than sort() matters as soon as the list leaves English: a byte-wise sort puts every name that starts with an accented letter after Z.
Customization
| What to change | How |
|---|---|
| Countries beyond ISO 3166-1 | Append entries with a flag and a label, as below. |
| What the search finds | Change keywords, for example to add a country's other official names. |
| The order | Build the Collator for another locale, or leave out the sort to keep the manifest's order. |
| A round flag | The option medium is always the 4:3 artwork. Where your own markup needs a round flag, use the flag component with shape="circle". |
The list holds the countries ISO 3166-1 assigns. The artwork also draws some regions and organizations, such as eu, un and gb-sct. Add the ones a form needs by hand, with a label of your own where the locale data has none:
// 1. Append entries after the countries; the sort from above still applies to the whole list.
$countries[] = ['value' => 'EU', 'label' => \Locale::getDisplayRegion('-EU', $locale), 'flag' => 'eu'];
// 2. A region the locale data does not name gets your own label.
$countries[] = ['value' => 'GB-SCT', 'label' => 'Scotland', 'flag' => 'gb-sct'];
When a Flag Is Missing
Every row still works. A code the artwork does not carry is drawn as an empty box of the same size, so the names stay aligned, and in debug mode the log carries one warning naming the code.
Accessibility
- The flag in a row is decorative. The option's label already names the country, so the flag is hidden from assistive technology and nothing is announced twice.
- The keyboard model is the combobox's own: type to filter, arrow keys to move, Enter to choose.
Related
- Combobox: options with a medium, keywords and a shorter selected label.
- Flag: the component the flags come from, and what it draws without the package.
- Language pickers: why a language picker lists endonyms and no flags.