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:
@@ -43,7 +43,9 @@
|
|||||||
"Bash(while read dir)",
|
"Bash(while read dir)",
|
||||||
"Bash(MIX_ENV=dev mix run:*)",
|
"Bash(MIX_ENV=dev mix run:*)",
|
||||||
"Bash(mix deps.get:*)",
|
"Bash(mix deps.get:*)",
|
||||||
"Bash(ls:*)"
|
"Bash(ls:*)",
|
||||||
|
"WebFetch(domain:raw.githubusercontent.com)",
|
||||||
|
"WebFetch(domain:github.com)"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"enableAllProjectMcpServers": false
|
"enableAllProjectMcpServers": false
|
||||||
|
|||||||
@@ -40,13 +40,7 @@ defmodule MusicLibrary.Artists.ArtistInfo do
|
|||||||
nil -> artist_info.musicbrainz_data["country"]
|
nil -> artist_info.musicbrainz_data["country"]
|
||||||
end
|
end
|
||||||
|
|
||||||
%{name: area["name"] || "World", code: keep_alpha_2(country_code) || "XW"}
|
%{name: area["name"] || "World", code: country_code || "XW"}
|
||||||
end
|
|
||||||
|
|
||||||
defp keep_alpha_2(nil), do: nil
|
|
||||||
|
|
||||||
defp keep_alpha_2(country_code) do
|
|
||||||
String.slice(country_code, 0..1)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def extract_image(artist_info) when is_nil(artist_info.discogs_data) do
|
def extract_image(artist_info) when is_nil(artist_info.discogs_data) do
|
||||||
|
|||||||
@@ -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 MusicBrainz.ReleaseSearchResult
|
||||||
alias MusicLibrary.Assets.Transform
|
alias MusicLibrary.Assets.Transform
|
||||||
|
alias MusicLibrary.Country
|
||||||
alias MusicLibrary.Records
|
alias MusicLibrary.Records
|
||||||
alias Phoenix.LiveView.JS
|
alias Phoenix.LiveView.JS
|
||||||
|
|
||||||
@@ -436,14 +437,7 @@ defmodule MusicLibraryWeb.RecordComponents do
|
|||||||
def country_label(nil), do: nil
|
def country_label(nil), do: nil
|
||||||
def country_label("XW"), do: "🌍"
|
def country_label("XW"), do: "🌍"
|
||||||
def country_label("XE"), do: "🇪🇺"
|
def country_label("XE"), do: "🇪🇺"
|
||||||
|
def country_label(country_code), do: Country.to_emoji(country_code)
|
||||||
def country_label(country_code) do
|
|
||||||
if flagmoji = Flagmojis.by_iso(country_code) do
|
|
||||||
flagmoji.emoji
|
|
||||||
else
|
|
||||||
country_code
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
attr :record, Records.Record, required: true
|
attr :record, Records.Record, required: true
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
defmodule MusicLibrary.CountryTest do
|
||||||
|
use ExUnit.Case, async: true
|
||||||
|
|
||||||
|
alias MusicLibrary.Country
|
||||||
|
|
||||||
|
describe "to_emoji/1" do
|
||||||
|
test "converts alpha-2 codes" do
|
||||||
|
assert Country.to_emoji("US") == "🇺🇸"
|
||||||
|
assert Country.to_emoji("PL") == "🇵🇱"
|
||||||
|
assert Country.to_emoji("GB") == "🇬🇧"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "is case insensitive" do
|
||||||
|
assert Country.to_emoji("us") == "🇺🇸"
|
||||||
|
assert Country.to_emoji("pl") == "🇵🇱"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "converts subdivision codes" do
|
||||||
|
assert Country.to_emoji("GB-SCT") == "🏴"
|
||||||
|
assert Country.to_emoji("GB-WLS") == "🏴"
|
||||||
|
assert Country.to_emoji("GB-ENG") == "🏴"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "maps GB-CYM to GB-WLS" do
|
||||||
|
assert Country.to_emoji("GB-CYM") == Country.to_emoji("GB-WLS")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "converts alpha-3 codes" do
|
||||||
|
assert Country.to_emoji("USA") == "🇺🇸"
|
||||||
|
assert Country.to_emoji("GBR") == "🇬🇧"
|
||||||
|
assert Country.to_emoji("POL") == "🇵🇱"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "converts IETF language tags" do
|
||||||
|
assert Country.to_emoji("en-US") == "🇺🇸"
|
||||||
|
assert Country.to_emoji("pl-PL") == "🇵🇱"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "matches Flagmojis output for all alpha-2 codes" do
|
||||||
|
for %Flagmojis.Flag{iso: iso, emoji: expected} <- Flagmojis.Data.all() do
|
||||||
|
assert Country.to_emoji(iso) == expected,
|
||||||
|
"mismatch for #{iso}: expected #{expected}, got #{Country.to_emoji(iso)}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user