Jump to content

Module:data consistency check

Wiktionary වෙතින්
(වික්ෂනරි:FSCK වෙතින් යළි-යොමු කරන ලදි)

This module checks the validity and internal consistency of the language, language family, and script data used on Wiktionary: the modules in Category:Language data modules as well as Module:scripts/data.

Discrepancies detected:

  • Code: EL.. Saw name: Ecclesiastical ලතින්. Expected name: Ecclesiastical Latin.
  • Code: LL.. Saw name: Late ලතින්. Expected name: Late Latin.
  • Code: ML.. Saw name: Medieval ලතින්. Expected name: Medieval Latin.
  • Code: VL.. Saw name: Vulgar ලතින්. Expected name: Vulgar Latin.
  • Code: abs. Saw name: Ambonese මැලේ. Expected name: Ambonese Malay.
  • Code: acw. Saw name: Hijazi අරාබි. Expected name: Hijazi Arabic.
  • Code: acy. Saw name: Cypriot අරාබි. Expected name: Cypriot Arabic.
  • Code: aeb. Saw name: Tunisian අරාබි. Expected name: Tunisian Arabic.
  • Code: afb. Saw name: Gulf අරාබි. Expected name: Gulf Arabic.
  • Code: ajp. Saw name: South Levantine අරාබි. Expected name: South Levantine Arabic.
  • Code: apc. Saw name: North Levantine අරාබි. Expected name: North Levantine Arabic.
  • Code: ary. Saw name: Moroccan අරාබි. Expected name: Moroccan Arabic.
  • Code: arz. Saw name: ඊජිප්තු අරාබි. Expected name: Egyptian Arabic.
  • Code: ayl. Saw name: Libyan අරාබි. Expected name: Libyan Arabic.
  • Code: br. Saw name: Breton. Expected name: බ්‍රෙටන්.
  • Code: cmn-ear. Saw name: Early මැන්ඩරීන්. Expected name: Early Mandarin.
  • Code: cy. Saw name: Welsh. Expected name: වේල්ස.
  • Code: dra-okn. Saw name: Old කන්නඩ. Expected name: Old Kannada.
  • Code: dum. Saw name: Middle ඕලන්ද. Expected name: Middle Dutch.
  • Code: enm. Saw name: Middle ඉංග්‍රීසි. Expected name: මධ්‍යකාලීන ඉංග්‍රීසි.
  • Code: euq-pro. Saw name: Proto-බාස්ක්. Expected name: ප්‍රොටෝ-බාස්ක්.
  • Code: fr-CA. Saw name: Canadian ප්‍රංශ. Expected name: Canadian French.
  • Code: frk. Saw name: Proto-West ජර්මානුic. Expected name: ප්‍රොටෝ-බටහිර ජර්මානු.
  • Code: frm. Saw name: Middle ප්‍රංශ. Expected name: මධ්‍යකාලීන ප්‍රංශ.
  • Code: fro-nor. Saw name: Old Northern ප්‍රංශ. Expected name: Old Northern French.
  • Code: gd. Saw name: Scottish Gaelic. Expected name: ස්කොට්ස් ගේලික්.
  • Code: gem. Saw name: ජර්මානුic. Expected name: ජර්මානු.
  • Code: gem-pro. Saw name: Proto-ජර්මානුic. Expected name: ප්‍රොටෝ-ජර්මානු.
  • Code: gim. Saw name: Gimi (Goroka). Expected name: Gimi (Papuan).
  • Code: gkm. Saw name: Byzantine ග්‍රීක. Expected name: Byzantine Greek.
  • Code: gmh. Saw name: Middle High ජර්මානු. Expected name: Middle High German.
  • Code: gml. Saw name: Middle Low ජර්මානු. Expected name: Middle Low German.
  • Code: gmq. Saw name: North ජර්මානුic. Expected name: North Germanic.
  • Code: gmq-mno. Saw name: Middle නෝර්වීජියානු. Expected name: Middle Norwegian.
  • Code: gmq-oda. Saw name: Old ඩෙන්මාර්ක. Expected name: Old Danish.
  • Code: gmq-osw. Saw name: Old ස්වීඩන්. Expected name: Old Swedish.
  • Code: gmw-ecg. Saw name: East Central ජර්මානු. Expected name: East Central German.
  • Code: gmw-jdt. Saw name: Jersey ඕලන්ද. Expected name: Jersey Dutch.
  • Code: gmw-pro. Saw name: Proto-West ජර්මානුic. Expected name: ප්‍රොටෝ-බටහිර ජර්මානු.
  • Code: gmy. Saw name: Mycenaean ග්‍රීක. Expected name: Mycenaean Greek.
  • Code: gn. Saw name: Guaraní. Expected name: Guarani.
  • Code: goh. Saw name: Old High ජර්මානු. Expected name: Old High German.
  • Code: grk-mar. Saw name: Mariupol ග්‍රීක. Expected name: Mariupol Greek.
  • Code: gsw. Saw name: Alemannic ජර්මානු. Expected name: Alemannic German.
  • Code: gug. Saw name: Paraguayan Guaraní. Expected name: Paraguayan Guarani.
  • Code: gun. Saw name: Mbyá Guaraní. Expected name: Mbya Guarani.
  • Code: gv. Saw name: Manx. Expected name: මැන්ක්ස්.
  • Code: hmn-pro. Saw name: Proto-Hmong. Expected name: Proto-Hmongic.
  • Code: idb. Saw name: Indo-පෘතුගීසි. Expected name: Indo-Portuguese.
  • Code: inc-ash. Saw name: Ashokan ප්‍රාකෘත. Expected name: අශෝක ප්‍රාකෘත.
  • Code: itc-ola. Saw name: Old ලතින්. Expected name: පුරාතන ලතින්.
  • Code: itc-pro. Saw name: Proto-Italic. Expected name: ප්‍රොටෝ-ඉතාලිකා.
  • Code: kaw. Saw name: Old ජාවා. Expected name: Old Javanese.
  • Code: kw. Saw name: Cornish. Expected name: කෝනිෂ්.
  • Code: kxd. Saw name: Brunei මැලේ. Expected name: Brunei Malay.
  • Code: la-ecc. Saw name: Ecclesiastical ලතින්. Expected name: Ecclesiastical Latin.
  • Code: la-lat. Saw name: Late ලතින්. Expected name: Late Latin.
  • Code: la-med. Saw name: Medieval ලතින්. Expected name: Medieval Latin.
  • Code: la-vul. Saw name: Vulgar ලතින්. Expected name: Vulgar Latin.
  • Code: lng. Saw name: Old High ජර්මානු. Expected name: Old High German.
  • Code: ltc. Saw name: Middle චීන. Expected name: Middle Chinese.
  • Code: meo. Saw name: Kedah මැලේ. Expected name: Kedah Malay.
  • Code: mga. Saw name: Middle අයිරිෂ්. Expected name: Middle Irish.
  • Code: mi. Saw name: Maori. Expected name: Māori.
  • Code: ml. Saw name: මැලේalam. Expected name: මලයාලම්.
  • Code: ms-cla. Saw name: Classical මැලේ. Expected name: Classical Malay.
  • Code: ms-old. Saw name: Old මැලේ. Expected name: Old Malay.
  • Code: nb. Saw name: නෝර්වීජියානු Bokmål. Expected name: Norwegian Bokmål.
  • Code: nds. Saw name: Low ජර්මානු. Expected name: Low German.
  • Code: nds-de. Saw name: ජර්මානු Low ජර්මානු. Expected name: German Low German.
  • Code: nds-nl. Saw name: ඕලන්ද Low Saxon. Expected name: Dutch Low Saxon.
  • Code: nn. Saw name: නෝර්වීජියානු Nynorsk. Expected name: Norwegian Nynorsk.
  • Code: nod. Saw name: Northern තායි. Expected name: Northern Thai.
  • Code: obr. Saw name: Old බුරුම. Expected name: Old Burmese.
  • Code: och. Saw name: Old චීන. Expected name: Old Chinese.
  • Code: odt. Saw name: Old ඕලන්ද. Expected name: Old Dutch.
  • Code: oge. Saw name: Old ජෝර්ජියානු. Expected name: Old Georgian.
  • Code: ohu. Saw name: Old හංගේරියානු. Expected name: Old Hungarian.
  • Code: ojp. Saw name: Old ජපන්. Expected name: Old Japanese.
  • Code: okm. Saw name: Middle කොරියානු. Expected name: Middle Korean.
  • Code: oko. Saw name: Old කොරියානු. Expected name: Old Korean.
  • Code: osp. Saw name: Old ස්පාඤ්ඤ. Expected name: පුරාතන ස්පාඤ්ඤ.
  • Code: ota. Saw name: Ottoman තුර්කි. Expected name: Ottoman Turkish.
  • Code: pal. Saw name: Middle පර්සියානු. Expected name: මධ්‍යකාලීන පර්සියානු.
  • Code: pdc. Saw name: Pennsylvania ජර්මානු. Expected name: Pennsylvania German.
  • Code: peo. Saw name: Old පර්සියානු. Expected name: Old Persian.
  • Code: plu. Saw name: පාලිkur. Expected name: Palikur.
  • Code: poz-cet-pro. Saw name: Proto-Central-Eastern මැලේo-Polynesian. Expected name: Proto-Central-Eastern Malayo-Polynesian.
  • Code: poz-mcm-pro. Saw name: Proto-මැලේo-Chamic. Expected name: Proto-Malayo-Chamic.
  • Code: poz-mly-pro. Saw name: Proto-මැලේic. Expected name: Proto-Malayic.
  • Code: poz-msa-pro. Saw name: Proto-මැලේo-Sumbawan. Expected name: Proto-Malayo-Sumbawan.
  • Code: poz-pro. Saw name: Proto-මැලේo-Polynesian. Expected name: Proto-Malayo-Polynesian.
  • Code: pqe-pro. Saw name: Proto-Eastern මැලේo-Polynesian. Expected name: Proto-Eastern Malayo-Polynesian.
  • Code: rm. Saw name: Romansch. Expected name: Romansh.
  • Code: rmf. Saw name: Kalo ෆින්ලන්ත Romani. Expected name: Kalo Finnish Romani.
  • Code: rmg. Saw name: Traveller නෝර්වීජියානු. Expected name: Traveller Norwegian.
  • Code: roa-opt. Saw name: Old Galician-පෘතුගීසි. Expected name: Old Galician-Portuguese.
  • Code: ruo. Saw name: Istro-රුමේනියානු. Expected name: Istro-Romanian.
  • Code: ruq. Saw name: Megleno-රුමේනියානු. Expected name: Megleno-Romanian.
  • Code: sa-ved. Saw name: Vedic සංස්කෘත. Expected name: Vedic Sanskrit.
  • Code: sga. Saw name: Old අයිරිෂ්. Expected name: Old Irish.
  • Code: sit. Saw name: Sino-ටිබෙට්. Expected name: Sino-Tibetan.
  • Code: sit-pro. Saw name: Proto-Sino-ටිබෙට්. Expected name: Proto-Sino-Tibetan.
  • Code: sou. Saw name: Southern තායි. Expected name: Southern Thai.
  • Code: tbq-lob-pro. Saw name: Proto-Lolo-බුරුම. Expected name: Proto-Lolo-Burmese.
  • Code: trk-oat. Saw name: Old Anatolian තුර්කි. Expected name: Old Anatolian Turkish.
  • Code: xaa. Saw name: Andalusian අරාබි. Expected name: Andalusian Arabic.
  • Code: xcl. Saw name: Old ආමේනියානු. Expected name: Old Armenian.
  • Code: zlw-ocs. Saw name: Old චෙක්. Expected name: Old Czech.
  • Code: zlw-opl. Saw name: Old පෝලන්ත. Expected name: Old Polish.
  • The code abr and the canonical name Abron should be removed; they are not found in Module:etymology languages/data.
  • Old Latin, the canonical name for the code itc-ola, is wrong; it should be පුරාතන ලතින්.
  • The canonical name පුරාතන ලතින් (itc-ola) is missing.
  • The code kze and the canonical name Kosena should be removed; they are not found in Module:etymology languages/data.
  • Literary Chinese, the canonical name for the code lzh-lit, is wrong; it should be Literary Chinese.
  • The canonical name බ්‍රසීල පෘතුගීසි (pt-BR) is missing.
  • Brazilian Portuguese, the canonical name for the code pt-BR, is wrong; it should be බ්‍රසීල පෘතුගීසි.
  • The code ska and the canonical name Skagit should be removed; they are not found in Module:etymology languages/data.
  • slh, the code for the canonical name Southern Lushootseed, is wrong; it should be slh.
  • The code sno and the canonical name Snohomish should be removed; they are not found in Module:etymology languages/data.
  • The code wss and the canonical name Wasa should be removed; they are not found in Module:etymology languages/data.
  • The code xaq and the canonical name Aquitanian should be removed; they are not found in Module:etymology languages/data.
  • xnn, the code for the canonical name Northern Kankanaey, is wrong; it should be xnn.
  • The code abr and the canonical name Abron should be removed; they are not found in Module:etymology languages/data.
  • Old Latin, the canonical name for the code itc-ola, is wrong; it should be පුරාතන ලතින්.
  • The code kze and the canonical name Kosena should be removed; they are not found in Module:etymology languages/data.
  • Literary Chinese, the canonical name for the code lzh-lit, is wrong; it should be Literary Chinese.
  • Brazilian Portuguese, the canonical name for the code pt-BR, is wrong; it should be බ්‍රසීල පෘතුගීසි.
  • The code ska and the canonical name Skagit should be removed; they are not found in Module:etymology languages/data.
  • slh, the code for the canonical name Southern Lushootseed, is wrong; it should be slh.
  • The code sno and the canonical name Snohomish should be removed; they are not found in Module:etymology languages/data.
  • The code wss and the canonical name Wasa should be removed; they are not found in Module:etymology languages/data.
  • The code xaq and the canonical name Aquitanian should be removed; they are not found in Module:etymology languages/data.
  • xnn, the code for the canonical name Northern Kankanaey, is wrong; it should be xnn.
  • yrk, the code for the canonical name Nenets, is wrong; it should be yrk.
  • Indo-Iranian, the canonical name for the code iir, is wrong; it should be ඉන්දු-ඉරාන.
  • Middle Indo-Aryan, the canonical name for the code inc-mid, is wrong; it should be මධ්‍යකාලීන ඉන්දු-ආර්ය.
  • yrk, the code for the canonical name Nenets, is wrong; it should be yrk.
  • Baïnounk Gubëeher, the canonical name for the code alv-bgu, is wrong; it should be Bainouk Gubeeher.
  • The canonical name Bainouk Gubeeher (alv-bgu) is missing.
  • The canonical name Naʼvi (art-nav) is missing.
  • Na'vi, the canonical name for the code art-nav, is wrong; it should be Naʼvi.
  • The canonical name Proto-Sabaki (bnt-sab-pro) is missing.
  • The canonical name Demotic Egyptian (egx-dem) is missing.
  • Demotic, the canonical name for the code egx-dem, is wrong; it should be Demotic Egyptian.
  • The code ggn and the canonical name Eastern Gurung should be removed; they are not found in a submodule of Module:languages.
  • The canonical name Gimi (Papuan) (gim) is missing.
  • Gimi (Goroka), the canonical name for the code gim, is wrong; it should be Gimi (Papuan).
  • The canonical name Magi (gkd) is missing.
  • Magɨ, the canonical name for the code gkd, is wrong; it should be Magi.
  • The canonical name Mirning (gmr) is missing.
  • The code gn and the canonical name Guaraní should be removed; they are not found in a submodule of Module:languages.
  • Classical Guaraní, the canonical name for the code gn-cls, is wrong; it should be Classical Guarani.
  • The canonical name Classical Guarani (gn-cls) is missing.
  • Western Bolivian Guaraní, the canonical name for the code gnw, is wrong; it should be Western Bolivian Guarani.
  • The canonical name Western Bolivian Guarani (gnw) is missing.
  • The canonical name Gorwaa (gow) is missing.
  • Gorowa, the canonical name for the code gow, is wrong; it should be Gorwaa.
  • The code grb and the canonical name Grebo should be removed; they are not found in a submodule of Module:languages.
  • Paraguayan Guaraní, the canonical name for the code gug, is wrong; it should be Paraguayan Guarani.
  • The canonical name Paraguayan Guarani (gug) is missing.
  • The canonical name Eastern Bolivian Guarani (gui) is missing.
  • Eastern Bolivian Guaraní, the canonical name for the code gui, is wrong; it should be Eastern Bolivian Guarani.
  • The canonical name Mbya Guarani (gun) is missing.
  • Mbyá Guaraní, the canonical name for the code gun, is wrong; it should be Mbya Guarani.
  • The canonical name Gurung (gvr) is missing.
  • Western Gurung, the canonical name for the code gvr, is wrong; it should be Gurung.
  • Proto-Hmong, the canonical name for the code hmn-pro, is wrong; it should be Proto-Hmongic.
  • The canonical name Proto-Hmongic (hmn-pro) is missing.
  • The canonical name Proto-Mienic (hmx-mie-pro) is missing.
  • Proto-Mien, the canonical name for the code hmx-mie-pro, is wrong; it should be Proto-Mienic.
  • The canonical name Proto-Dangari (inc-dng-pro) is missing.
  • Maori, the canonical name for the code mi, is wrong; it should be Māori.
  • The canonical name Māori (mi) is missing.
  • The code nds-de and the canonical name German Low German should be removed; they are not found in a submodule of Module:languages.
  • The code nds-nl and the canonical name Dutch Low Saxon should be removed; they are not found in a submodule of Module:languages.
  • The canonical name Proto-Binanderean (ngf-bin-pro) is missing.
  • The canonical name Chungli Ao (njo-jgl) is missing.
  • The canonical name Noakhali (oak) is missing.
  • Proto-Trique, the canonical name for the code omq-tri-pro, is wrong; it should be Proto-Triqui.
  • The canonical name Proto-Triqui (omq-tri-pro) is missing.
  • The code ont and the canonical name Ontenu should be removed; they are not found in a submodule of Module:languages.
  • paa-kom, the code for the canonical name Kómnzo, is wrong; it should be paa-kmn.
  • The canonical name Leitre (paa-lei) is missing.
  • The canonical name Romansh (rm) is missing.
  • Romansch, the canonical name for the code rm, is wrong; it should be Romansh.
  • The code roa-afr and the canonical name African Romance should be removed; they are not found in a submodule of Module:languages.
  • The canonical name Peba (sai-peb) is missing.
  • The canonical name Samalian (sem-sam) is missing.
  • syd-fne, the code for the canonical name Forest Nenets, is wrong; it should be yrk-for.
  • The canonical name Early Old Oghuz (trk-eog) is missing.
  • The canonical name Gashowu Yokuts (yok-gsy) is missing.
  • Gashowu, the canonical name for the code yok-gsy, is wrong; it should be Gashowu Yokuts.
  • The canonical name Palewyami Yokuts (yok-ply) is missing.
  • Palewyami, the canonical name for the code yok-ply, is wrong; it should be Palewyami Yokuts.
  • Baïnounk Gubëeher, the canonical name for the code alv-bgu, is wrong; it should be Bainouk Gubeeher.
  • Na'vi, the canonical name for the code art-nav, is wrong; it should be Naʼvi.
  • The code bnt-sab-pro (Proto-Sabaki) is missing.
  • Demotic, the canonical name for the code egx-dem, is wrong; it should be Demotic Egyptian.
  • The code ggn and the canonical name Eastern Gurung should be removed; they are not found in a submodule of Module:languages.
  • Gimi (Goroka), the canonical name for the code gim, is wrong; it should be Gimi (Papuan).
  • Magɨ, the canonical name for the code gkd, is wrong; it should be Magi.
  • The code gmr (Mirning) is missing.
  • The code gn and the canonical name Guaraní should be removed; they are not found in a submodule of Module:languages.
  • Classical Guaraní, the canonical name for the code gn-cls, is wrong; it should be Classical Guarani.
  • Western Bolivian Guaraní, the canonical name for the code gnw, is wrong; it should be Western Bolivian Guarani.
  • Gorowa, the canonical name for the code gow, is wrong; it should be Gorwaa.
  • The code grb and the canonical name Grebo should be removed; they are not found in a submodule of Module:languages.
  • Paraguayan Guaraní, the canonical name for the code gug, is wrong; it should be Paraguayan Guarani.
  • Eastern Bolivian Guaraní, the canonical name for the code gui, is wrong; it should be Eastern Bolivian Guarani.
  • Mbyá Guaraní, the canonical name for the code gun, is wrong; it should be Mbya Guarani.
  • Western Gurung, the canonical name for the code gvr, is wrong; it should be Gurung.
  • Proto-Hmong, the canonical name for the code hmn-pro, is wrong; it should be Proto-Hmongic.
  • Proto-Mien, the canonical name for the code hmx-mie-pro, is wrong; it should be Proto-Mienic.
  • The code inc-dng-pro (Proto-Dangari) is missing.
  • Maori, the canonical name for the code mi, is wrong; it should be Māori.
  • The code nds-de and the canonical name German Low German should be removed; they are not found in a submodule of Module:languages.
  • The code nds-nl and the canonical name Dutch Low Saxon should be removed; they are not found in a submodule of Module:languages.
  • The code ngf-bin-pro (Proto-Binanderean) is missing.
  • The code njo-jgl (Chungli Ao) is missing.
  • The code oak (Noakhali) is missing.
  • Proto-Trique, the canonical name for the code omq-tri-pro, is wrong; it should be Proto-Triqui.
  • The code ont and the canonical name Ontenu should be removed; they are not found in a submodule of Module:languages.
  • The code paa-kmn (Kómnzo) is missing.
  • paa-kom, the code for the canonical name Kómnzo, is wrong; it should be paa-kmn.
  • The code paa-lei (Leitre) is missing.
  • Romansch, the canonical name for the code rm, is wrong; it should be Romansh.
  • The code roa-afr and the canonical name African Romance should be removed; they are not found in a submodule of Module:languages.
  • The code sai-peb (Peba) is missing.
  • The code sem-sam (Samalian) is missing.
  • syd-fne, the code for the canonical name Forest Nenets, is wrong; it should be yrk-for.
  • The code trk-eog (Early Old Oghuz) is missing.
  • Gashowu, the canonical name for the code yok-gsy, is wrong; it should be Gashowu Yokuts.
  • Palewyami, the canonical name for the code yok-ply, is wrong; it should be Palewyami Yokuts.
  • The code yrk-for (Forest Nenets) is missing.
  • The code yrk-tun (Tundra Nenets) is missing.
  • Ari (aac) has the invalid family code "paa-pag".
  • Amal (aad) has the invalid family code "paa-iwm".
  • Ambrak (aag) has the invalid family code "qfa-tor".
  • Abu' (aah) has the invalid family code "qfa-tor".
  • Abau (aau) has the invalid family code "paa-spk".
  • Abaga (abg) has the invalid family code "paa-kag".
  • Ambulas (abt) has the invalid family code "paa-spk".
  • Abui (abz) has the invalid family code "qfa-tap".
  • Adang (adn) has the invalid family code "qfa-tap".
  • Obokuitai (afz) has the invalid family code "paa-lkp".
  • Agarabi (agd) has the invalid family code "paa-kag".
  • Angal (age) has the invalid family code "paa-eng".
  • Ainbai (aic) has the invalid family code "paa-brd".
  • Agi (aif) has the invalid family code "qfa-tor".
  • Airoran (air) has the invalid family code "paa-tkw".
  • Angal Heneng (akh) has the invalid family code "paa-eng".
  • Amol (alx) has the invalid family code "qfa-tor".
  • Ama (amm) has the invalid family code "qfa-mal".
  • Amanab (amn) has the invalid family code "paa-brd".
  • Amto (amt) has the invalid family code "paa-asa".
  • Angal Enen (aoe) has the invalid family code "paa-eng".
  • Bragat (aof) has the invalid family code "qfa-tor".
  • Angoram (aog) has the invalid family code "paa-lsp".
  • Mufian (aoj) has the invalid family code "qfa-tor".
  • Bumbita Arapesh (aon) has the invalid family code "qfa-tor".
  • Taikat (aos) has the invalid family code "paa-brd".
  • Bukiyip (ape) has the invalid family code "qfa-tor".
  • Dano (aso) has the invalid family code "paa-kag".
  • Heyo (auk) has the invalid family code "qfa-tor".
  • Molmo One (aun) has the invalid family code "qfa-tor".
  • Aruek (aur) has the invalid family code "qfa-tor".
  • Awyi (auw) has the invalid family code "paa-brd".
  • Auyana (auy) has the invalid family code "paa-kag".
  • Au (avt) has the invalid family code "qfa-tor".
  • Awa (New Guinea) (awb) has the invalid family code "paa-kag".
  • Awera (awr) has the invalid family code "paa-lkp".
  • Awun (aww) has the invalid family code "paa-spk".
  • Ayi (ayq) has the invalid family code "paa-spk".
  • The data key entry_name for Omani Arabic (acx) is invalid.
  • The data key entry_name for Lishanid Noshan (aij) is invalid.
  • The data key entry_name for Abaza (abq) is invalid.
  • The data key entry_name for Aramaic (arc) is invalid.
  • The data key entry_name for Yemeni Arabic (ayn) is invalid.
  • The data key entry_name for Sudanese Arabic (apd) is invalid.
  • The data key entry_name for Adyghe (ady) is invalid.
  • The data key entry_name for North Levantine Arabic (apc) is invalid.
  • The data key entry_name for Aiton (aio) is invalid.
  • The data key entry_name for Najdi Arabic (ars) is invalid.
  • The data key entry_name for Tunisian Arabic (aeb) is invalid.
  • The data key entry_name for Western Neo-Aramaic (amw) is invalid.
  • The data keys entry_name, standardChars for පුරාතන ඉංග්‍රීසි (ang) are invalid.
  • The data key entry_name for ඇල්ජීරියානු අරාබි (arq) is invalid.
  • The data key entry_name for Egyptian Arabic (arz) is invalid.
  • The data key entry_name for Dhofari Arabic (adf) is invalid.
  • The data key entry_name for Libyan Arabic (ayl) is invalid.
  • The data key entry_name for Assyrian Neo-Aramaic (aii) is invalid.
  • The data key entry_name for Arikara (ari) is invalid.
  • The data key entry_name for Tajiki Arabic (abh) is invalid.
  • The data key entry_name for Iraqi Arabic (acm) is invalid.
  • The data key entry_name for Middle Armenian (axm) is invalid.
  • The data key entry_name for Moroccan Arabic (ary) is invalid.
  • The data key entry_name for North Mesopotamian Arabic (ayp) is invalid.
  • The data key entry_name for Alutor (alr) is invalid.
  • The data key entry_name for Baharna Arabic (abv) is invalid.
  • The data key entry_name for Gulf Arabic (afb) is invalid.
  • The data keys entry_name, standardChars for Cypriot Arabic (acy) are invalid.
  • The data key entry_name for Hijazi Arabic (acw) is invalid.
  • The data key entry_name for Uzbeki Arabic (auz) is invalid.
  • The data key entry_name for South Levantine Arabic (ajp) is invalid.
  • The data key standardChars for Acehnese (ace) is invalid.
  • Benabena (bef) has the invalid family code "paa-kag".
  • Akuwagel (bey) has the invalid family code "qfa-tor".
  • Betaf (bfe) has the invalid family code "paa-tkw".
  • Odiai (bhf) has the invalid family code "paa-kwm".
  • Bikaru (bic) has the invalid family code "paa-eng".
  • Nai (bio) has the invalid family code "paa-kwm".
  • Bisorio (bir) has the invalid family code "paa-eng".
  • Berinomo (bit) has the invalid family code "paa-spk".
  • Bahinemo (bjh) has the invalid family code "paa-spk".
  • Binumarien (bjr) has the invalid family code "paa-kag".
  • Berik (bkl) has the invalid family code "paa-tkw".
  • Beneraf (bnv) has the invalid family code "paa-tkw".
  • Bisis (bnw) has the invalid family code "paa-spk".
  • Bagusa (bqb) has the invalid family code "paa-tkw".
  • Biritai (bqq) has the invalid family code "paa-lkp".
  • Burui (bry) has the invalid family code "paa-spk".
  • Bungain (but) has the invalid family code "qfa-tor".
  • Buna (bvn) has the invalid family code "qfa-tor".
  • Pouye (bye) has the invalid family code "paa-spk".
  • Qaqet (byx) has the invalid family code "paa-bng".
  • Boikin (bzf) has the invalid family code "paa-spk".
  • The data key entry_name for Buryat (bua) is invalid.
  • The data key entry_name for Bats (bbl) is invalid.
  • The data keys entry_name, standardChars for Bikol Central (bcl) are invalid.
  • The data key entry_name for Edo (bin) is invalid.
  • The data keys entry_name, standardChars for Buhid (bku) are invalid.
  • The data key entry_name for Burushaski (bsk) is invalid.
  • The data key entry_name for Gahri (bfu) is invalid.
  • The data key entry_name for Botlikh (bph) is invalid.
  • The data key entry_name for Brokkat (bro) is invalid.
  • The data key entry_name for Brahui (brh) is invalid.
  • The data key entry_name for Budukh (bdk) is invalid.
  • The data key entry_name for Balti (bft) is invalid.
  • The data key entry_name for Barzani Jewish Neo-Aramaic (bjf) is invalid.
  • The data key entry_name for Bukhari (bhh) is invalid.
  • Chambri (can) has the invalid family code "paa-lsp".
  • Chenapian (cjn) has the invalid family code "paa-spk".
  • Kairak (ckr) has the invalid family code "paa-bng".
  • The data key entry_name for Classical Mongolian (cmg) is invalid.
  • The data key entry_name for Western Highland Chatino (ctp) is invalid.
  • The data key entry_name for Cahuilla (chl) is invalid.
  • The data key entry_name for Choctaw (cho) is invalid.
  • The data key entry_name for Central Kurdish (ckb) is invalid.
  • The data key entry_name for Chaldean Neo-Aramaic (cld) is invalid.
  • The data key entry_name for Cappadocian Greek (cpg) is invalid.
  • The data key entry_name for Chagatai (chg) is invalid.
  • The data key entry_name for Knaanic (czk) is invalid.
  • The data key entry_name for Messapic (cms) is invalid.
  • The data key entry_name for කොප්ටික් (cop) is invalid.
  • The data keys entry_name, standardChars for Cebuano (ceb) are invalid.
  • The data key entry_name for Kari'na (car) is invalid.
  • The data key entry_name for Idu (clk) is invalid.
  • The data key entry_name for Chukchi (ckt) is invalid.
  • The data keys entry_name, standardChars for Chavacano (cbk) are invalid.
  • The data key standardChars for Cheyenne (chy) is invalid.
  • Dabe (dbe) has the invalid family code "paa-tkw".
  • Edopi (dbf) has the invalid family code "paa-lkp".
  • Fataluku (ddg) has the invalid family code "qfa-tap".
  • Dia (dia) has the invalid family code "qfa-tor".
  • Kapriman (dju) has the invalid family code "paa-spk".
  • Dubu (dmu) has the invalid family code "paa-pau".
  • Daonda (dnd) has the invalid family code "paa-brd".
  • Duvle (duv) has the invalid family code "paa-lkp".
  • The data key entry_name for Middle Dutch (dum) is invalid.
  • The data key entry_name for Tsez (ddo) is invalid.
  • The data key entry_name for Dargwa (dar) is invalid.
  • The data key entry_name for Daur (dta) is invalid.
  • The data key standardChars for Lower Sorbian (dsb) is invalid.
  • Eitiep (eit) has the invalid family code "qfa-tor".
  • Elepi (ele) has the invalid family code "qfa-tor".
  • Elkei (elk) has the invalid family code "qfa-tor".
  • Enga (enq) has the invalid family code "paa-eng".
  • Emem (enr) has the invalid family code "paa-pau".
  • The data key entry_name for Evenki (evn) is invalid.
  • The data key entry_name for Even (eve) is invalid.
  • The data keys entry_name, standardChars for මධ්‍යකාලීන ඉංග්‍රීසි (enm) are invalid.
  • Eritai (ert) has the invalid family code "paa-lkp".
  • The data key entry_name for පුරාතන ප්‍රංශ (fro) is invalid.
  • Fasu (faa) has the invalid family code "paa-kut".
  • Fayu (fau) has the invalid family code "paa-lkp".
  • Fiwaga (fiw) has the invalid family code "paa-kut".
  • Foau (flh) has the invalid family code "paa-lkp".
  • Foi (foi) has the invalid family code "paa-kut".
  • Fore (for) has the invalid family code "paa-kag".
  • Magi (gkd) has its canonical name ("Magi") repeated in the table of aliases.
  • The data key entry_name for Igala (igl) is invalid.
  • The data key entry_name for Ingush (inh) is invalid.
  • The data keys entry_name, standardChars for Ilocano (ilo) are invalid.
  • The data key entry_name for Itsekiri (its) is invalid.
  • The data key entry_name for Judeo-Italian (itk) is invalid.
  • The data key entry_name for Itelmen (itl) is invalid.
  • The data key entry_name for Marsian (ims) is invalid.
  • The data key entry_name for Ifè (ife) is invalid.
  • Iatmul (ian) has the invalid family code "paa-spk".
  • Imonda (imn) has the invalid family code "paa-brd".
  • Inoke-Yate (ino) has the invalid family code "paa-kag".
  • Ipili (ipi) has the invalid family code "paa-eng".
  • Isabi (isa) has the invalid family code "paa-kag".
  • Iteri (itr) has the invalid family code "paa-asa".
  • Itik (itx) has the invalid family code "paa-tkw".
  • Iwam (iwm) has the invalid family code "paa-spk".
  • Sepik Iwam (iws) has the invalid family code "paa-spk".
  • The data key entry_name for Judeo-Tat (jdt) is invalid.
  • The data key entry_name for Judeo-Persian (jpr) is invalid.
  • Jofotek-Bromnya (jbr) has the invalid family code "paa-tkw".
  • Manem (jet) has the invalid family code "paa-brd".
  • The data key entry_name for Khinalug (kjj) is invalid.
  • The data key entry_name for Khvarshi (khv) is invalid.
  • The data key entry_name for Kuvi (kxv) is invalid.
  • The data key entry_name for Kui (India) (kxu) is invalid.
  • The data key entry_name for Northern Kurdish (kmr) is invalid.
  • The data key entry_name for Karaim (kdr) is invalid.
  • The data key entry_name for Konkani (kok) is invalid.
  • The data key entry_name for Kryts (kry) is invalid.
  • The data key entry_name for Karata (kpt) is invalid.
  • The data key entry_name for Khamti (kht) is invalid.
  • The data key entry_name for Kachchi (kfr) is invalid.
  • The data key entry_name for Komi-Permyak (koi) is invalid.
  • The data key entry_name for Koryak (kpy) is invalid.
  • The data key entry_name for Ket (ket) is invalid.
  • The data key entry_name for Kabardian (kbd) is invalid.
  • The data key entry_name for Bagvalal (kva) is invalid.
  • The data key entry_name for Khowar (khw) is invalid.
  • The data key entry_name for (khb) is invalid.
  • The data key entry_name for Kalaktang Monpa (kkf) is invalid.
  • The data keys entry_name, standardChars for Kankanaey (kne) are invalid.
  • The data key entry_name for Khalaj (klj) is invalid.
  • The data key entry_name for Karakalpak (kaa) is invalid.
  • The data key entry_name for Bezhta (kap) is invalid.
  • The data key entry_name for Nubi (kcn) is invalid.
  • Kamano (kbq) has the invalid family code "paa-kag".
  • Keder (kdy) has the invalid family code "paa-tkw".
  • Keak (keh) has the invalid family code "paa-spk".
  • Ket (ket) has the invalid family code "qfa-yno".
  • Kewa (kew) has the invalid family code "paa-eng".
  • Kapauri (khp) has the invalid family code "paa-tkw".
  • Kilmeri (kih) has the invalid family code "paa-brd".
  • Kirikiri (kiy) has the invalid family code "paa-lkp".
  • East Kewa (kjs) has the invalid family code "paa-eng".
  • Erave (kjy) has the invalid family code "paa-eng".
  • Kwerisa (kkb) has the invalid family code "paa-lkp".
  • Kabola (klz) has the invalid family code "qfa-tap".
  • Awtuw (kmn) has the invalid family code "paa-spk".
  • Kwoma (kmo) has the invalid family code "paa-spk".
  • Kamasau (kms) has the invalid family code "qfa-tor".
  • Kanite (kmu) has the invalid family code "paa-kag".
  • Kaningra (knr) has the invalid family code "paa-spk".
  • Kafoa (kpu) has the invalid family code "qfa-tap".
  • Krisa (ksi) has the invalid family code "paa-msk".
  • Kwinsu (kuc) has the invalid family code "paa-tkw".
  • Wersing (kvw) has the invalid family code "qfa-tap".
  • Kwerba (kwe) has the invalid family code "paa-tkw".
  • Kwanga (kwj) has the invalid family code "paa-spk".
  • Kwesten (kwt) has the invalid family code "paa-tkw".
  • Koiwat (kxt) has the invalid family code "paa-spk".
  • Kyaka (kyc) has the invalid family code "paa-eng".
  • Keyagana (kyg) has the invalid family code "paa-kag".
  • Kambaira (kyy) has the invalid family code "paa-kag".
  • The translit field in the data table for S'gaw Karen (ksw) specifies the module Module:ksw-translit, which does not exist.
  • The translit field in the data table for Karata (kpt) specifies the module Module:kpt-translit, which does not exist.
  • The translit field in the data table for Khinalug (kjj) specifies the module Module:kjj-translit, which does not exist.
  • The translit field in the data table for Khakas (kjh) specifies the module Module:kjh-translit, which does not exist.
  • The translit field in the data table for Eastern Pwo (kjp) specifies the module Module:kjp-translit, which does not exist.
  • The translit field in the data table for Khvarshi (khv) specifies the module Module:khv-translit, which does not exist.
  • The translit field in the data table for Koryak (kpy) specifies the module Module:kpy-translit, which does not exist.
  • The translit field in the data table for Kui (India) (kxu) specifies the module Module:kxv-translit, which does not exist.
  • The translit field in the data table for Kuvi (kxv) specifies the module Module:kxv-translit, which does not exist.
  • The data key entry_name for Livonian (liv) is invalid.
  • The data key entry_name for Lak (lbe) is invalid.
  • The data key entry_name for Luiseño (lui) is invalid.
  • The data key entry_name for Laz (lzz) is invalid.
  • The data key entry_name for Lezgi (lez) is invalid.
  • The data key entry_name for Ladino (lad) is invalid.
  • The data key entry_name for Laki (lki) is invalid.
  • The data key entry_name for Lishana Deni (lsd) is invalid.
  • Lahnda (lah) has the ancestor Old Punjabi (inc-opa) listed in its ancestor field, which is redundant, since it is determined to be ancestral automatically.
  • Lembena (leq) has the invalid family code "paa-eng".
  • Pahi (lgt) has the invalid family code "paa-spk".
  • Laeko-Libuat (lkl) has the invalid family code "qfa-tor".
  • Lilau (lll) has the invalid family code "qfa-tor".
  • Lepki (lpe) has the invalid family code "paa-pau".
  • Aruop (lsr) has the invalid family code "qfa-tor".
  • The sort_key field in the data table for Lak (lbe) specifies the module Module:lbe-sortkey, which does not exist.
  • The sort_key field in the data table for Laboya (lmy) specifies the module Module:lmy-sortkey, which does not exist.
  • The translit field in the data table for Laki (lki) specifies the module Module:lki-translit, which does not exist.
  • The translit field in the data table for Lak (lbe) specifies the module Module:lbe-translit, which does not exist.
  • The translit field in the data table for Limbu (lif) specifies the module Module:lif-translit, which does not exist.
  • The data key entry_name for Eastern Mari (mhr) is invalid.
  • The data key entry_name for Kedah Malay (meo) is invalid.
  • The data key entry_name for Montagnais (moe) is invalid.
  • The data key entry_name for Miskito (miq) is invalid.
  • The data key entry_name for Mozarabic (mxi) is invalid.
  • The data key entry_name for Moksha (mdf) is invalid.
  • The data key entry_name for Middle Irish (mga) is invalid.
  • The data key entry_name for Pattani Malay (mfa) is invalid.
  • Sepik Mari (mbx) has the invalid family code "paa-spk".
  • Siliput (mkc) has the invalid family code "qfa-tor".
  • Manambu (mle) has the invalid family code "paa-spk".
  • Musan (mmp) has the invalid family code "paa-asa".
  • Mander (mqr) has the invalid family code "paa-tkw".
  • Elseng (mrf) has the invalid family code "paa-brd".
  • Dineor (mrx) has the invalid family code "paa-tkw".
  • Murik (New Guinea) (mtf) has the invalid family code "paa-lsp".
  • Nabi (mty) has the invalid family code "qfa-tor".
  • Massep (mvs) has the invalid family code "paa-tkw".
  • Juwal (mwb) has the invalid family code "qfa-tor".
  • Monumbo (mxk) has the invalid family code "qfa-tor".
  • The sort_key field in the data table for Middle Irish (mga) specifies the module Module:mga-sortkey, which does not exist.
  • The sort_key field in the data table for Moksha (mdf) specifies the module Module:mdf-sortkey, which does not exist.
  • The sort_key field in the data table for Western Mari (mrj) specifies the module Module:mrj-sortkey, which does not exist.
  • The data key standardChars for සර්ව භාෂාමය (mul) is invalid.
  • The translit field in the data table for Mozarabic (mxi) specifies the module Module:mxi-translit, which does not exist.
  • The translit field in the data table for Manda (India) (mha) specifies the module Module:kxv-translit, which does not exist.
  • The data key entry_name for Nupe (nup) is invalid.
  • The data key entry_name for Nzadi (nzd) is invalid.
  • The data key entry_name for Manangba (nmm) is invalid.
  • The data key entry_name for Classical Nahuatl (nci) is invalid.
  • The data key entry_name for Nyenkha (neh) is invalid.
  • The data key entry_name for Nivkh (niv) is invalid.
  • The data key entry_name for Northern Thai (nod) is invalid.
  • Namla (naa) has the invalid family code "paa-pau".
  • Nakwi (nax) has the invalid family code "qfa-mal".
  • Ningera (nby) has the invalid family code "paa-brd".
  • Nete (net) has the invalid family code "paa-eng".
  • Chiripá (nhd) lists the invalid language code "gn" as its ancestor.
  • Nimo (niw) has the invalid family code "paa-asa".
  • Ningil (niz) has the invalid family code "qfa-tor".
  • Namia (nnm) has the invalid family code "paa-spk".
  • Ngala (nud) has the invalid family code "paa-spk".
  • Mehek (nux) has the invalid family code "paa-spk".
  • The sort_key field in the data table for Nivkh (niv) specifies the module Module:niv-sortkey, which does not exist.
  • The translit field in the data table for Naskapi (nsk) specifies the module Module:nsk-translit, which does not exist.
  • The translit field in the data table for Nivkh (niv) specifies the module Module:niv-translit, which does not exist.
  • The translit field in the data table for Nganasan (nio) specifies the module Module:nio-translit, which does not exist.
  • The data key entry_name for Pontic Greek (pnt) is invalid.
  • The data key entry_name for Nigerian Pidgin (pcm) is invalid.
  • The data key entry_name for Pipil (ppl) is invalid.
  • The data key entry_name for Pahari-Potwari (phr) is invalid.
  • The data keys entry_name, standardChars for Kapampangan (pam) are invalid.
  • The data key entry_name for Pangasinan (pag) is invalid.
  • The data key entry_name for Pawnee (paw) is invalid.
  • The data key entry_name for Phalura (phl) is invalid.
  • The data key entry_name for Phake (phk) is invalid.
  • The data key entry_name for ප්‍රාකෘත (pra) is invalid.
  • The data key entry_name for Paelignian (pgn) is invalid.
  • Papasena (pas) has the invalid family code "paa-lkp".
  • Pyu (New Guinea) (pby) has the invalid family code "paa-asa".
  • Pagi (pgi) has the invalid family code "paa-brd".
  • The translit field in the data table for Pengo (peg) specifies the module Module:kxv-translit, which does not exist.
  • The data key entry_name for Carpathian Rusyn (rue) is invalid.
  • The data key entry_name for Rutul (rut) is invalid.
  • The data key entry_name for Aromanian (rup) is invalid.
  • Rasawa (rac) has the invalid family code "paa-lkp".
  • Yahang (rhp) has the invalid family code "qfa-tor".
  • Murkim (rmh) has the invalid family code "paa-pau".
  • The sort_key field in the data table for Carpathian Rusyn (rue) specifies the module Module:rue-sortkey, which does not exist.
  • The data key standardChars for Tarifit (rif) is invalid.
  • The data key standardChars for Pannonian Rusyn (rsk) is invalid.
  • The translit field in the data table for Rutul (rut) specifies the module Module:rut-translit, which does not exist.
  • The translit field in the data table for Pannonian Rusyn (rsk) specifies the module Module:rsk-translit, which does not exist.
  • Daonda (dnd) has no family.
  • The data key entry_name for Kildin Sami (sjd) is invalid.
  • The data key entry_name for Lule Sami (smj) is invalid.
  • The data key entry_name for Pite Sami (sje) is invalid.
  • The data key entry_name for Shihhi Arabic (ssh) is invalid.
  • The data key entry_name for Kemi Sami (sjk) is invalid.
  • The data key entry_name for Classical Syriac (syc) is invalid.
  • The data keys entry_name, standardChars for Old Irish (sga) are invalid.
  • The data key entry_name for Southern Kurdish (sdh) is invalid.
  • The data key entry_name for Ume Sami (sju) is invalid.
  • The data key entry_name for Chadian Arabic (shu) is invalid.
  • The data key entry_name for Saraiki (skr) is invalid.
  • The data key entry_name for Ter Sami (sjt) is invalid.
  • The data key entry_name for Inari Sami (smn) is invalid.
  • The data key entry_name for Samogitian (sgs) is invalid.
  • The data key entry_name for Akkala Sami (sia) is invalid.
  • The data key entry_name for Tashelhit (shi) is invalid.
  • The data key entry_name for Southern Sami (sma) is invalid.
  • The data key entry_name for South Picene (spx) is invalid.
  • The data key entry_name for Skolt Sami (sms) is invalid.
  • Sause (sao) has the invalid family code "paa-tkw".
  • Seti (sbi) has the invalid family code "qfa-tor".
  • Kimki (sbt) has the invalid family code "paa-pau".
  • Sos Kundi (sdk) has the invalid family code "paa-spk".
  • Seim (sim) has the invalid family code "paa-spk".
  • Sinagen (siu) has the invalid family code "qfa-tor".
  • Sumariup (siv) has the invalid family code "paa-spk".
  • Skou (skv) has the invalid family code "paa-msk".
  • Auwe (smf) has the invalid family code "paa-brd".
  • Simbali (smg) has the invalid family code "paa-bng".
  • Siane (snp) has the invalid family code "paa-kag".
  • Senggi (snu) has the invalid family code "paa-brd".
  • Saniyo-Hiyewe (sny) has the invalid family code "paa-spk".
  • The sort_key field in the data table for Pite Sami (sje) specifies the module Module:sje-sortkey, which does not exist.
  • The sort_key field in the data table for Ume Sami (sju) specifies the module Module:sju-sortkey, which does not exist.
  • The sort_key field in the data table for Southern Sami (sma) specifies the module Module:sma-sortkey, which does not exist.
  • The sort_key field in the data table for Lule Sami (smj) specifies the module Module:smj-sortkey, which does not exist.
  • The sort_key field in the data table for Inari Sami (smn) specifies the module Module:smn-sortkey, which does not exist.
  • The sort_key field in the data table for Old Irish (sga) specifies the module Module:sga-sortkey, which does not exist.
  • Sowanda (sow) has the invalid family code "paa-brd".
  • Sengo (spk) has the invalid family code "paa-spk".
  • Isirawa (srl) has the invalid family code "paa-tkw".
  • Samberigi (ssx) has the invalid family code "paa-eng".
  • Seta (stf) has the invalid family code "qfa-tor".
  • The translit field in the data table for Ter Sami (sjt) specifies the module Module:sjt-translit, which does not exist.
  • The translit field in the data table for Shughni (sgh) specifies the module Module:sgh-translit, which does not exist.
  • The translit field in the data table for Svan (sva) specifies the module Module:sva-translit, which does not exist.
  • The translit field in the data table for Southern Kurdish (sdh) specifies the module Module:sdh-translit, which does not exist.
  • The data key entry_name for Northwestern Tamang (tmk) is invalid.
  • The data key entry_name for Eastern Tamang (taj) is invalid.
  • The data key entry_name for Chali (tgf) is invalid.
  • The data key entry_name for Thracian (txh) is invalid.
  • The data key entry_name for Tshangla (tsj) is invalid.
  • The data key entry_name for Tabasaran (tab) is invalid.
  • The data key entry_name for Western Tamang (tdg) is invalid.
  • The data key entry_name for Eastern Gorkha Tamang (tge) is invalid.
  • The data key entry_name for Tsakhur (tkr) is invalid.
  • The data key entry_name for Tai Nüa (tdd) is invalid.
  • The data key entry_name for Turoyo (tru) is invalid.
  • The data key entry_name for Tuareg (tmh) is invalid.
  • The sort_key field in the data table for Talossan (tzl) specifies the module Module:tzl-sortkey, which does not exist.
  • The data key standardChars for Tocharian B (txb) is invalid.
  • Tause (tad) has the invalid family code "paa-lkp".
  • North Tairora (tbg) has the invalid family code "paa-kag".
  • Taworta (tbp) has the invalid family code "paa-lkp".
  • Kaiy (tcq) has the invalid family code "paa-lkp".
  • Doutai (tds) has the invalid family code "paa-lkp".
  • Torricelli (tei) has the invalid family code "qfa-tor".
  • Trimuris (tip) has the invalid family code "paa-tkw".
  • Tofanma (tlg) has the invalid family code "paa-pau".
  • Samarokena (tmj) has the invalid family code "paa-tkw".
  • Iau (tmu) has the invalid family code "paa-lkp".
  • Kula (tpg) has the invalid family code "qfa-tap".
  • The translit field in the data table for Tsakhur (tkr) specifies the module Module:tkr-translit, which does not exist.
  • The translit field in the data table for Tangut (txg) specifies the module Module:txg-translit, which does not exist.
  • Towei (ttn) has the invalid family code "paa-pau".
  • Sikaritai (tty) has the invalid family code "paa-lkp".
  • Wiarumus (tua) has the invalid family code "qfa-tor".
  • Taulil (tuh) has the invalid family code "paa-bng".
  • Tereweng (twg) has the invalid family code "qfa-tap".
  • Tuwari (tww) has the invalid family code "paa-spk".
  • Tabriak (tzx) has the invalid family code "paa-lsp".
  • The data key entry_name for Udi (udi) is invalid.
  • The data key entry_name for Munsee (umu) is invalid.
  • The data key entry_name for Marrucinian (umc) is invalid.
  • The data key entry_name for Ubykh (uby) is invalid.
  • The data key entry_name for Olukumi (ulb) is invalid.
  • The data key entry_name for Ulch (ulc) is invalid.
  • The sort_key field in the data table for Ulch (ulc) specifies the module Module:ulc-sortkey, which does not exist.
  • The sort_key field in the data table for Udmurt (udm) specifies the module Module:udm-sortkey, which does not exist.
  • The sort_key field in the data table for Ubykh (uby) specifies the module Module:uby-sortkey, which does not exist.
  • Afra (ulf) has the invalid family code "paa-pau".
  • Umeda (upi) has the invalid family code "paa-brd".
  • Urim (uri) has the invalid family code "qfa-tor".
  • Ura (Papua New Guinea) (uro) has the invalid family code "paa-bng".
  • Urat (urt) has the invalid family code "qfa-tor".
  • Urimo (urx) has the invalid family code "qfa-tor".
  • Orya (ury) has the invalid family code "paa-tkw".
  • Usarufa (usa) has the invalid family code "paa-kag".
  • The data key entry_name for Makhuwa (vmw) is invalid.
  • The data keys entry_name, standardChars for Votic (vot) are invalid.
  • The sort_key field in the data table for East Franconian (vmf) specifies the module Module:vmf-sortkey, which does not exist.
  • Vanimo (vam) has the invalid family code "paa-msk".
  • Valman (van) has the invalid family code "qfa-tor".
  • Vitou (vto) has the invalid family code "paa-tkw".
  • The data key entry_name for Middle Welsh (wlm) is invalid.
  • The data key entry_name for Vilamovian (wym) is invalid.
  • The data keys entry_name, standardChars for Waray-Waray (war) are invalid.
  • The sort_key field in the data table for Middle Welsh (wlm) specifies the module Module:wlm-sortkey, which does not exist.
  • Waffa (waj) has the invalid family code "paa-kag".
  • Waritai (wbe) has the invalid family code "paa-lkp".
  • Yafi (wfg) has the invalid family code "paa-pau".
  • Minidien (wii) has the invalid family code "qfa-tor".
  • Wam (wmo) has the invalid family code "qfa-tor".
  • Womo (wmx) has the invalid family code "paa-msk".
  • Wanap (wnp) has the invalid family code "qfa-tor".
  • Wogamusin (wog) has the invalid family code "paa-spk".
  • Hanga Hundi (wos) has the invalid family code "paa-spk".
  • Warapu (wra) has the invalid family code "paa-msk".
  • Waris (wrs) has the invalid family code "paa-brd".
  • Watakataui (wtk) has the invalid family code "paa-spk".
  • Wutung (wut) has the invalid family code "paa-msk".
  • The data key entry_name for Classical Tibetan (xct) is invalid.
  • The data key entry_name for Faliscan (xfa) is invalid.
  • The data key entry_name for Hernican (xhr) is invalid.
  • The data key entry_name for Alanic (xln) is invalid.
  • The data key entry_name for Andalusian Arabic (xaa) is invalid.
  • The data key entry_name for Kaitag (xdq) is invalid.
  • The data key entry_name for Old Armenian (xcl) is invalid.
  • The data key entry_name for Zhang-Zhung (xzh) is invalid.
  • The data key entry_name for Dacian (xdc) is invalid.
  • The data key entry_name for Galatian (xga) is invalid.
  • The data key entry_name for Volscian (xvo) is invalid.
  • The data key entry_name for Aequian (xae) is invalid.
  • The data key entry_name for Bactrian (xbc) is invalid.
  • The data key entry_name for Karakhanid (xqa) is invalid.
  • The data key entry_name for Vestinian (xvs) is invalid.
  • The data key entry_name for Kurtöp (xkz) is invalid.
  • The data key entry_name for Punic (xpu) is invalid.
  • The data key entry_name for Sherpa (xsr) is invalid.
  • The data key entry_name for Umbrian (xum) is invalid.
  • The data key entry_name for Middle Mongol (xng) is invalid.
  • The data key entry_name for Narragansett (xnt) is invalid.
  • The data key entry_name for Bulgar (xbo) is invalid.
  • The sort_key field in the data table for Kalmyk (xal) specifies the module Module:xal-sortkey, which does not exist.
  • The translit field in the data table for Kamassian (xas) specifies the module Module:xas-translit, which does not exist.
  • The translit field in the data table for Written Oirat (xwo) specifies the module Module:xwo-translit, which does not exist.
  • The translit field in the data table for Chuvan (xcv) specifies the module Module:xcv-translit, which does not exist.
  • Kauwera (xau) has the invalid family code "paa-tkw".
  • Kombio (xbi) has the invalid family code "qfa-tor".
  • Kembra (xkw) has the invalid family code "paa-pau".
  • Pumpokol (xpm) has the invalid family code "qfa-yso".
  • Arin (xrn) has the invalid family code "qfa-yso".
  • Karawa (xrw) has the invalid family code "paa-spk".
  • Assan (xss) has the invalid family code "qfa-yso".
  • Kwerba Mamberamo (xwr) has the invalid family code "paa-tkw".
  • The data key entry_name for Tundra Nenets (yrk) is invalid.
  • The data key entry_name for Sirenik (ysr) is invalid.
  • The data key entry_name for Yevanic (yej) is invalid.
  • The data key entry_name for Khamnigan Mongol (ykh) is invalid.
  • The translit field in the data table for Yamphu (ybi) specifies the module Module:ybi-translit, which does not exist.
  • The translit field in the data table for Sirenik (ysr) specifies the module Module:ysr-translit, which does not exist.
  • The translit field in the data table for Yazghulami (yah) specifies the module Module:yah-translit, which does not exist.
  • The translit field in the data table for Southern Yukaghir (yux) specifies the module Module:yux-translit, which does not exist.
  • Yawiyo (ybx) has the invalid family code "paa-spk".
  • Yaweyuha (yby) has the invalid family code "paa-kag".
  • Yangum Dey (yde) has the invalid family code "qfa-tor".
  • Yimas (yee) has the invalid family code "paa-lsp".
  • Yetfa (yet) has the invalid family code "paa-pau".
  • Yapunda (yev) has the invalid family code "qfa-tor".
  • Yangum Gel (ygl) has the invalid family code "qfa-tor".
  • Yagaria (ygr) has the invalid family code "paa-kag".
  • Yis (yis) has the invalid family code "qfa-tor".
  • Yelogu (ylg) has the invalid family code "paa-spk".
  • Yil (yll) has the invalid family code "qfa-tor".
  • Yambes (ymb) has the invalid family code "qfa-tor".
  • Yangum Mon (ymo) has the invalid family code "qfa-tor".
  • Yessan-Mayo (yss) has the invalid family code "paa-spk".
  • Yug (yug) has the invalid family code "qfa-yno".
  • Karkar-Yuri (yuj) has the invalid family code "paa-pau".
  • Kalou (ywa) has the invalid family code "paa-spk".
  • Torricelli Yau (yyu) has the invalid family code "qfa-tor".
  • The sort_key field in the data table for Zazaki (zza) specifies the module Module:zza-sortkey, which does not exist.
  • Kott (zko) has the invalid family code "qfa-yso".
  • Makolkol (zmh) has the invalid family code "paa-bng".

Checks performed

[සංස්කරණය]

For multiple data modules:

  • Codes for languages, families and etymology-only languages must be unique and cannot clash with one another.
  • Canonical names for languages, families, and etymology-only languages must not be found in the list of other names.
  • Each name in the list of other names must appear only once.
  • otherNames, if present, must be an array.
  • Wikidata item IDs must be a positive integer or a string starting with Q and ending with decimal digits.

The following must be true of the data used by Module:languages:

  • Each code must be defined in the correct submodule according to whether it is two-letter, three-letter or exceptional.
  • The canonical name (field 1) must be present and must not be the same as the canonical name of another language.
  • If field 2 is not nil, it must a valid Wikidata item ID.
  • If field 3 or family is given and not nil, it must be a valid family code.
  • If field 4 or scripts is given and not nil, it must be an array, and each string in the array must be a valid script code.
  • If ancestors is given, it must be an array, and each string in the array must be a valid language or etymology language code.
  • If family is given, it must be a valid family code.
  • If type is given, it must be one of the recognised values (regular, reconstructed, appendix-constructed).
  • If entry_name is given, it must be a table that contains either two arrays (from and to) or a string (remove_diacritics) or both.
  • If sort_key is given, it may either be a string, or at table that in turn contains either two arrays (from and to) or a string (remove_diacritics).
  • If entry_name or sort_key is given, the from array must be longer or equal in length to the to array.
  • If standard_chars is given, it must form a valid Lua string pattern when placed between square brackets with ^ before it ("[^...]). (It should match all characters regularly used in the language, but that cannot be tested.)
  • If override_translit is set, translit must also be set, because there must be a transliteration module that can override manual transliteration.
  • If link_tr is present, it must be true.
  • Have no data keys besides these: 1, 2, 3, "entry_name", "sort_key", "display", "otherNames", "aliases", "varieties", "type", "scripts", "ancestors", "wikimedia_codes", "wikipedia_article", "standard_chars", "translit", "override_translit", "link_tr".

Checks not performed:

  • If translit is present, it should be the name of a module, and this module should contain a tr function that takes a pagename (and optionally a language code and script code) as arguments.
  • If sort_key is a string, it should be the name of a module, and this module should contain a makeSortKey function that takes a pagename (and optionally a language code and script code) as arguments.
  • If entry_name or sort_key is a table and contains a field remove_diacritics, the value of the field should be a string that forms a valid Lua pattern when it is placed inside negated set notation ([^...]).

These are not checked here, because module errors will quickly crop up in entries if these conditions are not met, assuming that Module:utilities attempts to generate a sortkey for a category pertaining to the language in question, or full_link attempts to use the transliteration module.

Module:languages/code to canonical name and Module:languages/canonical names must contain all the codes and canonical names found in the data submodules of Module:languages, and no more.

The following must be true of the data used by Module:etymology languages:

  • canonicalName must be given.
  • parent must be given must be a valid language, family or etymology-only language code.
  • If ancestors is given, it must be an array, and each string in the array must be a valid language or etymology language code. The etymology language should also be listed as the ancestor of a regular language.
  • Have no data keys besides these: "canonicalName", "otherNames", "parent", "ancestors", "wikipedia_article", "wikidata_item".

Codes in Module:families data must:

  • Have canonicalName, which must not be the same as the canonical name of another family.
  • If family is given, it must be a valid family code.
  • Have at least one language or subfamily belonging to it.
  • Have no data keys besides these: "canonicalName", "otherNames", "family", "protoLanguage", "wikidata_item".

Codes in Module:scripts data must:

  • Have canonicalName.
  • Have at least one language that lists it as one of its scripts.
  • Have a characters pattern for script autodetection, and this must form a valid Lua string pattern when placed between square brackets ("[...]"). (It should match all characters in the script, but that cannot be tested.)
  • Have no data keys besides these: "canonicalName", "otherNames", "parent", "systems", "wikipedia_article", "characters", "direction".

-- TODO:
	-- ietf_subtag field used with a 2/3-letter langauge/family code except qaa-qtz, or a 4-letter script code.
	-- Check against files containing up-to-date ISO data, to cross-check validity.
local export = {}

local mw = mw
local require = require
local string = string

local Array = require("Module:array")
local m_en_utilities = require("Module:en-utilities")
local m_etym_languages_canonical_names = require("Module:etymology languages/canonical names")
local m_etym_languages_codes = require("Module:etymology languages/code to canonical name")
local m_etym_languages_data = require("Module:etymology languages/data")
local m_families = require("Module:families")
local m_families_canonical_names = require("Module:families/canonical names")
local m_families_codes = require("Module:families/code to canonical name")
local m_families_data = require("Module:families/data")
local m_languages = require("Module:languages")
local m_languages_canonical_names = require("Module:languages/canonical names")
local m_languages_codes = require("Module:languages/code to canonical name")
local m_languages_data_all = require("Module:languages/data/all")
local m_load = require("Module:load")
local m_scripts = require("Module:scripts")
local m_scripts_canonical_names = require("Module:scripts/canonical names")
local m_scripts_codes = require("Module:scripts/code to canonical name")
local m_scripts_data = require("Module:scripts/data")
local m_str_utils = require("Module:string utilities")
local m_table = require("Module:table")

local add_indefinite_article = m_en_utilities.add_indefinite_article
local codepoint = m_str_utils.codepoint
local concat = table.concat
local dump = mw.dumpObject
local format = string.format
local gcodepoint = m_str_utils.gcodepoint
local get_data_module_name = m_languages.getDataModuleName
local get_family_by_code = m_families.getByCode
local get_family_by_canonical_name = m_families.getByCanonicalName
local get_indefinite_article = m_en_utilities.get_indefinite_article
local get_language_by_code = m_languages.getByCode
local get_language_by_canonical_name = m_languages.getByCanonicalName
local get_script_by_code = m_scripts.getByCode
local get_script_by_canonical_name = m_scripts.getByCanonicalName
local gmatch = string.gmatch
local gsub = string.gsub
local insert = table.insert
local ipairs = ipairs
local is_callable = require("Module:fun").is_callable
local is_positive_integer = require("Module:math").is_positive_integer
local is_known_language_tag = mw.language.isKnownLanguageTag
local isutf8 = mw.ustring.isutf8
local json_decode = mw.text.jsonDecode
local language_link = require("Module:links").language_link
local list_to_set = m_table.listToSet
local list_to_text = mw.text.listToText
local load_data = m_load.load_data
local log = mw.log
local main_loader = package.loaders[2]
local make_family = m_families.makeObject
local make_lang = m_languages.makeObject
local make_script = m_scripts.makeObject
local match = string.match
local new_title = mw.title.new
local next = next
local pairs = pairs
local pcall = pcall
local remove_comments = require("Module:string/removeComments")
local safe_require = m_load.safe_require
local sorted_pairs = m_table.sortedPairs
local split = m_str_utils.split
local sub = string.sub
local table_len = m_table.length
local tag_text = require("Module:script utilities").tag_text
local type = type
local umatch = m_str_utils.match
local unpack = unpack or table.unpack -- Lua 5.2 compatibility

local aliases = require("Module:languages/data").aliases
local messages

local function discrepancy(modname, ...)
	local success, result = pcall(function(...)
		messages[modname]:insert(format(...))
	end, ...)
	if not success then
		log(result, ...)
	end
end

local messages_mt = {}

function messages_mt:__index(k)
	local val = Array()
	self[k] = val
	return val
end

local all_codes = {}

local language_names = {}
local etym_language_names = {}
local family_names = {}
local script_names = {}

local nonempty_families = {}
local allowed_empty_families = {tbq = true}
local nonempty_scripts = {}
	
local function link(obj, code_first)
	return type(obj) == "string" and obj or
		code_first and format("<code>%s</code> (%s)", obj:getCode(), obj:makeCategoryLink()) or
		format("%s (<code>%s</code>)", obj:makeCategoryLink(), obj:getCode())
end

local function check_data_keys(...)
	local valid_keys = Array(...):toSet()
	
	return function (modname, obj, data)
		local invalid_keys
		for k in pairs(data) do
			if not valid_keys[k] then
				if not invalid_keys then
					invalid_keys = Array(k)
				else
					invalid_keys:insert(k)
				end
			end
		end
		if invalid_keys == nil then
			return
		end
		local plural = #invalid_keys ~= 1
		discrepancy(modname,
			"The data key%s %s for %s %s invalid.",
			plural and "s" or "",
			invalid_keys:map(function(key)
				return "<code>" .. key .. "</code>"
			end):concat(", "),
			link(obj),
			plural and "are" or "is"
		)
	end
end

-- Modification of isArray in [[Module:table]].
-- This assumes all keys are either integers or non-numbers.
-- If there are fractional numbers, the results might be incorrect.
-- For instance, find_gap{"a", "b", [0.5] = true} evaluates to 3, but there
-- isn't a gap at 3 in the sense of there being an integer key greater than 3.
local function find_gap(t, can_contain_non_number_keys)
	local i = 0
	for k in pairs(t) do
		if not (can_contain_non_number_keys and type(k) ~= "number") then
			i = i + 1
			if t[i] == nil then
				return i
			end
		end
	end
end

local function check_true_or_string_or_nil(modname, obj, data, key)
	local field = data[key]
	if not (field == nil or field == true or type(field) == "string") then
		discrepancy(modname,
			"%s has %s <code>%s</code> value that is not <code>nil</code>, <code>true</code> or a string: <code>%s</code>",
			link(obj), get_indefinite_article(key), key, dump(data[key])
		)
	end
end

local function check_array(modname, obj, data, array_name, parent_array_name, can_contain_non_number_keys)
	local parent_table = data
	if parent_array_name then
		parent_table = assert(data[parent_array_name], parent_array_name)
		parent_array_name = "the <code>" .. parent_array_name .. "</code> field in "
	else
		parent_array_name = ""
	end
	local array_type = type(parent_table[array_name])
	if array_type == "table" then
		local gap = find_gap(parent_table[array_name], can_contain_non_number_keys)
		if gap then
			discrepancy(modname,
				"The <code>%s</code> array in %sthe data table for %s has a gap at index %d.",
				array_name,
				parent_array_name,
				link(obj),
				gap
			)
		else
			return true
		end
	else
		discrepancy(modname,
			"The <code>%s</code> field in %sthe data table for %s should be an array (table) but is %s.",
			array_name,
			parent_array_name,
			link(obj),
			array_type == "nil" and "nil" or "a " .. array_type
		)
	end
end

local function check_no_alias_codes(modname, mod_data)
	local lookup, discrepancies = {}, {}
	for k, v in pairs(mod_data) do
		local check = lookup[v]
		if check then
			discrepancies[check] = discrepancies[check] or {"<code>" .. check .. "</code>"}
			insert(discrepancies[check], "<code>" .. k .. "</code>")
		else
			lookup[v] = k
		end
	end
	for _, v in pairs(discrepancies) do
		discrepancy(modname,
			"The codes %s are currently alias codes. Only one code should be used in the data.",
			list_to_text(v, ", ", " and ")
		)
	end
end

local function check_wikidata_item(modname, obj, data, key)
	local data_item = data[key]
	if data_item == nil or is_positive_integer(data_item) then
		return
	end
	discrepancy(modname,
		"%s has a Wikidata item ID that is not a positive integer: <code>%s</code>",
		link(obj), dump(data_item)
	)
end

local function check_name_field(modname, obj, data, canonical_name, data_key, allow_nested, allow_canonical_name_in_table)
	local array = data[data_key]
	if not array then
		return
	end
	check_array(modname, obj, data, data_key, nil, true)

	local names = {}
	local function check_other_name(other_name)
		if not allow_canonical_name_in_table and other_name == canonical_name then
			discrepancy(modname,
				"%s has its canonical name (<code>%s</code>) repeated in the table of <code>%s</code>.",
				link(obj), dump(canonical_name), data_key
			)
		end
		if names[other_name] then
			discrepancy(modname,
				"The name %s is found twice or more in the list of <code>%s</code> for %s.",
				other_name, data_key, link(obj)
			)
		end
		names[other_name] = true
	end

	for _, other_name in ipairs(array) do
		if type(other_name) == "table" then
			if not allow_nested then
				discrepancy(modname,
					"A nested table is found in the list of <code>%s</code> for %s, but isn't allowed.",
					data_key, link(obj)
				)
			else
				for _, on in ipairs(other_name) do
					check_other_name(on)
				end
			end
		else
			check_other_name(other_name)
		end
	end
end

local function check_other_names_aliases_varieties(modname, obj, data, canonical_name)
	if data.other_names then
		check_name_field(modname, obj, data, canonical_name, "other_names")
	end
	if data.aliases then
		check_name_field(modname, obj, data, canonical_name, "aliases")
	end
	if data.varieties then
		-- Sometimes a variety legitimately has the same name as the language as a whole, so allow that.
		check_name_field(modname, obj, data, canonical_name, "varieties", "allow_nested", "allow_canonical_name_in_table")
	end
end

local function validate_pattern(pattern, modname, obj, standard_chars)
	if type(pattern) ~= "string" then
		return discrepancy(modname,
			"\"%s\", the %spattern for %s, is not a string.",
			pattern, standard_chars and "standard character " or "", link(obj)
		)
	elseif not isutf8(pattern) then
		return discrepancy(modname,
			"%s specifies a pattern for for %scharacter detection which is not valid UTF-8: <code>%s</code>",
			link(obj), standard_chars and "standard " or "", dump(pattern)
		)
	end
	local ranges
	for lower, higher in gmatch(pattern, "(.[\128-\191]*)%-%%?(.[\128-\191]*)") do
		if codepoint(lower) >= codepoint(higher) then
			ranges = ranges or Array()
			insert(ranges, { lower, higher })
		end
	end
	if ranges and ranges[1] then
		local plural = #ranges ~= 1 and "s" or ""
		discrepancy(modname,
			"%s specifies an invalid pattern " ..
			"for %scharacter detection: <code>%s</code>. The first codepoint%s " ..
			"in the range%s %s %s must be less than or equal to the second.",
			link(obj), standard_chars and "standard " or "", dump(pattern), plural, plural,
			ranges:map(function(range)
				return format(range[1] .. "-" .. range[2] .. " (U+%X, U+%X)", codepoint(range[1]), codepoint(range[2]))
			end):concat(", "),
			#ranges ~= 1 and "are" or "is"
		)
	end
	local success, result = pcall(umatch, "", "[" .. pattern .. "]")
	if not success then
		discrepancy(modname,
			"%s specifies an invalid pattern for %scharacter detection: <code>%s</code> (%s)",
			link(obj), standard_chars and "standard " or "", dump(pattern), result
		)
	end
end

local remove_exceptions_addition = 0xF0000
local maximum_code_point = 0x10FFFF
local remove_exceptions_maximum_code_point = maximum_code_point - remove_exceptions_addition

-- TODO: check modules exist.
-- TODO: validate script codes and check inner tables.
local function check_replacement_data(modname, obj, data, key, func_name)
	local replacements = data[key]
	if replacements == nil then
		return
	end
	local replacements_type = type(replacements)
	if replacements_type == "string" then
		local mod = main_loader("Module:" .. replacements)
		if not mod then
			discrepancy(modname,
				"The <code>%s</code> field in the data table for %s specifies the module [[Module:%s]], which does not exist.",
				key, link(obj), replacements
			)
		else
			mod = mod()
			if not (type(mod) == "table" and is_callable(mod[func_name])) then
				discrepancy(modname,
					"The <code>%s</code> field in the data table for %s specifies the module [[Module:%s]], which exists, but does not contain the expected function <code>%s()</code>.",
					key, link(obj), replacements, func_name
				)
			end
		end
		return
	elseif replacements_type ~= "table" then
		discrepancy(modname,
			"The <code>%s</code> field in the data table for %s must be a string or table, not a %s.",
			key, link(obj), replacements_type
		)
		return
	end
	
	local from, to = replacements.from, replacements.to
	if (from ~= nil) ~= (to ~= nil) then
		discrepancy(modname,
			"The <code>from</code> and <code>to</code> arrays in the <code>%s</code> table for %s are not both defined or both undefined.",
			key, link(obj)
		)
	elseif from then
		for _, k in ipairs {"from", "to"} do
			check_array(modname, obj, data, k, key)
		end
	end
	
	local remove_diacritics = replacements.remove_diacritics
	if not (remove_diacritics == nil or type(remove_diacritics) == "string") then
		discrepancy(modname,
			"The <code>remove_diacritics</code> field in the <code>%s</code> table for %s table must be a string.",
			key, link(obj)
		)
	end
	
	local remove_exceptions = replacements.remove_exceptions
	if remove_exceptions then
		if check_array(modname, obj, data, "remove_exceptions", key) then
			for sequence_i, sequence in ipairs(remove_exceptions) do
				local code_point_i = 0
				for code_point in gcodepoint(sequence) do
					code_point_i = code_point_i + 1
					if code_point > remove_exceptions_maximum_code_point then
						discrepancy(modname,
							"Code point #%d (0x%04X) in field #%d of the <code>remove_exceptions</code> array for %s is over U+%04X.",
							code_point_i, code_point, sequence_i, link(obj), remove_exceptions_maximum_code_point
						)
					end
					
				end
			end
		end
	end
	
	if from and to and table_len(to) > table_len(from) then
		discrepancy(modname,
			"The <code>from</code> array in the <code>%s</code> table for %s must be shorter or the same length as the <code>to</code> array.",
			key, link(obj)
		)
	end
end

local function check_replacements_data(modname, obj, data)
	for _, replacement_spec in ipairs{
		{"translit", "tr"},
		{"display_text", "makeDisplayText"},
		{"strip_diacritics", "stripDiacritics"},
		{"sort_key", "makeSortKey"},
	} do
		check_replacement_data(modname, obj, data, unpack(replacement_spec))
	end
end

local function has_ancestor(lang, code)
	for _, anc in ipairs(lang:getAncestors()) do
		if code == anc:getCode() or has_ancestor(anc, code) then
			return true
		end
	end
end

local function get_default_ancestors(lang)
	if lang:hasType("language", "etymology-only") then
		local parent = lang:getParent()
		if not has_ancestor(parent, lang:getCode()) then
			return parent:getAncestorCodes()
		end
	end
	local fam_code, def_anc = lang:getFamilyCode()
	while fam_code and fam_code ~= "qfa-not" do
		local fam = m_families_data[fam_code]
		def_anc = fam.protoLanguage or
			m_languages_data_all[fam_code .. "-pro"] and fam_code .. "-pro" or
			m_etym_languages_data[fam_code .. "-pro"] and fam_code .. "-pro"
		if def_anc and def_anc ~= lang:getCode() then
			return {def_anc}
		end
		fam_code = fam[3]
	end
end

local function iterate_ancestor(obj, modname, anc_code)
	local anc = get_language_by_code(anc_code, nil, true)
	if not anc then
		discrepancy(modname,
			"%s lists the invalid language code <code>%s</code> as its ancestor.",
			link(obj), dump(anc_code)
		)
		return
	end
	local anc_fam = anc:getFamily()
	if not anc_fam then
		discrepancy(modname,
			"%s has no family.",
			link(anc)
		)
		return
	end
	local anc_fam_code = anc_fam:getCode()
	local def_ancs = get_default_ancestors(obj)
	if def_ancs then
		for _, def_anc in ipairs(def_ancs) do
			def_anc = get_language_by_code(def_anc, nil, true)
			if def_anc and (
				anc_code == def_anc:getCode() or
				has_ancestor(def_anc, anc_code) or
				def_anc:hasParent(anc_code) and not has_ancestor(anc, def_anc:getCode())
			) then
				discrepancy(modname,
					"%s has the ancestor %s listed in its ancestor field, which is redundant, since it is determined to be ancestral automatically.",
					link(obj), link(anc)
				)
			end
		end
	end
	if not obj:inFamily(anc_fam_code) then
		discrepancy(modname,
			"%s has %s set as an ancestor, but is not in the %s.",
			link(obj), link(anc), link(anc_fam)
		)
	end
	local fam, proto = obj
	repeat
		fam = fam:getFamily()
		proto = fam and fam:getProtoLanguage()
	until proto or not fam or fam:getCode() == "qfa-not"
	if proto and not (
		proto:getCode() == anc:getCode() or
		proto:hasAncestor(anc:getCode()) or
		anc:hasAncestor(proto:getCode())
	) then
		local fam = obj:getFamily()
		discrepancy(modname,
			"%s is in the %s and has %s set as an ancestor, but it is not possible to form an ancestral chain between them.",
			link(obj), link(fam), link(anc)
		)
	end
end

local function check_ancestors(modname, obj, data)
	local ancestors = data.ancestors
	if ancestors == nil then
		return
	end
	local ancestors_type = type(ancestors)
	if ancestors_type == "string" then
		ancestors = split(ancestors, ",", true, true)
	elseif ancestors_type ~= "table" then
		discrepancy(modname,
			"The <code>ancestors</code> field in the data table for %s must be a string or table, not a %s.",
			link(obj), ancestors_type
		)
	end
	for _, anc in ipairs(ancestors) do
		iterate_ancestor(obj, modname, anc)
	end
end

local function check_wikimedia_codes(modname, obj, data)
	local wikimedia_codes = data.wikimedia_codes
	if wikimedia_codes == nil then
		return
	end
	local wikimedia_codes_type = type(wikimedia_codes)
	if wikimedia_codes_type == "string" then
		wikimedia_codes = split(wikimedia_codes, ",", true, true)
	elseif wikimedia_codes_type ~= "table" then
		discrepancy(modname,
			"The <code>wikimedia_codes</code> field in the data table for %s must be a string or table, not a %s.",
			link(obj), wikimedia_codes_type
		)
	end
	for _, code in ipairs(wikimedia_codes) do
		if not is_known_language_tag(code) then
			discrepancy(modname,
				"%s lists the invalid Wikimedia code <code>%s</code> in the <code>wikimedia_codes</code> field.",
				link(obj), dump(code)
			)
		end
	end
end
	
local function check_code_to_name_and_name_to_code_maps(
		source_module_type,
		source_module_description,
		code_to_module_map, name_to_code_map,
		code_to_name_modname, code_to_name_module,
		name_to_code_modname, name_to_code_module
)
	
	local function check_code_and_name(modname, code, canonical_name)
		-- Check the code is in code_to_module_map and that it didn't originate from the wrong data module.
		local check_mod = code_to_module_map[code] or code_to_module_map[aliases[code]]
		if not (check_mod and match(check_mod, "^" .. source_module_type .. "/data")) then
			if not name_to_code_map[canonical_name] then
				discrepancy(modname,
					"The code <code>%s</code> and the canonical name %s should be removed; they are not found in %s.",
					code, canonical_name, source_module_description
				)
			else
				discrepancy(modname,
					"<code>%s</code>, the code for the canonical name %s, is wrong; it should be <code>%s</code>.",
					code, canonical_name, name_to_code_map[canonical_name]
				)
			end
		elseif not name_to_code_map[canonical_name] then
			local data_table = require("Module:" .. code_to_module_map[code])[code]
			discrepancy(modname,
				"%s, the canonical name for the code <code>%s</code>, is wrong; it should be %s.",
				canonical_name, code, data_table[1]
			)
		end
	end

	for code, canonical_name in pairs(code_to_name_module) do
		check_code_and_name(code_to_name_modname, code, canonical_name)
	end
	
	for canonical_name, code in pairs(name_to_code_module) do
		check_code_and_name(name_to_code_modname, code, canonical_name)
	end
end

local function check_extraneous_extra_data(
		data_modname, data_module, extra_data_modname, extra_data_module)
	for code, _ in pairs(extra_data_module) do
		if not data_module[code] then
			discrepancy(extra_data_modname,
				"The code <code>%s</code> is not found in [[Module:%s]], and should be removed from [[Module:%s]].",
				code, data_modname, extra_data_modname
			)
		end
	end
end

-- TODO: add collision check between the canonical names "X" and "X [Ll]anguage".
local function check_languages(frame)
	local check_language_data_keys = check_data_keys(
		1, 2, 3, 4, -- canonical name, Wikidata item, family, scripts
		"display_text", "generate_forms", "strip_diacritics", "sort_key",
		"other_names", "aliases", "varieties", "ietf_subtag",
		"type", "ancestors", "pseudo_families",
		"wikimedia_codes", "wikipedia_article", "standard_chars",
		"translit", "override_translit", "link_tr",
		"dotted_dotless_i"
	)
	
	local function check_language(modname, code, data, extra_modname, extra_data)
		local obj, code_modname, canonical_name = make_lang(code, data, true), get_data_module_name(code), data[1]
		-- FIXME: this module should use the prefixed module name throughout.
		code_modname = code_modname:gsub("^Module:", "")
		
		if code_modname ~= modname then
			if code_modname == "languages/data/2" then
				discrepancy(modname,
					"%s is a two-letter code, so should be moved to [[Module:%s]].",
					link(obj), code_modname
				)
			elseif code_modname == "languages/data/exceptional" then
				discrepancy(modname,
					"%s is an exceptional code, as it does not consist of two or three lowercase letters, so should be moved to [[Module:%s]].",
					link(obj), code_modname
				)
			else
				discrepancy(modname,
					"%s is a three-letter code beginning with '%s', so should be moved to [[Module:%s]].",
					link(obj), sub(code, 1, 1), code_modname
				)
			end
		end
		
		check_language_data_keys(modname, obj, data)
		
		if all_codes[code] then
			discrepancy(modname,
				"The code <code>%s</code> is not unique; it is also defined in [[Module:%s]].",
				code, all_codes[code]
			)
		else
			if not m_languages_codes[code] then
				discrepancy("languages/code to canonical name",
					"The code %s is missing.",
					link(obj, true)
				)
			end
			all_codes[code] = modname
		end
		
		-- TODO: these checks should be consolidated with the proto-language checks in the family data,
		-- since bad settings there affect the warnings here (e.g. xxx-pro assigned to yyy when xxx also
		-- doesn't not exist - a warning that xxx has "no family" would be misleading).
		if sub(code, -4) == "-pro" then
			local fam_code = sub(code, 1, -5)
			local fam = get_language_by_code(fam_code, nil, true, true)
			if not fam then
				discrepancy(modname,
					"'''Proto-language with no family''': %s should be the proto-language of <code>%s</code>, which doesn't exist.",
					link(obj), dump(fam_code)
				)
			elseif not fam:hasType("family") then
				discrepancy(modname,
					"'''Proto-language with no family''': %s should be the proto-language of <code>%s</code>, but %s is not a family.",
					link(obj), dump(fam_code), link(fam)
				)
			else
				-- Reinstate this as low-priority once message priorities have been implemented.
--				local expected_name = "Proto-" .. fam:getCanonicalName()
--				if canonical_name ~= expected_name then
--					discrepancy(modname,
--						"%s does not have the expected name \"%s\", even though it is the proto-language of the %s.",
--						link(obj), expected_name, link(fam)
--					)
--				end
			end
		end
		
		if not canonical_name then
			discrepancy(modname,
				"The code <code>%s</code> has no canonical name specified.",
				code
			)
		elseif language_names[canonical_name] then
			local canonical_lang = get_language_by_canonical_name(canonical_name)
			if not canonical_lang then
				discrepancy(modname,
					"%s has a canonical name that cannot be looked up.",
					link(obj)
				)
			elseif data.main_code ~= canonical_lang:getCode() then
				discrepancy(modname,
					"%s has a canonical name that is not unique; it is also used by the code <code>%s</code>.",
					link(obj), language_names[canonical_name]
				)
			end
		else
			if not m_languages_canonical_names[canonical_name] then
				discrepancy("languages/canonical names",
					"The canonical name %s is missing.",
					link(obj)
				)
			end
			language_names[canonical_name] = code
		end
		
		check_wikidata_item(modname, obj, data, 2)

		if extra_data then
			check_other_names_aliases_varieties(modname, obj, extra_data, canonical_name)
		end
		
		local lang_type = data.type
		if lang_type and not (lang_type == "regular" or lang_type == "reconstructed" or lang_type == "appendix-constructed") then
			discrepancy(modname,
				"%s is of the invalid type <code>%s</code>.",
				link(obj), lang_type
			)
		end
		
		if data.aliases then
			discrepancy(modname,
				"%s has an <code>aliases</code> key in [[Module:%s]]. This must be moved to [[Module:%s]].",
				link(obj), modname, extra_modname
			)
		end
		
		if data.varieties then
			discrepancy(modname,
				"%s has the <code>varieties</code> key in [[Module:%s]]. This must be moved to [[Module:%s]].",
				link(obj), modname, extra_modname
			)
		end
		
		if data.other_names then
			discrepancy(modname,
				"%s has the <code>other_names</code> key in [[Module:%s]]. This must be moved to [[Module:%s]].",
				link(obj), modname, extra_modname
			)
		end
		
		if not extra_data then
			discrepancy(extra_modname,
				"%s has data in [[Module:%s]], but does not have corresponding data in [[Module:%s]].",
				link(obj), modname, extra_modname
			)
		--[[elseif extra_data.other_names then
			discrepancy(extra_modname,
				"%s has <code>other_names</code> key, but these should be changed to either <code>aliases</code> or <code>varieties</code>.",
				link(obj)
			)]]
		end
		
		local sc = data[4]
		if sc then
			if type(sc) == "string" then
				sc = split(sc, "%s*,%s*", true)
			end
			if type(sc) == "table" then
				if not sc[1] then
					discrepancy(modname,
						"%s has no scripts listed.",
						link(obj)
					)
				else
					for _, sccode in ipairs(sc) do
						local cur_sc = m_scripts_data[sccode]
						if not (cur_sc or sccode == "All" or sccode == "Hants") then
							discrepancy(modname,
								"%s lists the invalid script code <code>%s</code>.",
								link(obj), dump(sccode)
							)
						--[[elseif not cur_sc.characters then
							discrepancy(modname,
								"%s lists the %s, which does not have any characters.",
								link(obj), link(get_script_by_code(sccode))
							)]]
						end
			
						nonempty_scripts[sccode] = true
					end
				end
			else
				discrepancy(modname,
					"The %s field for %s must be a table or string.",
					4, link(obj)
				)
			end
		end
		
		if data.ancestors then
			check_ancestors(modname, obj, data)
		end
		
		if data.wikimedia_codes then
			check_wikimedia_codes(modname, obj, data)
		end
		
		if data[3] then
			local family = data[3]
			if not m_families_data[family] then
				discrepancy(modname,
					"%s has the invalid family code <code>%s</code>.",
					link(obj), dump(family)
				)
			end
			nonempty_families[family] = true
		end
		
		check_replacements_data(modname, obj, data)

		if data.standard_chars then
			if type(data.standard_chars) == "table" then
				local sccodes = {}
				for _, sccode in ipairs(sc) do
					sccodes[sccode] = true
				end
				for sccode in pairs(data.standard_chars) do
					if not (sccodes[sccode] or sccode == 1) then
						discrepancy(modname,
							"The field %s in the <code>standard_chars</code> table for %s does not match any script for that language.",
							sccode, link(obj)
						)
					end
				end
			elseif data.standard_chars and type(data.standard_chars) ~= "string" then
				discrepancy(modname,
					"The <code>standard_chars</code> field in the data table for %s must be a string or table.",
					link(obj)
				)
			end
		end
		
		check_true_or_string_or_nil(modname, obj, data, "override_translit")
		check_true_or_string_or_nil(modname, obj, data, "link_tr")

		-- This doesn't apply any more since scripts can be script-wide translit methods.		
		-- if data.override_translit and not data.translit then
		-- 	discrepancy(modname,
		-- 		"%s has the <code>override_translit</code> field set, but no transliteration module",
		-- 		link(obj)
		-- 	)
		-- end
	end
	
	local function check_module(modname)
		local mod_data = load_data("Module:" .. modname)
		local extra_modname = modname .. "/extra"
		local extra_mod_data = load_data("Module:" .. extra_modname)
		for code, data in pairs(mod_data) do
			check_language(modname, code, data, extra_modname, extra_mod_data[code])
		end
		check_no_alias_codes(modname, mod_data)
		check_no_alias_codes(extra_modname, extra_mod_data)
		check_extraneous_extra_data(modname, mod_data, extra_modname, extra_mod_data)
	end
	
	-- Check two-letter codes
	check_module(
		"languages/data/2"
	)
	
	-- Check three-letter codes
	for i = 0x61, 0x7A do -- a to z
		check_module(
			format("languages/data/3/%c", i)
		)
	end
	
	-- Check exceptional codes
	check_module(
		"languages/data/exceptional"
	)
	
	-- These checks must be done while all_codes only contains language codes:
	-- that is, after language data modules have been processed, but before
	-- etymology languages, families, and scripts have.
	check_code_to_name_and_name_to_code_maps(
		"languages",
		"a submodule of [[Module:languages]]",
		all_codes, language_names,
		"languages/code to canonical name", m_languages_codes,
		"languages/canonical names", m_languages_canonical_names
	)
	
	-- Check [[Template:langname-lite]]
	local modname = "Template:langname-lite"
	for code, name in gmatch(remove_comments(new_title(modname):getContent()), "\n\t*|#*([^\n]+)=([^\n]*)") do
		if #code > 1 and code ~= "default" then
			for _, code in pairs(split(code, "|", true)) do
				local lang = get_language_by_code(code, nil, true, true)
				if match(name, "etymcode") then
					local nonEtym_name = frame:preprocess(name)
					local nonEtym_real_name = lang:getFullName()
					if nonEtym_name ~= nonEtym_real_name then
						discrepancy(modname,
							"Code: <code>%s</code>. Saw name: %s. Expected name: %s.",
							code, nonEtym_name, nonEtym_real_name
						)
					end
					name = frame:preprocess(gsub(name, "{{{allow etym|}}}", "1"))
				elseif match(name, "familycode") then
					name = match(name, "familycode|(.-)|")
				else
					name = name
				end
				if not lang then
					discrepancy(modname,
						"Code: <code>%s</code>. Saw name: %s. Language not present in data.",
						code, name
					)
				else
					local real_name = lang:getCanonicalName()
					if name ~= real_name then
						discrepancy(modname,
							"Code: <code>%s</code>. Saw name: %s. Expected name: %s.",
							code, name, real_name
						)
					end
				end
			end
		end
	end
end

local function check_etym_languages()
	local modname = "etymology languages/data"
	
	local check_etymology_language_data_keys = check_data_keys(
		1, 2, 3, 4, -- canonical name, Wikidata item, family, scripts
		"parent", "display_text", "generate_forms", "strip_diacritics", "sort_key",
		"other_names", "aliases", "varieties", "ietf_subtag",
		"type", "main_code", "ancestors", "pseudo_families",
		"wikimedia_codes", "wikipedia_article", "standard_chars",
		"translit", "override_translit", "link_tr",
		"dotted_dotless_i"
	)
	
	local checked = {}
	for code, data in pairs(m_etym_languages_data) do
		local obj, canonical_name, parent = make_lang(code, data, true), data[1], data.parent
		
		check_etymology_language_data_keys(modname, obj, data)
		
		if all_codes[code] then
			discrepancy(modname,
				"The code <code>%s</code> is not unique; it is also defined in [[Module:%s]].",
				code, all_codes[code]
			)
		else
			if not m_etym_languages_codes[code] then
				discrepancy("etymology languages/code to canonical name",
					"The code %s is missing.",
					link(obj, true)
				)
			end
			all_codes[code] = modname
		end
		
		if not canonical_name then
			discrepancy(modname,
				"The code <code>%s</code> has no canonical name specified.",
				code
			)
		elseif language_names[canonical_name] then
			local canonical_lang = get_language_by_canonical_name(canonical_name, nil, true)
			if not canonical_lang then
				discrepancy(modname,
					"%s has a canonical name that cannot be looked up.",
					link(obj)
				)
			elseif data.main_code ~= canonical_lang:getCode() then
				discrepancy(modname,
					"%s has a canonical name that is not unique; it is also used by the code <code>%s</code>.",
					link(obj), language_names[canonical_name]
				)
			end
		else
			if not m_etym_languages_canonical_names[canonical_name] then
				discrepancy("etymology languages/canonical names",
					"The canonical name %s is missing.",
					link(obj)
				)
			end
			etym_language_names[canonical_name] = code
		end
		
		check_other_names_aliases_varieties(modname, obj, data, canonical_name)
		
		if parent then
			if type(parent) ~= "string" then
				discrepancy(modname,
					"%s has a parent code that is %s rather than a string.",
					link(obj), parent == nil and "nil" or "a " .. type(parent)
				)
			elseif not (m_languages_data_all[parent] or m_etym_languages_data[parent]) then
				discrepancy(modname,
					"%s has the invalid parent code <code>%s</code>%s.",
					link(obj), dump(parent), m_families_data[parent] and " (a family code)" or ""
				)
			end
			nonempty_families[parent] = true
		else
			discrepancy(modname,
				"%s has no parent code.",
				link(obj)
			)
		end
		
		if data.ancestors then
			check_ancestors(modname, obj, data)
		end
		
		if data.wikimedia_codes then
			check_wikimedia_codes(modname, obj, data)
		end
		
		if data[3] then
			local family = data[3]
			if not m_families_data[family] then
				discrepancy(modname,
					"%s has the invalid family code <code>%s</code>.",
					link(obj), dump(family))
			end
			nonempty_families[family] = true
		end
		
		check_replacements_data(modname, obj, data)
		
		check_wikidata_item(modname, obj, data, 2)
		
		local stack = {}
		while data do
			if checked[code] then
				break	
			elseif stack[code] then
				local parent = data.parent
				discrepancy(modname,
					"%s has a cyclic parental relationship to %s",
					link(make_lang(code, data, true)),
					link(get_language_by_code(parent, nil, true))
				)
				break
			end
			stack[code] = true
			code = data.parent
			data = m_etym_languages_data[code]
		end
		
		for code in pairs(stack) do
			checked[code] = true	
		end
	end
	
	check_no_alias_codes(modname, m_etym_languages_data)
	
	check_code_to_name_and_name_to_code_maps(
		"etymology languages",
		"[[Module:etymology languages/data]]",
		all_codes, etym_language_names,
		"etymology languages/code to canonical name", m_etym_languages_codes,
		"etymology languages/canonical names", m_etym_languages_canonical_names)
end

-- TODO: add collision check between the canonical names "X" and "X [Ll]anguages".
local function check_families()
	local modname = "families/data"
	
	local check_family_data_keys = check_data_keys(
		1, 2, 3, -- canonical name, Wikidata item, (parent) family
		"type", "ietf_subtag",
		"protoLanguage", "other_names", "aliases", "varieties", "pseudo_families"
	)
	
	local checked, double_check_if_empty = {["qfa-not"] = true}, {}
	for code, data in pairs(m_families_data) do
		local obj, canonical_name, family, protolang = make_family(code, data), data[1], data[3], data.protoLanguage
		
		check_family_data_keys(modname, obj, data)
		
		if all_codes[code] then
			discrepancy(modname,
				"The code <code>%s</code> is not unique; it is also defined in [[Module:%s]].",
				code, all_codes[code]
			)
		else
			if not m_families_codes[code] then
				discrepancy("families/code to canonical name",
					"The code %s is missing.",
					link(obj, true)
				)
			end
			all_codes[code] = modname
		end
		
		if not canonical_name then
			discrepancy(modname,
				"The code <code>%s</code> has no canonical name specified.",
				code
			)
		elseif family_names[canonical_name] then
			local canonical_family = get_family_by_canonical_name(canonical_name)
			if not canonical_family then
				discrepancy(modname,
					"%s has a canonical name that cannot be looked up.",
					link(obj)
				)
			elseif data.main_code ~= canonical_family:getCode() then
				discrepancy(modname,
					"%s has a canonical name that is not unique; it is also used by the code <code>%s</code>.",
					link(obj), family_names[canonical_name]
				)
			end
		else
			if not m_families_canonical_names[canonical_name] then
				discrepancy("families/canonical names",
					"The canonical name %s is missing.",
					link(obj)
				)
			end
			family_names[canonical_name] = code
		end
		
		check_other_names_aliases_varieties(modname, obj, data, canonical_name)
		
		if family then
			if family == code and code ~= "qfa-not" then
				discrepancy(modname,
					"%s has itself as its family.",
					link(obj)
				)
			elseif not m_families_data[family] then
				discrepancy(modname,
					"%s has the invalid parent family code <code>%s</code>.",
					link(obj), dump(family)
				)
			end
			nonempty_families[family] = true
		end
		
		if protolang then
			local protolang_obj = get_language_by_code(protolang, nil, true)
			if not protolang_obj then
				discrepancy(modname,
					"%s has the invalid proto-language code <code>%s</code>.",
					link(obj), dump(protolang)
				)
			elseif protolang == code .. "-pro" then
				discrepancy(modname,
					"%s has %s listed as its proto-language, which is redundant, since it is determined to be the proto-language automatically.",
					link(obj), link(protolang_obj)
				)
			elseif sub(protolang, -4) == "-pro" then
				discrepancy(modname,
					"%s has %s listed as its proto-language, which is supposed to be the proto-language for the family <code>%s</code>.", link(obj), link(protolang_obj), sub(protolang, 1, -5)
				)
			end
		end
		
		check_wikidata_item(modname, obj, data, 2)
		
		-- Could be a false-positive if a child family occurs on a later
		-- iteration, so set aside any that fail for a second check. This avoids
		-- having to iterate through the whole list of families once
		-- nonempty_families has been fully populated.
		if not (nonempty_families[code] or allowed_empty_families[code]) then
			double_check_if_empty[code] = obj
		end
		
		local stack = {}
		while data do
			if checked[code] then
				break	
			elseif stack[code] then
				local parent = data[3]
				discrepancy(modname,
					"%s has a cyclic familial relationship to %s",
					link(make_family(code, data)),
					link(get_family_by_code(parent))
				)
				break
			end
			stack[code] = true
			code = data[3]
			data = m_families_data[code]
		end
		
		for code in pairs(stack) do
			checked[code] = true	
		end
	end
	
	-- Any languages set aside as candidates for having no children are checked
	-- again, now that nonempty_families is definitely complete.
	for code, obj in next, double_check_if_empty do
		if not (nonempty_families[code] or allowed_empty_families[code]) then
			discrepancy(modname,
				"%s has no child families or languages.",
				link(obj)
			)
		end
	end
	
	check_no_alias_codes(modname, m_families_data)
	
	check_code_to_name_and_name_to_code_maps(
		"families",
		"[[Module:families/data]]",
		all_codes, family_names,
		"families/code to canonical name", m_families_codes,
		"families/canonical names", m_families_canonical_names)
end

-- TODO: add collision check between the canonical names "X" and "X [Ss]cript".
local function check_scripts()
	local modname = "scripts/data"
	
	local check_script_data_keys = check_data_keys(
		1, 2, 3, -- canonical name, Wikidata item, writing systems
		"other_names", "aliases", "varieties", "parent", "ietf_subtag", "type",
		"wikipedia_article", "ranges", "characters", "spaces", "capitalized", "translit", "direction",
		"character_category", "normalizationFixes", "sort_by_scraping",
		"display_text", "sort_key", "strip_diacritics"
	)
	
	-- Just to satisfy requirements of check_code_to_name_and_name_to_code_maps.
	local script_code_to_module_map = {}
	
	for code, data in pairs(m_scripts_data) do
		local obj, canonical_name = make_script(code, data), data[1]
		
		if not m_scripts_codes[code] and #code == 4 then
			discrepancy("scripts/code to canonical name",
				"The code %s is missing",
				link(obj, true)
			)
		end
		
		check_script_data_keys(modname, obj, data)
		
		if not canonical_name then
			discrepancy(modname,
				"The code <code>%s</code> has no canonical name specified.",
				code
			)
		elseif script_names[canonical_name] then
			local canonical_script = get_script_by_canonical_name(canonical_name)
			if not canonical_script then
				discrepancy(modname,
					"%s has a canonical name that cannot be looked up.",
					link(obj)
				)
			--[[elseif data.main_code ~= canonical_script:getCode() then
				discrepancy(modname,
					"%s has a canonical name that is not unique; it is also used by the code <code>%s</code>.",
					link(obj), script_names[canonical_name]
				)]]
			end
		else
			if not m_scripts_canonical_names[canonical_name] and #code == 4 then
				discrepancy("scripts/canonical names",
					"The canonical name %s is missing.",
					link(obj)
				)
			end
			script_names[canonical_name] = code
		end
		
		check_other_names_aliases_varieties(modname, obj, data, canonical_name)
		
		if not nonempty_scripts[code] then
			discrepancy(modname,
				"%s is not used by any language%s.",
				link(obj), data.characters and ""
					or " and has no characters listed for auto-detection")
		
		--[[elseif not data.characters then
			discrepancy(modname,
				"%s has no characters listed for auto-detection.",
				link(obj)
			)--]]
		end

		if data.characters then
			validate_pattern(data.characters, modname, obj, false)
		end
		
		check_wikidata_item(modname, obj, data, 2)
		
		script_code_to_module_map[code] = modname
	end
	
	check_no_alias_codes(modname, m_scripts_data)
	
	check_code_to_name_and_name_to_code_maps(
		"scripts",
		"a submodule of [[Module:scripts]]",
		script_code_to_module_map, script_names,
		"scripts/code to canonical name", m_scripts_codes,
		"scripts/canonical names", m_scripts_canonical_names)
end

-- FIXME: this is quite messy.
local function check_wikidata_languages()
	local data = json_decode(new_title("Module:languages/data/wikidata.json"):getContent())
	
	local seen = {{}, {}, {}, [5] = {}}
	for _, item in ipairs(data) do
		local id = item.id
		for k, v in pairs(item) do
			if k ~= "id" then
				local _seen = seen[k]
				for _, code in ipairs(v) do
					local _code = code[1]
					local _type = type(_seen[_code])
					if _type == "table" then
						insert(_seen[_code], id)
					elseif _type == "string" then
						_seen[_code] = {_seen[_code], id}
					else
						_seen[_code] = id
					end
				end
			end
		end
	end
	
	local modname = "languages/data/wikidata.json"
	for k, v in pairs(seen) do
		for code, ids in pairs(v) do
			if type(ids) == "table" then
				local t = {}
				for i, id in ipairs(ids) do
					t[i] = format("<code>[[d:%s|%s]]</code>", id, id)
				end
				discrepancy(modname,
					"<code>%s</code> is set as an ISO 639-%d code on multiple items: %s.",
					code, k, list_to_text(t)
				)
				
			end
		end
	end
end

local function check_labels()
	local check_label_data_keys = check_data_keys(
		"display", "Wikipedia", "glossary",
		"plain_categories", "topical_categories", "pos_categories", "regional_categories", "sense_categories",
		"omit_preComma", "omit_postComma", "omit_preSpace",
		"deprecated", "track"
	)
	
	local function check_label(modname, code, data)
		local _type = type(data)
		if _type == "table" then
			check_label_data_keys(modname, code, data)
		elseif _type ~= "string" then
			discrepancy(modname,
				"The data for the label <code>%s</code> is %s %s; only tables and strings are allowed.",
				code, add_indefinite_article(_type)
			)
		end
	end
	
	for _, module in ipairs{"", "/regional", "/topical"} do
		local modname = "Module:labels/data" .. module
		module = require(modname)
		for label, data in pairs(module) do
			check_label(modname, label, data)
		end
	end
	
	for code in pairs(m_languages_codes) do
		local modname = "Module:labels/data/lang/" .. code
		local module = safe_require(modname)
		if module then
			for label, data in pairs(module) do
				check_label(modname, label, data)
			end
		end
	end
end

local function check_zh_trad_simp()
	local m_ts = require("Module:zh/data/ts")
	local m_st = require("Module:zh/data/st")
	local ruby = require("Module:ja-ruby").ruby_auto
	local lang = get_language_by_code("zh")
	local Hant = get_script_by_code("Hant")
	local Hans = get_script_by_code("Hans")
	
	local data = {[0] = m_st, m_ts}
	local mod = {[0] = "st", "ts"}
	local var = {[0] = "Simp.", "Trad."}
	local sc = {[0] = Hans, Hant}
	
	local function find_stable_loop(chars, other, j)
		local display = ruby({["markup"] = "[" .. other .. "](" .. var[(j+1)%2] .. ")"})
		display = language_link{term = other, alt = display, lang = lang, sc = sc[(j+1)%2], tr = "-"}
		insert(chars, display)
		if data[(j+1)%2][other] == other then
			insert(chars, other)
			return chars, 1
		elseif not data[(j+1)%2][other] then
			insert(chars, "not found")
			return chars, 2
		elseif data[j%2][data[(j+1)%2][other]] ~= other then
			return find_stable_loop(chars, data[(j+1)%2][other], j + 1)
		else
			local display = ruby({["markup"] = "[" .. data[(j+1)%2][other] .. "](" .. var[j%2] .. ")"})
			display = language_link{term = data[(j+1)%2][other], alt = display, lang = lang, sc = sc[j%2], tr = "-"}
			insert(chars, display .. " (")
			display = ruby({["markup"] = "[" .. data[j%2][data[(j+1)%2][other]] .. "](" .. var[(j+1)%2] .. ")"})
			display = language_link{term = data[j%2][data[(j+1)%2][other]], alt = display, lang = lang, sc = sc[(j+1)%2], tr = "-"}
			insert(chars, display .. " etc.)")
			return chars, 3
		end
		
		return chars
	end
	
	for i = 0, 1, 1 do
		for ch, other_ch in pairs(data[i]) do
			if data[(i+1)%2][other_ch] ~= ch then
				local chars, issue = {}
				local display = ruby({["markup"] = "[" .. ch .. "](" .. var[i] .. ")"})
				display = language_link{term = ch, alt = display, lang = lang, sc = sc[i], tr = "-"}
				insert(chars, display)
				chars, issue = find_stable_loop(chars, other_ch, i)
				if issue == 1 or issue == 2 then
					local sc_this, mod_this, j = {}
					if match(chars[#chars-1], var[(i+1)%2]) then
						j = 1
					else
						j = 0
					end
					mod_this = mod[(i+j)%2]
					sc_this = {[0] = sc[(i+j)%2], sc[(i+j+1)%2]}
					for k, ch in ipairs(chars) do
						chars[k] = tag_text(ch, lang, sc_this[k%2], "term")
					end
					local modname = "zh/data/" .. mod_this
					if issue == 1 then
						discrepancy(modname,
							"character references itself: %s",
							concat(chars, " → ")
						)
					elseif issue == 2 then
						discrepancy(modname,
							"missing character: %s",
							concat(chars, " → ")
						)
					end
				elseif issue == 3 then
					for j, ch in ipairs(chars) do
						chars[j] = tag_text(ch, lang, sc[(i+j)%2], "term")
					end
					discrepancy("zh/data/" .. mod[i],
						"possible mismatched character: %s",
						concat(chars, " → ")
					)
				end
			end
		end
	end
end

local function check_serialization(modname)
	local serializers = {
		["Hani-sortkey/data/serialized"] = "Hani-sortkey/serializer",
	}
	
	if not serializers[modname] then
		return nil
	end
	
	local serializer = serializers[modname]
	local current_data = require("Module:" .. serializer).main(true)
	local stored_data = require("Module:" .. modname)
	if current_data ~= stored_data then
		discrepancy(modname,
			"<strong><u>Important!</u> Serialized data is out of sync. Use [[Module:%s]] to update it. If you have made any changes to the underlying data, the serialized data <u>must</u> be updated before these changes will take effect.</strong>",
			serializer
		)
	end
end

local find_code = require("Module:memoize")(function(message)
	return match(message, "<code>([^<]+)</code>")
end)

local function compare_messages(message1, message2)
	local code1, code2 = find_code(message1), find_code(message2)
	if code1 and code2 then
		return code1 < code2
	else
		return message1 < message2
	end
end

-- Warning: cannot be called twice in the same module invocation because
-- some module-global variables are not reset between calls.
local function do_checks(frame, modules)
	messages = setmetatable({}, messages_mt)
	
	if modules["zh/data/ts"] or modules["zh/data/st"] then
		check_zh_trad_simp()
	end
	check_languages(frame)
	check_etym_languages()

	-- families and scripts must be checked AFTER languages; languages checks fill out
	-- the nonempty_families and nonempty_scripts tables, used for testing if a family/script
	-- is ever used in the data
	check_families()
	check_scripts()
	
	check_wikidata_languages()
	
	if modules["labels/data"] then
		check_labels()
	end
	
	for module in pairs(modules) do
		check_serialization(module)
	end
	
	setmetatable(messages, nil)
	
	for _, msglist in pairs(messages) do
		msglist:sort(compare_messages)
	end
	
	local ret = messages
	messages = nil
	return ret
end

local function format_message(modname, msglist)
	local header; if match(modname, "^Module:") or match(modname, "^Template:") then
		header = "===[[" .. modname .. "]]==="
	else
		header = "===[[Module:" .. modname .. "]]==="
	end
	return header .. msglist:map(function(msg)
		return "\n* " .. msg
	end):concat()
end

function export.check_modules_t(frame)
	local args = frame.args
	
	local modules = list_to_set(args)
	local ret = Array()
	local messages = do_checks(frame, modules)
	
	for _, module in ipairs(args) do
		local msglist = messages[module]
		if msglist then
			ret:insert(format_message(module, msglist))
		end
	end
	return ret:concat("\n")
end

function export.perform(frame)
	local messages = do_checks(frame, {})
	
	-- Format the messages
	local ret = Array()
	for modname, msglist in sorted_pairs(messages) do
		ret:insert(format_message(modname, msglist))
	end
	
	-- Are there any messages?
	-- TODO: check how many messages there are.
	if false then --if i == 1 then
		return "<b class=\"success\">Glory to Arstotzka.</b>"
	else
		ret:insert(1, "<b class=\"warning\">Discrepancies detected:</b>")
		return ret:concat("\n")
	end
end

return export
"https://si.wiktionary.org/w/index.php?title=Module:data_consistency_check&oldid=228603" වෙතින් සම්ප්‍රවේශනය කෙරිණි