From 7b7d6758ef72fa60e2dec5ecfcc78157077e3bd7 Mon Sep 17 00:00:00 2001 From: InfiniteTF Date: Fri, 28 Oct 2022 07:37:57 +0200 Subject: [PATCH] Change performer country value to be ISO code (#1922) * Change performer country value to be ISO code * Localize country names * Use country select for filter Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com> --- pkg/scraper/country.go | 296 ++++++++++++++++++ pkg/scraper/postprocessing.go | 4 + pkg/sqlite/database.go | 2 +- .../migrations/37_iso_country_names.up.sql | 269 ++++++++++++++++ ui/v2.5/src/App.tsx | 5 +- .../src/components/List/AddFilterDialog.tsx | 14 + .../PerformerDetailsPanel.tsx | 9 +- .../PerformerDetails/PerformerEditPanel.tsx | 17 +- .../PerformerScrapeDialog.tsx | 3 +- .../PerformerStashBoxModal.tsx | 2 +- ui/v2.5/src/components/Shared/CountryFlag.tsx | 19 +- .../src/components/Shared/CountryLabel.tsx | 24 ++ .../src/components/Shared/CountrySelect.tsx | 51 +++ .../src/components/Shared/ScrapeDialog.tsx | 47 +++ ui/v2.5/src/components/Shared/index.ts | 3 + ui/v2.5/src/components/Shared/styles.scss | 8 + .../src/components/Tagger/PerformerModal.tsx | 2 +- ui/v2.5/src/docs/en/Changelog/v0180.md | 1 + ui/v2.5/src/locales/countryNames/zh-TW.json | 255 +++++++++++++++ ui/v2.5/src/locales/index.ts | 37 +++ .../models/list-filter/criteria/country.ts | 14 + .../models/list-filter/criteria/criterion.ts | 15 +- .../models/list-filter/criteria/factory.ts | 4 +- ui/v2.5/src/utils/country.ts | 61 ++-- ui/v2.5/src/utils/index.ts | 3 +- 25 files changed, 1105 insertions(+), 60 deletions(-) create mode 100644 pkg/scraper/country.go create mode 100644 pkg/sqlite/migrations/37_iso_country_names.up.sql create mode 100644 ui/v2.5/src/components/Shared/CountryLabel.tsx create mode 100644 ui/v2.5/src/components/Shared/CountrySelect.tsx create mode 100644 ui/v2.5/src/locales/countryNames/zh-TW.json diff --git a/pkg/scraper/country.go b/pkg/scraper/country.go new file mode 100644 index 00000000000..6ef75b11691 --- /dev/null +++ b/pkg/scraper/country.go @@ -0,0 +1,296 @@ +package scraper + +import ( + "strings" + + "github.com/stashapp/stash/pkg/logger" +) + +var countryNameMapping = map[string]string{ + "afghanistan": "AF", + "albania": "AL", + "algeria": "DZ", + "america": "US", + "american": "US", + "american samoa": "AS", + "andorra": "AD", + "angola": "AO", + "anguilla": "AI", + "antarctica": "AQ", + "antigua and barbuda": "AG", + "argentina": "AR", + "armenia": "AM", + "aruba": "AW", + "australia": "AU", + "austria": "AT", + "azerbaijan": "AZ", + "bahamas": "BS", + "bahrain": "BH", + "bangladesh": "BD", + "barbados": "BB", + "belarus": "BY", + "belgium": "BE", + "belize": "BZ", + "benin": "BJ", + "bermuda": "BM", + "bhutan": "BT", + "bolivia": "BO", + "bosnia and herzegovina": "BA", + "botswana": "BW", + "bouvet island": "BV", + "brazil": "BR", + "british indian ocean territory": "IO", + "brunei darussalam": "BN", + "bulgaria": "BG", + "burkina faso": "BF", + "burundi": "BI", + "cambodia": "KH", + "cameroon": "CM", + "canada": "CA", + "cape verde": "CV", + "cayman islands": "KY", + "central african republic": "CF", + "chad": "TD", + "chile": "CL", + "china": "CN", + "christmas island": "CX", + "cocos (keeling) islands": "CC", + "colombia": "CO", + "comoros": "KM", + "congo": "CG", + "congo the democratic republic of the": "CD", + "cook islands": "CK", + "costa rica": "CR", + "cote d'ivoire": "CI", + "croatia": "HR", + "cuba": "CU", + "cyprus": "CY", + "czech republic": "CZ", + "czechia": "CZ", + "denmark": "DK", + "djibouti": "DJ", + "dominica": "DM", + "dominican republic": "DO", + "ecuador": "EC", + "egypt": "EG", + "el salvador": "SV", + "equatorial guinea": "GQ", + "eritrea": "ER", + "estonia": "EE", + "ethiopia": "ET", + "falkland islands (malvinas)": "FK", + "faroe islands": "FO", + "fiji": "FJ", + "finland": "FI", + "france": "FR", + "french guiana": "GF", + "french polynesia": "PF", + "french southern territories": "TF", + "gabon": "GA", + "gambia": "GM", + "georgia": "GE", + "germany": "DE", + "ghana": "GH", + "gibraltar": "GI", + "greece": "GR", + "greenland": "GL", + "grenada": "GD", + "guadeloupe": "GP", + "guam": "GU", + "guatemala": "GT", + "guinea": "GN", + "guinea-bissau": "GW", + "guyana": "GY", + "haiti": "HT", + "heard island and mcdonald islands": "HM", + "holy see (vatican city state)": "VA", + "honduras": "HN", + "hong kong": "HK", + "hungary": "HU", + "iceland": "IS", + "india": "IN", + "indonesia": "ID", + "iran": "IR", + "iran islamic republic of": "IR", + "iraq": "IQ", + "ireland": "IE", + "israel": "IL", + "italy": "IT", + "jamaica": "JM", + "japan": "JP", + "jordan": "JO", + "kazakhstan": "KZ", + "kenya": "KE", + "kiribati": "KI", + "north korea": "KP", + "south korea": "KR", + "kuwait": "KW", + "kyrgyzstan": "KG", + "lao people's democratic republic": "LA", + "latvia": "LV", + "lebanon": "LB", + "lesotho": "LS", + "liberia": "LR", + "libya": "LY", + "liechtenstein": "LI", + "lithuania": "LT", + "luxembourg": "LU", + "macao": "MO", + "madagascar": "MG", + "malawi": "MW", + "malaysia": "MY", + "maldives": "MV", + "mali": "ML", + "malta": "MT", + "marshall islands": "MH", + "martinique": "MQ", + "mauritania": "MR", + "mauritius": "MU", + "mayotte": "YT", + "mexico": "MX", + "micronesia federated states of": "FM", + "moldova": "MD", + "moldova republic of": "MD", + "moldova, republic of": "MD", + "monaco": "MC", + "mongolia": "MN", + "montserrat": "MS", + "morocco": "MA", + "mozambique": "MZ", + "myanmar": "MM", + "namibia": "NA", + "nauru": "NR", + "nepal": "NP", + "netherlands": "NL", + "new caledonia": "NC", + "new zealand": "NZ", + "nicaragua": "NI", + "niger": "NE", + "nigeria": "NG", + "niue": "NU", + "norfolk island": "NF", + "north macedonia republic of": "MK", + "northern mariana islands": "MP", + "norway": "NO", + "oman": "OM", + "pakistan": "PK", + "palau": "PW", + "palestinian territory occupied": "PS", + "panama": "PA", + "papua new guinea": "PG", + "paraguay": "PY", + "peru": "PE", + "philippines": "PH", + "pitcairn": "PN", + "poland": "PL", + "portugal": "PT", + "puerto rico": "PR", + "qatar": "QA", + "reunion": "RE", + "romania": "RO", + "russia": "RU", + "russian federation": "RU", + "rwanda": "RW", + "saint helena": "SH", + "saint kitts and nevis": "KN", + "saint lucia": "LC", + "saint pierre and miquelon": "PM", + "saint vincent and the grenadines": "VC", + "samoa": "WS", + "san marino": "SM", + "sao tome and principe": "ST", + "saudi arabia": "SA", + "senegal": "SN", + "seychelles": "SC", + "sierra leone": "SL", + "singapore": "SG", + "slovakia": "SK", + "slovak republic": "SK", + "slovenia": "SI", + "solomon islands": "SB", + "somalia": "SO", + "south africa": "ZA", + "south georgia and the south sandwich islands": "GS", + "spain": "ES", + "sri lanka": "LK", + "sudan": "SD", + "suriname": "SR", + "svalbard and jan mayen": "SJ", + "eswatini": "SZ", + "sweden": "SE", + "switzerland": "CH", + "syrian arab republic": "SY", + "taiwan": "TW", + "tajikistan": "TJ", + "tanzania united republic of": "TZ", + "thailand": "TH", + "timor-leste": "TL", + "togo": "TG", + "tokelau": "TK", + "tonga": "TO", + "trinidad and tobago": "TT", + "tunisia": "TN", + "turkey": "TR", + "turkmenistan": "TM", + "turks and caicos islands": "TC", + "tuvalu": "TV", + "uganda": "UG", + "ukraine": "UA", + "united arab emirates": "AE", + "england": "GB", + "great britain": "GB", + "united kingdom": "GB", + "usa": "US", + "united states": "US", + "united states of america": "US", + "united states minor outlying islands": "UM", + "uruguay": "UY", + "uzbekistan": "UZ", + "vanuatu": "VU", + "venezuela": "VE", + "vietnam": "VN", + "virgin islands british": "VG", + "virgin islands u.s.": "VI", + "wallis and futuna": "WF", + "western sahara": "EH", + "yemen": "YE", + "zambia": "ZM", + "zimbabwe": "ZW", + "åland islands": "AX", + "bonaire sint eustatius and saba": "BQ", + "curaçao": "CW", + "guernsey": "GG", + "isle of man": "IM", + "jersey": "JE", + "montenegro": "ME", + "saint barthélemy": "BL", + "saint martin (french part)": "MF", + "serbia": "RS", + "sint maarten (dutch part)": "SX", + "south sudan": "SS", + "kosovo": "XK", +} + +func resolveCountryName(name *string) *string { + if name == nil { + return nil + } + + trimmedName := strings.TrimSpace(*name) + if len(trimmedName) == 2 { + // If name is two characters it's likely already an ISO value + return &trimmedName + } else if len(trimmedName) == 0 { + return nil + } + + v, exists := countryNameMapping[strings.ToLower(trimmedName)] + if exists { + return &v + } + + logger.Debugf("Scraped country was not recognized: %s", trimmedName) + + // return original name + return &trimmedName +} diff --git a/pkg/scraper/postprocessing.go b/pkg/scraper/postprocessing.go index 4151602d2ab..a7d59605b8e 100644 --- a/pkg/scraper/postprocessing.go +++ b/pkg/scraper/postprocessing.go @@ -66,6 +66,8 @@ func (c Cache) postScrapePerformer(ctx context.Context, p models.ScrapedPerforme logger.Warnf("Could not set image using URL %s: %s", *p.Image, err.Error()) } + p.Country = resolveCountryName(p.Country) + return p, nil } @@ -98,6 +100,8 @@ func (c Cache) postScrapeScenePerformer(ctx context.Context, p models.ScrapedPer } p.Tags = tags + p.Country = resolveCountryName(p.Country) + return nil } diff --git a/pkg/sqlite/database.go b/pkg/sqlite/database.go index 0c913737aae..33f604cda2b 100644 --- a/pkg/sqlite/database.go +++ b/pkg/sqlite/database.go @@ -22,7 +22,7 @@ import ( "github.com/stashapp/stash/pkg/logger" ) -var appSchemaVersion uint = 36 +var appSchemaVersion uint = 37 //go:embed migrations/*.sql var migrationsBox embed.FS diff --git a/pkg/sqlite/migrations/37_iso_country_names.up.sql b/pkg/sqlite/migrations/37_iso_country_names.up.sql new file mode 100644 index 00000000000..1044a17bfc8 --- /dev/null +++ b/pkg/sqlite/migrations/37_iso_country_names.up.sql @@ -0,0 +1,269 @@ +UPDATE `performers` +SET `country` = CASE + WHEN LENGTH(TRIM(`country`)) == 2 THEN TRIM(`country`) + ELSE CASE `country` + WHEN 'Afghanistan' THEN 'AF' + WHEN 'Albania' THEN 'AL' + WHEN 'Algeria' THEN 'DZ' + WHEN 'America' THEN 'US' + WHEN 'American' THEN 'US' + WHEN 'American Samoa' THEN 'AS' + WHEN 'Andorra' THEN 'AD' + WHEN 'Angola' THEN 'AO' + WHEN 'Anguilla' THEN 'AI' + WHEN 'Antarctica' THEN 'AQ' + WHEN 'Antigua and Barbuda' THEN 'AG' + WHEN 'Argentina' THEN 'AR' + WHEN 'Armenia' THEN 'AM' + WHEN 'Aruba' THEN 'AW' + WHEN 'Australia' THEN 'AU' + WHEN 'Austria' THEN 'AT' + WHEN 'Azerbaijan' THEN 'AZ' + WHEN 'Bahamas' THEN 'BS' + WHEN 'Bahrain' THEN 'BH' + WHEN 'Bangladesh' THEN 'BD' + WHEN 'Barbados' THEN 'BB' + WHEN 'Belarus' THEN 'BY' + WHEN 'Belgium' THEN 'BE' + WHEN 'Belize' THEN 'BZ' + WHEN 'Benin' THEN 'BJ' + WHEN 'Bermuda' THEN 'BM' + WHEN 'Bhutan' THEN 'BT' + WHEN 'Bolivia' THEN 'BO' + WHEN 'Bosnia and Herzegovina' THEN 'BA' + WHEN 'Botswana' THEN 'BW' + WHEN 'Bouvet Island' THEN 'BV' + WHEN 'Brazil' THEN 'BR' + WHEN 'British Indian Ocean Territory' THEN 'IO' + WHEN 'Brunei Darussalam' THEN 'BN' + WHEN 'Bulgaria' THEN 'BG' + WHEN 'Burkina Faso' THEN 'BF' + WHEN 'Burundi' THEN 'BI' + WHEN 'Cambodia' THEN 'KH' + WHEN 'Cameroon' THEN 'CM' + WHEN 'Canada' THEN 'CA' + WHEN 'Cape Verde' THEN 'CV' + WHEN 'Cayman Islands' THEN 'KY' + WHEN 'Central African Republic' THEN 'CF' + WHEN 'Chad' THEN 'TD' + WHEN 'Chile' THEN 'CL' + WHEN 'China' THEN 'CN' + WHEN 'Christmas Island' THEN 'CX' + WHEN 'Cocos (Keeling) Islands' THEN 'CC' + WHEN 'Colombia' THEN 'CO' + WHEN 'Comoros' THEN 'KM' + WHEN 'Congo' THEN 'CG' + WHEN 'Congo the Democratic Republic of the' THEN 'CD' + WHEN 'Cook Islands' THEN 'CK' + WHEN 'Costa Rica' THEN 'CR' + WHEN 'Cote D''Ivoire' THEN 'CI' + WHEN 'Croatia' THEN 'HR' + WHEN 'Cuba' THEN 'CU' + WHEN 'Cyprus' THEN 'CY' + WHEN 'Czech Republic' THEN 'CZ' + WHEN 'Czechia' THEN 'CZ' + WHEN 'Denmark' THEN 'DK' + WHEN 'Djibouti' THEN 'DJ' + WHEN 'Dominica' THEN 'DM' + WHEN 'Dominican Republic' THEN 'DO' + WHEN 'Ecuador' THEN 'EC' + WHEN 'Egypt' THEN 'EG' + WHEN 'El Salvador' THEN 'SV' + WHEN 'Equatorial Guinea' THEN 'GQ' + WHEN 'Eritrea' THEN 'ER' + WHEN 'Estonia' THEN 'EE' + WHEN 'Ethiopia' THEN 'ET' + WHEN 'Falkland Islands (Malvinas)' THEN 'FK' + WHEN 'Faroe Islands' THEN 'FO' + WHEN 'Fiji' THEN 'FJ' + WHEN 'Finland' THEN 'FI' + WHEN 'France' THEN 'FR' + WHEN 'French Guiana' THEN 'GF' + WHEN 'French Polynesia' THEN 'PF' + WHEN 'French Southern Territories' THEN 'TF' + WHEN 'Gabon' THEN 'GA' + WHEN 'Gambia' THEN 'GM' + WHEN 'Georgia' THEN 'GE' + WHEN 'Germany' THEN 'DE' + WHEN 'Ghana' THEN 'GH' + WHEN 'Gibraltar' THEN 'GI' + WHEN 'Greece' THEN 'GR' + WHEN 'Greenland' THEN 'GL' + WHEN 'Grenada' THEN 'GD' + WHEN 'Guadeloupe' THEN 'GP' + WHEN 'Guam' THEN 'GU' + WHEN 'Guatemala' THEN 'GT' + WHEN 'Guinea' THEN 'GN' + WHEN 'Guinea-Bissau' THEN 'GW' + WHEN 'Guyana' THEN 'GY' + WHEN 'Haiti' THEN 'HT' + WHEN 'Heard Island and McDonald Islands' THEN 'HM' + WHEN 'Holy See (Vatican City State)' THEN 'VA' + WHEN 'Honduras' THEN 'HN' + WHEN 'Hong Kong' THEN 'HK' + WHEN 'Hungary' THEN 'HU' + WHEN 'Iceland' THEN 'IS' + WHEN 'India' THEN 'IN' + WHEN 'Indonesia' THEN 'ID' + WHEN 'Iran' THEN 'IR' + WHEN 'Iran Islamic Republic of' THEN 'IR' + WHEN 'Iraq' THEN 'IQ' + WHEN 'Ireland' THEN 'IE' + WHEN 'Israel' THEN 'IL' + WHEN 'Italy' THEN 'IT' + WHEN 'Jamaica' THEN 'JM' + WHEN 'Japan' THEN 'JP' + WHEN 'Jordan' THEN 'JO' + WHEN 'Kazakhstan' THEN 'KZ' + WHEN 'Kenya' THEN 'KE' + WHEN 'Kiribati' THEN 'KI' + WHEN 'North Korea' THEN 'KP' + WHEN 'South Korea' THEN 'KR' + WHEN 'Kuwait' THEN 'KW' + WHEN 'Kyrgyzstan' THEN 'KG' + WHEN 'Lao People''s Democratic Republic' THEN 'LA' + WHEN 'Latvia' THEN 'LV' + WHEN 'Lebanon' THEN 'LB' + WHEN 'Lesotho' THEN 'LS' + WHEN 'Liberia' THEN 'LR' + WHEN 'Libya' THEN 'LY' + WHEN 'Liechtenstein' THEN 'LI' + WHEN 'Lithuania' THEN 'LT' + WHEN 'Luxembourg' THEN 'LU' + WHEN 'Macao' THEN 'MO' + WHEN 'Madagascar' THEN 'MG' + WHEN 'Malawi' THEN 'MW' + WHEN 'Malaysia' THEN 'MY' + WHEN 'Maldives' THEN 'MV' + WHEN 'Mali' THEN 'ML' + WHEN 'Malta' THEN 'MT' + WHEN 'Marshall Islands' THEN 'MH' + WHEN 'Martinique' THEN 'MQ' + WHEN 'Mauritania' THEN 'MR' + WHEN 'Mauritius' THEN 'MU' + WHEN 'Mayotte' THEN 'YT' + WHEN 'Mexico' THEN 'MX' + WHEN 'Micronesia Federated States of' THEN 'FM' + WHEN 'Moldova' THEN 'MD' + WHEN 'Moldova Republic of' THEN 'MD' + WHEN 'Moldova, Republic of' THEN 'MD' + WHEN 'Monaco' THEN 'MC' + WHEN 'Mongolia' THEN 'MN' + WHEN 'Montserrat' THEN 'MS' + WHEN 'Morocco' THEN 'MA' + WHEN 'Mozambique' THEN 'MZ' + WHEN 'Myanmar' THEN 'MM' + WHEN 'Namibia' THEN 'NA' + WHEN 'Nauru' THEN 'NR' + WHEN 'Nepal' THEN 'NP' + WHEN 'Netherlands' THEN 'NL' + WHEN 'New Caledonia' THEN 'NC' + WHEN 'New Zealand' THEN 'NZ' + WHEN 'Nicaragua' THEN 'NI' + WHEN 'Niger' THEN 'NE' + WHEN 'Nigeria' THEN 'NG' + WHEN 'Niue' THEN 'NU' + WHEN 'Norfolk Island' THEN 'NF' + WHEN 'North Macedonia Republic of' THEN 'MK' + WHEN 'Northern Mariana Islands' THEN 'MP' + WHEN 'Norway' THEN 'NO' + WHEN 'Oman' THEN 'OM' + WHEN 'Pakistan' THEN 'PK' + WHEN 'Palau' THEN 'PW' + WHEN 'Palestinian Territory Occupied' THEN 'PS' + WHEN 'Panama' THEN 'PA' + WHEN 'Papua New Guinea' THEN 'PG' + WHEN 'Paraguay' THEN 'PY' + WHEN 'Peru' THEN 'PE' + WHEN 'Philippines' THEN 'PH' + WHEN 'Pitcairn' THEN 'PN' + WHEN 'Poland' THEN 'PL' + WHEN 'Portugal' THEN 'PT' + WHEN 'Puerto Rico' THEN 'PR' + WHEN 'Qatar' THEN 'QA' + WHEN 'Reunion' THEN 'RE' + WHEN 'Romania' THEN 'RO' + WHEN 'Russia' THEN 'RU' + WHEN 'Russian Federation' THEN 'RU' + WHEN 'Rwanda' THEN 'RW' + WHEN 'Saint Helena' THEN 'SH' + WHEN 'Saint Kitts and Nevis' THEN 'KN' + WHEN 'Saint Lucia' THEN 'LC' + WHEN 'Saint Pierre and Miquelon' THEN 'PM' + WHEN 'Saint Vincent and the Grenadines' THEN 'VC' + WHEN 'Samoa' THEN 'WS' + WHEN 'San Marino' THEN 'SM' + WHEN 'Sao Tome and Principe' THEN 'ST' + WHEN 'Saudi Arabia' THEN 'SA' + WHEN 'Senegal' THEN 'SN' + WHEN 'Seychelles' THEN 'SC' + WHEN 'Sierra Leone' THEN 'SL' + WHEN 'Singapore' THEN 'SG' + WHEN 'Slovakia' THEN 'SK' + WHEN 'Slovak Republic' THEN 'SK' + WHEN 'Slovenia' THEN 'SI' + WHEN 'Solomon Islands' THEN 'SB' + WHEN 'Somalia' THEN 'SO' + WHEN 'South Africa' THEN 'ZA' + WHEN 'South Georgia and the South Sandwich Islands' THEN 'GS' + WHEN 'Spain' THEN 'ES' + WHEN 'Sri Lanka' THEN 'LK' + WHEN 'Sudan' THEN 'SD' + WHEN 'Suriname' THEN 'SR' + WHEN 'Svalbard and Jan Mayen' THEN 'SJ' + WHEN 'Eswatini' THEN 'SZ' + WHEN 'Sweden' THEN 'SE' + WHEN 'Switzerland' THEN 'CH' + WHEN 'Syrian Arab Republic' THEN 'SY' + WHEN 'Taiwan' THEN 'TW' + WHEN 'Tajikistan' THEN 'TJ' + WHEN 'Tanzania United Republic of' THEN 'TZ' + WHEN 'Thailand' THEN 'TH' + WHEN 'Timor-Leste' THEN 'TL' + WHEN 'Togo' THEN 'TG' + WHEN 'Tokelau' THEN 'TK' + WHEN 'Tonga' THEN 'TO' + WHEN 'Trinidad and Tobago' THEN 'TT' + WHEN 'Tunisia' THEN 'TN' + WHEN 'Turkey' THEN 'TR' + WHEN 'Turkmenistan' THEN 'TM' + WHEN 'Turks and Caicos Islands' THEN 'TC' + WHEN 'Tuvalu' THEN 'TV' + WHEN 'Uganda' THEN 'UG' + WHEN 'Ukraine' THEN 'UA' + WHEN 'United Arab Emirates' THEN 'AE' + WHEN 'England' THEN 'GB' + WHEN 'Great Britain' THEN 'GB' + WHEN 'United Kingdom' THEN 'GB' + WHEN 'USA' THEN 'US' + WHEN 'United States' THEN 'US' + WHEN 'United States of America' THEN 'US' + WHEN 'United States Minor Outlying Islands' THEN 'UM' + WHEN 'Uruguay' THEN 'UY' + WHEN 'Uzbekistan' THEN 'UZ' + WHEN 'Vanuatu' THEN 'VU' + WHEN 'Venezuela' THEN 'VE' + WHEN 'Vietnam' THEN 'VN' + WHEN 'Virgin Islands British' THEN 'VG' + WHEN 'Virgin Islands U.S.' THEN 'VI' + WHEN 'Wallis and Futuna' THEN 'WF' + WHEN 'Western Sahara' THEN 'EH' + WHEN 'Yemen' THEN 'YE' + WHEN 'Zambia' THEN 'ZM' + WHEN 'Zimbabwe' THEN 'ZW' + WHEN 'Åland Islands' THEN 'AX' + WHEN 'Bonaire Sint Eustatius and Saba' THEN 'BQ' + WHEN 'Curaçao' THEN 'CW' + WHEN 'Guernsey' THEN 'GG' + WHEN 'Isle of Man' THEN 'IM' + WHEN 'Jersey' THEN 'JE' + WHEN 'Montenegro' THEN 'ME' + WHEN 'Saint Barthélemy' THEN 'BL' + WHEN 'Saint Martin (French part)' THEN 'MF' + WHEN 'Serbia' THEN 'RS' + WHEN 'Sint Maarten (Dutch part)' THEN 'SX' + WHEN 'South Sudan' THEN 'SS' + WHEN 'Kosovo' THEN 'XK' + ELSE `country` + END +END; diff --git a/ui/v2.5/src/App.tsx b/ui/v2.5/src/App.tsx index 2ab84b1a1a8..71a0755e874 100755 --- a/ui/v2.5/src/App.tsx +++ b/ui/v2.5/src/App.tsx @@ -8,7 +8,7 @@ import { ToastProvider } from "src/hooks/Toast"; import LightboxProvider from "src/hooks/Lightbox/context"; import { initPolyfills } from "src/polyfills"; -import locales from "src/locales"; +import locales, { registerCountry } from "src/locales"; import { useConfiguration, useConfigureUI, @@ -85,6 +85,9 @@ export const App: React.FC = () => { const defaultMessageLanguage = languageMessageString(defaultLocale); const messageLanguage = languageMessageString(language); + // register countries for the chosen language + await registerCountry(language); + const defaultMessages = (await locales[defaultMessageLanguage]()).default; const mergedMessages = cloneDeep(Object.assign({}, defaultMessages)); const chosenMessages = (await locales[messageLanguage]()).default; diff --git a/ui/v2.5/src/components/List/AddFilterDialog.tsx b/ui/v2.5/src/components/List/AddFilterDialog.tsx index d7c0325bc20..183725c61f2 100644 --- a/ui/v2.5/src/components/List/AddFilterDialog.tsx +++ b/ui/v2.5/src/components/List/AddFilterDialog.tsx @@ -28,6 +28,8 @@ import { LabeledIdFilter } from "./Filters/LabeledIdFilter"; import { HierarchicalLabelValueFilter } from "./Filters/HierarchicalLabelValueFilter"; import { OptionsFilter } from "./Filters/OptionsFilter"; import { InputFilter } from "./Filters/InputFilter"; +import { CountryCriterion } from "src/models/list-filter/criteria/country"; +import { CountrySelect } from "../Shared"; interface IAddFilterProps { onAddCriterion: ( @@ -173,6 +175,18 @@ export const AddFilterDialog: React.FC = ({ ); } + if ( + criterion instanceof CountryCriterion && + (criterion.modifier === CriterionModifier.Equals || + criterion.modifier === CriterionModifier.NotEquals) + ) { + return ( + onValueChanged(v)} + /> + ); + } return ( ); diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerDetailsPanel.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerDetailsPanel.tsx index 63c279eca26..a71729a0468 100644 --- a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerDetailsPanel.tsx +++ b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerDetailsPanel.tsx @@ -2,7 +2,7 @@ import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; import { TagLink } from "src/components/Shared"; import * as GQL from "src/core/generated-graphql"; -import { TextUtils, getStashboxBase } from "src/utils"; +import { TextUtils, getStashboxBase, getCountryByISO } from "src/utils"; import { TextField, URLField } from "src/utils/field"; interface IPerformerDetails { @@ -114,7 +114,12 @@ export const PerformerDetailsPanel: React.FC = ({ - + diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx index 3be7d87e9d8..b9297ac99eb 100644 --- a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx +++ b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx @@ -20,9 +20,9 @@ import { CollapseButton, TagSelect, URLField, + CountrySelect, } from "src/components/Shared"; import { ImageUtils, getStashIDs } from "src/utils"; -import { getCountryByISO } from "src/utils/country"; import { useToast } from "src/hooks"; import { Prompt, useHistory } from "react-router-dom"; import { useFormik } from "formik"; @@ -545,7 +545,6 @@ export const PerformerEditPanel: React.FC = ({ const result: GQL.ScrapedPerformerDataFragment = { ...performerResult, images: performerResult.images ?? undefined, - country: getCountryByISO(performerResult.country), __typename: "ScrapedPerformer", }; @@ -880,7 +879,19 @@ export const PerformerEditPanel: React.FC = ({ {renderTextField("birthdate", "Birthdate", "YYYY-MM-DD")} {renderTextField("death_date", "Death Date", "YYYY-MM-DD")} - {renderTextField("country", "Country")} + + + + + + + formik.setFieldValue("country", value)} + /> + + + {renderTextField("ethnicity", "Ethnicity")} {renderTextField("hair_color", "Hair Color")} {renderTextField("eye_color", "Eye Color")} diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeDialog.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeDialog.tsx index d18844b9e65..963beb38e6b 100644 --- a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeDialog.tsx +++ b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeDialog.tsx @@ -8,6 +8,7 @@ import { ScrapedImageRow, ScrapeDialogRow, ScrapedTextAreaRow, + ScrapedCountryRow, } from "src/components/Shared/ScrapeDialog"; import { useTagCreate } from "src/core/StashService"; import { Form } from "react-bootstrap"; @@ -448,7 +449,7 @@ export const PerformerScrapeDialog: React.FC = ( result={ethnicity} onChange={(value) => setEthnicity(value)} /> - setCountry(value)} diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerStashBoxModal.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerStashBoxModal.tsx index acdca66a767..b9c7f4316ba 100644 --- a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerStashBoxModal.tsx +++ b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerStashBoxModal.tsx @@ -78,7 +78,7 @@ const PerformerStashBoxModal: React.FC = ({ ) : performers.length > 0 ? (
    {performers.map((p) => ( -
  • +
  • diff --git a/ui/v2.5/src/components/Shared/CountryFlag.tsx b/ui/v2.5/src/components/Shared/CountryFlag.tsx index 32b1f666fc2..3d73c280e18 100644 --- a/ui/v2.5/src/components/Shared/CountryFlag.tsx +++ b/ui/v2.5/src/components/Shared/CountryFlag.tsx @@ -1,21 +1,28 @@ import React from "react"; -import { getISOCountry } from "src/utils"; +import { useIntl } from "react-intl"; +import { getCountryByISO } from "src/utils"; interface ICountryFlag { country?: string | null; className?: string; } -const CountryFlag: React.FC = ({ className, country }) => { - const ISOCountry = getISOCountry(country); - if (!ISOCountry?.code) return <>; +const CountryFlag: React.FC = ({ + className, + country: isoCountry, +}) => { + const { locale } = useIntl(); + + const country = getCountryByISO(isoCountry, locale); + + if (!isoCountry || !country) return <>; return ( ); }; diff --git a/ui/v2.5/src/components/Shared/CountryLabel.tsx b/ui/v2.5/src/components/Shared/CountryLabel.tsx new file mode 100644 index 00000000000..3fb57e74952 --- /dev/null +++ b/ui/v2.5/src/components/Shared/CountryLabel.tsx @@ -0,0 +1,24 @@ +import React from "react"; +import { useIntl } from "react-intl"; +import { CountryFlag } from "src/components/Shared"; +import { getCountryByISO } from "src/utils"; + +interface IProps { + country: string | undefined; + showFlag?: boolean; +} + +const CountryLabel: React.FC = ({ country, showFlag = true }) => { + const { locale } = useIntl(); + + const fromISO = getCountryByISO(country, locale); + + return ( +
    + {showFlag && } + {fromISO ?? country} +
    + ); +}; + +export default CountryLabel; diff --git a/ui/v2.5/src/components/Shared/CountrySelect.tsx b/ui/v2.5/src/components/Shared/CountrySelect.tsx new file mode 100644 index 00000000000..e354279fd84 --- /dev/null +++ b/ui/v2.5/src/components/Shared/CountrySelect.tsx @@ -0,0 +1,51 @@ +import React from "react"; +import Creatable from "react-select/creatable"; +import { useIntl } from "react-intl"; +import { getCountries } from "src/utils"; +import CountryLabel from "./CountryLabel"; + +interface IProps { + value?: string | undefined; + onChange?: (value: string) => void; + disabled?: boolean; + className?: string; + showFlag?: boolean; + isClearable?: boolean; +} + +const CountrySelect: React.FC = ({ + value, + onChange, + disabled = false, + isClearable = true, + showFlag, + className, +}) => { + const { locale } = useIntl(); + const options = getCountries(locale); + const selected = options.find((opt) => opt.value === value) ?? { + label: value, + value, + }; + + return ( + ( + + )} + placeholder="Country" + options={options} + onChange={(selectedOption) => onChange?.(selectedOption?.value ?? "")} + isDisabled={disabled || !onChange} + components={{ + IndicatorSeparator: null, + }} + className={`CountrySelect ${className}`} + /> + ); +}; + +export default CountrySelect; diff --git a/ui/v2.5/src/components/Shared/ScrapeDialog.tsx b/ui/v2.5/src/components/Shared/ScrapeDialog.tsx index a81f86981d4..a6e78a2901d 100644 --- a/ui/v2.5/src/components/Shared/ScrapeDialog.tsx +++ b/ui/v2.5/src/components/Shared/ScrapeDialog.tsx @@ -20,6 +20,8 @@ import { faPlus, faTimes, } from "@fortawesome/free-solid-svg-icons"; +import { getCountryByISO } from "src/utils"; +import CountrySelect from "./CountrySelect"; export class ScrapeResult { public newValue?: T; @@ -392,3 +394,48 @@ export const ScrapeDialog: React.FC = ( ); }; + +interface IScrapedCountryRowProps { + title: string; + result: ScrapeResult; + onChange: (value: ScrapeResult) => void; + locked?: boolean; + locale?: string; +} + +export const ScrapedCountryRow: React.FC = ({ + title, + result, + onChange, + locked, + locale, +}) => ( + ( + + )} + renderNewField={() => ( + { + if (onChange) { + onChange(result.cloneWithValue(value)); + } + }} + showFlag={false} + isClearable={false} + className="flex-grow-1" + /> + )} + onChange={onChange} + /> +); diff --git a/ui/v2.5/src/components/Shared/index.ts b/ui/v2.5/src/components/Shared/index.ts index cfbaf804389..5997217fe59 100644 --- a/ui/v2.5/src/components/Shared/index.ts +++ b/ui/v2.5/src/components/Shared/index.ts @@ -21,4 +21,7 @@ export { default as DeleteEntityDialog } from "./DeleteEntityDialog"; export { IndeterminateCheckbox } from "./IndeterminateCheckbox"; export { OperationButton } from "./OperationButton"; export { URLField } from "./URLField"; +export { default as CountrySelect } from "./CountrySelect"; +export { default as CountryLabel } from "./CountryLabel"; + export const TITLE_SUFFIX = " | Stash"; diff --git a/ui/v2.5/src/components/Shared/styles.scss b/ui/v2.5/src/components/Shared/styles.scss index 67d5af7e568..9632de0c592 100644 --- a/ui/v2.5/src/components/Shared/styles.scss +++ b/ui/v2.5/src/components/Shared/styles.scss @@ -298,3 +298,11 @@ button.collapse-button.btn-primary:not(:disabled):not(.disabled):active { visibility: hidden; } } + +.CountrySelect { + /* stylelint-disable */ + .react-select__control:hover { + border: none; + } + /* stylelint-enable */ +} diff --git a/ui/v2.5/src/components/Tagger/PerformerModal.tsx b/ui/v2.5/src/components/Tagger/PerformerModal.tsx index e8cfdbb8353..e1613d2f5a7 100755 --- a/ui/v2.5/src/components/Tagger/PerformerModal.tsx +++ b/ui/v2.5/src/components/Tagger/PerformerModal.tsx @@ -132,7 +132,7 @@ const PerformerModal: React.FC = ({ birthdate: performer.birthdate, ethnicity: performer.ethnicity, eye_color: performer.eye_color, - country: getCountryByISO(performer.country), + country: performer.country, height: performer.height, measurements: performer.measurements, fake_tits: performer.fake_tits, diff --git a/ui/v2.5/src/docs/en/Changelog/v0180.md b/ui/v2.5/src/docs/en/Changelog/v0180.md index de1d97e9f4c..461050fd5df 100644 --- a/ui/v2.5/src/docs/en/Changelog/v0180.md +++ b/ui/v2.5/src/docs/en/Changelog/v0180.md @@ -1,4 +1,5 @@ ### ✨ New Features +* Added selector for Country field. ([#1922](https://github.com/stashapp/stash/pull/1922)) * Added tag description filter criterion. ([#3011](https://github.com/stashapp/stash/pull/3011)) ### 🐛 Bug fixes diff --git a/ui/v2.5/src/locales/countryNames/zh-TW.json b/ui/v2.5/src/locales/countryNames/zh-TW.json new file mode 100644 index 00000000000..b6fc33d0215 --- /dev/null +++ b/ui/v2.5/src/locales/countryNames/zh-TW.json @@ -0,0 +1,255 @@ +{ + "locale": "tw", + "countries": { + "AD": "安道爾", + "AE": "阿聯酋", + "AF": "阿富汗", + "AG": "安地卡及巴布達", + "AI": "安圭拉", + "AL": "阿爾巴尼亞", + "AM": "亞美尼亞", + "AO": "安哥拉", + "AQ": "南極洲", + "AR": "阿根廷", + "AS": "美屬薩摩亞", + "AT": "奧地利", + "AU": "澳大利亞", + "AW": "阿魯巴", + "AX": "奧蘭", + "AZ": "阿塞拜疆", + "BA": "波斯尼亞和黑塞哥維那", + "BB": "巴巴多斯", + "BD": "孟加拉國", + "BE": "比利時", + "BF": "布吉納法索", + "BG": "保加利亞", + "BH": "巴林", + "BI": "布隆迪", + "BJ": "貝寧", + "BL": "聖巴泰勒米", + "BM": "百慕大", + "BN": "文萊", + "BO": "玻利維亞", + "BQ": "加勒比荷蘭", + "BR": "巴西", + "BS": "巴哈馬", + "BT": "不丹", + "BV": "布韋島", + "BW": "博茨瓦納", + "BY": "白俄羅斯", + "BZ": "伯利茲", + "CA": "加拿大", + "CC": "科科斯(基林)群島", + "CD": "剛果(金)", + "CF": "中非", + "CG": "剛果(布)", + "CH": "瑞士", + "CI": "科特迪瓦", + "CK": "庫克群島", + "CL": "智利", + "CM": "喀麥隆", + "CN": "中國", + "CO": "哥倫比亞", + "CR": "哥斯達黎加", + "CU": "古巴", + "CV": "佛得角", + "CW": "庫拉索", + "CX": "聖誕島", + "CY": "賽普勒斯", + "CZ": "捷克", + "DE": "德國", + "DJ": "吉布提", + "DK": "丹麥", + "DM": "多米尼克", + "DO": "多米尼加", + "DZ": "阿爾及利亞", + "EC": "厄瓜多爾", + "EE": "愛沙尼亞", + "EG": "埃及", + "EH": "阿拉伯撒哈拉民主共和國", + "ER": "厄立特里亞", + "ES": "西班牙", + "ET": "衣索比亞", + "FI": "芬蘭", + "FJ": "斐濟", + "FK": "福克蘭群島", + "FM": "密克羅尼西亞聯邦", + "FO": "法羅群島", + "FR": "法國", + "GA": "加彭", + "GB": "英國", + "GD": "格瑞那達", + "GE": "格魯吉亞", + "GF": "法屬圭亞那", + "GG": "根西", + "GH": "加納", + "GI": "直布羅陀", + "GL": "格陵蘭", + "GM": "岡比亞", + "GN": "幾內亞", + "GP": "瓜德羅普", + "GQ": "赤道幾內亞", + "GR": "希臘", + "GS": "南喬治亞和南桑威奇群島", + "GT": "危地馬拉", + "GU": "關島", + "GW": "幾內亞比紹", + "GY": "圭亞那", + "HK": "香港", + "HM": "赫德島和麥克唐納群島", + "HN": "宏都拉斯", + "HR": "克羅地亞", + "HT": "海地", + "HU": "匈牙利", + "ID": "印尼", + "IE": "愛爾蘭", + "IL": "以色列", + "IM": "馬恩島", + "IN": "印度", + "IO": "英屬印度洋領地", + "IQ": "伊拉克", + "IR": "伊朗", + "IS": "冰島", + "IT": "意大利", + "JE": "澤西", + "JM": "牙買加", + "JO": "約旦", + "JP": "日本", + "KE": "肯尼亞", + "KG": "吉爾吉斯斯坦", + "KH": "柬埔寨", + "KI": "基里巴斯", + "KM": "科摩羅", + "KN": "聖基茨和尼維斯", + "KP": "朝鮮", + "KR": "韓國", + "KW": "科威特", + "KY": "開曼群島", + "KZ": "哈薩克斯坦", + "LA": "老撾", + "LB": "黎巴嫩", + "LC": "聖盧西亞", + "LI": "列支敦斯登", + "LK": "斯里蘭卡", + "LR": "利比里亞", + "LS": "賴索托", + "LT": "立陶宛", + "LU": "盧森堡", + "LV": "拉脫維亞", + "LY": "利比亞", + "MA": "摩洛哥", + "MC": "摩納哥", + "MD": "摩爾多瓦", + "ME": "蒙特內哥羅", + "MF": "法屬聖馬丁", + "MG": "馬達加斯加", + "MH": "馬紹爾群島", + "MK": "馬其頓", + "ML": "馬里", + "MM": "緬甸", + "MN": "蒙古", + "MO": "澳門", + "MP": "北馬里亞納群島", + "MQ": "馬提尼克", + "MR": "毛里塔尼亞", + "MS": "蒙特塞拉特", + "MT": "馬爾他", + "MU": "模里西斯", + "MV": "馬爾地夫", + "MW": "馬拉維", + "MX": "墨西哥", + "MY": "馬來西亞", + "MZ": "莫桑比克", + "NA": "納米比亞", + "NC": "新喀裡多尼亞", + "NE": "尼日爾", + "NF": "諾福克島", + "NG": "奈及利亞", + "NI": "尼加拉瓜", + "NL": "荷蘭", + "NO": "挪威", + "NP": "尼泊爾", + "NR": "瑙魯", + "NU": "紐埃", + "NZ": "新西蘭", + "OM": "阿曼", + "PA": "巴拿馬", + "PE": "秘魯", + "PF": "法屬玻里尼西亞", + "PG": "巴布亞新幾內亞", + "PH": "菲律賓", + "PK": "巴基斯坦", + "PL": "波蘭", + "PM": "聖皮埃爾和密克隆", + "PN": "皮特凱恩群島", + "PR": "波多黎各", + "PS": "巴勒斯坦", + "PT": "葡萄牙", + "PW": "帛琉", + "PY": "巴拉圭", + "QA": "卡塔爾", + "RE": "留尼汪", + "RO": "羅馬尼亞", + "RS": "塞爾維亞", + "RU": "俄羅斯", + "RW": "盧旺達", + "SA": "沙烏地阿拉伯", + "SB": "所羅門群島", + "SC": "塞舌爾", + "SD": "蘇丹", + "SE": "瑞典", + "SG": "新加坡", + "SH": "聖赫勒拿", + "SI": "斯洛維尼亞", + "SJ": "斯瓦爾巴群島和揚馬延島", + "SK": "斯洛伐克", + "SL": "塞拉利昂", + "SM": "聖馬力諾", + "SN": "塞內加爾", + "SO": "索馬利亞", + "SR": "蘇里南", + "SS": "南蘇丹", + "ST": "聖多美和普林西比", + "SV": "薩爾瓦多", + "SX": "荷屬聖馬丁", + "SY": "敘利亞", + "SZ": "斯威士蘭", + "TC": "特克斯和凱科斯群島", + "TD": "乍得", + "TF": "法屬南部領地", + "TG": "多哥", + "TH": "泰國", + "TJ": "塔吉克斯坦", + "TK": "托克勞", + "TL": "東帝汶", + "TM": "土庫曼斯坦", + "TN": "突尼西亞", + "TO": "湯加", + "TR": "土耳其", + "TT": "千里達及托巴哥", + "TV": "圖瓦盧", + "TW": "臺灣", + "TZ": "坦桑尼亞", + "UA": "烏克蘭", + "UG": "烏干達", + "UM": "美國本土外小島嶼", + "US": "美國", + "UY": "烏拉圭", + "UZ": "烏茲別克斯坦", + "VA": "梵蒂岡", + "VC": "聖文森及格瑞那丁", + "VE": "委內瑞拉", + "VG": "英屬維爾京群島", + "VI": "美屬維爾京群島", + "VN": "越南", + "VU": "瓦努阿圖", + "WF": "瓦利斯和富圖納", + "WS": "薩摩亞", + "YE": "葉門", + "YT": "馬約特", + "ZA": "南非", + "ZM": "尚比亞", + "ZW": "辛巴威", + "XK": "科索沃" + } +} diff --git a/ui/v2.5/src/locales/index.ts b/ui/v2.5/src/locales/index.ts index 23387cbd6f0..1124cb1bbeb 100644 --- a/ui/v2.5/src/locales/index.ts +++ b/ui/v2.5/src/locales/index.ts @@ -1,3 +1,40 @@ +import Countries from "i18n-iso-countries"; + +export const localeCountries = { + en: () => import("i18n-iso-countries/langs/en.json"), + da: () => import("i18n-iso-countries/langs/da.json"), + de: () => import("i18n-iso-countries/langs/de.json"), + es: () => import("i18n-iso-countries/langs/es.json"), + fi: () => import("i18n-iso-countries/langs/fi.json"), + fr: () => import("i18n-iso-countries/langs/fr.json"), + hr: () => import("i18n-iso-countries/langs/hr.json"), + it: () => import("i18n-iso-countries/langs/it.json"), + ja: () => import("i18n-iso-countries/langs/ja.json"), + ko: () => import("i18n-iso-countries/langs/ko.json"), + nl: () => import("i18n-iso-countries/langs/nl.json"), + pl: () => import("i18n-iso-countries/langs/pl.json"), + pt: () => import("i18n-iso-countries/langs/pt.json"), + ru: () => import("i18n-iso-countries/langs/ru.json"), + sv: () => import("i18n-iso-countries/langs/sv.json"), + tr: () => import("i18n-iso-countries/langs/tr.json"), + uk: () => import("i18n-iso-countries/langs/uk.json"), + zh: () => import("i18n-iso-countries/langs/zh.json"), + tw: () => import("src/locales/countryNames/zh-TW.json"), + // eslint-disable-next-line @typescript-eslint/no-explicit-any +} as { [key: string]: any }; + +export const getLocaleCode = (code: string) => { + if (code === "zh-CN") return "zh"; + if (code === "zh-TW") return "tw"; + return code.slice(0, 2); +}; + +export async function registerCountry(locale: string) { + const localeCode = getLocaleCode(locale); + const countries = await localeCountries[localeCode](); + Countries.registerLocale(countries); +} + export const localeLoader = { deDE: () => import("./de-DE.json"), enGB: () => import("./en-GB.json"), diff --git a/ui/v2.5/src/models/list-filter/criteria/country.ts b/ui/v2.5/src/models/list-filter/criteria/country.ts index b861b047482..56b67f6bbc5 100644 --- a/ui/v2.5/src/models/list-filter/criteria/country.ts +++ b/ui/v2.5/src/models/list-filter/criteria/country.ts @@ -1,3 +1,6 @@ +import { IntlShape } from "react-intl"; +import { CriterionModifier } from "src/core/generated-graphql"; +import { getCountryByISO } from "src/utils"; import { StringCriterion, StringCriterionOption } from "./criterion"; const countryCriterionOption = new StringCriterionOption( @@ -10,4 +13,15 @@ export class CountryCriterion extends StringCriterion { constructor() { super(countryCriterionOption); } + + public getLabelValue(intl: IntlShape) { + if ( + this.modifier === CriterionModifier.Equals || + this.modifier === CriterionModifier.NotEquals + ) { + return getCountryByISO(this.value, intl.locale) ?? this.value; + } + + return super.getLabelValue(intl); + } } diff --git a/ui/v2.5/src/models/list-filter/criteria/criterion.ts b/ui/v2.5/src/models/list-filter/criteria/criterion.ts index 7597874a166..3fdd843b42a 100644 --- a/ui/v2.5/src/models/list-filter/criteria/criterion.ts +++ b/ui/v2.5/src/models/list-filter/criteria/criterion.ts @@ -1,4 +1,5 @@ /* eslint-disable consistent-return */ +/* eslint @typescript-eslint/no-unused-vars: ["error", { "argsIgnorePattern": "^_" }] */ import { IntlShape } from "react-intl"; import { @@ -61,7 +62,7 @@ export abstract class Criterion { this._value = newValue; } - public abstract getLabelValue(): string; + public abstract getLabelValue(intl: IntlShape): string; constructor(type: CriterionOption, value: V) { this.criterionOption = type; @@ -85,7 +86,7 @@ export abstract class Criterion { this.modifier !== CriterionModifier.IsNull && this.modifier !== CriterionModifier.NotNull ) { - valueString = this.getLabelValue(); + valueString = this.getLabelValue(intl); } return intl.formatMessage( @@ -215,7 +216,7 @@ export class StringCriterion extends Criterion { super(type, ""); } - public getLabelValue() { + public getLabelValue(_intl: IntlShape) { return this.value; } } @@ -345,7 +346,7 @@ export class NumberCriterion extends Criterion { }; } - public getLabelValue() { + public getLabelValue(_intl: IntlShape) { const { value, value2 } = this.value; if ( this.modifier === CriterionModifier.Between || @@ -393,7 +394,7 @@ export class ILabeledIdCriterionOption extends CriterionOption { } export class ILabeledIdCriterion extends Criterion { - public getLabelValue(): string { + public getLabelValue(_intl: IntlShape): string { return this.value.map((v) => v.label).join(", "); } @@ -418,7 +419,7 @@ export class IHierarchicalLabeledIdCriterion extends Criterion v.label).join(", "); if (this.value.depth === 0) { @@ -478,7 +479,7 @@ export class DurationCriterion extends Criterion { }; } - public getLabelValue() { + public getLabelValue(_intl: IntlShape) { return this.modifier === CriterionModifier.Between || this.modifier === CriterionModifier.NotBetween ? `${DurationUtils.secondsToString( diff --git a/ui/v2.5/src/models/list-filter/criteria/factory.ts b/ui/v2.5/src/models/list-filter/criteria/factory.ts index 8fac6613ab1..6d0bc91c87b 100644 --- a/ui/v2.5/src/models/list-filter/criteria/factory.ts +++ b/ui/v2.5/src/models/list-filter/criteria/factory.ts @@ -44,6 +44,7 @@ import { InteractiveCriterion } from "./interactive"; import { RatingCriterionOption } from "./rating"; import { DuplicatedCriterion, PhashCriterionOption } from "./phash"; import { CaptionCriterion } from "./captions"; +import { CountryCriterion } from "./country"; export function makeCriteria(type: CriterionType = "none") { switch (type) { @@ -144,8 +145,9 @@ export function makeCriteria(type: CriterionType = "none") { return new StringCriterion(PhashCriterionOption); case "duplicated": return new DuplicatedCriterion(); - case "ethnicity": case "country": + return new CountryCriterion(); + case "ethnicity": case "hair_color": case "eye_color": case "height": diff --git a/ui/v2.5/src/utils/country.ts b/ui/v2.5/src/utils/country.ts index 68a087c10eb..b1cb2c6920b 100644 --- a/ui/v2.5/src/utils/country.ts +++ b/ui/v2.5/src/utils/country.ts @@ -1,41 +1,32 @@ import Countries from "i18n-iso-countries"; -import english from "i18n-iso-countries/langs/en.json"; - -Countries.registerLocale(english); - -const fuzzyDict: Record = { - USA: "US", - "United States": "US", - America: "US", - American: "US", - Czechia: "CZ", - England: "GB", - "United Kingdom": "GB", - Russia: "RU", - "Slovak Republic": "SK", - Iran: "IR", - Moldova: "MD", - Laos: "LA", +import { getLocaleCode } from "src/locales"; + +export const getCountryByISO = ( + iso: string | null | undefined, + locale: string = "en" +): string | undefined => { + if (!iso) return; + + const ret = Countries.getName(iso, getLocaleCode(locale)); + if (ret) { + return ret; + } + + // fallback to english if locale is not en + if (locale !== "en") { + return Countries.getName(iso, "en"); + } }; -const getISOCountry = (country: string | null | undefined) => { - if (!country) return null; - - const code = - fuzzyDict[country] ?? Countries.getAlpha2Code(country, "en") ?? country; - // Check if code is valid alpha2 iso - if (!Countries.alpha2ToAlpha3(code)) return null; +export const getCountries = (locale: string = "en") => { + let countries = Countries.getNames(getLocaleCode(locale)); - return { - code, - name: Countries.getName(code, "en"), - }; -}; - -export const getCountryByISO = (iso: string | null | undefined) => { - if (!iso) return null; + if (!countries.length) { + countries = Countries.getNames("en"); + } - return Countries.getName(iso, "en") ?? null; + return Object.entries(countries).map(([code, name]) => ({ + label: name, + value: code, + })); }; - -export default getISOCountry; diff --git a/ui/v2.5/src/utils/index.ts b/ui/v2.5/src/utils/index.ts index 262c020e9b5..d9bdc6c2484 100644 --- a/ui/v2.5/src/utils/index.ts +++ b/ui/v2.5/src/utils/index.ts @@ -8,9 +8,10 @@ export { default as FormUtils } from "./form"; export { default as DurationUtils } from "./duration"; export { default as SessionUtils } from "./session"; export { default as flattenMessages } from "./flattenMessages"; -export { default as getISOCountry } from "./country"; +export * from "./country"; export { default as useFocus } from "./focus"; export { default as downloadFile } from "./download"; export * from "./data"; export { getStashIDs } from "./stashIds"; export * from "./stashbox"; +export * from "./gender";