const { sprintf } = require('sprintf-js');

interface StringToStringMap {
	[key: string]: string;
}

interface CodeToCountryMap {
	[key: string]: string[];
}
type ParsePluralFormFunction = (n: number)=> number;

const codeToLanguageE_: StringToStringMap = {};
codeToLanguageE_['aa'] = 'Afar';
codeToLanguageE_['ab'] = 'Abkhazian';
codeToLanguageE_['af'] = 'Afrikaans';
codeToLanguageE_['am'] = 'Amharic';
codeToLanguageE_['an'] = 'Aragonese';
codeToLanguageE_['ar'] = 'Arabic';
codeToLanguageE_['as'] = 'Assamese';
codeToLanguageE_['ay'] = 'Aymara';
codeToLanguageE_['az'] = 'Azerbaijani';
codeToLanguageE_['ba'] = 'Bashkir';
codeToLanguageE_['be'] = 'Byelorussian';
codeToLanguageE_['bg'] = 'Bulgarian';
codeToLanguageE_['bh'] = 'Bihari';
codeToLanguageE_['bi'] = 'Bislama';
codeToLanguageE_['bn'] = 'Bangla';
codeToLanguageE_['bo'] = 'Tibetan';
codeToLanguageE_['br'] = 'Breton';
codeToLanguageE_['bs'] = 'Bosnian';
codeToLanguageE_['ca'] = 'Catalan';
codeToLanguageE_['co'] = 'Corsican';
codeToLanguageE_['cs'] = 'Czech';
codeToLanguageE_['cy'] = 'Welsh';
codeToLanguageE_['da'] = 'Danish';
codeToLanguageE_['de'] = 'German';
codeToLanguageE_['dz'] = 'Bhutani';
codeToLanguageE_['el'] = 'Greek';
codeToLanguageE_['en'] = 'English';
codeToLanguageE_['eo'] = 'Esperanto';
codeToLanguageE_['es'] = 'Spanish';
codeToLanguageE_['et'] = 'Estonian';
codeToLanguageE_['eu'] = 'Basque';
codeToLanguageE_['fa'] = 'Persian';
codeToLanguageE_['fi'] = 'Finnish';
codeToLanguageE_['fj'] = 'Fiji';
codeToLanguageE_['fo'] = 'Faroese';
codeToLanguageE_['fr'] = 'French';
codeToLanguageE_['fy'] = 'Frisian';
codeToLanguageE_['ga'] = 'Irish';
codeToLanguageE_['gd'] = 'Gaelic';
codeToLanguageE_['gl'] = 'Galician';
codeToLanguageE_['gn'] = 'Guarani';
codeToLanguageE_['gu'] = 'Gujarati';
codeToLanguageE_['ha'] = 'Hausa';
codeToLanguageE_['he'] = 'Hebrew';
codeToLanguageE_['hi'] = 'Hindi';
codeToLanguageE_['hr'] = 'Croatian';
codeToLanguageE_['hu'] = 'Hungarian';
codeToLanguageE_['hy'] = 'Armenian';
codeToLanguageE_['ia'] = 'Interlingua';
codeToLanguageE_['id'] = 'Indonesian';
codeToLanguageE_['ie'] = 'Interlingue';
codeToLanguageE_['ik'] = 'Inupiak';
codeToLanguageE_['is'] = 'Icelandic';
codeToLanguageE_['it'] = 'Italian';
codeToLanguageE_['iu'] = 'Inuktitut';
codeToLanguageE_['ja'] = 'Japanese';
codeToLanguageE_['jw'] = 'Javanese';
codeToLanguageE_['ka'] = 'Georgian';
codeToLanguageE_['kk'] = 'Kazakh';
codeToLanguageE_['kl'] = 'Greenlandic';
codeToLanguageE_['km'] = 'Cambodian';
codeToLanguageE_['kn'] = 'Kannada';
codeToLanguageE_['ko'] = 'Korean';
codeToLanguageE_['ks'] = 'Kashmiri';
codeToLanguageE_['ku'] = 'Kurdish';
codeToLanguageE_['ky'] = 'Kirghiz';
codeToLanguageE_['la'] = 'Latin';
codeToLanguageE_['ln'] = 'Lingala';
codeToLanguageE_['lo'] = 'Laothian';
codeToLanguageE_['lt'] = 'Lithuanian';
codeToLanguageE_['lv'] = 'Latvian';
codeToLanguageE_['mg'] = 'Malagasy';
codeToLanguageE_['mi'] = 'Maori';
codeToLanguageE_['mk'] = 'Macedonian';
codeToLanguageE_['ml'] = 'Malayalam';
codeToLanguageE_['mn'] = 'Mongolian';
codeToLanguageE_['mo'] = 'Moldavian';
codeToLanguageE_['mr'] = 'Marathi';
codeToLanguageE_['ms'] = 'Malay';
codeToLanguageE_['mt'] = 'Maltese';
codeToLanguageE_['my'] = 'Burmese';
codeToLanguageE_['na'] = 'Nauru';
codeToLanguageE_['nb'] = 'Norwegian';
codeToLanguageE_['ne'] = 'Nepali';
codeToLanguageE_['nl'] = 'Dutch';
codeToLanguageE_['no'] = 'Norwegian';
codeToLanguageE_['oc'] = 'Occitan';
codeToLanguageE_['om'] = 'Oromo';
codeToLanguageE_['or'] = 'Oriya';
codeToLanguageE_['pa'] = 'Punjabi';
codeToLanguageE_['pl'] = 'Polish';
codeToLanguageE_['ps'] = 'Pushto';
codeToLanguageE_['pt'] = 'Portuguese';
codeToLanguageE_['qu'] = 'Quechua';
codeToLanguageE_['rm'] = 'Rhaeto-Romance';
codeToLanguageE_['rn'] = 'Kirundi';
codeToLanguageE_['ro'] = 'Romanian';
codeToLanguageE_['ru'] = 'Russian';
codeToLanguageE_['rw'] = 'Kinyarwanda';
codeToLanguageE_['sa'] = 'Sanskrit';
codeToLanguageE_['sd'] = 'Sindhi';
codeToLanguageE_['sg'] = 'Sangho';
codeToLanguageE_['sh'] = 'Serbo-Croatian';
codeToLanguageE_['si'] = 'Sinhalese';
codeToLanguageE_['sk'] = 'Slovak';
codeToLanguageE_['sl'] = 'Slovenian';
codeToLanguageE_['sm'] = 'Samoan';
codeToLanguageE_['sn'] = 'Shona';
codeToLanguageE_['so'] = 'Somali';
codeToLanguageE_['sq'] = 'Albanian';
codeToLanguageE_['sr'] = 'Serbian';
codeToLanguageE_['ss'] = 'Siswati';
codeToLanguageE_['st'] = 'Sesotho';
codeToLanguageE_['su'] = 'Sundanese';
codeToLanguageE_['sv'] = 'Swedish';
codeToLanguageE_['sw'] = 'Swahili';
codeToLanguageE_['ta'] = 'Tamil';
codeToLanguageE_['te'] = 'Telugu';
codeToLanguageE_['tg'] = 'Tajik';
codeToLanguageE_['th'] = 'Thai';
codeToLanguageE_['ti'] = 'Tigrinya';
codeToLanguageE_['tk'] = 'Turkmen';
codeToLanguageE_['tl'] = 'Tagalog';
codeToLanguageE_['tn'] = 'Setswana';
codeToLanguageE_['to'] = 'Tonga';
codeToLanguageE_['tr'] = 'Turkish';
codeToLanguageE_['ts'] = 'Tsonga';
codeToLanguageE_['tt'] = 'Tatar';
codeToLanguageE_['tw'] = 'Twi';
codeToLanguageE_['ug'] = 'Uighur';
codeToLanguageE_['uk'] = 'Ukrainian';
codeToLanguageE_['ur'] = 'Urdu';
codeToLanguageE_['uz'] = 'Uzbek';
codeToLanguageE_['vi'] = 'Vietnamese';
codeToLanguageE_['vo'] = 'Volapuk';
codeToLanguageE_['wo'] = 'Wolof';
codeToLanguageE_['xh'] = 'Xhosa';
codeToLanguageE_['yi'] = 'Yiddish';
codeToLanguageE_['yo'] = 'Yoruba';
codeToLanguageE_['za'] = 'Zhuang';
codeToLanguageE_['zh'] = 'Chinese';
codeToLanguageE_['zu'] = 'Zulu';

const codeToLanguage_: StringToStringMap = {};
codeToLanguage_['an'] = 'Aragonés';
codeToLanguage_['da'] = 'Dansk';
codeToLanguage_['de'] = 'Deutsch';
codeToLanguage_['en'] = 'English';
codeToLanguage_['es'] = 'Español';
codeToLanguage_['fr'] = 'Français';
codeToLanguage_['he'] = 'עיברית';
codeToLanguage_['it'] = 'Italiano';
codeToLanguage_['lt'] = 'Lietuvių kalba';
codeToLanguage_['lv'] = 'Latviešu';
codeToLanguage_['nl'] = 'Nederlands';
codeToLanguage_['pl'] = 'Polski';
codeToLanguage_['pt'] = 'Português';
codeToLanguage_['ru'] = 'Русский';
codeToLanguage_['sk'] = 'Slovenčina';
codeToLanguage_['sq'] = 'Shqip';
codeToLanguage_['sr'] = 'српски језик';
codeToLanguage_['tr'] = 'Türkçe';
codeToLanguage_['ja'] = '日本語';
codeToLanguage_['ko'] = '한국어';
codeToLanguage_['sv'] = 'Svenska';
codeToLanguage_['el'] = 'Ελληνικά';
codeToLanguage_['zh'] = '中文';
codeToLanguage_['ro'] = 'Română';
codeToLanguage_['et'] = 'Eesti Keel';
codeToLanguage_['vi'] = 'Tiếng Việt';
codeToLanguage_['hu'] = 'Magyar';

const iso639Map_ = [
	['aar', 'aa'],
	['abk', 'ab'],
	['afr', 'af'],
	['aka', 'ak'],
	['amh', 'am'],
	['ara', 'ar'],
	['arg', 'an'],
	['asm', 'as'],
	['ava', 'av'],
	['ave', 'ae'],
	['aym', 'ay'],
	['aze', 'az'],
	['bak', 'ba'],
	['bam', 'bm'],
	['bel', 'be'],
	['ben', 'bn'],
	['bih', 'bh'],
	['bis', 'bi'],
	['bos', 'bs'],
	['bre', 'br'],
	['bul', 'bg'],
	['cat', 'ca'],
	['cha', 'ch'],
	['che', 'ce'],
	['chu', 'cu'],
	['chv', 'cv'],
	['cor', 'kw'],
	['cos', 'co'],
	['cre', 'cr'],
	['dan', 'da'],
	['div', 'dv'],
	['dzo', 'dz'],
	['eng', 'en'],
	['epo', 'eo'],
	['est', 'et'],
	['ewe', 'ee'],
	['fao', 'fo'],
	['fij', 'fj'],
	['fin', 'fi'],
	['fra', 'fr'],
	['fry', 'fy'],
	['ful', 'ff'],
	['gla', 'gd'],
	['gle', 'ga'],
	['glg', 'gl'],
	['glv', 'gv'],
	['grn', 'gn'],
	['guj', 'gu'],
	['hat', 'ht'],
	['hau', 'ha'],
	['heb', 'he'],
	['her', 'hz'],
	['hin', 'hi'],
	['hmo', 'ho'],
	['hrv', 'hr'],
	['hun', 'hu'],
	['ibo', 'ig'],
	['ido', 'io'],
	['iii', 'ii'],
	['iku', 'iu'],
	['ile', 'ie'],
	['ina', 'ia'],
	['ind', 'id'],
	['ipk', 'ik'],
	['ita', 'it'],
	['jav', 'jv'],
	['jpn', 'ja'],
	['kal', 'kl'],
	['kan', 'kn'],
	['kas', 'ks'],
	['kau', 'kr'],
	['kaz', 'kk'],
	['khm', 'km'],
	['kik', 'ki'],
	['kin', 'rw'],
	['kir', 'ky'],
	['kom', 'kv'],
	['kon', 'kg'],
	['kor', 'ko'],
	['kua', 'kj'],
	['kur', 'ku'],
	['lao', 'lo'],
	['lat', 'la'],
	['lav', 'lv'],
	['lim', 'li'],
	['lin', 'ln'],
	['lit', 'lt'],
	['ltz', 'lb'],
	['lub', 'lu'],
	['lug', 'lg'],
	['mah', 'mh'],
	['mal', 'ml'],
	['mar', 'mr'],
	['mlg', 'mg'],
	['mlt', 'mt'],
	['mon', 'mn'],
	['nau', 'na'],
	['nav', 'nv'],
	['nbl', 'nr'],
	['nde', 'nd'],
	['ndo', 'ng'],
	['nep', 'ne'],
	['nno', 'nn'],
	['nob', 'nb'],
	['nor', 'no'],
	['nya', 'ny'],
	['oci', 'oc'],
	['oji', 'oj'],
	['ori', 'or'],
	['orm', 'om'],
	['oss', 'os'],
	['pan', 'pa'],
	['pli', 'pi'],
	['pol', 'pl'],
	['por', 'pt'],
	['pus', 'ps'],
	['que', 'qu'],
	['roh', 'rm'],
	['run', 'rn'],
	['rus', 'ru'],
	['sag', 'sg'],
	['san', 'sa'],
	['sin', 'si'],
	['slv', 'sl'],
	['sme', 'se'],
	['smo', 'sm'],
	['sna', 'sn'],
	['snd', 'sd'],
	['som', 'so'],
	['sot', 'st'],
	['spa', 'es'],
	['srd', 'sc'],
	['srp', 'sr'],
	['ssw', 'ss'],
	['sun', 'su'],
	['swa', 'sw'],
	['swe', 'sv'],
	['tah', 'ty'],
	['tam', 'ta'],
	['tat', 'tt'],
	['tel', 'te'],
	['tgk', 'tg'],
	['tgl', 'tl'],
	['tha', 'th'],
	['tir', 'ti'],
	['ton', 'to'],
	['tsn', 'tn'],
	['tso', 'ts'],
	['tuk', 'tk'],
	['tur', 'tr'],
	['twi', 'tw'],
	['uig', 'ug'],
	['ukr', 'uk'],
	['urd', 'ur'],
	['uzb', 'uz'],
	['ven', 've'],
	['vie', 'vi'],
	['vol', 'vo'],
	['wln', 'wa'],
	['wol', 'wo'],
	['xho', 'xh'],
	['yid', 'yi'],
	['yor', 'yo'],
	['zha', 'za'],
	['zul', 'zu'],
];

const codeToCountry_: CodeToCountryMap = {
	AD: ['Andorra', 'Andorra'],
	AE: ['United Arab Emirates', 'دولة الإمارات العربيّة المتّحدة'],
	AF: ['Afghanistan', 'د افغانستان اسلامي دولتدولت اسلامی افغانستان, جمهوری اسلامی افغانستان'],
	AG: ['Antigua and Barbuda', 'Antigua and Barbuda'],
	AI: ['Anguilla', 'Anguilla'],
	AL: ['Albania', 'Shqipëria'],
	AM: ['Armenia', 'Հայաստան'],
	AO: ['Angola', 'Angola'],
	AQ: ['Antarctica', 'Antarctica, Antártico, Antarctique, Антарктике'],
	AR: ['Argentina', 'Argentina'],
	AS: ['American Samoa', 'American Samoa'],
	AT: ['Austria', 'Österreich'],
	AU: ['Australia', 'Australia'],
	AW: ['Aruba', 'Aruba'],
	AX: ['Aland Islands', 'Åland'],
	AZ: ['Azerbaijan', 'Azərbaycan'],
	BA: ['Bosnia and Herzegovina', 'Bosna i Hercegovina'],
	BB: ['Barbados', 'Barbados'],
	BD: ['Bangladesh', 'গণপ্রজাতন্ত্রী বাংলাদেশ'],
	BE: ['Belgium', 'België, Belgique, Belgien'],
	BF: ['Burkina Faso', 'Burkina Faso'],
	BG: ['Bulgaria', 'България'],
	BH: ['Bahrain', 'البحرين'],
	BI: ['Burundi', 'Burundi'],
	BJ: ['Benin', 'Bénin'],
	BL: ['Saint-Barthélemy', 'Saint-Barthélemy'],
	BM: ['Bermuda', 'Bermuda'],
	BN: ['Brunei Darussalam', 'Brunei Darussalam'],
	BO: ['Bolivia', 'Bolivia, Bulibiya, Volívia, Wuliwya'],
	BQ: ['Caribbean Netherlands', 'Caribisch Nederland'],
	BR: ['Brazil', 'Brasil'],
	BS: ['Bahamas', 'Bahamas'],
	BT: ['Bhutan', 'འབྲུག་ཡུལ'],
	BV: ['Bouvet Island', 'Bouvetøya'],
	BW: ['Botswana', 'Botswana'],
	BY: ['Belarus', 'Беларусь'],
	BZ: ['Belize', 'Belize'],
	CA: ['Canada', 'Canada'],
	CC: ['Cocos (Keeling) Islands', 'Cocos (Keeling) Islands'],
	CD: ['Democratic Republic of the Congo (Congo-Kinshasa, former Zaire)', 'République Démocratique du Congo'],
	CF: ['Centrafrican Republic', 'République centrafricaine, Ködörösêse tî Bêafrîka'],
	CG: ['Republic of the Congo (Congo-Brazzaville)', 'République du Congo'],
	CH: ['Switzerland', 'Schweiz, Suisse, Svizzera, Svizra'],
	CI: ['Côte d\'Ivoire', 'Côte d\'Ivoire'],
	CK: ['Cook Islands', 'Cook Islands, Kūki ʻĀirani'],
	CL: ['Chile', 'Chile'],
	CM: ['Cameroon', 'Cameroun, Cameroon'],
	CN: ['China', '中国'],
	CO: ['Colombia', 'Colombia'],
	CR: ['Costa Rica', 'Costa Rica'],
	CU: ['Cuba', 'Cuba'],
	CV: ['Cabo Verde', 'Cabo Verde'],
	CW: ['Curaçao', 'Curaçao'],
	CX: ['Christmas Island', 'Christmas Island'],
	CY: ['Cyprus', 'Κύπρος, Kibris'],
	CZ: ['Czech Republic', 'Česká republika'],
	DE: ['Germany', 'Deutschland'],
	DJ: ['Djibouti', 'Djibouti, جيبوتي, Jabuuti, Gabuutih'],
	DK: ['Denmark', 'Danmark'],
	DM: ['Dominica', 'Dominica'],
	DO: ['Dominican Republic', 'República Dominicana'],
	DZ: ['Algeria', 'الجزائر'],
	EC: ['Ecuador', 'Ecuador'],
	EE: ['Estonia', 'Eesti'],
	EG: ['Egypt', 'مصر'],
	EH: ['Western Sahara', 'Sahara Occidental'],
	ER: ['Eritrea', 'ኤርትራ, إرتريا, Eritrea'],
	ES: ['Spain', 'España'],
	ET: ['Ethiopia', 'ኢትዮጵያ, Itoophiyaa'],
	FI: ['Finland', 'Suomi'],
	FJ: ['Fiji', 'Fiji'],
	FK: ['Falkland Islands', 'Falkland Islands'],
	FM: ['Micronesia (Federated States of)', 'Micronesia'],
	FO: ['Faroe Islands', 'Føroyar, Færøerne'],
	FR: ['France', 'France'],
	GA: ['Gabon', 'Gabon'],
	GB: ['United Kingdom', 'United Kingdom'],
	GD: ['Grenada', 'Grenada'],
	GE: ['Georgia', 'საქართველო'],
	GF: ['French Guiana', 'Guyane française'],
	GG: ['Guernsey', 'Guernsey'],
	GH: ['Ghana', 'Ghana'],
	GI: ['Gibraltar', 'Gibraltar'],
	GL: ['Greenland', 'Kalaallit Nunaat, Grønland'],
	GM: ['The Gambia', 'The Gambia'],
	GN: ['Guinea', 'Guinée'],
	GP: ['Guadeloupe', 'Guadeloupe'],
	GQ: ['Equatorial Guinea', 'Guiena ecuatorial, Guinée équatoriale, Guiné Equatorial'],
	GR: ['Greece', 'Ελλάδα'],
	GS: ['South Georgia and the South Sandwich Islands', 'South Georgia and the South Sandwich Islands'],
	GT: ['Guatemala', 'Guatemala'],
	GU: ['Guam', 'Guam, Guåhån'],
	GW: ['Guinea Bissau', 'Guiné-Bissau'],
	GY: ['Guyana', 'Guyana'],
	HK: ['Hong Kong (SAR of China)', '香港, Hong Kong'],
	HM: ['Heard Island and McDonald Islands', 'Heard Island and McDonald Islands'],
	HN: ['Honduras', 'Honduras'],
	HR: ['Croatia', 'Hrvatska'],
	HT: ['Haiti', 'Haïti, Ayiti'],
	HU: ['Hungary', 'Magyarország'],
	ID: ['Indonesia', 'Indonesia'],
	IE: ['Ireland', 'Ireland, Éire'],
	IL: ['Israel', 'ישראל'],
	IM: ['Isle of Man', 'Isle of Man'],
	IN: ['India', 'भारत, India'],
	IO: ['British Indian Ocean Territory', 'British Indian Ocean Territory'],
	IQ: ['Iraq', 'العراق, Iraq'],
	IR: ['Iran', 'ایران'],
	IS: ['Iceland', 'Ísland'],
	IT: ['Italy', 'Italia'],
	JE: ['Jersey', 'Jersey'],
	JM: ['Jamaica', 'Jamaica'],
	JO: ['Jordan', 'الأُرْدُن'],
	JP: ['Japan', '日本'],
	KE: ['Kenya', 'Kenya'],
	KG: ['Kyrgyzstan', 'Кыргызстан, Киргизия'],
	KH: ['Cambodia', 'កម្ពុជា'],
	KI: ['Kiribati', 'Kiribati'],
	KM: ['Comores', 'ﺍﻟﻘﻤﺮي, Comores, Komori'],
	KN: ['Saint Kitts and Nevis', 'Saint Kitts and Nevis'],
	KP: ['North Korea', '북조선'],
	KR: ['South Korea', '대한민국'],
	KW: ['Kuwait', 'الكويت'],
	KY: ['Cayman Islands', 'Cayman Islands'],
	KZ: ['Kazakhstan', 'Қазақстан, Казахстан'],
	LA: ['Laos', 'ປະຊາຊົນລາວ'],
	LB: ['Lebanon', 'لبنان, Liban'],
	LC: ['Saint Lucia', 'Saint Lucia'],
	LI: ['Liechtenstein', 'Liechtenstein'],
	LK: ['Sri Lanka', 'ශ්‍රී ලංකා, இலங்கை'],
	LR: ['Liberia', 'Liberia'],
	LS: ['Lesotho', 'Lesotho'],
	LT: ['Lithuania', 'Lietuva'],
	LU: ['Luxembourg', 'Lëtzebuerg, Luxembourg, Luxemburg'],
	LV: ['Latvia', 'Latvija'],
	LY: ['Libya', 'ليبيا'],
	MA: ['Morocco', 'Maroc, ⵍⵎⵖⵔⵉⴱ, المغرب'],
	MC: ['Monaco', 'Monaco'],
	MD: ['Moldova', 'Moldova, Молдавия'],
	ME: ['Montenegro', 'Crna Gora, Црна Гора'],
	MF: ['Saint Martin (French part)', 'Saint-Martin'],
	MG: ['Madagascar', 'Madagasikara, Madagascar'],
	MH: ['Marshall Islands', 'Marshall Islands'],
	MK: ['North Macedonia', 'Северна Македонија'],
	ML: ['Mali', 'Mali'],
	MM: ['Myanmar', 'မြန်မာ'],
	MN: ['Mongolia', 'Монгол Улс'],
	MO: ['Macao (SAR of China)', '澳門, Macau'],
	MP: ['Northern Mariana Islands', 'Northern Mariana Islands'],
	MQ: ['Martinique', 'Martinique'],
	MR: ['Mauritania', 'موريتانيا, Mauritanie'],
	MS: ['Montserrat', 'Montserrat'],
	MT: ['Malta', 'Malta'],
	MU: ['Mauritius', 'Maurice, Mauritius'],
	MV: ['Maldives', ''],
	MW: ['Malawi', 'Malawi'],
	MX: ['Mexico', 'México'],
	MY: ['Malaysia', ''],
	MZ: ['Mozambique', 'Mozambique'],
	NA: ['Namibia', 'Namibia'],
	NC: ['New Caledonia', 'Nouvelle-Calédonie'],
	NE: ['Niger', 'Niger'],
	NF: ['Norfolk Island', 'Norfolk Island'],
	NG: ['Nigeria', 'Nigeria'],
	NI: ['Nicaragua', 'Nicaragua'],
	NL: ['The Netherlands', 'Nederland'],
	NO: ['Norway', 'Norge, Noreg'],
	NP: ['Nepal', ''],
	NR: ['Nauru', 'Nauru'],
	NU: ['Niue', 'Niue'],
	NZ: ['New Zealand', 'New Zealand'],
	OM: ['Oman', 'سلطنة عُمان'],
	PA: ['Panama', 'Panama'],
	PE: ['Peru', 'Perú'],
	PF: ['French Polynesia', 'Polynésie française'],
	PG: ['Papua New Guinea', 'Papua New Guinea'],
	PH: ['Philippines', 'Philippines'],
	PK: ['Pakistan', 'پاکستان'],
	PL: ['Poland', 'Polska'],
	PM: ['Saint Pierre and Miquelon', 'Saint-Pierre-et-Miquelon'],
	PN: ['Pitcairn', 'Pitcairn'],
	PR: ['Puerto Rico', 'Puerto Rico'],
	PS: ['Palestinian Territory', 'Palestinian Territory'],
	PT: ['Portugal', 'Portugal'],
	PW: ['Palau', 'Palau'],
	PY: ['Paraguay', 'Paraguay'],
	QA: ['Qatar', 'قطر'],
	RE: ['Reunion', 'La Réunion'],
	RO: ['Romania', 'România'],
	RS: ['Serbia', 'Србија'],
	RU: ['Russia', 'Россия'],
	RW: ['Rwanda', 'Rwanda'],
	SA: ['Saudi Arabia', 'السعودية'],
	SB: ['Solomon Islands', 'Solomon Islands'],
	SC: ['Seychelles', 'Seychelles'],
	SD: ['Sudan', 'السودان'],
	SE: ['Sweden', 'Sverige'],
	SG: ['Singapore', 'Singapore'],
	SH: ['Saint Helena', 'Saint Helena'],
	SI: ['Slovenia', 'Slovenija'],
	SJ: ['Svalbard and Jan Mayen', 'Svalbard and Jan Mayen'],
	SK: ['Slovakia', 'Slovensko'],
	SL: ['Sierra Leone', 'Sierra Leone'],
	SM: ['San Marino', 'San Marino'],
	SN: ['Sénégal', 'Sénégal'],
	SO: ['Somalia', 'Somalia, الصومال'],
	SR: ['Suriname', 'Suriname'],
	ST: ['São Tomé and Príncipe', 'São Tomé e Príncipe'],
	SS: ['South Sudan', 'South Sudan'],
	SV: ['El Salvador', 'El Salvador'],
	SX: ['Saint Martin (Dutch part)', 'Sint Maarten'],
	SY: ['Syria', 'سوريا, Sūriyya'],
	SZ: ['eSwatini', 'eSwatini'],
	TC: ['Turks and Caicos Islands', 'Turks and Caicos Islands'],
	TD: ['Chad', 'Tchad, تشاد'],
	TF: ['French Southern and Antarctic Lands', 'Terres australes et antarctiques françaises'],
	TG: ['Togo', 'Togo'],
	TH: ['Thailand', 'ประเทศไทย'],
	TJ: ['Tajikistan', ','],
	TK: ['Tokelau', 'Tokelau'],
	TL: ['Timor-Leste', 'Timor-Leste'],
	TM: ['Turkmenistan', 'Türkmenistan'],
	TN: ['Tunisia', 'تونس, Tunisie'],
	TO: ['Tonga', 'Tonga'],
	TR: ['Turkey', 'Türkiye'],
	TT: ['Trinidad and Tobago', 'Trinidad and Tobago'],
	TV: ['Tuvalu', 'Tuvalu'],
	TW: ['Taiwan', 'Taiwan'],
	TZ: ['Tanzania', 'Tanzania'],
	UA: ['Ukraine', 'Україна'],
	UG: ['Uganda', 'Uganda'],
	UM: ['United States Minor Outlying Islands', 'United States Minor Outlying Islands'],
	US: ['United States of America', 'United States of America'],
	UY: ['Uruguay', 'Uruguay'],
	UZ: ['Uzbekistan', ''],
	VA: ['City of the Vatican', 'Città del Vaticano'],
	VC: ['Saint Vincent and the Grenadines', 'Saint Vincent and the Grenadines'],
	VE: ['Venezuela', 'Venezuela'],
	VG: ['British Virgin Islands', 'British Virgin Islands'],
	VI: ['United States Virgin Islands', 'United States Virgin Islands'],
	VN: ['Vietnam', 'Việt Nam'],
	VU: ['Vanuatu', 'Vanuatu'],
	WF: ['Wallis and Futuna', 'Wallis-et-Futuna'],
	WS: ['Samoa', 'Samoa'],
	YE: ['Yemen', 'اليَمَن'],
	YT: ['Mayotte', 'Mayotte'],
	ZA: ['South Africa', 'South Africa'],
	ZM: ['Zambia', 'Zambia'],
	ZW: ['Zimbabwe', 'Zimbabwe'],
};

let supportedLocales_: any = null;
let localeStats_: any = null;

const loadedLocales_: Record<string, Record<string, string[]>> = {};

const pluralFunctions_: Record<string, ParsePluralFormFunction> = {};

const defaultLocale_ = 'en_GB';

let currentLocale_ = defaultLocale_;

// Copied from https://github.com/eugeny-dementev/parse-gettext-plural-form
// along with the tests
export const parsePluralForm = (form: string): ParsePluralFormFunction => {
	const pluralFormRegex = /^(\s*nplurals\s*=\s*[0-9]+\s*;\s*plural\s*=\s*(?:\s|[-?|&=!<>+*/%:;a-zA-Z0-9_()])+)$/m;

	if (!pluralFormRegex.test(form)) throw new Error(`Plural-Forms is invalid: ${form}`);

	if (!/;\s*$/.test(form)) {
		form += ';';
	}

	const code = [
		'var plural;',
		'var nplurals;',
		form,
		'return (plural === true ? 1 : plural ? plural : 0);',
	].join('\n');

	// eslint-disable-next-line no-new-func -- There's a regex to check the form but it's still slighlty unsafe, eventually we should automatically generate all the functions in advance in build-translations.ts
	return (new Function('n', code)) as ParsePluralFormFunction;
};

const getPluralFunction = (lang: string) => {
	if (!(lang in pluralFunctions_)) {
		const locale = closestSupportedLocale(lang);
		const stats = localeStats()[locale];

		if (!stats.pluralForms) {
			pluralFunctions_[lang] = null;
		} else {
			pluralFunctions_[lang] = parsePluralForm(stats.pluralForms);
		}
	}

	return pluralFunctions_[lang];
};

function defaultLocale() {
	return defaultLocale_;
}

function localeStats() {
	if (!localeStats_) localeStats_ = require('./locales/index.js').stats;
	return localeStats_;
}

function supportedLocales(): string[] {
	if (!supportedLocales_) supportedLocales_ = require('./locales/index.js').locales;

	const output = [];
	for (const n in supportedLocales_) {
		if (!supportedLocales_.hasOwnProperty(n)) continue;
		output.push(n);
	}
	return output;
}

interface SupportedLocalesToLanguagesOptions {
	includeStats?: boolean;
}

function supportedLocalesToLanguages(options: SupportedLocalesToLanguagesOptions = null) {
	if (!options) options = {};
	const stats = localeStats();
	const locales = supportedLocales();
	const output: StringToStringMap = {};
	for (let i = 0; i < locales.length; i++) {
		const locale = locales[i];
		output[locale] = countryDisplayName(locale);

		const stat = stats[locale];
		if (options.includeStats && stat) {
			output[locale] += ` (${stat.percentDone}%)`;
		}
	}
	return output;
}

function closestSupportedLocale(canonicalName: string, defaultToEnglish = true, locales: string[] = null) {
	locales = locales === null ? supportedLocales() : locales;
	if (locales.indexOf(canonicalName) >= 0) return canonicalName;

	const requiredLanguage = languageCodeOnly(canonicalName).toLowerCase();

	for (let i = 0; i < locales.length; i++) {
		const locale = locales[i];
		const language = locale.split('_')[0];
		if (requiredLanguage === language) return locale;
	}

	return defaultToEnglish ? 'en_GB' : null;
}

function countryName(countryCode: string) {
	const r = codeToCountry_[countryCode] ? codeToCountry_[countryCode] : null;
	if (!r) return '';
	return r.length > 1 && !!r[1] ? r[1] : r[0];
}

function languageNameInEnglish(languageCode: string) {
	return codeToLanguageE_[languageCode] ? codeToLanguageE_[languageCode] : '';
}

function languageName(canonicalName: string, defaultToEnglish = true) {
	const languageCode = languageCodeOnly(canonicalName);
	if (codeToLanguage_[languageCode]) return codeToLanguage_[languageCode];
	if (defaultToEnglish) return languageNameInEnglish(languageCode);
	return '';
}

function languageCodeOnly(canonicalName: string) {
	if (canonicalName.length < 2) return canonicalName;
	return canonicalName.substr(0, 2);
}

function countryCodeOnly(canonicalName: string) {
	if (canonicalName.length <= 2) return '';
	return canonicalName.substr(3);
}

function countryDisplayName(canonicalName: string) {
	const languageCode = languageCodeOnly(canonicalName);
	const countryCode = countryCodeOnly(canonicalName);

	let output = languageName(languageCode);

	let extraString;

	if (countryCode) {
		if (languageCode === 'zh' && countryCode === 'CN') {
			extraString = '简体'; // "Simplified" in "Simplified Chinese"
		} else {
			extraString = countryName(countryCode);
		}
	}

	if (languageCode === 'zh' && (countryCode === '' || countryCode === 'TW')) extraString = '繁體'; // "Traditional" in "Traditional Chinese"

	if (extraString) {
		output += ` (${extraString})`;
	} else if (countryCode) {
		// If we have a country code but couldn't match it to a country name,
		// just display the full canonical name (applies for example to es-419
		// for Latin American Spanish).
		output += ` (${canonicalName})`;
	}

	return output;
}

function localeStrings(canonicalName: string) {
	const locale = closestSupportedLocale(canonicalName);

	if (loadedLocales_[locale]) return loadedLocales_[locale];

	loadedLocales_[locale] = { ...supportedLocales_[locale] };

	return loadedLocales_[locale];
}

const currentLocale = () => {
	return currentLocale_;
};

function setLocale(canonicalName: string) {
	if (currentLocale_ === canonicalName) return;
	currentLocale_ = closestSupportedLocale(canonicalName);
}

function languageCode() {
	return languageCodeOnly(currentLocale_);
}

function localesFromLanguageCode(languageCode: string, locales: string[]): string[] {
	return locales.filter((l: string) => {
		return languageCodeOnly(l) === languageCode;
	});
}

export const toIso639 = (code: string) => {
	if (code.includes('_')) {
		const s = code.split('_');
		code = s[0];
	}
	const line = iso639Map_.find(l => l[1] === code);
	if (!line) throw new Error(`Cannot convert to ISO-639 code: ${code}`);
	return line[0];
};

function _(s: string, ...args: any[]): string {
	return stringByLocale(currentLocale_, s, ...args);
}

function _n(singular: string, plural: string, n: number, ...args: any[]) {
	if (['en_GB', 'en_US'].includes(currentLocale_)) {
		if (n > 1) return _(plural, ...args);
		return _(singular, ...args);
	} else {
		const pluralFn = getPluralFunction(currentLocale_);
		const stringIndex = pluralFn ? pluralFn(n) : 0;
		const strings = localeStrings(currentLocale_);
		const result = strings[singular];

		let translatedString = '';
		if (result === undefined || !result.join('')) {
			translatedString = singular;
		} else {
			translatedString = stringIndex < result.length ? result[stringIndex] : result[0];
		}

		try {
			return sprintf(translatedString, ...args);
		} catch (error) {
			return `${translatedString} ${args.join(', ')} (Translation error: ${error.message})`;
		}
	}
}

const stringByLocale = (locale: string, s: string, ...args: any[]): string => {
	const strings = localeStrings(locale);
	const result = strings[s];
	let translatedString = '';

	if (result === undefined || !result.join('')) {
		translatedString = s;
	} else {
		translatedString = result[0];
	}

	try {
		return sprintf(translatedString, ...args);
	} catch (error) {
		return `${translatedString} ${args.join(', ')} (Translation error: ${error.message})`;
	}
};

export { _, _n, supportedLocales, languageName, currentLocale, localesFromLanguageCode, languageCodeOnly, countryDisplayName, localeStrings, setLocale, supportedLocalesToLanguages, defaultLocale, closestSupportedLocale, languageCode, countryCodeOnly };
