Use custom country module to resolve flags more accurately

Ported from https://github.com/wojtekmaj/country-code-to-flag-emoji
(MIT)
This commit is contained in:
Claudio Ortolina
2026-02-24 08:57:30 +00:00
parent ec131498e7
commit 8eaaa3cfec
5 changed files with 382 additions and 16 deletions
+1 -7
View File
@@ -40,13 +40,7 @@ defmodule MusicLibrary.Artists.ArtistInfo do
nil -> artist_info.musicbrainz_data["country"]
end
%{name: area["name"] || "World", code: keep_alpha_2(country_code) || "XW"}
end
defp keep_alpha_2(nil), do: nil
defp keep_alpha_2(country_code) do
String.slice(country_code, 0..1)
%{name: area["name"] || "World", code: country_code || "XW"}
end
def extract_image(artist_info) when is_nil(artist_info.discogs_data) do
+330
View File
@@ -0,0 +1,330 @@
defmodule MusicLibrary.Country do
@moduledoc """
Converts country codes to flag emojis.
Supports alpha-2 (`"US"`), alpha-3 (`"USA"`), subdivision (`"GB-SCT"`),
and IETF language tag (`"en-US"`) formats.
"""
@regional_indicator_offset 0x1F1E6 - ?a
@tag_offset 0xE0000
@black_flag <<0x1F3F4::utf8>>
@cancel_tag <<0xE007F::utf8>>
# ISO 3166-1 alpha-3 to alpha-2 mapping
@alpha3_to_alpha2 %{
"ABW" => "AW",
"AFG" => "AF",
"AGO" => "AO",
"AIA" => "AI",
"ALA" => "AX",
"ALB" => "AL",
"AND" => "AD",
"ARE" => "AE",
"ARG" => "AR",
"ARM" => "AM",
"ASM" => "AS",
"ATA" => "AQ",
"ATF" => "TF",
"ATG" => "AG",
"AUS" => "AU",
"AUT" => "AT",
"AZE" => "AZ",
"BDI" => "BI",
"BEL" => "BE",
"BEN" => "BJ",
"BES" => "BQ",
"BFA" => "BF",
"BGD" => "BD",
"BGR" => "BG",
"BHR" => "BH",
"BHS" => "BS",
"BIH" => "BA",
"BLM" => "BL",
"BLR" => "BY",
"BLZ" => "BZ",
"BMU" => "BM",
"BOL" => "BO",
"BRA" => "BR",
"BRB" => "BB",
"BRN" => "BN",
"BTN" => "BT",
"BVT" => "BV",
"BWA" => "BW",
"CAF" => "CF",
"CAN" => "CA",
"CCK" => "CC",
"CHE" => "CH",
"CHL" => "CL",
"CHN" => "CN",
"CIV" => "CI",
"CMR" => "CM",
"COD" => "CD",
"COG" => "CG",
"COK" => "CK",
"COL" => "CO",
"COM" => "KM",
"CPV" => "CV",
"CRI" => "CR",
"CUB" => "CU",
"CUW" => "CW",
"CXR" => "CX",
"CYM" => "KY",
"CYP" => "CY",
"CZE" => "CZ",
"DEU" => "DE",
"DJI" => "DJ",
"DMA" => "DM",
"DNK" => "DK",
"DOM" => "DO",
"DZA" => "DZ",
"ECU" => "EC",
"EGY" => "EG",
"ERI" => "ER",
"ESH" => "EH",
"ESP" => "ES",
"EST" => "EE",
"ETH" => "ET",
"FIN" => "FI",
"FJI" => "FJ",
"FLK" => "FK",
"FRA" => "FR",
"FRO" => "FO",
"FSM" => "FM",
"GAB" => "GA",
"GBR" => "GB",
"GEO" => "GE",
"GGY" => "GG",
"GHA" => "GH",
"GIB" => "GI",
"GIN" => "GN",
"GLP" => "GP",
"GMB" => "GM",
"GNB" => "GW",
"GNQ" => "GQ",
"GRC" => "GR",
"GRD" => "GD",
"GRL" => "GL",
"GTM" => "GT",
"GUF" => "GF",
"GUM" => "GU",
"GUY" => "GY",
"HKG" => "HK",
"HMD" => "HM",
"HND" => "HN",
"HRV" => "HR",
"HTI" => "HT",
"HUN" => "HU",
"IDN" => "ID",
"IMN" => "IM",
"IND" => "IN",
"IOT" => "IO",
"IRL" => "IE",
"IRN" => "IR",
"IRQ" => "IQ",
"ISL" => "IS",
"ISR" => "IL",
"ITA" => "IT",
"JAM" => "JM",
"JEY" => "JE",
"JOR" => "JO",
"JPN" => "JP",
"KAZ" => "KZ",
"KEN" => "KE",
"KGZ" => "KG",
"KHM" => "KH",
"KIR" => "KI",
"KNA" => "KN",
"KOR" => "KR",
"KWT" => "KW",
"LAO" => "LA",
"LBN" => "LB",
"LBR" => "LR",
"LBY" => "LY",
"LCA" => "LC",
"LIE" => "LI",
"LKA" => "LK",
"LSO" => "LS",
"LTU" => "LT",
"LUX" => "LU",
"LVA" => "LV",
"MAC" => "MO",
"MAF" => "MF",
"MAR" => "MA",
"MCO" => "MC",
"MDA" => "MD",
"MDG" => "MG",
"MDV" => "MV",
"MEX" => "MX",
"MHL" => "MH",
"MKD" => "MK",
"MLI" => "ML",
"MLT" => "MT",
"MMR" => "MM",
"MNE" => "ME",
"MNG" => "MN",
"MNP" => "MP",
"MOZ" => "MZ",
"MRT" => "MR",
"MSR" => "MS",
"MTQ" => "MQ",
"MUS" => "MU",
"MWI" => "MW",
"MYS" => "MY",
"MYT" => "YT",
"NAM" => "NA",
"NCL" => "NC",
"NER" => "NE",
"NFK" => "NF",
"NGA" => "NG",
"NIC" => "NI",
"NIU" => "NU",
"NLD" => "NL",
"NOR" => "NO",
"NPL" => "NP",
"NRU" => "NR",
"NZL" => "NZ",
"OMN" => "OM",
"PAK" => "PK",
"PAN" => "PA",
"PCN" => "PN",
"PER" => "PE",
"PHL" => "PH",
"PLW" => "PW",
"PNG" => "PG",
"POL" => "PL",
"PRI" => "PR",
"PRK" => "KP",
"PRT" => "PT",
"PRY" => "PY",
"PSE" => "PS",
"PYF" => "PF",
"QAT" => "QA",
"REU" => "RE",
"ROU" => "RO",
"RUS" => "RU",
"RWA" => "RW",
"SAU" => "SA",
"SDN" => "SD",
"SEN" => "SN",
"SGP" => "SG",
"SGS" => "GS",
"SHN" => "SH",
"SJM" => "SJ",
"SLB" => "SB",
"SLE" => "SL",
"SLV" => "SV",
"SMR" => "SM",
"SOM" => "SO",
"SPM" => "PM",
"SRB" => "RS",
"SSD" => "SS",
"STP" => "ST",
"SUR" => "SR",
"SVK" => "SK",
"SVN" => "SI",
"SWE" => "SE",
"SWZ" => "SZ",
"SXM" => "SX",
"SYC" => "SC",
"SYR" => "SY",
"TCA" => "TC",
"TCD" => "TD",
"TGO" => "TG",
"THA" => "TH",
"TJK" => "TJ",
"TKL" => "TK",
"TKM" => "TM",
"TLS" => "TL",
"TON" => "TO",
"TTO" => "TT",
"TUN" => "TN",
"TUR" => "TR",
"TUV" => "TV",
"TWN" => "TW",
"TZA" => "TZ",
"UGA" => "UG",
"UKR" => "UA",
"UMI" => "UM",
"URY" => "UY",
"USA" => "US",
"UZB" => "UZ",
"VAT" => "VA",
"VCT" => "VC",
"VEN" => "VE",
"VGB" => "VG",
"VIR" => "VI",
"VNM" => "VN",
"VUT" => "VU",
"WLF" => "WF",
"WSM" => "WS",
"XKX" => "XK",
"YEM" => "YE",
"ZAF" => "ZA",
"ZMB" => "ZM",
"ZWE" => "ZW"
}
@doc """
Converts a country code to a flag emoji.
Accepts alpha-2 (`"US"`), alpha-3 (`"USA"`), subdivision (`"GB-SCT"`),
and IETF language tag (`"en-US"`) formats. Case insensitive.
## Examples
iex> MusicLibrary.Country.to_emoji("US")
"🇺🇸"
iex> MusicLibrary.Country.to_emoji("GB-SCT")
"🏴󠁧󠁢󠁳󠁣󠁴󠁿"
iex> MusicLibrary.Country.to_emoji("USA")
"🇺🇸"
"""
@spec to_emoji(String.t()) :: String.t()
def to_emoji(code) when is_binary(code) do
code
|> normalize()
|> convert()
end
defp normalize(code) do
code = String.upcase(code)
cond do
String.contains?(code, "-") -> normalize_with_separator(code)
String.length(code) == 3 -> Map.get(@alpha3_to_alpha2, code, code)
true -> code
end
end
defp normalize_with_separator(code) do
case String.split(code, "-") do
[country, subdivision] when byte_size(country) == 2 and byte_size(subdivision) > 2 ->
subdivision = if subdivision == "CYM", do: "WLS", else: subdivision
{:subdivision, country <> subdivision}
parts ->
List.last(parts)
end
end
defp convert({:subdivision, code}) do
tags =
code
|> String.downcase()
|> String.to_charlist()
|> Enum.map_join(fn char -> <<char + @tag_offset::utf8>> end)
@black_flag <> tags <> @cancel_tag
end
defp convert(code) when is_binary(code) do
code
|> String.downcase()
|> String.to_charlist()
|> Enum.map_join(fn char -> <<char + @regional_indicator_offset::utf8>> end)
end
end
@@ -3,6 +3,7 @@ defmodule MusicLibraryWeb.RecordComponents do
alias MusicBrainz.ReleaseSearchResult
alias MusicLibrary.Assets.Transform
alias MusicLibrary.Country
alias MusicLibrary.Records
alias Phoenix.LiveView.JS
@@ -436,14 +437,7 @@ defmodule MusicLibraryWeb.RecordComponents do
def country_label(nil), do: nil
def country_label("XW"), do: "🌍"
def country_label("XE"), do: "🇪🇺"
def country_label(country_code) do
if flagmoji = Flagmojis.by_iso(country_code) do
flagmoji.emoji
else
country_code
end
end
def country_label(country_code), do: Country.to_emoji(country_code)
attr :record, Records.Record, required: true