File indexing completed on 2024-12-08 09:38:10

0001 /*
0002     This file is part of the kholidays library.
0003 
0004     SPDX-FileCopyrightText: 2001 Cornelius Schumacher <schumacher@kde.org>
0005     SPDX-FileCopyrightText: 2004-2020 Allen Winter <winter@kde.org>
0006     SPDX-FileCopyrightText: 2008 David Jarvie <djarvie@kde.org>
0007     SPDX-FileCopyrightText: 2010 John Layt <john@layt.net>
0008 
0009     The functions languageToCode(), codeToLanguage(), countryToCode() and codeToCounty() are copyright by
0010     SPDX-FileCopyrightText: 2014 Digia Plc and/or its subsidiary(-ies).
0011 
0012     SPDX-License-Identifier: LGPL-2.0-or-later
0013 */
0014 
0015 #include "holidayregion.h"
0016 
0017 #include <QCoreApplication>
0018 #include <QDirIterator>
0019 #include <QFile>
0020 #include <QFileInfo>
0021 #include <QLocale>
0022 #include <QSharedData>
0023 #include <QStandardPaths>
0024 
0025 #include "holiday_p.h"
0026 #include "parsers/plan2/holidayparserdriverplan_p.h"
0027 
0028 using namespace KHolidays;
0029 
0030 // Copied from qlocale_data_p.h and qlocale.h, see copyright information at top of file
0031 // To later be replaced by OpenCodes or public Qt api
0032 static const unsigned char country_code_list[] =
0033     "ZZ\0" // AnyCountry
0034     "AF\0" // Afghanistan
0035     "AL\0" // Albania
0036     "DZ\0" // Algeria
0037     "AS\0" // AmericanSamoa
0038     "AD\0" // Andorra
0039     "AO\0" // Angola
0040     "AI\0" // Anguilla
0041     "AQ\0" // Antarctica
0042     "AG\0" // AntiguaAndBarbuda
0043     "AR\0" // Argentina
0044     "AM\0" // Armenia
0045     "AW\0" // Aruba
0046     "AU\0" // Australia
0047     "AT\0" // Austria
0048     "AZ\0" // Azerbaijan
0049     "BS\0" // Bahamas
0050     "BH\0" // Bahrain
0051     "BD\0" // Bangladesh
0052     "BB\0" // Barbados
0053     "BY\0" // Belarus
0054     "BE\0" // Belgium
0055     "BZ\0" // Belize
0056     "BJ\0" // Benin
0057     "BM\0" // Bermuda
0058     "BT\0" // Bhutan
0059     "BO\0" // Bolivia
0060     "BA\0" // BosniaAndHerzegowina
0061     "BW\0" // Botswana
0062     "BV\0" // BouvetIsland
0063     "BR\0" // Brazil
0064     "IO\0" // BritishIndianOceanTerritory
0065     "BN\0" // Brunei
0066     "BG\0" // Bulgaria
0067     "BF\0" // BurkinaFaso
0068     "BI\0" // Burundi
0069     "KH\0" // Cambodia
0070     "CM\0" // Cameroon
0071     "CA\0" // Canada
0072     "CV\0" // CapeVerde
0073     "KY\0" // CaymanIslands
0074     "CF\0" // CentralAfricanRepublic
0075     "TD\0" // Chad
0076     "CL\0" // Chile
0077     "CN\0" // China
0078     "CX\0" // ChristmasIsland
0079     "CC\0" // CocosIslands
0080     "CO\0" // Colombia
0081     "KM\0" // Comoros
0082     "CD\0" // CongoKinshasa
0083     "CG\0" // CongoBrazzaville
0084     "CK\0" // CookIslands
0085     "CR\0" // CostaRica
0086     "CI\0" // IvoryCoast
0087     "HR\0" // Croatia
0088     "CU\0" // Cuba
0089     "CY\0" // Cyprus
0090     "CZ\0" // CzechRepublic
0091     "DK\0" // Denmark
0092     "DJ\0" // Djibouti
0093     "DM\0" // Dominica
0094     "DO\0" // DominicanRepublic
0095     "TL\0" // EastTimor
0096     "EC\0" // Ecuador
0097     "EG\0" // Egypt
0098     "SV\0" // ElSalvador
0099     "GQ\0" // EquatorialGuinea
0100     "ER\0" // Eritrea
0101     "EE\0" // Estonia
0102     "ET\0" // Ethiopia
0103     "FK\0" // FalklandIslands
0104     "FO\0" // FaroeIslands
0105     "FJ\0" // Fiji
0106     "FI\0" // Finland
0107     "FR\0" // France
0108     "GG\0" // Guernsey
0109     "GF\0" // FrenchGuiana
0110     "PF\0" // FrenchPolynesia
0111     "TF\0" // FrenchSouthernTerritories
0112     "GA\0" // Gabon
0113     "GM\0" // Gambia
0114     "GE\0" // Georgia
0115     "DE\0" // Germany
0116     "GH\0" // Ghana
0117     "GI\0" // Gibraltar
0118     "GR\0" // Greece
0119     "GL\0" // Greenland
0120     "GD\0" // Grenada
0121     "GP\0" // Guadeloupe
0122     "GU\0" // Guam
0123     "GT\0" // Guatemala
0124     "GN\0" // Guinea
0125     "GW\0" // GuineaBissau
0126     "GY\0" // Guyana
0127     "HT\0" // Haiti
0128     "HM\0" // HeardAndMcDonaldIslands
0129     "HN\0" // Honduras
0130     "HK\0" // HongKong
0131     "HU\0" // Hungary
0132     "IS\0" // Iceland
0133     "IN\0" // India
0134     "ID\0" // Indonesia
0135     "IR\0" // Iran
0136     "IQ\0" // Iraq
0137     "IE\0" // Ireland
0138     "IL\0" // Israel
0139     "IT\0" // Italy
0140     "JM\0" // Jamaica
0141     "JP\0" // Japan
0142     "JO\0" // Jordan
0143     "KZ\0" // Kazakhstan
0144     "KE\0" // Kenya
0145     "KI\0" // Kiribati
0146     "KP\0" // NorthKorea
0147     "KR\0" // SouthKorea
0148     "KW\0" // Kuwait
0149     "KG\0" // Kyrgyzstan
0150     "LA\0" // Laos
0151     "LV\0" // Latvia
0152     "LB\0" // Lebanon
0153     "LS\0" // Lesotho
0154     "LR\0" // Liberia
0155     "LY\0" // Libya
0156     "LI\0" // Liechtenstein
0157     "LT\0" // Lithuania
0158     "LU\0" // Luxembourg
0159     "MO\0" // Macau
0160     "MK\0" // Macedonia
0161     "MG\0" // Madagascar
0162     "MW\0" // Malawi
0163     "MY\0" // Malaysia
0164     "MV\0" // Maldives
0165     "ML\0" // Mali
0166     "MT\0" // Malta
0167     "MH\0" // MarshallIslands
0168     "MQ\0" // Martinique
0169     "MR\0" // Mauritania
0170     "MU\0" // Mauritius
0171     "YT\0" // Mayotte
0172     "MX\0" // Mexico
0173     "FM\0" // Micronesia
0174     "MD\0" // Moldova
0175     "MC\0" // Monaco
0176     "MN\0" // Mongolia
0177     "MS\0" // Montserrat
0178     "MA\0" // Morocco
0179     "MZ\0" // Mozambique
0180     "MM\0" // Myanmar
0181     "NA\0" // Namibia
0182     "NR\0" // Nauru
0183     "NP\0" // Nepal
0184     "NL\0" // Netherlands
0185     "CW\0" // CuraSao
0186     "NC\0" // NewCaledonia
0187     "NZ\0" // NewZealand
0188     "NI\0" // Nicaragua
0189     "NE\0" // Niger
0190     "NG\0" // Nigeria
0191     "NU\0" // Niue
0192     "NF\0" // NorfolkIsland
0193     "MP\0" // NorthernMarianaIslands
0194     "NO\0" // Norway
0195     "OM\0" // Oman
0196     "PK\0" // Pakistan
0197     "PW\0" // Palau
0198     "PS\0" // PalestinianTerritories
0199     "PA\0" // Panama
0200     "PG\0" // PapuaNewGuinea
0201     "PY\0" // Paraguay
0202     "PE\0" // Peru
0203     "PH\0" // Philippines
0204     "PN\0" // Pitcairn
0205     "PL\0" // Poland
0206     "PT\0" // Portugal
0207     "PR\0" // PuertoRico
0208     "QA\0" // Qatar
0209     "RE\0" // Reunion
0210     "RO\0" // Romania
0211     "RU\0" // Russia
0212     "RW\0" // Rwanda
0213     "KN\0" // SaintKittsAndNevis
0214     "LC\0" // SaintLucia
0215     "VC\0" // SaintVincentAndTheGrenadines
0216     "WS\0" // Samoa
0217     "SM\0" // SanMarino
0218     "ST\0" // SaoTomeAndPrincipe
0219     "SA\0" // SaudiArabia
0220     "SN\0" // Senegal
0221     "SC\0" // Seychelles
0222     "SL\0" // SierraLeone
0223     "SG\0" // Singapore
0224     "SK\0" // Slovakia
0225     "SI\0" // Slovenia
0226     "SB\0" // SolomonIslands
0227     "SO\0" // Somalia
0228     "ZA\0" // SouthAfrica
0229     "GS\0" // SouthGeorgiaAndTheSouthSandwichIslands
0230     "ES\0" // Spain
0231     "LK\0" // SriLanka
0232     "SH\0" // SaintHelena
0233     "PM\0" // SaintPierreAndMiquelon
0234     "SD\0" // Sudan
0235     "SR\0" // Suriname
0236     "SJ\0" // SvalbardAndJanMayenIslands
0237     "SZ\0" // Swaziland
0238     "SE\0" // Sweden
0239     "CH\0" // Switzerland
0240     "SY\0" // Syria
0241     "TW\0" // Taiwan
0242     "TJ\0" // Tajikistan
0243     "TZ\0" // Tanzania
0244     "TH\0" // Thailand
0245     "TG\0" // Togo
0246     "TK\0" // Tokelau
0247     "TO\0" // Tonga
0248     "TT\0" // TrinidadAndTobago
0249     "TN\0" // Tunisia
0250     "TR\0" // Turkey
0251     "TM\0" // Turkmenistan
0252     "TC\0" // TurksAndCaicosIslands
0253     "TV\0" // Tuvalu
0254     "UG\0" // Uganda
0255     "UA\0" // Ukraine
0256     "AE\0" // UnitedArabEmirates
0257     "GB\0" // UnitedKingdom
0258     "US\0" // UnitedStates
0259     "UM\0" // UnitedStatesMinorOutlyingIslands
0260     "UY\0" // Uruguay
0261     "UZ\0" // Uzbekistan
0262     "VU\0" // Vanuatu
0263     "VA\0" // VaticanCityState
0264     "VE\0" // Venezuela
0265     "VN\0" // Vietnam
0266     "VG\0" // BritishVirginIslands
0267     "VI\0" // UnitedStatesVirginIslands
0268     "WF\0" // WallisAndFutunaIslands
0269     "EH\0" // WesternSahara
0270     "YE\0" // Yemen
0271     "IC\0" // CanaryIslands
0272     "ZM\0" // Zambia
0273     "ZW\0" // Zimbabwe
0274     "CP\0" // ClippertonIsland
0275     "ME\0" // Montenegro
0276     "RS\0" // Serbia
0277     "BL\0" // Saint Barthelemy
0278     "MF\0" // Saint Martin
0279     "419" // LatinAmericaAndTheCaribbean
0280     "AC\0" // AscensionIsland
0281     "AX\0" // AlandIslands
0282     "DG\0" // DiegoGarcia
0283     "EA\0" // CeutaAndMelilla
0284     "IM\0" // IsleOfMan
0285     "JE\0" // Jersey
0286     "TA\0" // TristanDaCunha
0287     "SS\0" // SouthSudan
0288     "BQ\0" // Bonaire
0289     "SX\0" // SintMaarten
0290     "XK\0" // Kosovo
0291     ;
0292 
0293 static const unsigned char language_code_list[] =
0294     "  \0" // AnyLanguage
0295     "  \0" // C
0296     "ab\0" // Abkhazian
0297     "om\0" // Oromo
0298     "aa\0" // Afar
0299     "af\0" // Afrikaans
0300     "sq\0" // Albanian
0301     "am\0" // Amharic
0302     "ar\0" // Arabic
0303     "hy\0" // Armenian
0304     "as\0" // Assamese
0305     "ay\0" // Aymara
0306     "az\0" // Azerbaijani
0307     "ba\0" // Bashkir
0308     "eu\0" // Basque
0309     "bn\0" // Bengali
0310     "dz\0" // Dzongkha
0311     "bh\0" // Bihari
0312     "bi\0" // Bislama
0313     "br\0" // Breton
0314     "bg\0" // Bulgarian
0315     "my\0" // Burmese
0316     "be\0" // Belarusian
0317     "km\0" // Khmer
0318     "ca\0" // Catalan
0319     "zh\0" // Chinese
0320     "co\0" // Corsican
0321     "hr\0" // Croatian
0322     "cs\0" // Czech
0323     "da\0" // Danish
0324     "nl\0" // Dutch
0325     "en\0" // English
0326     "eo\0" // Esperanto
0327     "et\0" // Estonian
0328     "fo\0" // Faroese
0329     "fj\0" // Fijian
0330     "fi\0" // Finnish
0331     "fr\0" // French
0332     "fy\0" // Western Frisian
0333     "gd\0" // Gaelic
0334     "gl\0" // Galician
0335     "ka\0" // Georgian
0336     "de\0" // German
0337     "el\0" // Greek
0338     "kl\0" // Greenlandic
0339     "gn\0" // Guarani
0340     "gu\0" // Gujarati
0341     "ha\0" // Hausa
0342     "he\0" // Hebrew
0343     "hi\0" // Hindi
0344     "hu\0" // Hungarian
0345     "is\0" // Icelandic
0346     "id\0" // Indonesian
0347     "ia\0" // Interlingua
0348     "ie\0" // Interlingue
0349     "iu\0" // Inuktitut
0350     "ik\0" // Inupiak
0351     "ga\0" // Irish
0352     "it\0" // Italian
0353     "ja\0" // Japanese
0354     "jv\0" // Javanese
0355     "kn\0" // Kannada
0356     "ks\0" // Kashmiri
0357     "kk\0" // Kazakh
0358     "rw\0" // Kinyarwanda
0359     "ky\0" // Kirghiz
0360     "ko\0" // Korean
0361     "ku\0" // Kurdish
0362     "rn\0" // Rundi
0363     "lo\0" // Lao
0364     "la\0" // Latin
0365     "lv\0" // Latvian
0366     "ln\0" // Lingala
0367     "lt\0" // Lithuanian
0368     "mk\0" // Macedonian
0369     "mg\0" // Malagasy
0370     "ms\0" // Malay
0371     "ml\0" // Malayalam
0372     "mt\0" // Maltese
0373     "mi\0" // Maori
0374     "mr\0" // Marathi
0375     "mh\0" // Marshallese
0376     "mn\0" // Mongolian
0377     "na\0" // Nauru
0378     "ne\0" // Nepali
0379     "nb\0" // NorwegianBokmal
0380     "oc\0" // Occitan
0381     "or\0" // Oriya
0382     "ps\0" // Pashto
0383     "fa\0" // Persian
0384     "pl\0" // Polish
0385     "pt\0" // Portuguese
0386     "pa\0" // Punjabi
0387     "qu\0" // Quechua
0388     "rm\0" // Romansh
0389     "ro\0" // Romanian
0390     "ru\0" // Russian
0391     "sm\0" // Samoan
0392     "sg\0" // Sango
0393     "sa\0" // Sanskrit
0394     "sr\0" // Serbian
0395     "os\0" // Ossetic
0396     "st\0" // Southern Sotho
0397     "tn\0" // Tswana
0398     "sn\0" // Shona
0399     "sd\0" // Sindhi
0400     "si\0" // Sinhala
0401     "ss\0" // Swati
0402     "sk\0" // Slovak
0403     "sl\0" // Slovenian
0404     "so\0" // Somali
0405     "es\0" // Spanish
0406     "su\0" // Sundanese
0407     "sw\0" // Swahili
0408     "sv\0" // Swedish
0409     "sc\0" // Sardinian
0410     "tg\0" // Tajik
0411     "ta\0" // Tamil
0412     "tt\0" // Tatar
0413     "te\0" // Telugu
0414     "th\0" // Thai
0415     "bo\0" // Tibetan
0416     "ti\0" // Tigrinya
0417     "to\0" // Tongan
0418     "ts\0" // Tsonga
0419     "tr\0" // Turkish
0420     "tk\0" // Turkmen
0421     "ty\0" // Tahitian
0422     "ug\0" // Uighur
0423     "uk\0" // Ukrainian
0424     "ur\0" // Urdu
0425     "uz\0" // Uzbek
0426     "vi\0" // Vietnamese
0427     "vo\0" // Volapuk
0428     "cy\0" // Welsh
0429     "wo\0" // Wolof
0430     "xh\0" // Xhosa
0431     "yi\0" // Yiddish
0432     "yo\0" // Yoruba
0433     "za\0" // Zhuang
0434     "zu\0" // Zulu
0435     "nn\0" // NorwegianNynorsk
0436     "bs\0" // Bosnian
0437     "dv\0" // Divehi
0438     "gv\0" // Manx
0439     "kw\0" // Cornish
0440     "ak\0" // Akan
0441     "kok" // Konkani
0442     "gaa" // Ga
0443     "ig\0" // Igbo
0444     "kam" // Kamba
0445     "syr" // Syriac
0446     "byn" // Blin
0447     "gez" // Geez
0448     "kfo" // Koro
0449     "sid" // Sidamo
0450     "cch" // Atsam
0451     "tig" // Tigre
0452     "kaj" // Jju
0453     "fur" // Friulian
0454     "ve\0" // Venda
0455     "ee\0" // Ewe
0456     "wal" // Walamo
0457     "haw" // Hawaiian
0458     "kcg" // Tyap
0459     "ny\0" // Nyanja
0460     "fil" // Filipino
0461     "gsw" // Swiss German
0462     "ii\0" // Sichuan Yi
0463     "kpe" // Kpelle
0464     "nds" // Low German
0465     "nr\0" // South Ndebele
0466     "nso" // Northern Sotho
0467     "se\0" // Northern Sami
0468     "trv" // Taroko
0469     "guz" // Gusii
0470     "dav" // Taita
0471     "ff\0" // Fulah
0472     "ki\0" // Kikuyu
0473     "saq" // Samburu
0474     "seh" // Sena
0475     "nd\0" // North Ndebele
0476     "rof" // Rombo
0477     "shi" // Tachelhit
0478     "kab" // Kabyle
0479     "nyn" // Nyankole
0480     "bez" // Bena
0481     "vun" // Vunjo
0482     "bm\0" // Bambara
0483     "ebu" // Embu
0484     "chr" // Cherokee
0485     "mfe" // Morisyen
0486     "kde" // Makonde
0487     "lag" // Langi
0488     "lg\0" // Ganda
0489     "bem" // Bemba
0490     "kea" // Kabuverdianu
0491     "mer" // Meru
0492     "kln" // Kalenjin
0493     "naq" // Nama
0494     "jmc" // Machame
0495     "ksh" // Colognian
0496     "mas" // Masai
0497     "xog" // Soga
0498     "luy" // Luyia
0499     "asa" // Asu
0500     "teo" // Teso
0501     "ssy" // Saho
0502     "khq" // Koyra Chiini
0503     "rwk" // Rwa
0504     "luo" // Luo
0505     "cgg" // Chiga
0506     "tzm" // Central Morocco Tamazight
0507     "ses" // Koyraboro Senni
0508     "ksb" // Shambala
0509     "brx" // Bodo
0510     "av\0" // Avaric
0511     "ch\0" // Chamorro
0512     "ce\0" // Chechen
0513     "cu\0" // Church
0514     "cv\0" // Chuvash
0515     "cr\0" // Cree
0516     "ht\0" // Haitian
0517     "hz\0" // Herero
0518     "ho\0" // Hiri Motu
0519     "kr\0" // Kanuri
0520     "kv\0" // Komi
0521     "kg\0" // Kongo
0522     "kj\0" // Kwanyama
0523     "li\0" // Limburgish
0524     "lu\0" // LubaKatanga
0525     "lb\0" // Luxembourgish
0526     "nv\0" // Navaho
0527     "ng\0" // Ndonga
0528     "oj\0" // Ojibwa
0529     "pi\0" // Pali
0530     "wa\0" // Walloon
0531     "agq" // Aghem
0532     "bas" // Basaa
0533     "dje" // Zarma
0534     "dua" // Duala
0535     "dyo" // JolaFonyi
0536     "ewo" // Ewondo
0537     "ksf" // Bafia
0538     "mgh" // MakhuwaMeetto
0539     "mua" // Mundang
0540     "nmg" // Kwasio
0541     "nus" // Nuer
0542     "sah" // Sakha
0543     "sbp" // Sangu
0544     "swc" // Congo Swahili
0545     "twq" // Tasawaq
0546     "vai" // Vai
0547     "wae" // Walser
0548     "yav" // Yangben
0549     "ae\0" // Avestan
0550     "ast" // Asturian
0551     "jgo" // Ngomba
0552     "kkj" // Kako
0553     "mgo" // Meta
0554     "nnh" // Ngiemboon
0555     "an\0" // Aragonese
0556     "akk" // Akkadian
0557     "egy" // AncientEgyptian
0558     "grc" // AncientGreek
0559     "arc" // Aramaic
0560     "ban" // Balinese
0561     "bax" // Bamun
0562     "bbc" // BatakToba
0563     "bug" // Buginese
0564     "bku" // Buhid
0565     "xcr" // Carian
0566     "ccp" // Chakma
0567     "myz" // ClassicalMandaic
0568     "cop" // Coptic
0569     "doi" // Dogri
0570     "cjm" // EasternCham
0571     "eky" // EasternKayah
0572     "ett" // Etruscan
0573     "got" // Gothic
0574     "hnn" // Hanunoo
0575     "inh" // Ingush
0576     "hmd" // LargeFloweryMiao
0577     "lep" // Lepcha
0578     "lif" // Limbu
0579     "lis" // Lisu
0580     "khb" // Lu
0581     "xlc" // Lycian
0582     "xld" // Lydian
0583     "man" // Mandingo
0584     "mni" // Manipuri
0585     "xmr" // Meroitic
0586     "nod" // NorthernThai
0587     "sga" // OldIrish
0588     "non" // OldNorse
0589     "peo" // OldPersian
0590     "otk" // OldTurkish
0591     "pal" // Pahlavi
0592     "xpr" // Parthian
0593     "phn" // Phoenician
0594     "pra" // PrakritLanguage
0595     "rej" // Rejang
0596     "xsa" // Sabaean
0597     "smp" // Samaritan
0598     "sat" // Santali
0599     "saz" // Saurashtra
0600     "srb" // Sora
0601     "syl" // Sylheti
0602     "tbw" // Tagbanwa
0603     "blt" // TaiDam
0604     "tdd" // TaiNua
0605     "uga" // Ugaritic
0606     "bss" // Akoose
0607     "lkt" // Lakota
0608     "zgh" // Standard Moroccan Tamazight
0609     ;
0610 
0611 static QString languageToCode(QLocale::Language language)
0612 {
0613     if (language == QLocale::AnyLanguage) {
0614         return QString();
0615     }
0616     if (language == QLocale::C) {
0617         return QStringLiteral("C");
0618     }
0619 
0620     const unsigned char *c = language_code_list + 3 * (uint(language));
0621 
0622     QString code(c[2] == 0 ? 2 : 3, Qt::Uninitialized);
0623 
0624     code[0] = ushort(c[0]);
0625     code[1] = ushort(c[1]);
0626     if (c[2] != 0) {
0627         code[2] = ushort(c[2]);
0628     }
0629 
0630     return code;
0631 }
0632 
0633 static QString countryToCode(QLocale::Country country)
0634 {
0635     if (country == QLocale::AnyCountry) {
0636         return QString();
0637     }
0638 
0639     const unsigned char *c = country_code_list + 3 * (uint(country));
0640 
0641     QString code(c[2] == 0 ? 2 : 3, Qt::Uninitialized);
0642 
0643     code[0] = ushort(c[0]);
0644     code[1] = ushort(c[1]);
0645     if (c[2] != 0) {
0646         code[2] = ushort(c[2]);
0647     }
0648 
0649     return code;
0650 }
0651 
0652 static QLocale::Country codeToCountry(const QString &code)
0653 {
0654     int len = code.length();
0655     if (len != 2 && len != 3) {
0656         return QLocale::AnyCountry;
0657     }
0658     ushort uc1 = len-- > 0 ? code[0].toUpper().unicode() : 0;
0659     ushort uc2 = len-- > 0 ? code[1].toUpper().unicode() : 0;
0660     ushort uc3 = len-- > 0 ? code[2].toUpper().unicode() : 0;
0661 
0662     const unsigned char *c = country_code_list;
0663     for (; *c != 0; c += 3) {
0664         if (uc1 == c[0] && uc2 == c[1] && uc3 == c[2]) {
0665             return QLocale::Country((c - country_code_list) / 3);
0666         }
0667     }
0668 
0669     return QLocale::AnyCountry;
0670 }
0671 
0672 #if 0
0673 static QLocale::Language codeToLanguage(const QString &code)
0674 {
0675     int len = code.length();
0676     if (len != 2 && len != 3) {
0677         return QLocale::C;
0678     }
0679     ushort uc1 = len-- > 0 ? code[0].toLower().unicode() : 0;
0680     ushort uc2 = len-- > 0 ? code[1].toLower().unicode() : 0;
0681     ushort uc3 = len-- > 0 ? code[2].toLower().unicode() : 0;
0682 
0683     const unsigned char *c = language_code_list;
0684     for (; *c != 0; c += 3) {
0685         if (uc1 == c[0] && uc2 == c[1] && uc3 == c[2]) {
0686             return QLocale::Language((c - language_code_list) / 3);
0687         }
0688     }
0689 
0690     // legacy codes
0691     if (uc1 == 'n' && uc2 == 'o' && uc3 == 0) { // no -> nb
0692         Q_STATIC_ASSERT(QLocale::Norwegian == QLocale::NorwegianBokmal);
0693         return QLocale::Norwegian;
0694     }
0695     if (uc1 == 't' && uc2 == 'l' && uc3 == 0) { // tl -> fil
0696         Q_STATIC_ASSERT(QLocale::Tagalog == QLocale::Filipino);
0697         return QLocale::Tagalog;
0698     }
0699     if (uc1 == 's' && uc2 == 'h' && uc3 == 0) { // sh -> sr[_Latn]
0700         Q_STATIC_ASSERT(QLocale::SerboCroatian == QLocale::Serbian);
0701         return QLocale::SerboCroatian;
0702     }
0703     if (uc1 == 'm' && uc2 == 'o' && uc3 == 0) { // mo -> ro
0704         Q_STATIC_ASSERT(QLocale::Moldavian == QLocale::Romanian);
0705         return QLocale::Moldavian;
0706     }
0707 
0708     return QLocale::C;
0709 }
0710 #endif
0711 // End Qt copied code
0712 
0713 static void initResources()
0714 {
0715     static bool initDone = false;
0716     if (!initDone) {
0717         Q_INIT_RESOURCE(holidays);
0718         initDone = true;
0719     }
0720 }
0721 
0722 static QStringList allHolidayFiles(const QString &location = QString())
0723 {
0724     initResources();
0725     QStringList dirs{QStringLiteral(":/org.kde.kholidays/plan2")};
0726     dirs += QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, //
0727                                       QStringLiteral("kf5/libkholidays/plan2"),
0728                                       QStandardPaths::LocateDirectory);
0729     QStringList files;
0730     for (const QString &dir : std::as_const(dirs)) {
0731         QDirIterator it(dir, QStringList() << QStringLiteral("holiday_") + location + '*');
0732         while (it.hasNext()) {
0733             files.push_back(it.next());
0734         }
0735     }
0736     return files;
0737 }
0738 
0739 namespace KHolidays
0740 {
0741 class HolidayRegionPrivate : public QSharedData
0742 {
0743 public:
0744     explicit HolidayRegionPrivate(const QString &regionCode)
0745         : mDriver(nullptr)
0746         , mRegionCode(regionCode)
0747     {
0748         if (!mRegionCode.isEmpty()) {
0749             auto file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, //
0750                                                QLatin1String("kf5/libkholidays/plan2/holiday_") + mRegionCode);
0751             if (!file.isEmpty()) {
0752                 mHolidayFile.setFile(file);
0753             } else {
0754                 initResources();
0755                 file = QStringLiteral(":/org.kde.kholidays/plan2/holiday_") + mRegionCode;
0756                 if (QFile::exists(file)) {
0757                     mHolidayFile.setFile(file);
0758                 }
0759             }
0760         }
0761 
0762         init();
0763     }
0764 
0765     explicit HolidayRegionPrivate(const QFileInfo &regionFile)
0766         : mDriver(nullptr)
0767         , mHolidayFile(regionFile)
0768     {
0769         init();
0770     }
0771 
0772     ~HolidayRegionPrivate()
0773     {
0774         delete mDriver;
0775     }
0776 
0777     void init()
0778     {
0779         if (mHolidayFile.exists()) {
0780             mDriver = new HolidayParserDriverPlan(mHolidayFile.absoluteFilePath());
0781             if (mDriver) {
0782                 if (mRegionCode.isEmpty()) {
0783                     if (mHolidayFile.fileName().startsWith(QLatin1String("holiday_"))) {
0784                         mRegionCode = mHolidayFile.fileName().mid(8);
0785                     } else {
0786                         mRegionCode = mHolidayFile.fileName();
0787                     }
0788                 }
0789 
0790             } else {
0791                 mRegionCode.clear();
0792             }
0793         } else {
0794             mRegionCode.clear();
0795         }
0796     }
0797 
0798     HolidayParserDriver *mDriver; // The parser driver for the holiday file
0799     QString mRegionCode; // region code of holiday region
0800     QFileInfo mHolidayFile; // file containing holiday data, or null
0801 };
0802 }
0803 
0804 HolidayRegion::HolidayRegion(const QString &regionCode)
0805     : d(new HolidayRegionPrivate(regionCode))
0806 {
0807 }
0808 
0809 HolidayRegion::HolidayRegion(const QFileInfo &regionFile)
0810     : d(new HolidayRegionPrivate(regionFile))
0811 {
0812 }
0813 
0814 HolidayRegion::HolidayRegion(const HolidayRegion &) = default;
0815 HolidayRegion::HolidayRegion(HolidayRegion &&) = default;
0816 HolidayRegion::~HolidayRegion() = default;
0817 
0818 HolidayRegion &HolidayRegion::operator=(const HolidayRegion &) = default;
0819 HolidayRegion &HolidayRegion::operator=(HolidayRegion &&) = default;
0820 
0821 QStringList HolidayRegion::regionCodes()
0822 {
0823     const QStringList files = allHolidayFiles();
0824 
0825     QStringList regionCodesList;
0826     regionCodesList.reserve(files.count());
0827     for (const QString &filename : files) {
0828         regionCodesList.append(filename.mid(filename.lastIndexOf(QLatin1String("holiday_")) + 8));
0829     }
0830 
0831     std::sort(regionCodesList.begin(), regionCodesList.end());
0832     return regionCodesList;
0833 }
0834 
0835 QString HolidayRegion::regionCode() const
0836 {
0837     return d->mRegionCode;
0838 }
0839 
0840 QString HolidayRegion::countryCode() const
0841 {
0842     return d->mDriver->fileCountryCode();
0843 }
0844 
0845 QString HolidayRegion::countryCode(const QString &regionCode)
0846 {
0847     HolidayRegion temp(regionCode);
0848     if (temp.isValid()) {
0849         return temp.countryCode();
0850     } else {
0851         return QString();
0852     }
0853 }
0854 
0855 QString HolidayRegion::languageCode() const
0856 {
0857     return d->mDriver->fileLanguageCode();
0858 }
0859 
0860 QString HolidayRegion::languageCode(const QString &regionCode)
0861 {
0862     HolidayRegion temp(regionCode);
0863     if (temp.isValid()) {
0864         return temp.languageCode();
0865     } else {
0866         return QString();
0867     }
0868 }
0869 
0870 QString HolidayRegion::name() const
0871 {
0872     QString tempName = d->mDriver->fileName();
0873 
0874     if (tempName.isEmpty()) {
0875         QStringList countryParts = countryCode().toLower().split(QLatin1Char('-'));
0876         const QString &country = countryParts.at(0);
0877         QString regionName;
0878         QString typeName;
0879 
0880         if (country != QLatin1String("xx")) {
0881             if (countryParts.count() == 2) {
0882                 // Temporary measure to get regions translated, only those files that already exist
0883                 // In 4.6 hope to have isocodes project integration for translations via KLocale
0884                 const QString &subdivision = countryParts.at(1);
0885                 if (country == QLatin1String("ca") && subdivision == QLatin1String("qc")) {
0886                     regionName = QCoreApplication::translate("HolidayRegion", "Quebec", "Canadian region");
0887                 } else if (country == QLatin1String("de")) {
0888                     if (subdivision == QLatin1String("by")) {
0889                         regionName = QCoreApplication::translate("HolidayRegion", "Germany, Bavaria", "German region");
0890                     } else if (subdivision == QLatin1String("bb")) {
0891                         regionName = QCoreApplication::translate("HolidayRegion", "Germany, Brandenburg", "German region");
0892                     } else if (subdivision == QLatin1String("be")) {
0893                         regionName = QCoreApplication::translate("HolidayRegion", "Germany, Berlin", "German region");
0894                     } else if (subdivision == QLatin1String("bw")) {
0895                         regionName = QCoreApplication::translate("HolidayRegion", "Germany, Baden-Wuerttemberg", "German region");
0896                     } else if (subdivision == QLatin1String("he")) {
0897                         regionName = QCoreApplication::translate("HolidayRegion", "Germany, Hesse", "German region");
0898                     } else if (subdivision == QLatin1String("mv")) {
0899                         regionName = QCoreApplication::translate("HolidayRegion", "Germany, Mecklenburg-Hither Pomerania", "German region");
0900                     } else if (subdivision == QLatin1String("ni")) {
0901                         regionName = QCoreApplication::translate("HolidayRegion", "Germany, Lower Saxony", "German region");
0902                     } else if (subdivision == QLatin1String("nw")) {
0903                         regionName = QCoreApplication::translate("HolidayRegion", "Germany, North Rhine-Westphalia", "German region");
0904                     } else if (subdivision == QLatin1String("rp")) {
0905                         regionName = QCoreApplication::translate("HolidayRegion", "Germany, Rhineland-Palatinate", "German region");
0906                     } else if (subdivision == QLatin1String("sh")) {
0907                         regionName = QCoreApplication::translate("HolidayRegion", "Germany, Schleswig-Holstein", "German region");
0908                     } else if (subdivision == QLatin1String("sl")) {
0909                         regionName = QCoreApplication::translate("HolidayRegion", "Germany, Saarland", "German region");
0910                     } else if (subdivision == QLatin1String("sn")) {
0911                         regionName = QCoreApplication::translate("HolidayRegion", "Germany, Saxony", "German region");
0912                     } else if (subdivision == QLatin1String("st")) {
0913                         regionName = QCoreApplication::translate("HolidayRegion", "Germany, Saxony-Anhalt", "German region");
0914                     } else if (subdivision == QLatin1String("th")) {
0915                         regionName = QCoreApplication::translate("HolidayRegion", "Germany, Thuringia", "German region");
0916                     }
0917                 } else if (country == QLatin1String("es") && subdivision == QLatin1String("ct")) {
0918                     regionName = QCoreApplication::translate("HolidayRegion", "Catalonia", "Spanish region");
0919                 } else if (country == QLatin1String("gb") && subdivision == QLatin1String("eaw")) {
0920                     regionName = QCoreApplication::translate("HolidayRegion", "England and Wales", "UK Region");
0921                 } else if (country == QLatin1String("gb") && subdivision == QLatin1String("eng")) {
0922                     regionName = QCoreApplication::translate("HolidayRegion", "England", "UK Region");
0923                 } else if (country == QLatin1String("gb") && subdivision == QLatin1String("wls")) {
0924                     regionName = QCoreApplication::translate("HolidayRegion", "Wales", "UK Region");
0925                 } else if (country == QLatin1String("gb") && subdivision == QLatin1String("sct")) {
0926                     regionName = QCoreApplication::translate("HolidayRegion", "Scotland", "UK Region");
0927                 } else if (country == QLatin1String("gb") && subdivision == QLatin1String("nir")) {
0928                     regionName = QCoreApplication::translate("HolidayRegion", "Northern Ireland", "UK Region");
0929                 } else if (country == QLatin1String("it") && subdivision == QLatin1String("bz")) {
0930                     regionName = QCoreApplication::translate("HolidayRegion", "South Tyrol", "Italian Region");
0931                 } else if (country == QLatin1String("au") && subdivision == QLatin1String("nsw")) {
0932                     regionName = QCoreApplication::translate("HolidayRegion", "New South Wales");
0933                 } else if (country == QLatin1String("au") && subdivision == QLatin1String("qld")) {
0934                     regionName = QCoreApplication::translate("HolidayRegion", "Queensland", "Australian Region");
0935                 } else if (country == QLatin1String("au") && subdivision == QLatin1String("vic")) {
0936                     regionName = QCoreApplication::translate("HolidayRegion", "Victoria", "Australian Region");
0937                 } else if (country == QLatin1String("au") && subdivision == QLatin1String("sa")) {
0938                     regionName = QCoreApplication::translate("HolidayRegion", "South Australia", "Australian Region");
0939                 } else if (country == QLatin1String("au") && subdivision == QLatin1String("nt")) {
0940                     regionName = QCoreApplication::translate("HolidayRegion", "Northern Territory", "Australian Region");
0941                 } else if (country == QLatin1String("au") && subdivision == QLatin1String("act")) {
0942                     regionName = QCoreApplication::translate("HolidayRegion", "Australian Capital Territory", "Australian Region");
0943                 } else if (country == QLatin1String("au") && subdivision == QLatin1String("wa")) {
0944                     regionName = QCoreApplication::translate("HolidayRegion", "Western Australia", "Australian Region");
0945                 } else if (country == QLatin1String("au") && subdivision == QLatin1String("tas")) {
0946                     regionName = QCoreApplication::translate("HolidayRegion", "Tasmania", "Australian Region");
0947                 } else if (country == QLatin1String("ba") && subdivision == QLatin1String("srp")) {
0948                     regionName = QCoreApplication::translate("HolidayRegion", "Republic of Srpska", "Bosnian and Herzegovinian Region");
0949                 } else {
0950                     // TODO Note this does not give the current QLocale translation!
0951                     regionName = QLocale::countryToString(codeToCountry(country)) + QLatin1String(" (") + subdivision + QLatin1Char(')');
0952                 }
0953             } else {
0954                 // TODO Note this does not give the current QLocale translation!
0955                 regionName = QLocale::countryToString(codeToCountry(country));
0956             }
0957         }
0958 
0959         // Cheat on type for now,take direct from region code until API is introduced in SC 4.6
0960         QStringList regionParts = regionCode().toLower().split(QLatin1Char('_'));
0961         if (regionParts.count() == 3) {
0962             const QString &type = regionParts.at(2);
0963             // Will create lots more in 4.6
0964             // Religious types, just simple for now
0965             if (type == QLatin1String("public")) {
0966                 typeName = QCoreApplication::translate("HolidayRegion", "Public", "Holiday type");
0967             } else if (type == QLatin1String("civil")) {
0968                 typeName = QCoreApplication::translate("HolidayRegion", "Civil", "Holiday type");
0969             } else if (type == QLatin1String("religious")) {
0970                 typeName = QCoreApplication::translate("HolidayRegion", "Religious", "Holiday type");
0971             } else if (type == QLatin1String("government")) {
0972                 typeName = QCoreApplication::translate("HolidayRegion", "Government", "Holiday type");
0973             } else if (type == QLatin1String("financial")) {
0974                 typeName = QCoreApplication::translate("HolidayRegion", "Financial", "Holiday type");
0975             } else if (type == QLatin1String("cultural")) {
0976                 typeName = QCoreApplication::translate("HolidayRegion", "Cultural", "Holiday type");
0977             } else if (type == QLatin1String("commemorative")) {
0978                 typeName = QCoreApplication::translate("HolidayRegion", "Commemorative", "Holiday type");
0979             } else if (type == QLatin1String("historical")) {
0980                 typeName = QCoreApplication::translate("HolidayRegion", "Historical", "Holiday type");
0981             } else if (type == QLatin1String("school")) {
0982                 typeName = QCoreApplication::translate("HolidayRegion", "School", "Holiday type");
0983             } else if (type == QLatin1String("seasonal")) {
0984                 typeName = QCoreApplication::translate("HolidayRegion", "Seasonal", "Holiday type");
0985             } else if (type == QLatin1String("nameday")) {
0986                 typeName = QCoreApplication::translate("HolidayRegion", "Name Days", "Holiday type");
0987             } else if (type == QLatin1String("personal")) {
0988                 typeName = QCoreApplication::translate("HolidayRegion", "Personal", "Holiday type");
0989             } else if (type == QLatin1String("christian")) {
0990                 typeName = QCoreApplication::translate("HolidayRegion", "Christian", "Holiday type");
0991             } else if (type == QLatin1String("anglican")) {
0992                 typeName = QCoreApplication::translate("HolidayRegion", "Anglican", "Holiday type");
0993             } else if (type == QLatin1String("catholic")) {
0994                 typeName = QCoreApplication::translate("HolidayRegion", "Catholic", "Holiday type");
0995             } else if (type == QLatin1String("protestant")) {
0996                 typeName = QCoreApplication::translate("HolidayRegion", "Protestant", "Holiday type");
0997             } else if (type == QLatin1String("orthodox")) {
0998                 typeName = QCoreApplication::translate("HolidayRegion", "Orthodox", "Holiday type");
0999             } else if (type == QLatin1String("jewish")) {
1000                 typeName = QCoreApplication::translate("HolidayRegion", "Jewish", "Holiday type");
1001             } else if (type == QLatin1String("jewish-orthodox")) {
1002                 typeName = QCoreApplication::translate("HolidayRegion", "Jewish Orthodox", "Holiday type");
1003             } else if (type == QLatin1String("jewish-conservative")) {
1004                 typeName = QCoreApplication::translate("HolidayRegion", "Jewish Conservative", "Holiday type");
1005             } else if (type == QLatin1String("jewish-reform")) {
1006                 typeName = QCoreApplication::translate("HolidayRegion", "Jewish Reform", "Holiday type");
1007             } else if (type == QLatin1String("islamic")) {
1008                 typeName = QCoreApplication::translate("HolidayRegion", "Islamic", "Holiday type");
1009             } else if (type == QLatin1String("islamic-sunni")) {
1010                 typeName = QCoreApplication::translate("HolidayRegion", "Islamic Sunni", "Holiday type");
1011             } else if (type == QLatin1String("islamic-shia")) {
1012                 typeName = QCoreApplication::translate("HolidayRegion", "Islamic Shia", "Holiday type");
1013             } else if (type == QLatin1String("islamic-sufi")) {
1014                 typeName = QCoreApplication::translate("HolidayRegion", "Islamic Sufi", "Holiday type");
1015             }
1016         }
1017 
1018         if (!regionName.isEmpty()) {
1019             if (!typeName.isEmpty()) {
1020                 tempName = QCoreApplication::translate("HolidayRegion", "%1 - %2", "Holiday file display name, %1 = region name, %2 = holiday type")
1021                                .arg(regionName, typeName);
1022             } else {
1023                 tempName = regionName;
1024             }
1025         } else if (!typeName.isEmpty()) {
1026             tempName = typeName;
1027         } else {
1028             tempName = QCoreApplication::translate("HolidayRegion", "Unknown", "Unknown holiday region");
1029         }
1030     }
1031     return tempName;
1032 }
1033 
1034 QString HolidayRegion::name(const QString &regionCode)
1035 {
1036     HolidayRegion temp(regionCode);
1037     if (temp.isValid()) {
1038         return temp.name();
1039     } else {
1040         return QString();
1041     }
1042 }
1043 
1044 QString HolidayRegion::description() const
1045 {
1046     return d->mDriver->fileDescription();
1047 }
1048 
1049 QString HolidayRegion::description(const QString &regionCode)
1050 {
1051     HolidayRegion temp(regionCode);
1052     if (temp.isValid()) {
1053         return temp.description();
1054     } else {
1055         return QString();
1056     }
1057 }
1058 
1059 bool HolidayRegion::isValid() const
1060 {
1061     return d->mHolidayFile.exists() && d->mDriver;
1062 }
1063 
1064 bool HolidayRegion::isValid(const QString &regionCode)
1065 {
1066     HolidayRegion temp(regionCode);
1067     return temp.isValid();
1068 }
1069 
1070 Holiday::List HolidayRegion::rawHolidays(const QDate &startDate, const QDate &endDate, const QString &category) const
1071 {
1072     if (isValid()) {
1073         return d->mDriver->parseHolidays(startDate, endDate, category);
1074     } else {
1075         return Holiday::List();
1076     }
1077 }
1078 
1079 #if KHOLIDAYS_BUILD_DEPRECATED_SINCE(5, 95)
1080 Holiday::List HolidayRegion::holidays(const QDate &startDate, const QDate &endDate) const
1081 {
1082     return rawHolidaysWithAstroSeasons(startDate, endDate);
1083 }
1084 #endif
1085 
1086 Holiday::List HolidayRegion::rawHolidays(const QDate &startDate, const QDate &endDate) const
1087 {
1088     if (isValid()) {
1089         return d->mDriver->parseRawHolidays(startDate, endDate);
1090     } else {
1091         return Holiday::List();
1092     }
1093 }
1094 
1095 Holiday::List HolidayRegion::rawHolidaysWithAstroSeasons(const QDate &startDate, const QDate &endDate) const
1096 {
1097     if (isValid()) {
1098         return d->mDriver->parseHolidays(startDate, endDate);
1099     } else {
1100         return Holiday::List();
1101     }
1102 }
1103 
1104 #if KHOLIDAYS_BUILD_DEPRECATED_SINCE(5, 95)
1105 Holiday::List HolidayRegion::holidays(const QDate &date) const
1106 {
1107     return rawHolidaysWithAstroSeasons(date);
1108 }
1109 #endif
1110 
1111 Holiday::List HolidayRegion::rawHolidaysWithAstroSeasons(const QDate &date) const
1112 {
1113     if (isValid()) {
1114         return d->mDriver->parseHolidays(date);
1115     } else {
1116         return Holiday::List();
1117     }
1118 }
1119 
1120 #if KHOLIDAYS_BUILD_DEPRECATED_SINCE(5, 95)
1121 Holiday::List HolidayRegion::holidays(int calendarYear) const
1122 {
1123     return rawHolidaysWithAstroSeasons(calendarYear);
1124 }
1125 #endif
1126 
1127 Holiday::List HolidayRegion::rawHolidaysWithAstroSeasons(int calendarYear) const
1128 {
1129     if (isValid()) {
1130         return d->mDriver->parseHolidays(calendarYear);
1131     } else {
1132         return Holiday::List();
1133     }
1134 }
1135 
1136 bool HolidayRegion::isHoliday(const QDate &date) const
1137 {
1138     const Holiday::List holidayList = rawHolidaysWithAstroSeasons(date);
1139     if (!holidayList.isEmpty()) {
1140         for (const KHolidays::Holiday &holiday : holidayList) {
1141             if (holiday.dayType() == Holiday::NonWorkday) {
1142                 return true;
1143             }
1144         }
1145     }
1146     return false;
1147 }
1148 
1149 static bool maybeCountry(QStringView holidayId, QStringView country)
1150 {
1151     if (country.isEmpty()) {
1152         return false;
1153     }
1154     if (holidayId.size() < 2 || country.size() < 2 || (country.size() > 2 && country[2] != QLatin1Char('-'))) {
1155         return true; // not the format we expect, check the content
1156     }
1157     return holidayId.startsWith(country.left(2));
1158 }
1159 
1160 QString HolidayRegion::defaultRegionCode(const QString &country, const QString &language)
1161 {
1162     // Try to match against the users country and language, or failing that the language country.
1163     // Scan through all the regions finding the first match for each possible default
1164     // Holiday Region Country Code can be a country subdivision or the country itself,
1165     // e.g. US or US-CA for California, so we can try match on both but an exact match has priority
1166     // The Holiday Region file is in one language only, so give priority to any file in the
1167     // users language, e.g. bilingual countries with a separate file for each language
1168     // Locale language can have a country code embedded in it e.g. en_GB, which we can try use if
1169     // no country set, but a lot of countries use en_GB so it's a lower priority option
1170 
1171     QString localeCountry;
1172     QString localeSubdivision;
1173     QString localeLanguage;
1174     QString localeLanguageCountry;
1175 
1176     if (country.isEmpty()) {
1177         localeCountry = countryToCode(QLocale().country()).toLower();
1178     } else {
1179         localeCountry = country.toLower();
1180         const auto idx = localeCountry.indexOf(QLatin1Char('-'));
1181         localeSubdivision = idx > 0 ? localeCountry.left(idx) : localeCountry;
1182     }
1183 
1184     if (language.isEmpty()) {
1185         localeLanguage = languageToCode(QLocale().language()).toLower();
1186     } else {
1187         localeLanguage = language.toLower();
1188     }
1189 
1190     if (localeLanguage.split(QLatin1Char('_')).count() > 1) {
1191         localeLanguageCountry = localeLanguage.split(QLatin1Char('_')).at(1);
1192     }
1193 
1194     QString countryAndLanguageMatch;
1195     QString countryOnlyMatch;
1196     QString subdivisionAndLanguageMatch;
1197     QString subdivisionOnlyMatch;
1198     QString languageCountryAndLanguageMatch;
1199     QString languageCountryOnlyMatch;
1200     QString languageSubdivisionAndLanguageMatch;
1201     QString languageSubdivisionOnlyMatch;
1202 
1203     const QStringList regionList = KHolidays::HolidayRegion::regionCodes();
1204     for (const QString &aRegionCode : regionList) {
1205         // avoid expensive parsing in most cases by leveraging the country being in the file name
1206         if (!maybeCountry(aRegionCode, localeCountry) && !maybeCountry(aRegionCode, localeLanguageCountry)) {
1207             continue;
1208         }
1209 
1210         const auto hr = KHolidays::HolidayRegion(aRegionCode);
1211         QString regionCountry = hr.countryCode().toLower();
1212         QString regionSubdivisionCountry;
1213         if (regionCountry.split(QLatin1Char('-')).count() > 1) {
1214             regionSubdivisionCountry = regionCountry.split(QLatin1Char('-')).at(0);
1215         } else {
1216             regionSubdivisionCountry = regionCountry;
1217         }
1218         QString regionLanguage = hr.languageCode().toLower();
1219 
1220         if (regionCountry == localeCountry && regionLanguage == localeLanguage) {
1221             // exact match so don't look further
1222             return aRegionCode;
1223         } else if (regionCountry == localeSubdivision && regionLanguage == localeLanguage) {
1224             countryAndLanguageMatch = aRegionCode;
1225         } else if (regionCountry == localeCountry) {
1226             if (countryOnlyMatch.isEmpty()) {
1227                 countryOnlyMatch = aRegionCode;
1228             }
1229         } else if (!regionSubdivisionCountry.isEmpty() && regionSubdivisionCountry == localeSubdivision && regionLanguage == localeLanguage) {
1230             if (subdivisionAndLanguageMatch.isEmpty()) {
1231                 subdivisionAndLanguageMatch = aRegionCode;
1232             }
1233         } else if (!regionSubdivisionCountry.isEmpty() && regionSubdivisionCountry == localeSubdivision) {
1234             if (subdivisionOnlyMatch.isEmpty()) {
1235                 subdivisionOnlyMatch = aRegionCode;
1236             }
1237         } else if (!localeLanguageCountry.isEmpty() && regionCountry == localeLanguageCountry && regionLanguage == localeLanguage) {
1238             if (languageCountryAndLanguageMatch.isEmpty()) {
1239                 languageCountryAndLanguageMatch = aRegionCode;
1240             }
1241         } else if (!localeLanguageCountry.isEmpty() && regionCountry == localeLanguageCountry) {
1242             if (languageCountryOnlyMatch.isEmpty()) {
1243                 languageCountryOnlyMatch = aRegionCode;
1244             }
1245         } else if (!regionSubdivisionCountry.isEmpty() && !localeLanguageCountry.isEmpty() && regionSubdivisionCountry == localeLanguageCountry
1246                    && regionLanguage == localeLanguage) {
1247             if (languageSubdivisionAndLanguageMatch.isEmpty()) {
1248                 languageSubdivisionAndLanguageMatch = aRegionCode;
1249             }
1250         } else if (!regionSubdivisionCountry.isEmpty() && !localeLanguageCountry.isEmpty() && regionSubdivisionCountry == localeLanguageCountry) {
1251             if (languageSubdivisionOnlyMatch.isEmpty()) {
1252                 languageSubdivisionOnlyMatch = aRegionCode;
1253             }
1254         }
1255     }
1256 
1257     QString defaultRegionCode;
1258 
1259     if (!countryAndLanguageMatch.isEmpty()) {
1260         defaultRegionCode = countryAndLanguageMatch;
1261     } else if (!countryOnlyMatch.isEmpty()) {
1262         defaultRegionCode = countryOnlyMatch;
1263     } else if (!subdivisionAndLanguageMatch.isEmpty()) {
1264         defaultRegionCode = subdivisionAndLanguageMatch;
1265     } else if (!subdivisionOnlyMatch.isEmpty()) {
1266         defaultRegionCode = subdivisionOnlyMatch;
1267     } else if (!languageCountryAndLanguageMatch.isEmpty()) {
1268         defaultRegionCode = languageCountryAndLanguageMatch;
1269     } else if (!languageCountryOnlyMatch.isEmpty()) {
1270         defaultRegionCode = languageCountryOnlyMatch;
1271     } else if (!languageSubdivisionAndLanguageMatch.isEmpty()) {
1272         defaultRegionCode = languageSubdivisionAndLanguageMatch;
1273     } else if (!languageSubdivisionOnlyMatch.isEmpty()) {
1274         defaultRegionCode = languageSubdivisionOnlyMatch;
1275     }
1276 
1277     return defaultRegionCode;
1278 }