Module:typing-aids

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

This module is invoked by {{chars}} (and {{chars/example}}). It replaces ASCII keyboard shortcuts with characters used in various languages.

To edit the list of shortcuts, see Module:typing-aids/data.

Testcases[edit]

All tests passed. (refresh)

TextExpectedActual
test_Akkadian:
Passedša𒊭𒊭
TextExpectedActual
test_Arabic:
Passedal-Huruuf al-qamariyyat'الْحُرُوف الْقَمَرِيَّةالْحُرُوف الْقَمَرِيَّة
Passedal-Huruuf al-xamsiyyat'الْحُرُوف الشَّمْسِيَّةالْحُرُوف الشَّمْسِيَّة
Passedealifu WlwaSliأَلِفُ ٱلْوَصْلِأَلِفُ ٱلْوَصْلِ
Passedmaaeمَاءمَاء
Passedmueminمُؤْمِنمُؤْمِن
PassedeiDaafat'إِضَافَةإِضَافَة
Passedeaabآبآب
Passedqureaanقُرْآنقُرْآن
PassedqiTTat'قِطَّةقِطَّة
PassedfaEEaalفَعَّالفَعَّال
Passedxayeuشَيْءُشَيْءُ
PassedxayeaNشَيْءًشَيْءً
PasseddaaeimaNدَائِمًادَائِمًا
Passedmabduueat'مَبْدُوءَةمَبْدُوءَة
Passedmabduu'at'مَبْدُوءَةمَبْدُوءَة
PassedbadaaeiyyuNبَدَائِيٌّبَدَائِيٌّ
Passedbadaaeat'بَدَاءَةبَدَاءَة
Passedmaktuubمَكْتُوبمَكْتُوب
PassedtaHriirتَحْرِيرتَحْرِير
PassedEuZmaaaعُظْمَىعُظْمَى
Passedean0أَنْأَنْ
Passedlaw0لَوْلَوْ
Passedxay'aNشَيْءًشَيْءً
Passedta7riirتَحْرِيرتَحْرِير
Passed3axarat'عَشَرَةعَشَرَة
TextExpectedActual
test_Armenian:
Passedazgaynac`umազգայնացումազգայնացում
Passedazgaynacʿumազգայնացումազգայնացում
Passedterewտերևտերև
Passedburz^uaziaբուրժուազիաբուրժուազիա
Passedburžuaziaբուրժուազիաբուրժուազիա
Passedkol_mnakiկողմնակիկողմնակի
Passedkołmnakiկողմնակիկողմնակի
TextExpectedActual
test_Armenian_tr:
Passedazgaynac`umazgaynacʿumazgaynacʿum
Passedterewterewterew
Passedburz^uaziaburžuaziaburžuazia
Passedkol_mnakikołmnakikołmnaki
TextExpectedActual
test_Avestan:
Passedap𐬀𐬞𐬀𐬞
Passedxs.^uuas^𐬑𐬴𐬎𐬎𐬀𐬱𐬑𐬴𐬎𐬎𐬀𐬱
Passedxṣ̌uuaš𐬑𐬴𐬎𐬎𐬀𐬱𐬑𐬴𐬎𐬎𐬀𐬱
Passedv@hrka_na𐬬𐬆𐬵𐬭𐬐𐬁𐬥𐬀𐬬𐬆𐬵𐬭𐬐𐬁𐬥𐬀
Passedvəhrkāna𐬬𐬆𐬵𐬭𐬐𐬁𐬥𐬀𐬬𐬆𐬵𐬭𐬐𐬁𐬥𐬀
Passednae_za𐬥𐬀𐬉𐬰𐬀𐬥𐬀𐬉𐬰𐬀
Passednaēza𐬥𐬀𐬉𐬰𐬀𐬥𐬀𐬉𐬰𐬀
PassedzaaO𐬰𐬃𐬰𐬃
Passedzā̊𐬰𐬃𐬰𐬃
PassedhizwaO𐬵𐬌𐬰𐬎𐬎𐬂𐬵𐬌𐬰𐬎𐬎𐬂
Passedhizuuå𐬵𐬌𐬰𐬎𐬎𐬂𐬵𐬌𐬰𐬎𐬎𐬂
TextExpectedActual
test_Avestan_tr:
Passedapapap
Passedxs.^uuas^xṣ̌uuašxṣ̌uuaš
Passedv@hrka_navəhrkānavəhrkāna
Passednae_zanaēzanaēza
PassedzaaOzā̊zā̊
PassedhizwaOhizuuåhizuuå
TextExpectedActual
test_Germanic:
Passed*t'a_ko^`*þākǫ̂*þākǫ̂
Passed*T'eudo_balt'az*Þeudōbalþaz*Þeudōbalþaz
Passed*bo_kijo_`*bōkijǭ*bōkijǭ
TextExpectedActual
test_Gothic:
Passedƕaiwa𐍈𐌰𐌹𐍅𐌰𐍈𐌰𐌹𐍅𐌰
Passedanþar𐌰𐌽𐌸𐌰𐍂𐌰𐌽𐌸𐌰𐍂
Passedfidwōr𐍆𐌹𐌳𐍅𐍉𐍂𐍆𐌹𐌳𐍅𐍉𐍂
Passedfidwor𐍆𐌹𐌳𐍅𐍉𐍂𐍆𐌹𐌳𐍅𐍉𐍂
Passedmikils𐌼𐌹𐌺𐌹𐌻𐍃𐌼𐌹𐌺𐌹𐌻𐍃
Passedhēr𐌷𐌴𐍂𐌷𐌴𐍂
Passedher𐌷𐌴𐍂𐌷𐌴𐍂
Passedvac𐍈𐌰𐌸𐍈𐌰𐌸
TextExpectedActual
test_Greek:
Passeda__iᾱͅᾱͅ
Passeda)lhqh/sἀληθήςἀληθής
Passeda)lhqhs*ἀληθησἀληθησ
Passeda)lhqhs-ἀληθησ-ἀληθησ-
Passeda^)nh/rᾰ̓νήρᾰ̓νήρ
PassedPhlhi+a/dhsΠηληϊάδηςΠηληϊάδης
PassedPhlhi^+a^/dhsΠηληῐ̈ᾰ́δηςΠηληῐ̈ᾰ́δης
PassedΠηληϊ^ά^δηςΠηληῐ̈ᾰ́δηςΠηληῐ̈ᾰ́δης
Passede)a_/nἐᾱ́νἐᾱ́ν
Passedἐά_νἐᾱ́νἐᾱ́ν
Passedpa=sa^πᾶσᾰπᾶσᾰ
Passedu_(mei=sῡ̔μεῖςῡ̔μεῖς
Passeda/)^nerᾰ̓́νερᾰ̓́νερ
Passeda/^)nerᾰ̓́νερᾰ̓́νερ
Passeda)/^nerᾰ̓́νερᾰ̓́νερ
Passeda)^/nerᾰ̓́νερᾰ̓́νερ
Passeddai+/frwnδαΐφρωνδαΐφρων
Passeddai/+frwnδαΐφρωνδαΐφρων
TextExpectedActual
test_Hellenic:
Passed*tat^t^o_*taťťō*taťťō
Passed*d^o_'yyon*ďṓyyon*ďṓyyon
Passed*gw@n'n'o_*gʷəňňō*gʷəňňō
Passed*gw@n^n^o_*gʷəňňō*gʷəňňō
Passed*kwhe_r*kʷʰēr*kʷʰēr
Passed*khwe_r*kʷʰēr*kʷʰēr
TextExpectedActual
test_Hittite:
Passeda-ku𒀀𒆪𒀀𒆪
Passedan-tu-wa-ah-ha-as𒀭𒌅𒉿𒄴𒄩𒀸𒀭𒌅𒉿𒄴𒄩𒀸
Passedan-tu-wa-aḫ-ḫa-aš𒀭𒌅𒉿𒄴𒄩𒀸𒀭𒌅𒉿𒄴𒄩𒀸
PassedDINGIRIŠKUR𒀭𒅎𒀭𒅎
TextExpectedActual
test_Imperial_Aramaic:
Passed'nḥn𐡀𐡍𐡇𐡍𐡀𐡍𐡇𐡍
TextExpectedActual
test_Japanese:
Passediro ha nihohetoいろ は にほへといろ は にほへと
Passeduwyi no okuyamaうゐ の おくやまうゐ の おくやま
PassedFAMIRI-MA-TOファミリーマートファミリーマート
Passedaltuあっあっ
Passedhi/mi/tuひ・み・つひ・み・つ
Passedhan'iはんいはんい
Passedhanniはんいはんい
Passedkonnyouこんようこんよう
Passedmannnakaまんなかまんなか
Passedattiikeあっちいけあっちいけ
Passedacchiikeあっちいけあっちいけ
Passedupnusiうpぬしうpぬし
TextExpectedActual
test_Kannada:
PassedyaMtraಯಂತ್ರಯಂತ್ರ
Passedsadāśivaಸದಾಶಿವಸದಾಶಿವ
Passedmuṣṭiಮುಷ್ಟಿಮುಷ್ಟಿ
Passeddhairyaಧೈರ್ಯಧೈರ್ಯ
PassedELuಏಳುಏಳು
PassediMguzETiyAಇಂಗುಶೇಟಿಯಾಇಂಗುಶೇಟಿಯಾ
PassedupayOgaಉಪಯೋಗಉಪಯೋಗ
TextExpectedActual
test_Maithili:
PassedmaithilI𑒧𑒻𑒟𑒱𑒪𑒲𑒧𑒻𑒟𑒱𑒪𑒲
PassedghO_r_A𑒒𑒼𑒛𑓃𑒰𑒒𑒼𑒛𑓃𑒰
Passedga_rh_a𑒑𑒜𑓃𑒑𑒜𑓃
PassedmokAma𑒧𑒽𑒏𑒰𑒧𑒧𑒽𑒏𑒰𑒧
Passedpa~cakhaNDI𑒣𑒿𑒔𑒐𑒝𑓂𑒛𑒲𑒣𑒿𑒔𑒐𑒝𑓂𑒛𑒲
Passedheraba𑒯𑒺𑒩𑒥𑒯𑒺𑒩𑒥
TextExpectedActual
test_Marwari:
PassedmahAjanI𑅬𑅱𑅛𑅧𑅑𑅬𑅱𑅛𑅧𑅑
PassedmukAMm𑅬𑅒𑅕𑅧𑅬𑅬𑅒𑅕𑅧𑅬
PassedAvalA𑅐𑅯𑅮𑅐𑅯𑅮
PassedAgarA𑅐𑅗𑅭𑅐𑅗𑅭
Passedupama𑅒𑅨𑅬𑅒𑅨𑅬
PassediMdaura𑅑𑅧𑅥𑅒𑅭𑅑𑅧𑅥𑅒𑅭
TextExpectedActual
test_Old_Church_Slavonic:
Passedljudijeлюдиѥлюдиѥ
Passedazuhазъазъ
Passedbukyбоукꙑбоукꙑ
Passedmŭčatiмъчатимъчати
PassedIosijaИосиꙗИосиꙗ
TextExpectedActual
test_Old_Marathi:
PassedkuhA𑘎𑘳𑘮𑘰𑘎𑘳𑘮𑘰
Passedkuhā𑘎𑘳𑘮𑘰𑘎𑘳𑘮𑘰
Passednibara𑘡𑘲𑘤𑘨𑘡𑘲𑘤𑘨
PassednIbara𑘡𑘲𑘤𑘨𑘡𑘲𑘤𑘨
Passednībara𑘡𑘲𑘤𑘨𑘡𑘲𑘤𑘨
PassedAi𑘁𑘃𑘁𑘃
Passedāi𑘁𑘃𑘁𑘃
PassedAI𑘁𑘃𑘁𑘃
Passedāī𑘁𑘃𑘁𑘃
Passedsuta𑘭𑘳𑘝𑘭𑘳𑘝
PassedsUta𑘭𑘳𑘝𑘭𑘳𑘝
Passedsuta𑘭𑘳𑘝𑘭𑘳𑘝
Passeduta𑘄𑘝𑘄𑘝
PassedUta𑘄𑘝𑘄𑘝
Passeduta𑘄𑘝𑘄𑘝
Passedna-i𑘡𑘃𑘡𑘃
Passednaï𑘡𑘃𑘡𑘃
Passeda-ila𑘀𑘃𑘩𑘀𑘃𑘩
Passedaïla𑘀𑘃𑘩𑘀𑘃𑘩
Passedbhavai𑘥𑘪𑘺𑘥𑘪𑘺
Passedcauka𑘓𑘼𑘎𑘓𑘼𑘎
Passedca-utha𑘓𑘄𑘞𑘓𑘄𑘞
Passedcaütha𑘓𑘄𑘞𑘓𑘄𑘞
Passeda-ukSa𑘀𑘄𑘎𑘿𑘬𑘀𑘄𑘎𑘿𑘬
Passeda-ukṣa𑘀𑘄𑘎𑘿𑘬𑘀𑘄𑘎𑘿𑘬
PassedaükSa𑘀𑘄𑘎𑘿𑘬𑘀𑘄𑘎𑘿𑘬
Passedaükṣa𑘀𑘄𑘎𑘿𑘬𑘀𑘄𑘎𑘿𑘬
PassedAThoLI𑘁𑘙𑘻𑘯𑘲𑘁𑘙𑘻𑘯𑘲
Passedāṭhoḷī𑘁𑘙𑘻𑘯𑘲𑘁𑘙𑘻𑘯𑘲
PassedraMbhA𑘨𑘽𑘥𑘰𑘨𑘽𑘥𑘰
Passedraṃbhā𑘨𑘽𑘥𑘰𑘨𑘽𑘥𑘰
PassedhRdA𑘮𑘵𑘟𑘰𑘮𑘵𑘟𑘰
Passedhṛdā𑘮𑘵𑘟𑘰𑘮𑘵𑘟𑘰
PassedRkha𑘆𑘏𑘆𑘏
Passedṛkha𑘆𑘏𑘆𑘏
PassedSaDa𑘬𑘚𑘬𑘚
Passedṣaḍa𑘬𑘚𑘬𑘚
PassedkSeNa𑘎𑘿𑘬𑘹𑘜𑘎𑘿𑘬𑘹𑘜
Passedkṣeṇa𑘎𑘿𑘬𑘹𑘜𑘎𑘿𑘬𑘹𑘜
PassedzobhaNe𑘫𑘻𑘥𑘜𑘹𑘫𑘻𑘥𑘜𑘹
Passedśobhaṇe𑘫𑘻𑘥𑘜𑘹𑘫𑘻𑘥𑘜𑘹
Passedarha𑘀𑘨𑘿𑘮𑘀𑘨𑘿𑘮
Passedmar_hATI𑘦𑘨𑘿‍𑘮𑘰𑘘𑘲𑘦𑘨𑘿‍𑘮𑘰𑘘𑘲
Passedmaṟhāṭī𑘦𑘨𑘿‍𑘮𑘰𑘘𑘲𑘦𑘨𑘿‍𑘮𑘰𑘘𑘲
TextExpectedActual
test_Old_Marathi_tr:
PassedkuhAkuhākuhā
Passednibaranibaranibara
PassednIbaranībaranībara
PassedAiāiāi
PassedAIāīāī
Passedsutasutasuta
PassedsUtasutasuta
Passedutautauta
PassedUtautauta
Passedna-ina-ina-i
Passednaïnaïnaï
Passeda-ilaa-ilaa-ila
Passedaïlaaïlaaïla
Passedbhavaibhavaibhavai
Passedcaukacaukacauka
Passedca-uthaca-uthaca-utha
Passedcaüthacaüthacaütha
Passeda-ukSaa-ukṣaa-ukṣa
PassedaükSaaükṣaaükṣa
PassedAThoLIāṭhoḷīāṭhoḷī
PassedraMbhAraṃbhāraṃbhā
PassedhRdAhṛdāhṛdā
PassedRkhaṛkhaṛkha
PassedSaDaṣaḍaṣaḍa
PassedkSeNakṣeṇakṣeṇa
PassedzobhaNeśobhaṇeśobhaṇe
Passedarhaarhaarha
Passedmar_hATImaṟhāṭīmaṟhāṭī
TextExpectedActual
test_Old_Persian:
Passedaitiiy𐎠𐎡𐎫𐎡𐎹𐎠𐎡𐎫𐎡𐎹
Passedraucah𐎼𐎢𐎨𐏃𐎼𐎢𐎨𐏃
Passedham𐏃𐎶𐏃𐎶
Passedjiva𐎪𐎺𐎪𐎺
Passeddaraniyakara𐎭𐎼𐎴𐎹𐎣𐎼𐎭𐎼𐎴𐎹𐎣𐎼
Passeddaragama𐎭𐎼𐎥𐎶𐎭𐎼𐎥𐎶
TextExpectedActual
test_Old_South_Arabian:
Passeds²ms¹𐩦𐩣𐩪𐩦𐩣𐩪
TextExpectedActual
test_Ossetian:
Passedfynʒфындзфындз
Passedæxsævӕхсӕвӕхсӕв
Passedc’æxцъӕхцъӕх
Passedbiræǧбирӕгъбирӕгъ
PassedRæstʒinadРӕстдзинадРӕстдзинад
TextExpectedActual
test_PIE:
Passed*dye_'ws*dyḗws*dyḗws
Passed*n0mr0to's*n̥mr̥tós*n̥mr̥tós
Passed*tk'e'yti*tḱéyti*tḱéyti
Passed*h1es-*h₁es-*h₁es-
Passed*t_ep-e'h1(ye)-ti*tₔp-éh₁(ye)-ti*tₔp-éh₁(ye)-ti
Passed*h1e'k'wos*h₁éḱwos*h₁éḱwos
Passed*bhebho'ydhe*bʰebʰóydʰe*bʰebʰóydʰe
Passed*dh3to's*dh₃tós*dh₃tós
Passed*dhewg'h-*dʰewǵʰ-*dʰewǵʰ-
TextExpectedActual
test_Parthian:
Passedtšynd𐫤𐫢𐫏𐫗𐫅𐫤𐫢𐫏𐫗𐫅
Passedxʾrtʾg𐫟𐫀𐫡𐫤𐫀𐫃𐫟𐫀𐫡𐫤𐫀𐫃
Passedhʾmhyrz𐫍𐫀𐫖𐫍𐫏𐫡𐫉𐫍𐫀𐫖𐫍𐫏𐫡𐫉
Passedʿšnwhr𐫙𐫢𐫗𐫇𐫍𐫡𐫙𐫢𐫗𐫇𐫍𐫡
Passedhʾwsʾr𐫍𐫀𐫇𐫘𐫀𐫡𐫍𐫀𐫇𐫘𐫀𐫡
TextExpectedActual
test_Persian:
PassedbrAdrبرادربرادر
TextExpectedActual
test_Sanskrit:
PassedsaMskRta/संस्कृतसंस्कृत
Passedsaṃskṛtáसंस्कृतसंस्कृत
PassedkSatri/yaक्षत्रियक्षत्रिय
Passedkṣatríyaक्षत्रियक्षत्रिय
PassedrAja suprabuddhaराज सुप्रबुद्धराज सुप्रबुद्ध
Passedrāja suprabuddhaराज सुप्रबुद्धराज सुप्रबुद्ध
PassedzAkyamuniशाक्यमुनिशाक्यमुनि
Passedśākyamuniशाक्यमुनिशाक्यमुनि
PassedsiMhaसिंहसिंह
Passedsiṃhaसिंहसिंह
PassednAmanनामन्नामन्
Passednāmanनामन्नामन्
PassedanA/अनाअना
Passedanā́अनाअना
PassedayuSmAnअयुष्मान्अयुष्मान्
Passedayuṣmānअयुष्मान्अयुष्मान्
Passedghatsyatiघत्स्यतिघत्स्यति
PassedtApa-iतापइतापइ
Passedtāpa-iतापइतापइ
PassedtApaïतापइतापइ
Passedtāpaïतापइतापइ
TextExpectedActual
test_Sanskrit_tr:
PassedsaMskRta/saṃskṛtásaṃskṛtá
PassedkSatri/yakṣatríyakṣatríya
PassedrAja suprabuddharāja suprabuddharāja suprabuddha
PassedzAkyamuniśākyamuniśākyamuni
PassedsiMhasiṃhasiṃha
PassednAmannāmannāman
PassedanA/anā́anā́
PassedayuSmAnayuṣmānayuṣmān
Passedghatsyatighatsyatighatsyati
PassedtApa-itāpa-itāpa-i
PassedtApaïtāpaïtāpaï
TextExpectedActual
test_Saurashtra:
Passedpaniꢦꢥꢶꢦꢥꢶ
PassedvAgꢮꢵꢔ꣄ꢮꢵꢔ꣄
PassedghoDoꢕꣁꢞꣁꢕꣁꢞꣁ
Passeddukkarꢣꢸꢒ꣄ꢒꢬ꣄ꢣꢸꢒ꣄ꢒꢬ꣄
Passedl:ovoꢭꢴꣁꢮꣁꢭꢴꣁꢮꣁ
TextExpectedActual
test_Sindhi:
PassedsiMdhī𑋝𑋡𑋟𑋐𑋢𑋝𑋡𑋟𑋐𑋢
PassedbhAGo𑋖𑋠𑊿𑋧𑋖𑋠𑊿𑋧
PassedmAlu𑋗𑋠𑋚𑋣𑋗𑋠𑋚𑋣
Passedjeko𑋂𑋥𑊺𑋧𑋂𑋥𑊺𑋧
Passedxabara𑊻𑋩𑋔𑋙𑊻𑋩𑋔𑋙
PassedmuqAmu𑋗𑋣𑊺𑋩𑋠𑋗𑋣𑋗𑋣𑊺𑋩𑋠𑋗𑋣
PassedmeM𑋗𑋥𑋟𑋗𑋥𑋟
PassedgunAhu𑊼𑋣𑋑𑋠𑋞𑋣𑊼𑋣𑋑𑋠𑋞𑋣
Passed_gh_araza𑊼𑋩𑋙𑋂𑋩𑊼𑋩𑋙𑋂𑋩
Passed_gh_ufA𑊼𑋩𑋣𑋓𑋩𑋠𑊼𑋩𑋣𑋓𑋩𑋠
PassedbA_gh_u𑋔𑋠𑊼𑋩𑋣𑋔𑋠𑊼𑋩𑋣
Passedba_gh_adAdu𑋔𑊼𑋩𑋏𑋠𑋏𑋣𑋔𑊼𑋩𑋏𑋠𑋏𑋣
PassedghaTaNu𑊾𑋆𑋌𑋣𑊾𑋆𑋌𑋣
TextExpectedActual
test_all:
Passed*dye_'ws*dyḗws*dyḗws
Passed*n0mr0to's*n̥mr̥tós*n̥mr̥tós
Passed*tk'e'yti*tḱéyti*tḱéyti
Passed*h1es-*h₁es-*h₁es-
Passed*t_ep-e'h1(ye)-ti*tₔp-éh₁(ye)-ti*tₔp-éh₁(ye)-ti
Passed*h1e'k'wos*h₁éḱwos*h₁éḱwos
Passed*bhebho'ydhe*bʰebʰóydʰe*bʰebʰóydʰe
Passed*dh3to's*dh₃tós*dh₃tós
Passed*t'a_ko^`*þākǫ̂*þākǫ̂
Passed*T'eudo_balt'az*Þeudōbalþaz*Þeudōbalþaz
Passed*bo_kijo_`*bōkijǭ*bōkijǭ
Passed*tat^t^o_*taťťō*taťťō
Passed*d^o_'yyon*ďṓyyon*ďṓyyon

local export = {}

local m_data = mw.loadData("Module:typing-aids/data")
local m_string_utils = require("Module:string utilities")
local reorderDiacritics = require("Module:grc-utilities").reorderDiacritics
local format_link = require("Module:template link").format_link
local listToSet = require("Module:table").listToSet

--[=[
	Other data modules:
-- [[Module:typing-aids/data/ar]]
-- [[Module:typing-aids/data/fa]]
-- [[Module:typing-aids/data/gmy]]
-- [[Module:typing-aids/data/grc]]
-- [[Module:typing-aids/data/hit]]
-- [[Module:typing-aids/data/hy]]
-- [[Module:typing-aids/data/sa]]
-- [[Module:typing-aids/data/sux]]
-- [[Module:typing-aids/data/got]]
-- [[Module:typing-aids/data/inc-pra]]
--]=]

local U = m_string_utils.char
local gsub = m_string_utils.gsub
local find = m_string_utils.find
local toNFC = mw.ustring.toNFC
local toNFD = mw.ustring.toNFD

local acute = U(0x0301)
local macron = U(0x0304)

local function load_or_nil(module_name)
	local success, module = pcall(mw.loadData, module_name)
	if success then
		return module
	end
end

-- Try to load a list of modules. Return the first successfully loaded module
-- and its name.
local function get_module_and_title(...)
	for i = 1, select("#", ...) do
		local module_name = select(i, ...)
		if module_name then
			local module = load_or_nil(module_name)
			if module then
				return module, module_name
			end
		end
	end
end

local function clone_args(frame)
	local args = frame.getParent and frame:getParent().args or frame
	local newargs = {}
	for k, v in pairs(args) do
		if v ~= "" then
			newargs[k] = v
		end
	end
	return newargs
end
			
local function tag(text, lang)
	if lang and not find(lang, "%-tr$") then
		return '<span lang="' .. lang .. '">' .. text .. '</span>'
	else
		return text
	end
end

local acute_decomposer
-- compose Latin text, then decompose into sequences of letter and combining
-- accent, either partly or completely depending on the language.
local function compose_decompose(text, lang)
	if lang == "sa" or lang == "hy" or lang == "xcl" or lang == "kn" or lang == "inc-ash" or lang == "inc-pra" or lang == "omr" or lang == "mai" or lang == "saz" or lang == "sd" or lang == "mwr" or lang == "inc-pra-Knda" or lang == "inc-pra-Deva" or lang == "doi" or lang == "sa-Modi" or lang == "omr-Deva" then
		acute_decomposer = acute_decomposer or m_data.acute_decomposer
		text = toNFC(text)
		text = gsub(text, ".", acute_decomposer)
	else
		text = toNFD(text)
	end
	return text
end

local function do_one_replacement(text, from, to, before, after)
	-- FIXME! These won't work properly if there are any captures in FROM.
	if before then
		from = "(" .. before .. ")" .. from
		to = "%1" .. to
	end
	if after then
		from = from .. "(" .. after .. ")"
		to = to .. (before and "%2" or "%1")
	end
	text = gsub(text, from, to) -- discard second retval
	return text
end

local function do_key_value_replacement_table(text, tab)
	for from, repl in pairs(tab) do
		local to, before, after
		if type(repl) == "string" then
			to = repl
		else
			to = repl[1]
			before = repl.before
			after = repl.after
		end
		text = do_one_replacement(text, from, to, before, after)
	end
	-- FIXME, why is this being done here after each table?
	text = mw.text.trim(text)

	return text
end


local function do_replacements(text, repls)
	if repls[1] and repls[1][1] then
		-- new-style list
		for _, from_to in ipairs(repls) do
			text = do_one_replacement(text, from_to[1], from_to[2], from_to.before, from_to.after)
		end
		text = mw.text.trim(text)
	elseif repls[1] then
		for _, repl_table in ipairs(repls) do
			text = do_key_value_replacement_table(text, repl_table)
		end
	else
		text = do_key_value_replacement_table(text, repls)
	end

	return text
end


local function get_replacements(lang, script)
	local module_data = m_data.modules[lang]
	local replacements_module
	if not module_data then
		replacements_module = m_data
	else
		local success
		local resolved_name = "Module:typing-aids/data/"
			.. (module_data[1] or module_data[script] or module_data.default)
		replacements_module = load_or_nil(resolved_name)
		if not replacements_module then
			error("Data module " .. resolved_name
				.. " specified in 'modules' table of [[Module:typing-aids/data]] does not exist.")
		end
	end
	
	local replacements
	if not module_data then
		if lang then
			replacements = replacements_module[lang]
		else
			replacements = replacements_module.all
		end
	elseif module_data[2] then
		replacements = replacements_module[module_data[2]]
	else
		replacements = replacements_module
	end
	
	return replacements
end

local function interpret_shortcuts(text, origlang, script, untouchedDiacritics, moduleName)
	if not text or type(text) ~= "string" then
		return nil
	end

	local lang = origlang
	if lang == "xcl" then lang = "hy" end
	local replacements = moduleName and load_or_nil("Module:typing-aids/data/" .. moduleName)
		or get_replacements(lang, script)
		or error("The language code \"" .. tostring(origlang) ..
			"\" does not have a set of replacements in Module:typing-aids/data or its submodules.")
	
	-- Hittite transliteration must operate on composed letters, because it adds
	-- diacritics to Basic Latin letters: s -> š, for instance.
	if lang ~= "hit-tr" then
		text = compose_decompose(text, lang)
	end
	
	if lang == "ae" or lang == "sa" or lang == "got" or lang == "hy" or lang == "xcl" or lang == "kn" or lang == "inc-ash" or lang == "inc-pra" or lang == "pal" or lang == "sog" or lang == "xpr" or lang == "omr" or lang == "mai" or lang == "saz" or lang == "sd" or lang == "mwr" or lang == "inc-pra-Knda" or lang == "inc-pra-Deva" or lang == "doi" or lang == "sa-Modi" or lang == "omr-Deva" then
		local transliterationTable = get_replacements(lang .. "-tr")
			or script and get_replacements(script .. "-tr")
		
		if not transliterationTable then
			error("No transliteration table for " .. lang .. "-tr" .. (script and (" or " .. script .. "-tr") or " and no script has been provided"))
		end
		
		text = do_replacements(text, transliterationTable)
		
		text = compose_decompose(text, lang)
		
		text = do_replacements(text, replacements)
	else
		text = do_replacements(text, replacements)
		
		if lang == "grc" and not untouchedDiacritics then
			text = reorderDiacritics(text)
		end
	end
	
	return text
end

export.interpret_shortcuts = interpret_shortcuts

local function hyphen_separated_replacements(text, lang)
	local module = mw.loadData("Module:typing-aids/data/" .. lang)
	local replacements = module[lang] or module
	if not replacements then
		error("??")
	end
	
	text = text:gsub("<sup>(.-)</sup>%-?", "%1-")
	
	if replacements.pre then
		for k, v in pairs(replacements.pre) do
			text = gsub(text, k, v)
		end
	end
	
	local output = {}
	-- Find groups of characters that aren't hyphens or whitespace.
	for symbol in text:gmatch("([^%-%s]+)") do
		table.insert(output, replacements[symbol] or symbol)
	end
	
	return table.concat(output)
end

local function add_parameter(list, args, key, content)
	if not content then content = args[key] end
	args[key] = nil
	if not content then return false end

	if find(content, "=") or type(key) == "string" then
		table.insert(list, key .. "=" .. content)
	else
		while list.maxarg < key - 1 do
			table.insert(list, "")
			list.maxarg = list.maxarg + 1
		end
		table.insert(list, content)
		list.maxarg = key
	end
	return true
end

local function add_and_convert_parameter(list, args, key, altkey1, altkey2, trkey, lang, scriptKey)
	if altkey1 and args[altkey1] then
		add_and_convert_parameter(list, args, key, nil, nil, nil, lang, scriptKey)
		key = altkey1
	elseif altkey2 and args[altkey2] then
		add_and_convert_parameter(list, args, key, nil, nil, nil, lang, scriptKey)
		key = altkey2
	end
	local content = args[key]
	if trkey and args[trkey] then
		if not content then
			content = args[trkey]
			args[trkey] = nil
		else
			if args[trkey] ~= "-" then
				error("Can't specify manual translit " .. trkey .. "=" ..
					args[trkey] .. " along with parameter " .. key .. "=" .. content)
			end
		end
	end
	if not content then return false end
	local trcontent = nil
	-- If Sanskrit or Prakrit or Kannada and there's an acute accent specified somehow or other
	-- in the source content, preserve the translit, which includes the
	-- accent when the Devanagari doesn't. 
	if lang == "sa" or lang == "kn" or lang == "inc-ash" or lang == "inc-pra" or lang == "omr" or lang == "mai" or lang == "saz" or lang == "sd" or lang == "mwr" or lang == "inc-pra-Knda" or lang == "inc-pra-Deva" or lang == "doi" or lang == "sa-Modi" or lang == "omr-Deva" then
		local proposed_trcontent = interpret_shortcuts(content, lang .. "-tr")
		if find(proposed_trcontent, acute) then
			trcontent = proposed_trcontent
		end
	end
	-- If Gothic and there's a macron specified somehow or other
	-- in the source content that remains after canonicalization, preserve
	-- the translit, which includes the accent when the Gothic doesn't.
	if lang == "got" then
		local proposed_trcontent = interpret_shortcuts(content, "got-tr")
		if find(proposed_trcontent, macron) then
			trcontent = proposed_trcontent
		end
	end
	
	--[[
	if lang == "gmy" then
		local proposed_trcontent = interpret_shortcuts(content, "gmy-tr")
		if find(proposed_trcontent, macron) then
			trcontent = proposed_trcontent
		end
	end
	--]]
	
	local converted_content
	if lang == "hit" or lang == "akk" then
		trcontent = interpret_shortcuts(content, lang .. "-tr")
		converted_content = hyphen_separated_replacements(content, lang)
	elseif lang == "sux" or lang == "gmy" then
		converted_content = hyphen_separated_replacements(content, lang)
	elseif lang == "pal" or lang == "sog" or lang == "xpr" then
		local script = args[scriptKey] or m_data.modules[lang].default
		local script_object = require "Module:scripts".getByCode(script)
		local proposed_trcontent = interpret_shortcuts(content, script .. "-tr")
		local auto_tr = (require "Module:languages".getByCode(lang)
			:transliterate(converted_content, script_object))
		if proposed_trcontent ~= auto_tr then
			trcontent = proposed_trcontent
		end
		converted_content = interpret_shortcuts(content, lang, script, nil, args.module)
	else
		converted_content = interpret_shortcuts(content, lang, args[scriptKey], nil, args.module)
	end
	
	add_parameter(list, args, key, converted_content)
	if trcontent then
		add_parameter(list, args, trkey, trcontent)
	end
	return true
end
	

local is_compound = listToSet{ "affix", "af", "compound", "com", "suffix", "suf", "prefix", "pre", "con", "confix", "surf" }
-- Technically lang, ux, and uxi aren't link templates, but they have many of the same parameters.
local is_link_template = listToSet{
	"m", "m+", "langname-mention", "l", "ll",
	"cog", "noncog", "cognate", "ncog", "nc", "noncognate", "cog+",
	"m-self", "l-self",
	"alter", "alt", "syn",
	"alt sp", "alt form",
	"alternative spelling of", "alternative form of",
	"desc", "desctree", "lang", "usex", "ux", "uxi"
}
local is_two_lang_link_template = listToSet{ "der", "inh", "bor", "slbor",  "lbor", "calque", "cal", "translit", "inh+", "bor+" }
local is_trans_template = listToSet{ "t", "t+", "t-check", "t+check" }

local function print_template(args)
	local parameters = {}
	for key, value in pairs(args) do
		parameters[key] = value
	end
	
	local template = parameters[1]
	
	local result = { }
	local lang = nil
	result.maxarg = 0
	
	add_parameter(result, parameters, 1)
	lang = parameters[2]
	add_parameter(result, parameters, 2)
	if is_link_template[template] then
		add_and_convert_parameter(result, parameters, 3, "alt", 4, "tr", lang, "sc")
		for _, param in ipairs({ 5, "gloss", "t" }) do
			add_parameter(result, parameters, param)
		end
	elseif is_two_lang_link_template[template] then
		lang = parameters[3]
		add_parameter(result, parameters, 3)
		add_and_convert_parameter(result, parameters, 4, "alt", 5, "tr", lang, "sc")
		for _, param in ipairs({ 6, "gloss", "t" }) do
			add_parameter(result, parameters, param)
		end
	elseif is_trans_template[template] then
		add_and_convert_parameter(result, parameters, 3, "alt", nil, "tr", lang, "sc")
		local i = 4
		while true do
			if not parameters[i] then
				break
			end
			add_parameter(result, parameters, i)
		end
	elseif is_compound[template] then
		local i = 1
		while true do
			local sawparam = add_and_convert_parameter(result, parameters, i + 2, "alt" .. i, nil, "tr" .. i, lang, "sc")
			if not sawparam then
				break
			end
			for _, param in ipairs({ "id", "lang", "sc", "t", "pos", "lit" }) do
				add_parameter(result, parameters, param .. i)
			end
			i = i + 1
		end
	else
		error("Unrecognized template name '" .. template .. "'")
	end
	-- Copy any remaining parameters
	for k in pairs(parameters) do
		add_parameter(result, parameters, k)
	end
	return "{{" .. table.concat(result, "|") .. "}}"
end

function export.link(frame)
	local args = frame.args or frame
	
	return print_template(args)
end

function export.replace(frame)
	local args = clone_args(frame)
	local text, lang
	
	if args[4] or args[3] or args.tr then
		return print_template(args)
	else
		if args[2] then
			lang, text = args[1], args[2]
		else
			lang, text = "all", args[1]
		end
	end
	
	if lang == "akk" or lang == "gmy" or lang == "hit" or lang == "sux" then
		return hyphen_separated_replacements(text, lang)
	else
		text = interpret_shortcuts(text, lang, args.sc, args.noreorder, args.module)
	end
	
	return text or ""
end

function export.example(frame)
	local args = clone_args(frame)
	
	local text, lang
	
	if args[2] then
		lang, text = args[1], args[2]
	else
		lang, text = "all", args[1]
	end
	
	local textparam
	if find(text, "=") then
		textparam = "2="..text -- Currently, "=" is only used in the shortcuts for Greek, and Greek is always found in the second parameter, since the first parameter specify the language, "grc".
	else
		textparam = text
	end
	
	local template = {
		[1] = "subst:chars",
		[2] = lang ~= "all" and lang or textparam,
		[3] = lang ~= "all" and textparam or nil,
	}
	
	local output = { format_link(template) }
	table.insert(output, "\n| ")
	table.insert(output, lang ~= "all" and "<span lang=\""..lang.."\">" or "")
	table.insert(output, export.replace({lang, text}))
	table.insert(output, lang ~= "all" and "</span>" or "")
	
	return table.concat(output)
end

function export.examples(frame)
	local args = frame.getParent and frame:getParent().args or frame.args[1] and frame.args or frame
	
	local examples = args[1] and mw.text.split(args[1], ";%s+") or error('No content in the first parameter.')
	local lang = args["lang"]
	
	local output = {
[[
{| class="wikitable"
! shortcut !! result
]]
	}
	
	local row = [[
|-
| templateCode || result
]]
	
	for _, example in pairs(examples) do
		local textparam
		if find(example, "=") then
			textparam = "2=" .. example -- Currently, "=" is only used in the shortcuts for Greek, and Greek is always found in the second parameter, since the first parameter specify the language, "grc".
		else
			textparam = example
		end
		
		local template = {
			[1] = "subst:chars",
			[2] = lang or textparam,
			[3] = lang and textparam,
		}
		
		local result = export.replace{lang, example}
		
		local content = {
			templateCode = format_link(template),
			result = tag(result, lang),
		}
		
		local function addContent(item)
			if content[item] then
				return content[item]
			else
				return 'No content for "' .. item .. '".'
			end
		end
		
		local row = gsub(row, "%a+", addContent)
		
		table.insert(output, row)
	end
	
	return table.concat(output) .. "|}"
end

return export