User:So9q/Improved-xte.js

From Wiktionary, the free dictionary
Jump to navigation Jump to search

Note – after saving, you may have to bypass your browser’s cache to see the changes.

  • Mozilla / Firefox / Safari: hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (Command-R on a Macintosh);
  • Konqueror and Chrome: click Reload or press F5;
  • Opera: clear the cache in Tools → Preferences;
  • Internet Explorer: hold Ctrl while clicking Refresh, or press Ctrl-F5.

Etymology

From experimental translator's extension.

Proper noun

xte

  1. a script developed by Keφr and improved by So9q to assist in working on translations.

Usage notes

I really mean the experimental part. Features and bugs will appear and disappear at the author's discretion. Install at your own risk. (To do that, put {{subst:iusc|1=User:So9q/Improved-xte.js}} into your common.js, on its own line.) Adding the script to your watchlist is a good idea if you do not want to miss anything.

xte recognises names and codes of all languages and scripts encoded into the appropriate modules, like Module:languages. When xte fails to recognise a valid entry, you should probably add the support to {{langrev}}, {{scriptrev}} and such.

Bug reports, feature requests, and other questions are to be answered on User talk:So9q/Improved-xte.js.

Source code

Originally from User:Kephir/gadgets/xte.js

Features

The following features are thought to be functioning correctly in most cases and are not considered (yet) for removal:

Fixing translation lists
A a "[fix]" link appears next to "[edit]" at each "Translations" section. After clicking it the script attempts to convert translation lists to use the {{t}} template, attaching gender, transcription and script parameters as appropriate; then it presents the user with a diff which, after reviewing, the user may save. Whole articles can also be processed; just pull down the "xte" menu and select "Fix translations".
Quick code queries
The "Query" menu item brings up a dialog box allowing you to check language name by giving its code and vice versa.
Substitute in edit box
Substitute templates without saving, using a single keypress. Select some markup and press Alt+; (in Chromium) or Alt+Shift+; (in Firefox) to immediately substitute appropriately marked templates. (Also expands four tildes into your signature.)

Features in development:

Partial diff undoing
When viewing a diff, the editor can click the minus signs to restore the previous version of a line. Useful when working with the translation fixer.
Translation fixing

You probably got pointed here because xte found a suspicious entry in a page. "Suspicious" probably means one of the following:

Multi-word phrase
When multiple words are linked in a single translation entry, xte assumes the translation is into a sum-of-parts phrase, and wraps the individual words into links if necessary. This assumption might be wrong at times; a translation of an idiomatic multi-word English term into other languages may be likewise an idiomatic multi-word phrase. If this is the case, un-link the individual words.
To judge idiomaticity, try some of the tests linked below:
Inflected word or vocalised spelling
Some entries may link to a different word than they display; most of the time, the displayed label is an inflected form of a word, and the link target is its base. A proper correction will probably depend on context. It should be safe to link to the inflected form; ideally, the target page will point to the base form of the word and/or explain context in which the inflected form is used. However, as of now, most of the time that page simply does not exist and you should not create it if you have little knowledge of the target language.
Some alphabets (like Arabic and Hebrew) do not mark vowels in normal writing; however, putting these marks in a dictionary is obviously beneficial. Latin entries may mark vowel length with macrons; these also do not appear in normal writing. Similarly, a Russian translation entry might want to mark the stressed syllable with a acute accent mark.
In the past, xte would change the link to point to the displayed text. Current behaviour is to put link text into |alt= and preserve the original link. In the latter case, the suggested replacement is most probably fine, but the former might need some attention. Since xte cannot distinguish these two cases, the item is marked for review.
"Chinese"
The language name "Chinese" (zh) is ambiguous and has been deprecated in favour of more precise terms like "Mandarin" (cmn), "Cantonese" (yue), "Min Nan" (nan), etc. However, some entries still contain translations into "Chinese". Most probably these will be into Mandarin (almost surely if they use {{zh-ts}} or {{zh-tsp}}), and xte will convert them to say "Mandarin", but they may need to be checked whether this is actually the case.

If you are unsure what to do, leave the translation entry in the state suggested by xte, or keep the old version.


// so9q's improved-xte.js
// original source: [[User:Kephir/gadgets/xte]]
/*jshint shadow:true, scripturl:true, undef:true, latedef:true, boss:true, loopfunc:true, unused:true */ // <nowiki>
/*global mw, jQuery */
mw.loader.using(['mediawiki.util', 'mediawiki.api', 'mediawiki.Uri'], function() {
"use strict";
la
// PRO settings
// Warning if you enable this you have to carefully review the changes to ensure 
// that you do not introduce errors.
var xtepro = true;  // enable with caution!
// set these langs do disable adding {{attention}} for them automatically
var prolangs = ["af", "da", "de", "eo", "es", "is", "no", "nb", "nl", "nn", "sv"];


var code2nameLookup = null;
var code2scLookup = null;

var name2codeLookup = {
	// "false" values indicate translation groups, i.e. names with no own code under which
	// distinct languages are kept together, like Chinese which contains the Mandarin (cmn),
	// Cantonese (yue) and Min Nan (nan) varieties as sub-items. xte warns when translations
	// are found directly under group headers, and ignores them otherwise.
	// Greek (el) for example should not be added here, because translations directly under
	// "Greek" item are accepted, even though "Greek" serves as a parent for Ancient Greek (grc)
	"Apache"               : false,
	"Sorbian"              : false,
	"Berber"               : false,
	"Sami"                 : false,
	"Marquesan"            : false,
	"Miwok"                : false,
	// so9q speedup
	"Aghul" : "agx",
"Akhvakh" : "akv",
"Andi" : "ani",
"Angor" : "agg",
"Hebrew Classical Syriac" : "null",
"Asháninka" : "cni",
"Avestan" : "ae",
"Bactrian" : "xbc",
"Bagvalal" : "kva",
"Bakung" : "xkl",
"Banda" : "bnd",
"Banggai" : "bgz",
"Beja" : "bej",
"Bemba" : "bem",
"Bilba" : "bpz",
"Bintulu" : "bny",
"Botlikh" : "bph",
"Bouyei" : "pcc",
"Middle Breton Breton" : "null",
"Middle Breton" : "xbm",
"Brunei Bisaya" : "bsb",
"Budukh" : "bdk",
"Bunun" : "bnn",
"Car Nicobarese" : "caq",
"Carrier" : "crx",
"Chakma" : "ccp",
"Chamalal" : "cji",
"Chamorro" : "ch",
"Chru" : "cje",
"Cia-Cia" : "cia",
"Coastal Kadazan" : "kzj",
"Comanche" : "com",
"Dusner" : "dsn",
"Middle Dutch Dutch" : "null",
"Middle Dutch" : "dum",
"Dyirbal" : "dbl",
"Eritai" : "ert",
"Eshtehardi" : "esh",
"Even" : "eve",
"Old French Middle French" : "null",
"North Frisian Frisian" : "null",
"Old Frisian North Frisian" : "null",
"Old Frisian" : "ofs",
"Saterland Frisian Old Frisian" : "null",
"West Frisian Saterland Frisian" : "null",
"Gallurese" : "sdn",
"Gaulish" : "cel-gau",
"Gayo" : "gay",
"Ge'ez" : "gez",
"Middle High German German" : "null",
"Middle High German" : "gmh",
"Old High German Middle High German" : "null",
"Old High German" : "goh",
"Guerrero Amuzgo" : "amu",
"Gurindji" : "gue",
"Guugu Yimidhirr" : "kky",
"Hadrami" : "xhd",
"Han" : "haa",
"Havasupai-Walapai-Yavapai" : "yuf",
"Hunzib" : "huz",
"Igbo" : "ig",
"Ilocano" : "ilo",
"Iranun" : "ill",
"Isthmus Zapotec" : "zai",
"Old Javanese Javanese" : "null",
"Old Javanese" : "kaw",
"Karata" : "kpt",
"Kemak" : "kem",
"Khasi" : "kha",
"Khinalug" : "kjj",
"Kickapoo" : "kic",
"Kimaragang" : "kqr",
"Klallam" : "clm",
"Kongo" : "kg",
"Krisa" : "ksi",
"Kuna" : "cuk",
"Kwanyama" : "kj",
"Munsee Lenape" : "null",
"Leti (Indonesia)" : "lti",
"Middle Low German German Low German" : "null",
"Middle Low German" : "gml",
"Loxicha Zapotec" : "ztp",
"Maasai" : "mas",
"Madurese" : "mad",
"Makasar" : "mak",
"Jawi Malay" : "null",
"Jawi" : "null",
"Rumi Malay" : "null",
"Rumi" : "null",
"Mansi" : "mns",
"Marshallese" : "mh",
"Mayo" : "mfy",
"Mbabaram" : "vmb",
"Old Median" : "xme-old",
"Mende" : "men",
"Middle English" : "enm",
"Minangkabau" : "min",
"Mingo" : "iro-min",
"Miskito" : "miq",
"Mon" : "mnw",
"Montana Salish" : "fla",
"Nauruan" : "na",
"Negidal" : "neg",
"Nenets" : "yrk",
"Newar" : "new",
"Ngiyambaa" : "wyb",
"Nias" : "nia",
"Northern Amami-Oshima" : "ryn",
"Nuer" : "nus",
"Manitoba Saulteux Ojibwe" : "null",
"Manitoba Saulteux" : "null",
"Oroch" : "oac",
"Orok" : "oaa",
"Pamona" : "pmf",
"Pangasinan" : "pag",
"Paulohi" : "plh",
"Middle Persian Persian" : "null",
"Middle Persian" : "pal",
"Phoenician" : "phn",
"Phrygian" : "xpg",
"Pohnpeian" : "pon",
"Draweno-Polabian Polabian" : "null",
"Draweno-Polabian" : "null",
"Modern Polabian Polabian" : "null",
"Modern Polabian" : "null",
"Old Polabian Polabian" : "null",
"Old Polabian" : "null",
"Rade" : "rad",
"Rutul" : "rut",
"Rwanda-Rundi" : "rw",
"S'gaw Karen" : "ksw",
"Sabaean" : "xsa",
"Santali" : "sat",
"Campidanese Sardinian" : "sc",
"Sassarese" : "sdc",
"Sebop" : "sib",
"Seri" : "sei",
"Sherpa" : "xsr",
"Simeulue" : "smr",
"Siraya" : "fos",
"Southern Amami-Oshima" : "ams",
"Southern Puebla Mixtec" : "mit",
"Southern Thai" : "sou",
"Sumbawa" : "smw",
"Swazi" : "ss",
"Tae'" : "rob",
"Tagal Murut" : "mvv",
"Tat" : "ttt",
"Ter Sami" : "sjt",
"Tigre" : "tig",
"Tiwi" : "tiw",
"Toba Batak" : "bbc",
"Tontemboan" : "tnt",
"Tsakhur" : "tkr",
"Tulu" : "tcy",
"Uab Meto" : "aoz",
"Udi" : "udi",
"Ulch" : "ulc",
"Middle Welsh Welsh" : "null",
"Middle Welsh" : "wlm",
"Western Maninkakan" : "mlq",
"Yaeyama" : "rys",
"Yakan" : "yka",
"Yamdena" : "jmd",
"Yan-nhangu" : "jay",
"Yapese" : "yap",
"Yaweyuha" : "yby",
"Yonaguni" : "yoi",
"Yuchi" : "yuc",
"Záparo" : "zro",
"Old French French" : "null",
"Pirahã" : "myp",
	"Abaza" : "abq",
"Atayal" : "tay",
"Baekje" : "pkc",
"Bahnar" : "bdq",
"Bats" : "bbl",
"Bau Bidayuh" : "sne",
"Bezhta" : "kap",
"Chepang" : "cdm",
"Min-nan Min Dong" : "null",
"Min-nan" : "null",
"Min-nan Chinese" : "null",
"Wu Min Dong" : "null",
"Chipewyan" : "chp",
"Northern East Cree" : "crl",
"Plains Northern East" : "null",
"Plains" : "null",
"Plains Cree" : "crk",
"Southern East Plains" : "null",
"Southern East" : "null",
"Southern East Cree" : "crj",
"Dargwa" : "dar",
"Dzongkha" : "dz",
"Eastern Cham" : "cjm",
"Frisian" : "fy",
"Saterland North" : "null",
"Saterland" : "null",
"Saterland Frisian" : "stq",
"West Saterland" : "null",
"West" : "null",
"Godoberi" : "gdo",
"Pontic Greek Ancient" : "null",
"Pontic Greek" : "pnt",
"Itelmen" : "itl",
"Jingpho" : "kac",
"Kabardian" : "kbd",
"Karaim" : "kdr",
"Karakhanid" : "xqa",
"Ket" : "ket",
"Komi-Zyrian" : "kpv",
"Koryak" : "kpy",
"Lak" : "lbe",
"Lenape" : "null",
"Munsee null" : "null",
"Unami Munsee" : "null",
"Lolopo" : "ycl",
"Machiguenga" : "mcb",
"Mazanderani" : "mzn",
"Montagnais" : "moe",
"Muong" : "mtq",
"Nanai" : "gld",
"Nganasan" : "nio",
"Nivkh" : "niv",
"Northern Sotho" : "nso",
"Northern Yukaghir" : "ykg",
"Old Saxon" : "osx",
"Oroqen" : "orh",
"Ottawa" : "otw",
"Pacoh" : "pac",
"Plautdietsch" : "pdt",
"Kildin Inari" : "null",
"Kildin" : "null",
"Kildin Sami" : "sjd",
"Northern Kildin" : "null",
"Selkup" : "sel",
"Shuswap" : "shs",
"Southern Kam" : "kmc",
"Svan" : "sva",
"Tabasaran" : "tab",
"Tachawit" : "shy",
"Tindi" : "tin",
"Tofa" : "kim",
"Tsez" : "ddo",
"Tundra Enets" : "enh",
"Ugaritic" : "uga",
"Yurok" : "yur",
	"Aba" : "utp",
"Abanyom" : "abm",
"Abau" : "aau",
"Abenaki" : "abe",
"Abenlen Ayta" : "abp",
"Abua" : "abn",
"Abung" : "abl",
"Acatepec Me'phaa" : "tpx",
"Achagua" : "aca",
"Agarabi" : "agd",
"Agi" : "aif",
"Ahtna" : "aht",
"Aisi" : "mmq",
"Aklanon" : "akl",
"Akolet" : "akt",
"Akoye" : "miw",
"Akuwagel" : "bey",
"Alaba" : "alw",
"Alak" : "alk",
"Algonquin" : "alq",
"Alsea" : "aes",
"Alune" : "alp",
"Alutiiq" : "ems",
"Amahuaca" : "amc",
"Amal" : "aad",
"Amasi" : "alv-ama",
"Amele" : "aey",
"Amondawa" : "adw",
"Anaang" : "anw",
"Angolar" : "aoa",
"Anmatyerre" : "amx",
"Annobonese" : "fab",
"Antigua and Barbuda Creole English" : "aig",
"Antillean Creole" : "gcf",
"Anuki" : "aui",
"Anus" : "auq",
"Anuta" : "aud",
"Ao" : "njo",
"Apiaká" : "api",
"Apinayé" : "apn",
"Apurinã" : "apu",
"Aputai" : "apx",
"Moroccan Arabic Egyptian Arabic" : "null",
"Araki" : "akr",
"Classical Syriac Aramaic" : "null",
"Arapaho" : "arp",
"Archi" : "aqc",
"Are" : "mwc",
"Arhuaco" : "arh",
"Ari" : "aac",
"Arifama-Miniafia" : "aai",
"Arikara" : "ari",
"Aruop" : "lsr",
"Ashéninka Perené" : "prq",
"Assiniboine" : "asb",
"Asumboa" : "aua",
"Atakapa" : "aqp",
"Atong (India)" : "aot",
"Au" : "avt",
"Aukan" : "djk",
"Avikam" : "avi",
"Awa-Cuaiquer" : "kwi",
"Awadhi" : "awa",
"Awtuw" : "kmn",
"Ayabadhu" : "ayd",
"Aynu" : "aib",
"Bagusa" : "bqb",
"Baldemu" : "bdn",
"Bambam" : "ptu",
"Bamu" : "bcf",
"Baniwa" : "bwi",
"Bankon" : "abb",
"Bantawa" : "bap",
"Barasana" : "bsn",
"Barbareño" : "boi",
"Bardi" : "bcj",
"Bargam" : "mlp",
"Bari" : "bfa",
"Bariai" : "bch",
"Bariji" : "bjc",
"Barok" : "bjk",
"Barí" : "mot",
"Batad Ifugao" : "ifb",
"Batu" : "btu",
"Bau" : "bbd",
"Bauro" : "bxa",
"Beezen" : "bnz",
"Belizean Creole" : "bzj",
"Bende" : "bdp",
"Berbice Creole Dutch" : "brc",
"Biak" : "bhw",
"Bibaali" : "bcn",
"Biem" : "bmc",
"Bih" : "ibh",
"Bilbil" : "brz",
"Biliau" : "bcu",
"Biloxi" : "bll",
"Bilur" : "bxf",
"Binukid" : "bkd",
"Bitare" : "brt",
"Bogaya" : "boq",
"Bole" : "bol",
"Bonggo" : "bpg",
"Bora" : "boa",
"Borôro" : "bor",
"Boselewa" : "bwf",
"Bourguignon" : "roa-brg",
"Bughotu" : "bgt",
"Buhutu" : "bxh",
"Bukawa" : "buk",
"Bukusu" : "bxk",
"Buli (Ghana)" : "bwu",
"Buli (Indonesia)" : "bzq",
"Bulu (New Guinea)" : "bjl",
"Bunama" : "bdd",
"Buwal" : "bhs",
"Bwaidoka" : "bwd",
"Bwanabwana" : "tte",
"Bwatoo" : "bwa",
"Cacua" : "cbv",
"Caddo" : "cad",
"Cahuilla" : "chl",
"Caka" : "ckx",
"Camarines Norte Agta" : "abd",
"Cameroon Pidgin" : "wes",
"Camsá" : "kbh",
"Carapana" : "cbc",
"Carijona" : "cbd",
"Casiguran Dumagat Agta" : "dgc",
"Cavineña" : "cav",
"Cayuga" : "cay",
"Cebuano" : "ceb",
"Cemuhî" : "cam",
"Central Bai" : "bca",
"Central Bontoc" : "lbk",
"Central Cagayan Agta" : "agt",
"Central Pomo" : "poo",
"Central Tunebo" : "tuf",
"Ch'orti'" : "caa",
"Chachi" : "cbi",
"Chaha" : "sem-cha",
"Champenois" : "roa-cha",
"Chavacano" : "cbk",
"Chayahuita" : "cbt",
"Chewong" : "cwg",
"Chichicapan Zapotec" : "zpv",
"Chickasaw" : "cic",
"Chimila" : "cbg",
"Chimwiini" : "bnt-cmw",
"Chinook Jargon" : "chn",
"Chiquihuitlán Mazatec" : "maq",
"Chiquimulilla" : "nai-chi",
"Chitimacha" : "ctm",
"Chrau" : "crw",
"Chuukese" : "chk",
"Cimbrian" : "cim",
"Cinta Larga" : "cin",
"Coast Miwok" : "csi",
"Coatzospan Mixtec" : "miz",
"Cocama" : "cod",
"Cocopa" : "coc",
"Cogui" : "kog",
"Coos" : "csz",
"Copainalá Zoque" : "zoc",
"Cora" : "crn",
"Corsican" : "co",
"Creek" : "mus",
"Cruzeño" : "crz",
"Cubeo" : "cub",
"Cuiba" : "cui",
"Culli" : "sai-cul",
"Curripaco" : "kpc",
"Daasanach" : "dsh",
"Dakota" : "dak",
"Dawawa" : "dww",
"Day" : "dai",
"Deccani" : "dcc",
"Deg Xinag" : "ing",
"Dena'ina" : "tfn",
"Dení" : "dny",
"Desano" : "des",
"Dia" : "dia",
"Dibiyaso" : "dby",
"Dilling" : "dil",
"Dime" : "dim",
"Diodio" : "ddi",
"Dobu" : "dob",
"Doga" : "dgg",
"Dogrib" : "dgr",
"Dongolawi" : "kzh",
"Dorig" : "wwo",
"Drehu" : "dhv",
"Duau" : "dva",
"Dumi" : "dus",
"Dupaningan Agta" : "duo",
"Duwet" : "gve",
"East Futuna" : "fud",
"Eastern Arrernte" : "aer",
"Efik" : "efi",
"Efutop" : "ofu",
"Ega" : "ega",
"Ekajuk" : "eka",
"Elfdalian" : "ovd",
"Elkei" : "elk",
"Eman" : "emn",
"Emberá-Catío" : "cto",
"Emberá-Chamí" : "cmi",
"Emberá-Tadó" : "tdc",
"Emerillon" : "eme",
"Ende" : "end",
"Epena" : "sja",
"Ese Ejja" : "ese",
"Esimbi" : "ags",
"Eton (Cameroon)" : "eto",
"Evant" : "bzz",
"Fagani" : "faf",
"Fala" : "fax",
"Folopa" : "ppo",
"Fon" : "fon",
"Fox" : "sac",
"Middle French French" : "null",
"Middle French" : "frm",
"Fulniô" : "fun",
"Gabadi" : "kbt",
"Gagadu" : "gbu",
"Gallo" : "roa-gal",
"Gapapaiwa" : "pwg",
"Gedaged" : "gdd",
"Gela" : "nlg",
"Rhine Franconian Alemannic German" : "null",
"Rhine Franconian" : "gmw-rfr",
"Ghari" : "gri",
"Ghayavi" : "bmk",
"Gidar" : "gid",
"Gilbertese" : "gil",
"Girawa" : "bbr",
"Gobasi" : "goi",
"Goemai" : "ank",
"Golin" : "gvf",
"Gooniyandi" : "gni",
"Gottscheerish" : "gmw-gts",
"Grass Koiari" : "kbk",
"Modern Ancient" : "null",
"Modern" : "null",
"Modern Greek" : "el",
"Grenadian Creole English" : "gcl",
"Gros Ventre" : "ats",
"Guahibo" : "guh",
"Guajajára" : "gub",
"Guambiano" : "gum",
"Guanano" : "gvc",
"Guayabero" : "guo",
"Guazacapán" : "nai-guz",
"Guianese Creole" : "gcr",
"Guinea-Bissau Creole" : "pov",
"Gullah" : "gul",
"Gumatj" : "gnn",
"Gweda" : "grw",
"Haida" : "hai",
"Hamtai" : "hmt",
"Hanunoo" : "hnn",
"Harsusi" : "hss",
"Hazaragi" : "haz",
"Hdi" : "xed",
"Hidatsa" : "hid",
"Highland Totonac" : "tos",
"Hiligaynon" : "hil",
"Hinukh" : "gin",
"Hiw" : "hiw",
"Hoava" : "hoa",
"Hote" : "hot",
"Hoyahoya" : "hhy",
"Huehuetla Tepehua" : "tee",
"Hupa" : "hup",
"Hupdë" : "jup",
"Iamalele" : "yml",
"Iau" : "tmu",
"Iceve-Maci" : "bec",
"Ida'an" : "dbj",
"Ignaciano" : "ign",
"Imonda" : "imn",
"Inebu One" : "oin",
"Ineseño" : "inz",
"Inga" : "inb",
"Ingrian" : "izh",
"Ipulo" : "ass",
"Iraqw" : "irk",
"Isan" : "tts",
"Islander Creole English" : "icr",
"Itzá" : "itz",
"Iwam" : "iwm",
"Ixil" : "ixl",
"Iyive" : "uiv",
"Izon" : "ijc",
"Jabutí" : "jbt",
"Jamaican Creole" : "jam",
"Jamamadí" : "jaa",
"Jaminjung" : "djd",
"Janday" : "jan",
"Jarai" : "jra",
"Jebero" : "jeb",
"Jehai" : "jhi",
"Jemez" : "tow",
"Jingulu" : "jig",
"Jola-Fonyi" : "dyo",
"Juǀ'hoan" : "ktz",
"Kabuverdianu" : "kea",
"Kadaru" : "kdu",
"Kadiwéu" : "kbc",
"Kaera" : "jka",
"Kahua" : "agw",
"Kaiep" : "kbw",
"Kairiru" : "kxa",
"Kakabai" : "kqf",
"Kala Lagaw Ya" : "mwp",
"Kamasau" : "kms",
"Kamayurá" : "kay",
"Kambera" : "xbr",
"Kamula" : "xla",
"Kandas" : "kqw",
"Kaninuwa" : "wat",
"Kankanaey" : "kne",
"Kanoé" : "kxo",
"Kansa" : "ksk",
"Kanufi" : "kni",
"Kap" : "ykm",
"Kapampangan" : "pam",
"Kara (New Guinea)" : "leu",
"Karajá" : "kpj",
"Karawa" : "xrw",
"Karitiâna" : "ktn",
"Karok" : "kyh",
"Kaska" : "kkz",
"Kaskihá" : "gva",
"Kasua" : "khs",
"Kathlamet" : "nai-kat",
"Kato" : "ktw",
"Kawaiisu" : "xaw",
"Kaxararí" : "ktx",
"Kayabí" : "kyz",
"Kayardild" : "gyd",
"Keapara" : "khz",
"Kedang" : "ksx",
"Kein" : "bmh",
"Kerewo" : "kxz",
"Khanty" : "kca",
"Khowar" : "khw",
"Kilivila" : "kij",
"Kinikinao" : "gqn",
"Kirikiri" : "kiy",
"Kis" : "kis",
"Kitsai" : "kii",
"Klamath-Modoc" : "kla",
"Koasati" : "cku",
"Koho" : "kpm",
"Kokota" : "kkk",
"Koluwawa" : "klx",
"Kom (Cameroon)" : "bkm",
"Kombio" : "xbi",
"Komering" : "kge",
"Komodo" : "kvh",
"Konomala" : "koa",
"Konomihu" : "nai-knm",
"Koreguaje" : "coe",
"Korlai Creole Portuguese" : "vkp",
"Koronadal Blaan" : "bpr",
"Kott" : "zko",
"Kovai" : "kqb",
"Kove" : "kvc",
"Koyra Chiini" : "khq",
"Koyraboro Senni" : "ses",
"Kpasam" : "pbn",
"Krio" : "kri",
"Kriol" : "rop",
"Kristang" : "mcm",
"Krobu" : "kxb",
"Kua-nsi" : "ykn",
"Kuamasi" : "yku",
"Kumak" : "nee",
"Kuman" : "kue",
"Central Kurdish Kurdish" : "null",
"Central Kurdish" : "ckb",
"Laki Central Kurdish" : "null",
"Laki" : "lki",
"Northern Kurdish Laki" : "null",
"Northern Kurdish" : "kmr",
"Southern Kurdish Northern Kurdish" : "null",
"Southern Kurdish" : "sdh",
"Kutenai" : "kut",
"Kwaio" : "kwd",
"Kwaza" : "xwa",
"Kwerba" : "kwe",
"Kwoma" : "kmo",
"Label" : "lbb",
"Lahu" : "lhu",
"Lake Miwok" : "lmw",
"Lama Bai" : "lay",
"Lamboya" : "lmy",
"Lamma" : "lev",
"Lampung Api" : "ljp",
"Larike-Wakasihu" : "alo",
"Lawangan" : "lbx",
"Leonese" : "roa-leo",
"Lepcha" : "lep",
"Li'o" : "ljl",
"Lillooet" : "lil",
"Limilngan" : "lmc",
"Lo-Toga" : "lht",
"Lombard" : "lmo",
"Loniu" : "los",
"Lorrain" : "roa-lor",
"Lou" : "loj",
"Louisiana Creole French" : "lou",
"Lovono" : "vnk",
"Lower Tanana" : "taa",
"Lushootseed" : "lut",
"Luvale" : "lue",
"Ma'anyan" : "mhy",
"Ma'di" : "mhi",
"Macaguán" : "mbn",
"Macuna" : "myy",
"Madak" : "mmx",
"Magori" : "zgr",
"Mahican" : "mjy",
"Makah" : "myh",
"Makalero" : "mjb",
"Malalí" : "sai-mal",
"Malecite-Passamaquoddy" : "pqm",
"Malila" : "mgq",
"Mamasa" : "mqj",
"Manam" : "mva",
"Mandan" : "mhq",
"Mandara" : "tbf",
"Mangarevan" : "mrv",
"Manipuri" : "mni",
"Maranungku" : "zmr",
"Maratino" : "sai-mar",
"Margi" : "mrt",
"Masakará" : "sai-msk",
"Massachusett" : "wam",
"Matlatzinca" : "mat",
"Mato" : "met",
"Matsés" : "mcf",
"Matukar" : "mjk",
"Mauwake" : "mhl",
"Maxakalí" : "mbl",
"Maybrat" : "ayz",
"Mbula" : "mna",
"Mbyá Guaraní" : "gun",
"Medebur" : "mjm",
"Media Lengua" : "mue",
"Mehek" : "nux",
"Mehri" : "gdq",
"Mele-Fila" : "mxe",
"Menya" : "mcr",
"Meramera" : "mxm",
"Merei" : "lmb",
"Mesaka" : "iyo",
"Mfumte" : "nfu",
"Miami" : "mia",
"Mian" : "mpt",
"Midob" : "mei",
"Mina" : "hna",
"Minaveha" : "mvn",
"Mindiri" : "mpn",
"Minica Huitoto" : "hto",
"Minigir" : "vmg",
"Misantla Totonac" : "tlc",
"Miyako" : "mvi",
"Mmani" : "buy",
"Mo" : "wkd",
"Mochica" : "omc",
"Molima" : "mox",
"Mondé" : "mnd",
"Mongghul" : "xgn-mgl",
"Mopan Maya" : "mop",
"Mori Atas" : "mzq",
"Mota" : "mtt",
"Mountain Koiari" : "kpx",
"Mubami" : "tsx",
"Muduapa" : "wiv",
"Muinane" : "bmr",
"Muna" : "mnb",
"Mundari" : "unr",
"Munsee" : "umu",
"Murui Huitoto" : "huu",
"Mussau-Emira" : "emi",
"Muyuw" : "myw",
"Mwani" : "wmw",
"Mwotlap" : "mlv",
"Nabi" : "mty",
"Classical Nahuatl" : "nci",
"Mecayapan Classical" : "null",
"Mecayapan" : "null",
"Mecayapan Nahuatl" : "nhx",
"Northern Puebla Mecayapan" : "null",
"Northern Puebla" : "null",
"Northern Puebla Nahuatl" : "ncj",
"Nakanai" : "nak",
"Nali" : "nss",
"Nambikwara" : "nab",
"Nanticoke" : "nnt",
"Narragansett" : "xnt",
"Nauna" : "ncn",
"Navarro-Aragonese" : "roa-oan",
"Nawaru" : "nwr",
"Nawathinehena" : "nwa",
"Nde-Nsele-Nta" : "ndd",
"Nehan" : "nsn",
"Neverver" : "lgk",
"Nez Perce" : "nez",
"Ngadha" : "nxg",
"Ngiemboon" : "nnh",
"Nicaraguan Creole" : "bzk",
"Nigerian Pidgin" : "pcm",
"Nihali" : "nll",
"Nimoa" : "nmw",
"Nisenan" : "nsz",
"Niuean" : "niu",
"Niwer Mil" : "hrc",
"Nkem-Nkum" : "isi",
"Nnam" : "nbp",
"Nomatsiguenga" : "not",
"Nootka" : "nuk",
"North Marquesan" : "mrq",
"North Muyu" : "kti",
"Northern Bai" : "bfc",
"Northern Emberá" : "emp",
"Northern Paiute" : "pao",
"Northern Pomo" : "pej",
"Northern Sierra Miwok" : "nsq",
"Nottoway" : "ntw",
"Nubi" : "kcn",
"Nukak Makú" : "mbr",
"Nukuoro" : "nkr",
"Nungon" : "paa-nun",
"Nyankole" : "nyn",
"Nzadi" : "nzd",
"Nüpode Huitoto" : "hux",
"Obispeño" : "obi",
"Ocaina" : "oca",
"Old Catalan" : "roa-oca",
"Old Leonese" : "roa-ole",
"Old Nubian" : "onw",
"Old Portuguese" : "roa-opt",
"Old Tupi" : "tpw",
"Olo" : "ong",
"Oneida" : "one",
"Onobasulu" : "onn",
"Ontong Java" : "ojv",
"Orejón" : "ore",
"Osage" : "osa",
"Otank" : "uta",
"Otomaco" : "sai-oto",
"Paama" : "pma",
"Pahi" : "lgt",
"Paiwan" : "pwn",
"Palawan Batak" : "bya",
"Palenquero" : "pln",
"Palu'e" : "ple",
"Pamosu" : "hih",
"Panare" : "pbh",
"Panim" : "pnr",
"Papapana" : "ppn",
"Papora" : "ppu",
"Parakanã" : "pak",
"Pare" : "asa",
"Pareci" : "pab",
"Pará Arára" : "aap",
"Patpatar" : "gfk",
"Paumarí" : "pad",
"Pawnee" : "paw",
"Pele-Ata" : "ata",
"Pemon" : "aoc",
"Penchal" : "pek",
"Penobscot" : "aaq",
"Pero" : "pip",
"Piapoco" : "pio",
"Picard" : "pcd",
"Pichinglis" : "fpe",
"Piedmontese" : "pms",
"Pijin" : "pis",
"Pima Bajo" : "pia",
"Piratapuyo" : "pir",
"Piscataway" : "psy",
"Plains Miwok" : "pmw",
"Playero" : "gob",
"Poitevin-Saintongeais" : "roa-poi",
"Pokangá" : "pok",
"Potawatomi" : "pot",
"Principense" : "pre",
"Puinave" : "pui",
"Purepecha" : "pua",
"Puyuma" : "pyu",
"Páez" : "pbb",
"Quapaw" : "qua",
"Quileute" : "qui",
"Quiripi" : "qyp",
"Rabha" : "rah",
"Raga" : "lml",
"Rama" : "rma",
"Ramoaaina" : "rai",
"Rarotongan" : "rar",
"Rasawa" : "rac",
"Rendille" : "rel",
"Rennellese" : "mnv",
"Reshe" : "res",
"Resígaro" : "rgr",
"Rikbaktsa" : "rkb",
"Romagnol" : "rgn",
"Ronji" : "roe",
"Roro" : "rro",
"Rotokas" : "roo",
"Rukai" : "dru",
"Sabu" : "hvn",
"Salinan" : "sln",
"Pite Northern" : "null",
"Pite" : "null",
"Pite Sami" : "sje",
"Skolt Pite" : "null",
"Samo" : "smq",
"San Juan Atzingo Popoloca" : "poe",
"San Miguel El Grande Mixtec" : "mig",
"San Pedro Amuzgos Amuzgo" : "azg",
"San Pedro Quiatoni Zapotec" : "zpf",
"Sangir" : "sxn",
"Saniyo-Hiyewe" : "sny",
"Saramaccan" : "srm",
"Sasak" : "sas",
"Sauraseni Prakrit" : "psu",
"Sechura" : "sai-sec",
"Secoya" : "sey",
"Sepa (New Guinea)" : "spe",
"Sera" : "sry",
"Seta" : "stf",
"Seychellois Creole" : "crs",
"Shawnee" : "sjw",
"Shehri" : "shv",
"Shona" : "sn",
"Shoshone" : "shh",
"Siar-Lak" : "sjr",
"Sika" : "ski",
"Sikaiana" : "sky",
"Sikkimese" : "sip",
"Sinacantán" : "nai-sin",
"Sinaugoro" : "snc",
"Sio" : "xsi",
"Siona" : "snn",
"Sipakapense" : "qum",
"Siriano" : "sri",
"Siroi" : "ssd",
"Sissano" : "sso",
"Sobei" : "sob",
"Sochiapam Chinantec" : "cso",
"Sonaga" : "ysg",
"Soo" : "teu",
"Sori-Harengan" : "sbh",
"South Marquesan" : "mqm",
"Southeastern Puebla Nahuatl" : "npl",
"Southeastern Tepehuan" : "stp",
"Southern Bai" : "bfs",
"Southern Ohlone" : "css",
"Southern Yukaghir" : "yux",
"Old Spanish Spanish" : "null",
"Old Spanish" : "osp",
"Suabo" : "szp",
"Suau" : "swp",
"Sumerian" : "sux",
"Sunwar" : "suz",
"Saliba (Colombia)" : "slc",
"Sãotomense" : "cri",
"Tabo" : "knv",
"Takia" : "tbc",
"Takuu" : "nho",
"Talise" : "tlr",
"Tanimbili" : "tbe",
"Tanimuca-Retuarã" : "tnc",
"Taparita" : "sai-tpr",
"Taraon" : "mhu",
"Tariana" : "tae",
"Tarifit" : "rif",
"Tarok" : "yer",
"Taroko" : "trv",
"Tarpia" : "tpf",
"Tataltepec Chatino" : "cta",
"Tatuyo" : "tav",
"Taupota" : "tpa",
"Tausug" : "tsg",
"Tawala" : "tbo",
"Tawasa" : "nai-taw",
"Tayo" : "cks",
"Teanu" : "tkw",
"Tenharim" : "pah",
"Teribe" : "tfr",
"Tezoatlán Mixtec" : "mxb",
"Tiang" : "tbj",
"Timucua" : "tjm",
"Tipai" : "nai-tip",
"Tiri" : "cir",
"Tiruray" : "tiy",
"Titan" : "ttv",
"Tivi" : "tiv",
"Tlahuica" : "ocu",
"Tlingit" : "tli",
"To'abaita" : "mlu",
"Tocantins Asurini" : "asu",
"Tocharian A" : "xto",
"Tocharian B" : "txb",
"Tokelauan" : "tkl",
"Tolai" : "ksd",
"Tommo So" : "dto",
"Tonkawa" : "tqw",
"Torau" : "ttu",
"Toro" : "tdv",
"Torres Strait Creole" : "tcs",
"Totoro" : "ttk",
"Transylvanian Saxon" : "gmw-tsx",
"Trimuris" : "tip",
"Trinitario" : "trn",
"Trumai" : "tpy",
"Tsafiki" : "cof",
"Tsakonian" : "tsd",
"Tsimané" : "cas",
"Tsimshian" : "tsi",
"Tsonga" : "ts",
"Tucano" : "tuo",
"Tukang Besi North" : "khc",
"Tukang Besi South" : "bhq",
"Tuki" : "bag",
"Tulu-Bohuai" : "rak",
"Tumleo" : "tmq",
"Tundra Nenets" : "yrk",
"Tungag" : "lcm",
"Tunica" : "tun",
"Tunjung" : "tjg",
"Tuscarora" : "tus",
"Tuyuca" : "tue",
"Tz'utujil" : "tzj",
"Tzeltal" : "tzh",
"Tzotzil" : "tzo",
"Ubir" : "ubr",
"Udihe" : "ude",
"Ulau-Suain" : "svb",
"Unami" : "unm",
"Uneapa" : "bbn",
"Unserdeutsch" : "uln",
"Ura (Vanuatu)" : "uur",
"Urarina" : "ura",
"Urat" : "urt",
"Uru-Eu-Wau-Wau" : "urz",
"Urubú-Kaapor" : "urb",
"Uyajitaya" : "duk",
"Venda" : "ve",
"Wab" : "wab",
"Wagi" : "fad",
"Wakhi" : "wbl",
"Wala" : "lgl",
"Wandamen" : "wad",
"Wardaman" : "wrr",
"Warekena" : "gae",
"Waris" : "wrs",
"Warungu" : "wrg",
"Wayampi" : "oym",
"Wayuu" : "guc",
"Wedau" : "wed",
"West Coast Bajau" : "bdr",
"West Tarangan" : "txn",
"Western Bukidnon Manobo" : "mbb",
"Wichita" : "wic",
"Wiwa" : "mbp",
"Wogeo" : "woc",
"Woleaian" : "woe",
"Wolio" : "wlo",
"Worora" : "wro",
"Woun Meu" : "noa",
"Wuvulu-Aua" : "wuv",
"Xingú Asuriní" : "asn",
"Yaa" : "iyx",
"Yagaria" : "ygr",
"Yakkha" : "ybh",
"Yanesha'" : "ame",
"Yanomamö" : "guu",
"Yaqui" : "yaq",
"Yareba" : "yrb",
"Yareni Zapotec" : "zae",
"Yawuru" : "ywr",
"Yelogu" : "ylg",
"Yeyi" : "yey",
"Yidiny" : "yii",
"Yimas" : "yee",
"Yola" : "yol",
"Yorta Yorta" : "xyy",
"Yucuna" : "ycn",
"Yukpa" : "yup",
"Yurumanguí" : "sai-yur",
"Yurutí" : "yui",
"Zaghawa" : "zag",
"Zande" : "zne",
"Zangskari" : "zau",
"Zoogocho Zapotec" : "zpq",
	"Abkhaz" : "ab",
	"Achuar" : "acu",
	"Aja" : "aja",
	"Aleut" : "ale",
	"Western Apache Apache" : "null",
	"Western Apache" : "apw",
	"Hijazi Arabic Egyptian Arabic" : "null",
	"Hijazi Arabic" : "acw",
	"Moroccan Arabic Hijazi Arabic" : "null",
	"Aragonese" : "an",
	"Aramaic" : "arc",
	"Hebrew Aramaic" : "null",
	"Syriac Hebrew" : "null",
	"Syriac" : "null",
	"Syriac Aramaic" : "null",
	"Atikamekw" : "atj",
	"Avar" : "av",
	"Kabyle Berber" : "null",
	"Tashelhit Kabyle" : "null",
	"Tashelhit" : "shi",
	"Bilua" : "blb",
	"Bislama" : "bi",
	"Catawba" : "chc",
	"Chechen" : "ce",
	"Chichewa" : "ny",
	"Hakka Dungan" : "null",
	"Chukchi" : "ckt",
	"Darkinjung" : "xda",
	"Emilian" : "egl",
	"Evenki" : "evn",
	"Ewe" : "ee",
	"Extremaduran" : "ext",
	"Haitian Creole" : "ht",
	"Hausa" : "ha",
	"Interlingue" : "ie",
	"Inuktitut" : "iu",
	"Old Irish Irish" : "null",
	"Old Irish" : "sga",
	"Isubu" : "szv",
	"Karelian" : "krl",
	"Komi-Permyak" : "koi",
	"Konkani" : "kok",
	"Lakota" : "lkt",
	"Lezgi" : "lez",
	"Ligurian" : "lij",
	"Luganda" : "lg",
	"Malagasy" : "mg",
	"Manchu" : "mnc",
	"Mapudungun" : "arn",
	"Mirandese" : "mwl",
	"Moksha" : "mdf",
	"Mòcheno" : "mhn",
	"Nahuatl" : "nah",
	"Neapolitan" : "nap",
	"Nogai" : "nog",
	"Novial" : "nov",
	"Old Church Slavonic" : "cu",
	"Glagolitic Old Church Slavonic" : "null",
	"Glagolitic" : "null",
	"Old Turkic" : "otk",
	"Ossetian" : "os",
	"Pali" : "pi",
	"Devanagari Pali" : "null",
	"Devanagari" : "null",
	"Papiamentu" : "pap",
	"Pipil" : "ppl",
	"Pitjantjatjara" : "pjt",
	"Polabian" : "pox",
	"Rapa Nui" : "rap",
	"Rohingya" : "rhg",
	"Samoan" : "sm",
	"Sanskrit" : "sa",
	"Seimat" : "ssg",
	"Silesian" : "szl",
	"Sindhi" : "sd",
	"Somali" : "so",
	"Southern Sierra Miwok" : "skd",
	"Sylheti" : "syl",
	"Tok Pisin" : "tpi",
	"Tongan" : "to",
	"Tuvaluan" : "tvl",
	"Veps" : "vep",
	"Votic" : "vot",
	"Vurës" : "msn",
	"Wallisian" : "wls",
	"Wauja" : "wau",
	"West Frisian" : "fy",
	"Wolof" : "wo",
	"Xhosa" : "xh",
	"Yoruba" : "yo",
	"Yucatec Maya" : "yua",
	"Zealandic" : "zea",
	"Aasax" : "aas",
	"Abu' Arapesh" : "aah",
	"Acehnese" : "ace",
	"Adyghe" : "ady",
	"Ahom" : "aho",
	"Akan" : "ak",
	"Akkadian" : "akk",
	"Alabama" : "akz",
	"Alutor" : "alr",
	"Alviri-Vidari" : "avd",
	"Ama" : "amm",
	"Imperial Aramaic Aramaic" : "null",
	"Imperial Aramaic" : "arc",
	"Classical Syriac Imperial Aramaic" : "null",
	"Classical Syriac" : "syc",
	"Mandaic Classical Syriac" : "null",
	"Mandaic" : "mid",
	"Samaritan Aramaic Mandaic" : "null",
	"Samaritan Aramaic" : "sam",
	"Arosi" : "aia",
	"Aymara" : "ay",
	"Bakhtiari" : "bqi",
	"Balinese" : "ban",
	"Baluchi" : "bal",
	"Bambara" : "bm",
	"Banjarese" : "bjn",
	"Baure" : "brg",
	"Betoi" : "sai-bet",
	"Blackfoot" : "bla",
	"Buginese" : "bug",
	"Bukiyip" : "ape",
	"Bulu (Cameroon)" : "bum",
	"Buryat" : "bua",
	"Central Atlas Tamazight" : "tzm",
	"Cheyenne" : "chy",
	"Teochew Min Nan" : "null",
	"Teochew" : "zhx-teo",
	"Wu Teochew" : "null",
	"Chiricahua" : "apm",
	"Choctaw" : "cho",
	"Chuave" : "cjv",
	"Cree" : "cr",
	"Crow" : "cro",
	"Dolgan" : "dlg",
	"East Central German" : "gmw-ecg",
	"Fijian" : "fj",
	"Fula" : "ff",
	"Futuna-Aniwa" : "fut",
	"Fuyug" : "fuy",
	"Alemannic German German" : "null",
	"Alemannic German" : "gsw",
	"Greenlandic" : "kl",
	"Guaraní" : "gn",
	"Hittite" : "hit",
	"Hopi" : "hop",
	"Indo-Portuguese" : "idb",
	"Ingush" : "inh",
	"Inupiak" : "ik",
	"Isnag" : "isd",
	"Istriot" : "ist",
	"Istro-Romanian" : "ruo",
	"Itawit" : "itv",
	"Jersey Dutch" : "gmw-jdt",
	"Jicarilla" : "apj",
	"Kaluli" : "bco",
	"Kamta" : "rkt",
	"Karipúna Creole French" : "kmv",
	"Kunigami" : "xug",
	"Ladin" : "lld",
	"Lavukaleve" : "lvk",
	"Laz" : "lzz",
	"Ledo Kaili" : "lew",
	"Livonian" : "liv",
	"Lote" : "uvl",
	"Lü" : "khb",
	"Mandar" : "mdr",
	"Maranao" : "mrw",
	"Watam" : "wax",
	"Megleno-Romanian" : "ruq",
	"Meänkieli" : "fit",
	"Mi'kmaq" : "mic",
	"Michif" : "crg",
	"Mingrelian" : "xmf",
	"Mohegan-Pequot" : "xpq",
	"Moroccan Amazigh" : "zgh",
	"Motu" : "meu",
	"Nama" : "naq",
	"Ngarrindjeri" : "nay",
	"North Slavey" : "scs",
	"Northern Ohlone" : "cst",
	"Nyunga" : "nys",
	"O'odham" : "ood",
	"Ojibwe" : "oj",
	"Oki-No-Erabu" : "okn",
	"Okinawan" : "ryu",
	"Old French" : "fro",
	"Old Occitan" : "pro",
	"Old Prussian" : "prg",
	"Ongota" : "bxe",
	"Ottoman Turkish" : "ota",
	"Pennsylvania German" : "pdc",
	"Pileni" : "piv",
	"Powhatan" : "pim",
	"Saanich" : "str",
	"Inari Sami" : "smn",
	"Northern Inari" : "null",
	"Northern" : "null",
	"Skolt Northern" : "null",
	"Skolt" : "null",
	"Southern Skolt" : "null",
	"Southern" : "null",
	"Savosavo" : "svs",
	"Slovincian" : "zlw-slv",
	"Sranan Tongo" : "srn",
	"Tahitian" : "ty",
	"Tai Nüa" : "tdd",
	"Talysh" : "tly",
	"Tetum" : "tet",
	"Tswana" : "tn",
	"Tupinambá" : "tpn",
	"Võro" : "vro",
	"Warlpiri" : "wbp",
	"Yagara" : "yxg",
	"Yoron" : "yox",
	"Zay" : "zwa",
	"ǃXóõ" : "nmn",
	"Central Sierra Miwok" : "csm",
	"Bavarian" : "bar",
	"Franco-Provençal" : "frp",
	"Gamilaraay" : "kld",
	"Kabyle" : "kab",
	"Kannada" : "kn",
	"Low German" : "nds",
	"German Low German Low German" : "null",
	"German Low German" : "nds-de",
	"Mapun" : "sjm",
	"Mari" : "chm",
	"Eastern Mari Mari" : "null",
	"Eastern Mari" : "chm",
	"Western Mari Eastern Mari" : "null",
	"Western Mari" : "mrj",
	"Norman" : "nrf",
	"Old Norse" : "non",
	"Samogitian" : "sgs",
	"Scots" : "sco",
	"Skolt Sami" : "sms",
	"Sundanese" : "su",
	"Westrobothnian" : "gmq-bot",
	"Kamkata-viri" : "bsh",
	"Prasuni" : "prn",
	"Tregami" : "trm",
	"Waigali" : "wbk",
	"Ainu" : "ain",
	"American Sign Language" : "ase",
	"Berawan" : "lod",
	"Tashelhit Berber" : "shi",
	"Central Melanau" : "mel",
	"Dungan Chinese" : "null",
	"Embaloh" : "emb",
	"Mandarin Cantonese" : "null",
	"Volapük" : "vo",
	"Afrikaans" : "af",
	"Arabic" : "ar",
	"Armenian" : "hy",
	"Asturian" : "ast",
	"Belarusian" : "be",
	"Bulgarian" : "bg",
	"Catalan" : "ca",
	"Chinese" : "zh",
	"Cantonese Chinese" : "null",
	"Cantonese" : "yue",
	"Gan Cantonese" : "null",
	"Gan" : "gan",
	"Hakka Gan" : "null",
	"Hakka" : "hak",
	"Mandarin Hakka" : "null",
	"Mandarin" : "cmn",
	"Min Dong Mandarin" : "null",
	"Min Dong" : "cdo",
	"Min Nan Min Dong" : "null",
	"Min Nan" : "nan",
	"Wu Min Nan" : "null",
	"Wu" : "wuu",
	"Czech" : "cs",
	"Danish" : "da",
	"Dutch" : "nl",
	"Esperanto" : "eo",
	"Finnish" : "fi",
	"French" : "fr",
	"Galician" : "gl",
	"Georgian" : "ka",
	"German" : "de",
	"Greek" : "el",
	"Ancient Greek" : "grc",
	"Hungarian" : "hu",
	"Ido" : "io",
	"Indonesian" : "id",
	"Irish" : "ga",
	"Italian" : "it",
	"Japanese" : "ja",
	"Javanese" : "jv",
	"Kashmiri" : "ks",
	"Kazakh" : "kk",
	"Korean" : "ko",
	"Ladino" : "lad",
	"Latgalian" : "ltg",
	"Latin" : "la",
	"Latvian" : "lv",
	"Lithuanian" : "lt",
	"Macedonian" : "mk",
	"Malay" : "ms",
	"Malayalam" : "ml",
	"Maori" : "mi",
	"Norwegian" : "no",
	"Bokmål Norwegian" : "null",
	"Bokmål" : "nb",
	"Nynorsk Bokmål" : "null",
	"Nynorsk" : "nn",
	"Occitan" : "oc",
	"Polish" : "pl",
	"Portuguese" : "pt",
	"Romanian" : "ro",
	"Russian" : "ru",
	"Serbo-Croatian" : "sh",
	"Slovak" : "sk",
	"Slovene" : "sl",
	"Spanish" : "es",
	"Swedish" : "sv",
	"Tagalog" : "tl",
	"Telugu" : "te",
	"Thai" : "th",
	"Turkish" : "tr",
	"Ukrainian" : "uk",
	"Vietnamese" : "vi",
	"Welsh" : "cy",
	"Albanian" : "sq",
	"Amharic" : "am",
	"Egyptian Arabic Arabic" : "null",
	"Egyptian Arabic" : "arz",
	"Gulf Arabic Egyptian Arabic" : "null",
	"Gulf Arabic" : "afb",
	"Iraqi Arabic Gulf Arabic" : "null",
	"Iraqi Arabic" : "acm",
	"Lebanese Arabic Iraqi Arabic" : "null",
	"Lebanese Arabic" : "null",
	"Lebanese Arabic Arabic" : "null",
	"Moroccan Arabic Iraqi Arabic" : "null",
	"Moroccan Arabic" : "ary",
	"Yemeni Arabic (San'ani) Moroccan Arabic" : "null",
	"Yemeni Arabic (San'ani)" : "null",
	"Yemeni Arabic (San'ani) Arabic" : "null",
	"Aromanian" : "rup",
	"Assamese" : "as",
	"Azerbaijani" : "az",
	"Baba Malay" : "mbf",
	"Bashkir" : "ba",
	"Basque" : "eu",
	"Bengali" : "bn",
	"Breton" : "br",
	"Brunei Malay" : "kxd",
	"Burmese" : "my",
	"Central Dusun" : "dtp",
	"Chagatai" : "chg",
	"Chamicuro" : "ccc",
	"Cherokee" : "chr",
	"Dungan Cantonese" : "null",
	"Dungan" : "dng",
	"Mandarin Dungan" : "null",
	"Min Nan Mandarin" : "null",
	"Chuvash" : "cv",
	"Comox" : "coo",
	"Coptic" : "cop",
	"Bohairic Coptic" : "null",
	"Bohairic" : "cop",
	"Sahidic Bohairic" : "null",
	"Sahidic" : "cop",
	"Cornish" : "kw",
	"Crimean Tatar" : "crh",
	"Dalmatian" : "dlm",
	"Dhivehi" : "dv",
	"Dongxiang" : "sce",
	"Drung" : "duu",
	"Egyptian" : "egy",
	"Erzya" : "myv",
	"Estonian" : "et",
	"Faroese" : "fo",
	"Friulian" : "fur",
	"Gagauz" : "gag",
	"Gothic" : "got",
	"Gujarati" : "gu",
	"Hawaiian" : "haw",
	"Hebrew" : "he",
	"Hindi" : "hi",
	"Hunsrik" : "hrx",
	"Iban" : "iba",
	"Icelandic" : "is",
	"Interlingua" : "ia",
	"Iu Mien" : "ium",
	"Kaingang" : "kgp",
	"Kalmyk" : "xal",
	"Kamba" : "kam",
	"Kaqchikel" : "cak",
	"Karachay-Balkar" : "krc",
	"Karakalpak" : "kaa",
	"Kashubian" : "csb",
	"Khakas" : "kjh",
	"Khmer" : "km",
	"Kikuyu" : "ki",
	"Kumyk" : "kum",
	"Kurdish" : "ku",
	"Kurmanji Kurdish" : "null",
	"Kurmanji" : "kmr",
	"Sorani Kurmanji" : "null",
	"Sorani" : "ckb",
	"Kyrgyz" : "ky",
	"Lao" : "lo",
	"Limburgish" : "li",
	"Lingala" : "ln",
	"Luhya" : "luy",
	"Luo" : "luo",
	"Luxembourgish" : "lb",
	"Maltese" : "mt",
	"Mandinka" : "mnk",
	"Manx" : "gv",
	"Marathi" : "mr",
	"Maricopa" : "mrc",
	"Mohawk" : "moh",
	"Mongolian" : "mn",
	"Navajo" : "nv",
	"Nepali" : "ne",
	"Ngazidja Comorian" : "zdj",
	"North Frisian" : "frr",
	"Northern Sami" : "se",
	"Northern Thai" : "nod",
	"Old East Slavic" : "orv",
	"Old English" : "ang",
	"Oriya" : "or",
	"Palauan" : "pau",
	"Pashto" : "ps",
	"Persian" : "fa",
	"Punjabi" : "pa",
	"Quechua" : "qu",
	"Romani" : "rom",
	"Romansch" : "rm",
	"Rusyn" : "rue",
	"Sardinian" : "sc",
	"Scottish Gaelic" : "gd",
	"Shan" : "shn",
	"Shor" : "cjs",
	"Sichuan Yi" : "ii",
	"Sicilian" : "scn",
	"Sinhalese" : "si",
	"Lower Sorbian" : "dsb",
	"Upper Sorbian Lower Sorbian" : "null",
	"Upper Sorbian" : "hsb",
	"Sotho" : "st",
	"Southern Altai" : "alt",
	"Southern Sami" : "sma",
	"Swahili" : "sw",
	"Tajik" : "tg",
	"Tamil" : "ta",
	"Taos" : "twf",
	"Tatar" : "tt",
	"Tibetan" : "bo",
	"Tigrinya" : "ti",
	"Turkmen" : "tk",
	"Tuvan" : "tyv",
	"Udmurt" : "udm",
	"Umbundu" : "umb",
	"Urdu" : "ur",
	"Uyghur" : "ug",
	"Uzbek" : "uz",
	"Venetian" : "vec",
	"Vilamovian" : "wym",
	"Walloon" : "wa",
	"Waray-Waray" : "war",
	"White Hmong" : "mww",
	"Winnebago" : "win",
	"Yagnobi" : "yai",
	"Yakut" : "sah",
	"Yiddish" : "yi",
	"Yup'ik" : "esu",
	"Zazaki" : "zza",
	"Zhuang" : "za",
	"Zulu" : "zu",
	"Mandarin Chinese" : "cmn",
	"Sorani Kurdish" : "null",
	// nulls are explicit negative entries to avtoid having xte round-trip
	// to servers with known bad names. why does it even try these? when encountering
	// a translation sub-item, xte prepends the sub-item name before the item name.
	// this works well for "Egyptian" (otherwise egy) under "Arabic" (otherwise ar)
	// = "Egyptian Arabic" (arz), but it also results in "Lower Sorbian Sorbian".
	"Lower Sorbian Sorbian": null,
	"Upper Sorbian Sorbian": null
};

/*
=== Scripts ===
 */
var scname2scLookup = {
	"Roman"    : "Latn",
	"Latin"    : "Latn",
	"Cyrillic" : "Cyrl",
	"Mandarin" :  null,
	"Cantonese":  null
};

function code2name(code) {
	if (code2nameLookup != 'null') {
		if (code in code2nameLookup) {
		//mw.notify('code2name: ' + code);
		return code2nameLookup[code];
		}
	} else {
		//mw.notify('code2name: ' + code);
		var api = new mw.Api();
		var result = null;
		api.get({
			'action': 'expandtemplates',
			'text': '{{#invoke:languages/templates|getByCode|' + code + '|getCanonicalName}}'
		}, {
			async: false,
			success: function (obj) {
				result = obj.expandtemplates['*'];
				if (/class="error"/.test(result))
					result = null;
				code2nameLookup[code] = result;
			}
		});
		return result;
	}
}

function name2code(name) {
	if (name in name2codeLookup) {
		//console.info('"' +name + '" : "' + name2codeLookup[name] + '",');
		return name2codeLookup[name];
	} else {
		var api = new mw.Api();
		var result = null;
		api.get({
			'action': 'expandtemplates',
			'text': '{{#invoke:languages/templates|getByName|' + name + '}}'
		}, {
			async: false,
			success: function (obj) {
				result = obj.expandtemplates['*'];
				if (result === '')
					result = null;
				if (/^\[\[/.test(result))
					result = null;
				if (/class="error"/.test(result))
					result = null;
				name2codeLookup[name] = result;
				console.info('"' +name + '" : "' + name2codeLookup[name] + '",');
			}
		});
		return result;
	}
}

function code2sc(code) {
	if (code in code2scLookup)
		return code2scLookup[code];
	else {
		//console.warn('script for "' + code + '" is missing from the lookup table; ignoring');
		var result = null;
		return result;
		/*console.warn('script for "' + code + '" is missing from the lookup table; falling back to API query');
		var api = new mw.Api();
		var result = null;
		api.get({
			'action': 'expandtemplates',
			'text': '{{#invoke:languages/templates|getByCode|' + code + '|getScripts|1}}'
		}, {
			async: false,
			success: function (obj) {
				result = obj.expandtemplates['*'];
				if (/class="error"/.test(result))
					result = null;
				code2scLookup[code] = result = obj.expandtemplates['*'];
			}
		});
		return result;*/
	}
}

function scname2sc(scname) {
	if (scname in scname2scLookup)
		return scname2scLookup[scname];
	else {
		//console.warn('script code for "' + scname + '" is missing from the lookup table; ignoring');
		var result = null;
		return result;
		/*console.warn('script code for "' + scname + '" is missing from the lookup table; falling back to API query');
		var api = new mw.Api();
		var result = null;
		api.get({
			'action': 'expandtemplates',
			'text': '{{scriptrev|' + scname + '}}'
		}, {
			async: false,
			success: function (obj) {
				result = obj.expandtemplates['*'];
				if (result === '')
					result = null;
				scname2scLookup[scname] = result;
			}
		});
		return result;*/
	}
}

function el(tag, child, attr, events) {
	var node = document.createElement(tag);

	if (child) for (var i = 0; i < child.length; ++i) {
		var ch = child[i];
		if (typeof ch === 'string')
			ch = document.createTextNode(ch);
		else if ((ch === null) || (ch === void(null)))
			continue;
		node.appendChild(ch);
	}

	if (attr) for (var key in attr) {
		node.setAttribute(key, attr[key]);
	}

	if (events) for (var key in events) {
		node.addEventListener(key, events[key], false);
	}

	return node;
}

function ih(name, val) {
	var it = el('input', null, { 'type': 'hidden', 'name': name });
	if (val)
		it.value = val;
	return it;
}

function count(subs, s) {
	var i = 0, j = -subs.length;
	while ((j = s.indexOf(subs, j + subs.length)) !== -1) ++i;
	return i;
}

// 500 lines somewhat messy workhorse
function fix(wikicode, susp, errors) {
	var i, dirty = false;
	var name = null, code = null, before, subbef;
	function addError(message) {
		console.error(message);
		errors[errors.length] = {
			'line': i + 1,
			'code': code,
			'mesg': message
		};
	}
	var m, lines = wikicode.split('\n');
	var mode = 0;
	if (!(susp instanceof Array))
		susp = [];
	if (!(errors instanceof Array))
		errors = [];
	for (i = 0; i < lines.length; ++i) {
		var ttbcline = false, issub = false, entrysc = '';
		if (m = /^\s*\{\{(checktrans|ttbc)-top(\||}})/.exec(lines[i])) {
			name = code = null;
			mode = 2;
			continue;
		} else if (m = /^\s*\{\{(checktrans|trans|ttbc)-bottom(\||}})\s*/.exec(lines[i])) {
			name = code = null;
			if (m[0] !== lines[i])
				addError('Stray markup after translation table end');
			mode = 0;
			continue;
		} else if (m = /^\s*\{\{trans-top(?:-also)?(\||}})\s*/.exec(lines[i])) {
			name = code = null;
			mode = 1;
			continue;
		}
		if (mode === 0)
			continue;
		// what does this do?
		if (m = /^\*(?![\*:])\s*(?:\[\[)?([^:\{]+?)(?:\]\])?\s*:\s*/.exec(lines[i])) {
			before = subbef = m[1];
			name = m[1];
			code = name2code(m[1]);
			if (code === false) { // language group, "Sorbian", which serves as a group for "Lower Sorbian" and "Upper Sorbian"
				if (lines[i] !== m[0]) {
					addError('Language group "' + m[1] + '" has direct items; these should be placed under a more specific header.');
					susp.push('"' + m[1] + '"');
					lines[i] = '* {{ttbc|' + m[1] + '}} {{attention|und|Wiktionary considers this a language group; please specify the language more precisely}}: ' + lines[i].substr(m[0].length);
					dirty = true;
					continue;
				} else continue;
			}
			if (!code) {
				if ((name === 'Serbian') || (name === 'Croatian') || (name === 'Bosnian')) {
					ttbcline = true;
					addError('Warning: converting "' + name + '" into "Serbo-Croatian" [sh].');
					subbef = 'Serbo-Croatian {{attention|sh|was "' + name + '"; verify correctness, check for duplicates, mark script, sort and merge if appropriate}}:';
					name = 'Serbo-Croatian';
					code = 'sh';
					susp.push("sh");
				} /*else if (name === 'Ancient') {
					if (xtepro) {
						// what is name used for? 
						addError('Warning: converting "' + name + '" into "Ancient Greek".');
						name = 'Ancient Greek';
						subbef = 'Ancient Greek:';
					} else {
					ttbcline = true;
					addError('Warning: converting "' + name + '" into "Ancient Greek".');
					subbef = 'Ancient Greek {{attention|grc|was "' + name + '"; verify correctness, check for duplicates, mark script, sort and merge if appropriate}}:';
					name = 'Ancient Greek';
					//code = 'sh';
					susp.push("grc");
					}
				}*/ else {
					code = name = null;
					addError('"' + m[1] + '" does not refer to a known language.');
					susp.push('"' + m[1] + '"');
					//lines[i] = '* {{ttbc|' + m[1] + '}} {{attention|und|name not recognised by xte: update [[Module:languages]] appropriately or report to [[User talk:Kephir/gadgets/xte]]}}: ' + lines[i].substr(m[0].length);
					dirty = true;
					continue;
				}
			}
			// add t-check if ttbc or attention on the line
			} else if (m = /^\*\s*(\{\{ttbc\s*\|([^|]*?)}})(\s*\{\{\s*attention\s*\|.*?}})?\s*:\s*/.exec(lines[i])) {
			before = subbef = m[1];
			code = m[2];
			name = code2name(m[2]);
			ttbcline = true;
			if (!name) {
				if (code = name2code(m[2])) {
					name = m[2];
					before = subbef = '{{ttbc|' + code + '}}';
				} else {
					code = name = null;
					addError('"' + m[2] + '" under ttbc does not refer to a known language.');
					susp.push('"' + m[2] + '"');
					continue;
				}
			}
		} else if (m = /^\*[*:]\s*(?:\[\[)?([^:\{<]+?)(?:\]\])?\s*:\s*/.exec(lines[i])) {
			subbef = m[1];
			var s;
			if (s = scname2sc(subbef)) {
				if (code === null)
					continue;
				entrysc = s;
			} 
			// this should not be neccesary now that module:languages contains information about ancestors
			/*else if (s = name2code(subbef + ' ' + name)) { // prepend sub-item name before group name: "Ancient" + "Greek", "Lower" + "Sorbian", etc.
				code = s;
				name = subbef;
			}*/ else if (s = name2code(subbef)) {
				code = s;
				name = subbef;
			} else if (s = name2code(subbef + ' ' + before)) {
				code = s;
				// Correct subbef like "Ancient" -> "Ancient Greek"
				name = subbef + ' ' + before;
				addError('Warning: converting' + subbef + ' into ' + name + '.');
				lines[i] = '*: ' + name + ': ' + lines[i].substr(m[0].length);
				console.info('Line: ' + lines[i]);
				//dirty = true;
				continue;
			} else {
				if (subbef === 'Teochew') {
					ttbcline = true;
					code = 'nan'; 
					addError('Warning: treating "Teochew" header as if it were Min Nan [nan]. If you want to merge it with Min Nan, use {{qualifier}}.');
					susp.push('nan (Teochew)');
				} else {
					addError('Failed to recognize "' + subbef + '" sub-item for "' + before + '"');
					/*lines[i] = '*: {{ttbc|' + subbef + '}}<!-- name not recognised by xte: update [[Template:scriptrev]] or [[Module:languages]] appropriately or report to [[User talk:Kephir/gadgets/xte]] -->: ' + lines[i].substr(m[0].length);
					dirty = true;*/
					continue;
				}
			}
			issub = true;
		} else if (m = /^\s*\{\{(checktrans|trans|ttbc)-mid(\||}})\s*/.exec(lines[i])) {
			name = code = null;
			if (m[0] !== lines[i])
				addError('Stray markup after translation table midbreak');
			continue;
		} else if (m = /^\*\s*\{\{trreq\|(.*?)}}\s*?(?=\s|$)/.exec(lines[i])) {
			name = code = null;
			if (!(name = code2name(m[1]))) {
				if (!(code = name2code(m[1]))) {
					addError('"' + m[1] + '" under {{trreq}} is not recognised');					
				} else {
					if (m[0] !== lines[i])
						addError('Stray markup after {{trreq}}');
					lines[i] = '* {{trreq|' + code + '}}' + lines[i].substr(m[0].length);
					continue;
				}
			}
			if (m[0] !== lines[i])
				addError('Stray markup after {{trreq}}');
			continue;
		} else {
			addError('Line not recognised');
			continue;
		}
		var parsed = lines[i].substr(m[0].length);
		var token = '';
		var t9ns = [];
		var or = 0, oc = 0, os = 0, ot = 0;
		while (parsed !== '') {
			if (!(m = /\s*[,;\.\/]\s*/.exec(parsed))) {
				t9ns[t9ns.length] = token + parsed;
				break;
			}
			var pre = parsed.substr(0, parsed.indexOf(m[0]));
			or += count('(', pre) - count(')', pre);
			oc += count('{', pre) - count('}', pre);
			os += count('[', pre) - count(']', pre);
			ot += count('<', pre) - count('>', pre);
			if ((or === 0) && (oc === 0) && (os === 0) && (ot === 0)) {
				t9ns[t9ns.length] = token + pre;
				token = '';
			} else {
				token += pre + m[0];
			}
			parsed = parsed.substr(parsed.indexOf(m[0]) + m[0].length);
		}
		var allok = true;
		// This loop adds t-check to lines being processed if they do not pass 
		// the validation.
		for (var j = 0; j < t9ns.length; ++j) {
			// skip if one of these
			if (/\{\{\s*(t[-+]check|t-needed)\|/.test(t9ns[j]))
				continue;
				
			// mode 2 means that it is under a checktrans-top header
			var suspc = (mode === 2) || ttbcline;

			// validate (rudimentary!)
			// add qual and q to validation
			if (!t9ns[j].replace(/<!--(.*?)-->/g, '').replace(/\s*\{\{t\+?\|(\|tr=\{\{IPAchar\|[^}|]+}}|[^}])+}}\s*/, '').replace(/\s*\{\{(qualifier|qual|q|i)\|[^}]+}}\s*/g, '')) {
				if (suspc) {
					t9ns[j] = t9ns[j].replace(/\{\{t(\+?)\|/, function (m, t) {
						// this adds check to t-template
						console.info('adding check to {{t}}');
						return '{{t' + (t ? '+' : '-') + 'check|';
					});
				}
				continue;
			}
			if (/\{\{t/.test(t9ns[j])) {
				addError('Translation item "' + t9ns[j] + '" failed to validate despite containing {{t}}. Will reconstruct.');
				// This enables adding check to {{t}} when deconstructing
				if (xtepro)
					suspc = false;
				else
					suspc = true;
			}

			var inp = t9ns[j];
			// validation failed. deconstruct {{t}}s
			inp = inp.replace(/\{\{t\+?(\|[^|}]+\|[^|}]+)((\|tr=\{\{IPAchar\|[^}|]+}}|\|[^}|]+)*)}}(?!\))/g, function (m, ct, rest) {
				var newrest = '', g = '', tr = '';
				while (rest) {
					var r = rest.replace(/^(\|[^|=]+)+(?=\||$)/, function (m) {
						g += m;
						return '';	
					}).replace(/^\|alt=([^|}]+)/, function (m, a) {
						if (newrest)
							return m;
						newrest = '|' + a;
						return '';
					}).replace(/^\|sc=([^|}]+)/, '');
					if (r === rest)
						return m; // failed to deconstruct {{t}}
					rest = r;
				}
				return '{{l' + ct + newrest + '}}' + (tr ? ' (' + tr + ')' : '') + (g ? ' {{g' + g + '}}' : '');
			});
			
			if (/\{\{t/.test(inp)) {
				addError('Deconstruction failed for "' + t9ns[j] + '". Please fix it manually.');
				allok = false;
				continue;
			}

			console.info('transforming ', t9ns[j]);
			var qual = '', word = '', gend = '', tr = '', sc = entrysc, alt = '', sense= '';
			inp = inp.replace(/\{\{l[\|/]([a-z\-]+)\|([^=|}]+(?:\|[^=|}]+)?)}}/g, function (m, lc, cont) {
				if (lc !== code) {
					// I have never actually encountered this, but just to be safe...
					addError('Warning: {{l}} template used with non-matching language code ' + lc);
					suspc = true;
					return m;
				}
				return '[[' + cont + ']]';
			});
			// process the deconstructed line
			// todo: support parens also; refactor
			// if qual OR gloss OR sense times 4
			if (m = /\s*\{\{(?:qualifier|qual|q|sense|gloss|i)\|(.+?)}}\s*/.exec(inp)) {
				console.info('qualifier, gloss, sense [1]: ', m[1]);
				qual = '; ' + m[1];
				inp = inp.replace(m[0], '');
				if (m = /\s*\{\{(?:qualifier|qual|q|sense|gloss|i)\|(.+?)}}\s*/.exec(inp)) {
					console.info('qualifier, gloss, sense [2]: ', m[1]);
					qual += '|' + m[1];
					inp = inp.replace(m[0], '');
					if (m = /\s*\{\{(?:qualifier|qual|q|sense|gloss|i)\|(.+?)}}\s*/.exec(inp)) {
						console.info('qualifier, gloss, sense [3]: ', m[1]);
						qual += '|' + m[1];
						inp = inp.replace(m[0], '');
						if (m = /\s*\{\{(?:qualifier|qual|q|sense|gloss|i)\|(.+?)}}\s*/.exec(inp)) {
							console.info('qualifier, gloss, sense [4]: ', m[1]);
							qual += '|' + m[1];
							inp = inp.replace(m[0], '');
							console.info('inp after 4 matches: ', inp);
						}
					}
				}
			} else {
				// handle unknown parens
				inp = inp.replace(/\s*(?:\(''|''\()([A-Za-z,;:'"\- ]+?)(?:''\)|\)'')/g, function(m, inside) {
					console.info('probably qualifier: ', arguments);
					qual += '; ' + inside;
					return '';
				});
			}
			var getWord = function (m) {
				var n;
				console.info('words: ', m);
				// remove garbage 
				m = m.replace(/&nbsp/, '');
				console.info('words after garbage removal: ', m);
				if (!/^\s*(\[\[[^\]\|]*?]]|[^ \[\]\{\}'<>]+)$/.test(m)) {
					var n = /^([^\[\{]*)\[\[([^#\|\]]*?)(?:#([^\|\]]*?))?(?:\|([^#\]]*?))?]]([^\[\{]*)$/.exec(m);
					if (n) {
						if (!(((n[2] === n[4]) || !n[2]) && (n[3] === name))) {
							if (xtepro) {
								// do not warn if one of "-?!"
								if (!/[!\-?]/.test(m)) {
									// do not warn if one of prolangs
									if (prolangs.indexOf( code ) == -1) {
										addError('Warning: single piped or non-clean link in an item "' + m + '". Assuming an inflected form or vocalised spelling. See [[User:So9q/Improved-xte.js/documentation#Translation_fixing]].');
										suspc = 'was "' + m + '" - assumed inflected form or vocalised spelling; please verify if linking is acceptable. see [[User:So9q/Improved-xte.js/documentation#Translation_fixing]]';
										susp.push(code);
									}
								}
							} else {
								addError('Warning: single piped or non-clean link in an item "' + m + '". Assuming an inflected form or vocalised spelling. See [[User:So9q/Improved-xte.js/documentation#Translation_fixing]].');
								suspc = 'was "' + m + '" - assumed inflected form or vocalised spelling; please verify if linking is acceptable. see [[User:So9q/Improved-xte.js/documentation#Translation_fixing]]';
								susp.push(code);
							}
							alt = n[1] + (n[4] || n[2] || wgPageName) + n[5];
							word = n[2] || wgPageName;
							return '';
						}
					} else {
						// only add warnings if not one of the prolangs
						if (prolangs.indexOf( code ) == -1) {
							addError('Warning: multiple words in an item: "' + m + '". Assuming it is a [[WT:SOP|sum-of-parts]]. If it is not, remove the links to individual words.');
							suspc = 'was "' + m + '" - assumed sum-of-parts; if an idiom, remove the wikilinks to the individual words. see [[User:So9q/Improved-xte.js/documentation#Translation_fixing]]';
							susp.push(code);
						}
						word = m;
						if (!/\[\[.*?]]/.test(word)) {
							word = word.replace(/([^{}\[\]\(\) \t,]+)/g, function (wm) {
								return '[[' + wm + ']]';
							});
						}
						return '';
					}
				}
				word = m.replace(/\[\[(?:[^|\]]+?\|)?(.+?)]]/g, '$1').replace(/\s+/g, ' ');
				return '';
			}; // end getWord
			if (m = /^\{\{((?:[a-z][a-z]-)?[A-Z][a-z][a-z][a-z])\s*\|\s*(.+?)\s*}}/.exec(inp)) {
				sc = m[1];
				getWord(m[2]);
				inp = inp.replace(m[0], '');
			} else if (/''+(.+?)''+/.test(inp)) {
				// whole line is a qualifier like "* Finnish: ''adessive of the means''"
				inp = inp.replace(/''+(.+?)''+/, function (m, t) {
					console.info('whole line is a qualifier in single quotes: ', arguments, '; script: ', sc);
					if (qual === '')
						qual = '; ' + t;
					else
						qual += '|' + t;
					word = ''; // no words on this line
					return '';
					//return m; // ???
				});
			} else
				inp = inp.replace(/^((\s*)(?!''|<!--)(?:\[\[(?:[^|\]]+?\|)?(.+?)]]|([^\{\(\[<' ]+)))+/, getWord);
			if (/^\s*$/.test(word)) {
				console.info('word: ' + word);
				addError('Failed to process item: "' + t9ns[j] + '"');
				allok = false;
				continue;
			}
			if (!sc)
				sc = code2sc(code);
			if (sc !== 'Latn') {
				inp = inp.replace(/\{\{(IPAchar)\|\(?(.+?)\)?}}/, function (m, w, t) {
					console.info('most probably transcription: ', arguments, '; script: ', sc);
					tr = '{{' + w + '|' + t + '}}';
					return '';
				});
			}	
			inp = inp.replace(/\((.+?)\)/, function (m, t) {
				console.info('text in parens: ', arguments, '; script: ', sc);
				if (/[Ѐ-ӿ]/.test(t)) // SOP Russian translation
					return m;
				if (!tr && (sc !== 'Latn') && (sc !== null)) { // probably a transcription
					console.info('probably a transcription:', t, 'script is: ', sc);
					tr = t;
					return '';
				} else if (/^[^\[\]]+$/.test(t)) { // probably qualifier
					console.info('probably a qualifier:', t);
					// discard "" like in {{t|mic|nseduwaqan}} ("my ear")
					t = t.replace(/"/g, '');
					t = t.replace(/'/g, '');
					if (qual === '')
						qual = '; ' + t;
					else
						qual += '|' + t;
					return '';
				}  else if (/\[\[(.*case|infinitive|)+?\]\]/.test(t)) { // probably qualifier
					// detect if qualifier, e.g. ([[allative case]])
					t = t.replace(/\[\[/, '')
					t = t.replace(/\]\]/, '')
					console.info('probably a qualifier in [[]]:', t);
					if (qual === '')
						qual = '; ' + t;
					else
						qual += '|' + t;
					return '';
				}
				return m; // ???
			});
			
			inp = inp.replace(/"(.+?)"/, function (m, t) {
				console.info('text in duoble quotes: ', arguments, '; script: ', sc);
				if (/[Ѐ-ӿ]/.test(t)) // SOP Russian translation
					return m;
				if (!tr && (sc !== 'Latn') && (sc !== null)) { // probably a transcription
					console.info('probably a transcription:', t, 'script is: ', sc);
					tr = t;
					return '';
				} else if (/^[^\[\]]+$/.test(t)) { // probably qualifier
					console.info('probably a qualifier:', t);
					if (qual === '')
						qual = '; ' + t;
					else
						qual += '|' + t;
					return '';
				}
				return m; // ???
			});
			inp = inp.replace(/''+(.+?)''+/, function (m, t) {
				console.info('text in single quotes: ', arguments, '; script: ', sc);
				if (/[Ѐ-ӿ]/.test(t)) // SOP Russian translation
					return m;
				if (!tr && (sc !== 'Latn') && (sc !== null)) { // probably a transcription
					console.info('probably a transcription:', t, 'script is: ', sc);
					tr = t;
					return '';
				} else if (/^[^\[\]]+$/.test(t)) { // probably qualifier
					console.info('probably a qualifier:', t);
					if (qual === '')
						qual = '; ' + t;
					else
						qual += '|' + t;
					return '';
				}
				
				return m; // ???
			});
			
			inp = inp.replace(/\{\{g(\|((?:[mfncsp]|pf|impf)(?:-[amfncsp]|impf|pf)*)*)}}/g, function (m, g) {
				gend = g;
				console.info('gender: ', arguments);
				return '';
			});
			
			if (qual.indexOf('(') !== -1) {
				addError('Opening parenthesis inside qualifier for item "' + t9ns[j] + '". Not touching.');
				allok = false;
				continue;
			}
			if (qual.indexOf('{') !== -1) {
				addError('Opening bracket parens inside qualifier for item "' + t9ns[j] + '". Not touching.');
				allok = false;
				continue;
			}
			if (tr.indexOf('(') !== -1) {
				addError('Opening parenthesis inside transliteration for item "' + t9ns[j] + '". Not touching.');
				allok = false;
				continue;
			}

			var debris = '';
			if (!/^\s*$/.test(inp)) {
				debris = ' ' + inp.trim();
				addError('Warning: some markup with unclear purpose detected: "' + inp + '". Putting after {{t}} invocation.');
				suspc = true;
				susp.push(code);
			}
			
			t9ns[j] = '';
			t9ns[j] += '{{t' + ((suspc || (mode === 2)) ? '-check' : '')  + '|' + code + '|' + word;
			t9ns[j] += gend;
			if (tr !== '')
				t9ns[j] += '|tr=' + tr;
			if (alt !== '')
				t9ns[j] += '|alt=' + alt;
			if (sc && (sc !== 'Latn') && (sc !== 'None'))
				t9ns[j] += '|sc=' + sc;
			t9ns[j] += '}}' 
			// qualifier comes after {{t}}
			if (qual !== '')
			t9ns[j] += ' {{q|' + qual.substr(2) + '}} '
			t9ns[j] += debris;
			if (typeof suspc === 'string')
				t9ns[j] += ' {{attention|' + code + '|2=' + suspc + '}}';
			console.info('transformed into ', t9ns[j]);
		}
		if (allok && (subbef === ('{{ttbc|' + code + '}}')))
			subbef = name;
		var newl = (issub ? '*: ' + subbef : ('* ' + subbef)) + ':' + (t9ns.length ? ' ' + t9ns.join(', ') : '');
		if (lines[i] !== newl) {
			lines[i] = newl;
			dirty = true;
		}
	} // end for loop
	if (!dirty)
		return null;
	return lines.join('\n');
}

function fmtts(ts) {
	function pad(wut) {
		return wut < 10 ? '0' + wut : wut.toString();
	}
	var d = new Date(ts);
	return d.getUTCFullYear().toString() + pad(d.getUTCMonth() + 1) + pad(d.getUTCDate()) + pad(d.getUTCHours()) + pad(d.getUTCMinutes()) + pad(d.getUTCSeconds());
}

function fixer(secnum) {
	// Begin querying languages found on the page
	var wgPageName = mw.config.get('wgPageName');
	jQuery.ajax({
		'url': mw.config.get('wgServer') + mw.config.get('wgScript') + '?action=raw&templates=expand&ctype=text/javascript&maxage=172800&title=User:Kephir/gadgets/xte/langdata.js',
		'cache': true,
		'dataType': 'json',
		'success': function (data) {
			try {
				code2nameLookup = {};
				code2scLookup = {};
				for (var key in data) {
					code2nameLookup[key] = data[key].names[0];
					code2scLookup[key] = data[key].scripts[0];
				}
				for (var key in code2nameLookup) {
					name2codeLookup[code2nameLookup[key]] = key;
				}
			} catch (e) {
				mw.notify('xte: error while processing language data. See console for details.');
				throw e;
			}
		},
		'error': function (xhr, message) {
			if (message !== 'abort')
				mw.notify('xte: failed to grab language data. See console for details.');
			console.error(arguments);
		}
	});
	
	return function () {
		if (!code2nameLookup) {
			mw.notify('xte: Please wait a moment until language data is downloaded.');
		}
		var formcont, formstart, formtoken, formedit, formwatch, formsumm;
		var form = el('form', [
			formcont = ih('wpTextbox1'),
			formstart = ih('wpStarttime'),
			formedit = ih('wpEdittime'),
			formtoken = ih('wpEdittoken'),
			formwatch = ih('wpWatchthis', '1'),
			ih('oldid', '0'),
			secnum !== null ? ih('wpSection', secnum.toString()) : '',
			formsumm = ih('wpSummary', 'Fixing translation table using ([[User:So9q/Improved-xte.js|assisted]])'),
			ih('wpDiff', 'wpDiff')
		], {
			'action': mw.config.get('wgScript') + '?xtewarn&action=submit' + (secnum !== null ? '&section=' + secnum : '') + '&title=' + encodeURIComponent(wgPageName),
			'method': 'post',
			'enctype': 'multipart/form-data'
		});

		var api = new mw.Api();
		api.get({
			action: 'query',
			prop: 'info|revisions',
			rvprop: 'timestamp|content',
			rvsection: secnum !== null ? secnum : void(0),
			rvlimit: 1,
			rvdir: 'older',
			intoken: 'edit',
			inprop: 'watched',
			titles: wgPageName
		}, {
			'success': function (result) {
				var pid = Object.keys(result.query.pages)[0];
				var pg = result.query.pages[pid];
				var susp = [], errs = [];
				var fixd = fix(pg.revisions[0]['*'], susp, errs);
				if (errs.length) {
					try {
						var cnam = 'kephir-xte-errors-' + wgPageName;
						var cval = JSON.stringify(errs);
						window.sessionStorage.setItem(cnam, cval);
						if (window.sessionStorage.getItem(cnam) !== cval) {
							throw new Error("sessionStorage failed to work");
						}
					} catch (e) {
						alert('Errors while processing:\n\n' + errs.map(function (item) {
							return 'Line ' + item.line + (item.code === null ? '' : ' [' + item.code + ']') + ': ' + item.mesg;
						}).join('\n'));
					}
				}
				if (fixd === null) {
					if (confirm('Nothing fixed automatically (' + errs.length + ' messages). Edit manually?')) {
						location.href = mw.config.get('wgScript') + '?xtewarn&action=edit&title=' + encodeURIComponent(wgPageName) + (secnum !== null ? '&section=' + secnum.toString() : '');
					}
					//mw.notify('Nothing fixed automatically (' + errs.length + ' messages).');
					return;
				}
				if (susp.length) {
					susp.sort();
					var last;
					formsumm.value += ' — please review: ' + susp.filter(function(it) {
						if (it !== last) {
							last = it;
							return true;
						}
						return false;
					}).join(', ');
				}
				formedit.value = fmtts(pg.revisions[0].timestamp);
				formstart.value = fmtts(pg.starttimestamp);
				formtoken.value = pg.edittoken;
				formcont.value = fixd;
				if (!(('watched' in pg) || (mw.user.options.get('watchdefault') == '1'))) {
					formwatch.parentNode.removeChild(formwatch);
				}
				document.body.appendChild(form);
				//form.submit();
				// so9q do not submit automatically
				if (confirm('Fixed automatically (' + errs.length + ' messages). Submit?')) {
						form.submit();
				}
			},
			'error': function () {
				console.error(arguments);
				alert('Error while requesting page source. See console for details.');
			}
		});
	};
}



if (((mw.config.get('wgAction') === 'submit') || (mw.config.get('wgAction') === 'edit'))) {
	var genErrorList = function (errlist) {
		function lineSelector(ln) {
			return function () {
				var start = 0;
				for (var i = 1; i < ln; ++i)
					start = textbox.value.indexOf('\n', start) + 1;
				var end = textbox.value.indexOf('\n', start);

				textbox.focus();
				if (textbox.createTextRange) { // MSIE	
					var range = textbox.createTextRange();
					range.collapse(true);
					range.moveStart('character', start);
					range.moveEnd('character', end);
					range.select();
				} else if (textbox.setSelectionRange) { // browsers
					textbox.setSelectionRange(start, end);
					var phantom = document.createElement('div');
					var style = window.getComputedStyle(textbox, '');
					phantom.style.padding = '0';
					phantom.style.lineHeight = style.lineHeight;
					phantom.style.fontFamily = style.fontFamily;
					phantom.style.fontSize = style.fontSize;
					phantom.style.fontStyle = style.fontStyle;
					phantom.style.fontVariant = style.fontVariant;
					phantom.style.letterSpacing = style.letterSpacing;
					phantom.style.border = style.border;
					phantom.style.outline = style.outline;
					try { phantom.style.whiteSpace = "-moz-pre-wrap" } catch(e) {}
					try { phantom.style.whiteSpace = "-o-pre-wrap" } catch(e) {}
					try { phantom.style.whiteSpace = "-pre-wrap" } catch(e) {}
					try { phantom.style.whiteSpace = "pre-wrap" } catch(e) {}
					phantom.textContent = textbox.value.substr(0, start);
					document.body.appendChild(phantom); // XXX: do I need this?
					textbox.scrollTop = phantom.scrollHeight - (textbox.clientHeight / 2);
					document.body.removeChild(phantom);
				}
				textbox.scrollIntoViewIfNeeded();
			};
		}
		var errul = el('ul');
		var listdom = el('div', [
			el('p', ['Errors and warnings while processing:']),
			errul
		]);
		if (!errlist.length)
			return null;
		for (var i = 0; i < errlist.length; ++i) {
			var linelink = el('a', ['Line ' + errlist[i].line], { 'href': 'javascript:void(0);' });
			linelink.addEventListener('click', lineSelector(errlist[i].line));
			var m, errmsg = [], mesg = errlist[i].mesg;
			while (m = /(.*?)(\[\[(.*?)(\|.*?)?\]\])/.exec(mesg)) {
				errmsg.push(m[1]);
				errmsg.push(el('a', [m[2]], { 'href': mw.util.getUrl(m[3]) }));
				mesg = mesg.substr(m[0].length);
			}
			errmsg.push(mesg);
			errul.appendChild(el('li', [
				linelink, (errlist[i].code ? ' [' + errlist[i].code + ']' : ''), ': '
			].concat(errmsg)));
		}
		return listdom;
	};
	var diff = document.getElementById('wikiDiff');
	var xtebox = document.createElement('div');
	var fishy = false;
	var textbox = document.getElementById('wpTextbox1');
	if (diff) {
		diff.parentNode.insertBefore(xtebox, diff);
		if (/[?&]xtewarn/.test(location.search)) {
			xtebox.appendChild(el('p', [
				el('strong', ["Warning"]), ": the translation fixing mechanism is less than perfect and may generate erroneous markup in some unusual cases. Thoroughly review and correct the diff before saving the page. When unsure what to do, keep the old version of a line and leave a message in the edit summary, or not save at all. You take all responsibility for any changes you save."
			], { 'class': 'warning' }), diff);
			try {
				var errlist = JSON.parse(window.sessionStorage.getItem('kephir-xte-errors-' + wgPageName));
				var eldom = genErrorList(errlist);
				if (eldom)
					xtebox.appendChild(eldom);
				window.sessionStorage.removeItem('kephir-xte-errors-' + wgPageName);
			} catch (e) {
				console.error('failed to parse error list ', e.message, e);
			}
		}
		var restorer = function (linenum, linetext, linechk) {
			return function () {
				if (fishy)
					return;
				var oldline = textbox.value.split(/\n/g)[linenum - 1];
			};
		};

		var rows = diff.getElementsByTagName('tr');
		var curlin = null;
		for (var i = 0; i < rows.length; ++i) {
			var dln = rows[i].getElementsByClassName('diff-lineno');
			if (dln.length === 2) {
				var m = /(\d+):$/.exec(dln[1].textContent);
				curlin = parseInt(m[1], 10);
				continue;
			}
			if (curlin === null)
				continue;
			var dm = rows[i].getElementsByClassName('diff-marker');
			if (!dm.length) {
				fishy = true;
				alert('Something is fishy with the diff.');
				continue;
			}
			if (dm[0].nextElementSibling.className == 'diff-deletedline') {
				var text = dm[0].nextElementSibling.textContent;
				var link = el('a', [], { 'href': 'javascript:void(0);' }, { 'click': restorer(curlin, text) });
				while (dm[0].hasChildNodes()) {
					link.appendChild(dm[0].firstChild);
				}
				dm[0].appendChild(link);
			}
			++curlin;
		}
	}
}

});

jQuery(document).ready(function xteload() {
	if (// Run only in mainspace
		mw.config.get('wgNamespaceNumber') === 0 && 
		// if view
		mw.config.get('wgAction') === 'view' &&
		// and if a translation section exist
		$('.translations').length !== 0) {
			console.info('xte loaded');
			// add section links
			$('#Translations.mw-editsection').each(function(){
				var $link = $(this);
				$link.parents.append(
					$('span').text("fix2").attr({
						'href': 'javascript:void(null);', 
						'click': 'fixer(secnum)',
						'class': 'mw-editsection'
					}))
			})
			mw.util.addPortletLink( mw.config.get('skin') === 'vector' ? 'p-views' : 'p-cactions', 'fixer(null)()', 'Fix translations', 'Make translations use {{t}}');
	}
});
// </nowiki>